Chapter 15: JSP and XML

Download Report

Transcript Chapter 15: JSP and XML

JSP and XML Working Together
Presented by: Lin Yang
Mar 5, 2001
1
Outline
 JSP and XML
 A Profiled Newsletter- A Case Study
 The Configuration Component
 The Database Component
 XML Data Access Component
 The Presentation Component
 The Control Component
2
JSP and XML
Presentation
Layer:
Controller
Servlet/JSP
Java
Middleware
XML
Information
Source
XML within
JSP
3
 JSP functions best in the Transformation and Presentation layer as
template pages that can easily incorporate dynamic material from the
Java classes of the application (usually JavaBeans). JSp can also
function as the main entry point and controller of the application, the
role frequently played by a servlet.
 XML is best know as a standard for defining both presentation formats
(language) and formats for data representation and interchange. XML
can also be used for dynamic configuration of J2EE applications. The
tree structure of XML documents is well suited for describing alll
kinds of structured entries, including networks of Java objects that
instantiate and use each other.
 In this case study, associated with each login is an XML configuration
file that instantiates one of a number of applications.Each application
is internally a different set of JavaBeans, instantiated from the XML
configuration file. The JavaBeans query different information sources,
including relational databases and XML files.
4
Case Study Design Features
 The use interacts with the program via JSP pages.
 Each user maintains an XML configuration document that
the user can view and edit.
 Configuration documents are kept as text strings in a
database table.
 The content comes from relational databases and XML
document. In either case, the content is sent to the
application as a stream of SAX events filtered through
XSLT stylesheets.
5
Application Structure
Login
Request
Presentation
response
XML data
access
Control
Information
sources
Database
access
Configuration
To output
6
Presentation Component
 top.jsp is the login page that submits login data to frames.jsp
 frames.jsp is the main user interface page that consists of a control frame
(spControl.jsp) and a data frame (spQuery.jsp)
 errorpage.jsp is for errors
 spLogout.jsp is for logout (invalid session and say goodby
Configuration Component
 spConfigure.jsp is where the user can view and edit his configuration string
 spSaveconfig.jsp saves the editedconfiguration string to a database table and
forwards to frame.jsp
Control Component
 StampPageFactory.java produces a StampPage bean for the session, configured
to the user’s XML configuration string or default XML configuration file
 The stampPage bean interacts with the database and XML information sources
to produce the results for the spQuery.jsp page
7
Database Access Component
 DBConnector interface and its default implementation, DBConnectorImpl
 QueryHandler that extends DBConnectorImpl
 ResultSetParser that converts a result set into a stream of SAX
DocumentHandler calls
XML Data Access Component
 DomSearch parses an XML document into a DOM tree and outputs the entire
tree or a subtree satisfying certain search conditions
 Dom2Sax parses a DOM node into a stream of SAX DocumentHandler calls
8
9
10
11
12
13
14
The Configuration Component
 All configuring is done by the static method Config ( ). This method
receives an XML description of the objects it needs to configure and
operates on that description.
 There are three overloaded versions of Config ( ). The really important
one takes a DOM node as an argument and works with it to configure
an application. The other two receive an XML document, parse it into
a DOM object and look for <top-config> element nodes in it. If found,
they pass the first <top-config> to the version Config ( ) that expects a
DOM node.
 The configuration process consists of Java code operating on an XML
document.
15
//Config.java
public static Object Config(String fileName) throws Exception
{ Document doc = DomSearch.readFile(fileName);
NodeList topConfigL = doc.getElementsByTagName("top-config");
if(null == topConfigL || topConfigL.getLength() < 1)
throw new Exception("no 'top-config nodes' in document " + fileName);
return config(topConfigL.item(0));}
public static Object Config(InputSource in) throws Exception
{Document doc = DomSearch.readInputSource(in);
NodeList topConfigL=doc.getElementsByTagName("top-config");
if(null == topConfigL || topConfigL.getLength() < 1)
throw new Exception("no 'top-config' nodes in in inputsource document");return
config(topConfigL.item(0));}
public static Object config(Node node)throws Exception {
short nodeType = node.getNodeType();
if(nodeType != Node.ELEMENT_NODE) {
return nonNullString(node.getNodeValue()); }
Element elt = (Element)node;
String obClassName = elt.getAttribute("obClass");
if("".equals(obClassName) || "string".equals(obClassName)) {
return nonNullStringValue(elt); }
Object ob;
if("".equals(elt.getAttribute("cons"))) {
ob = newObject(obClassName); }
else {ob = newObject(obClassName,elt.getAttribute("arg")); }
configChildren(ob, elt.getChildNodes());
return ob;}
16
The Database Component
 The database consists of an interface and three classes:
 DBConnector declares the most general functionality of databse
interaction, including Connection manager
 DBConnectorImpl is a do-nothing default implementation of
DBConnector
 QueryHandler extends DBConnectorImpl to provide functionality
actually used in our case study
 ResultSetParser plays the same role in the database component as
Dom2Sax plays in the XML component: it coverts query results
(ResultSet objects) produced by QueryHandler into a sequence of
DocumentHandler calls
17
Interface
implements
DBConnector
Class
DBConnectorImpl
Class
Interface
Class
ResultSetParser
ResultSet
QueryHandler
implements
Interface
Org.xml.sax.Parser
18
DBConnector Interface
import java.sql.*;
public interface DBConnector {
// The first three methods have to do with connection management
Connection getConnection() throws Exception;
void freeConnection() throws SQLException;
void close() throws SQLException;
// The remaining four methods have to do with information
// that is needed to open a connection
void setDriver(String driverName); // The name of the JDBC driver to use
void setURL(String dbURL); // The name of the "database URL"
// The syntax varies from driver to driver
void setUser(String user); // User name for the database
void setPwd(String pwd); // Password
}
19
QueryHandler
QueryHandler contains an inner class that implements the “named query”
abstraction. In QueryHandler, this is a protected inner class called DBQuery.
QueryHandler contains two Hashtables of such DBQuery objects, indexed by
their names: one Hashtable for SELECT queries, the other for UPDATE
queries.
SELECT * FROM FORCAST WHERE zip=12345
This command would proceed as follows:
 Creat a query string with question marks for query parameters, in this case
“SELECT * FROM FORCAST WHERE zip=?”
 Give the query a name, e.g. “weather”
 Add the query string to the QueryHandler either by adding material to the
XML initialization file, or directly by calling the addQuery( ) method of
QueryHandler
 Using the query by running this line of code:
ResultSet rs = spBean.GetQueryHandler ( ). query( “weather”, “12345”);
20
The DBQuery Class
protected class DBQuery {
String name = null, queryStr = null;
PreparedStatement pS = null;
public DBQuery(String name, String qStr) {
this.name = name;
queryStr = qStr;}
// Create PreparedStatement, run the query
public ResultSet query(String[] params) throws Exception {
if (pS == null) { // create PreparedStatement the first time it's run
pS = getConnection().prepareStatement(queryStr);}
for (int i = 0; i < params.length; i++) {
setString(i + 1, params[i]);}
return pS.executeQuery();}
public int update(String[] params) throws Exception {
if (pS == null) {
pS = getConnection().prepareStatement(queryStr);}
for (int i = 0; i < params.length; i++) {
setString(i + 1, params[i]);}
return pS.executeUpdate();}
public void setString(int i, String S) throws SQLException {
if (S == null || S.length() < 256) {
pS.setString(i, S); // default ok here
} else {
// use setObject, with explicit type conversion
pS.setObject(i, S, java.sql.Types.LONGVARCHAR); } }
public void close() {
if (pS == null) {
return; }
try {pS.close();} catch (Exception ex) {}
pS = null;} }
21
XML Data Access Component
The output of a DomSearch is a DOM node that satisfies the search. To make
that node available to the rest of the applications, we pass it through Dom2Sax.
Dom2Sax uses a SAX parser to traverse a DOM Node and output its contents as
a sequence of SAX events. Dom2Sax plays exactly the same role in the XML
data access component that ResultSetParser plays in the data access component.
In both cases, the contents of a Java data structure is serialized as a sequence of
SAX events and sent to a Sax2Writer object. In this case study, the writer in
question is the JSPWriter associated with spQuery.jsp:
XML Date
Source
DOM
DomSearch
DOM
Dom2Sax
SAX
Sax2Writer
Writer
22
Instantiating DomSearch - DSNextMatch
A DomSearch object is created within this line of code in frames.jsp:
StampPage spBean = spBeanF.getStampPage ( );
getStampPage ( ) calls findStampPageByString ( ) or findStampPageByFile ( ).
Either of those calls Config. Config ( ) to create a StampPage object and its
possessions. In the process, Config. Config ( ) uses XML configuration data,
coming either from the XML configuration string stored in the database or
from an XML configuration file.
public class DSNextMatch extends DomSearch {
protected String initFile() throws Exception {
super.initFile();
return nextMatch();}
public String doCommand() { // Assumes values read in from dict
try { if ("reset".equalsIgnoreCase(domCmd)) {
resetNode();return nextMatch();
} else {return super.doCommand();}} catch (Exception ex) {
lg.logIt("DSNextMatch.doCommand: ", ex);
return ("Error: DSNextMatch.doCommand " + ex);} }}
23
Configuration XML data for DomSearch
//spStampPage.xml
<top-config obClass="StampPage">
<!-- configure DSNextMatch, subclass of DomSearch -->
<set fieldName="domSearch" obClass="DSNextMatch">
<set fieldName="dict" obClass="PropDict">
<apply name="setDef"><ob>outerTag</ob><ob>item</ob></apply>
<apply name="setDef"><ob>outerAttr</ob><ob>type</ob></apply>
<apply name="setDef"><ob>outerAVal</ob><ob>stamp</ob></apply>
<apply name="setDef"><ob>innerTag</ob><ob>country</ob></apply>
<apply name="setDef"><ob>innerCVal</ob><ob>Germany</ob></apply>
<apply name="setDef"><ob>domCmd</ob><ob>initFile</ob></apply>
<apply
name="setDef"><ob>fileName</ob><ob>C:\TextSourceCode\Chapter15\data\st
amps.xml</ob> </apply></set> <apply name="initFromDict" /></set>
……
24
The Presentation Component
//Top.jsp:
<%@ page errorPage="errorpage.jsp" %>
<html>
<head><title> The Tele-Philately Follies </title>
</head><body><center>
<h1> The Tele-Philately Follies </h1>
<p><%= new java.util.Date() %>. </p>
</center>
<form method="post" action="frames.jsp" >
User:
<input name="userName" type="text" size="10" /><br />
Password: <input name="userPwd" type="password" size="10" /><br />
<input type="submit" value="go!" /></form>
</body></html>
25
The Main Frames Page and Initialization- frame.jsp
<%@ page %><jsp:useBean id="spBeanF“ class="StampPageFactory" scope="session"/>
<%// Validation section
boolean userNameAndPasswordOkay = true; // Bean could check login data here
if(!userNameAndPasswordOkay) { %><jsp:forward page="badLogin.jsp" />
<%}// End validation section; start initialization section// This code is best revisited after
// Set up PropDict to hold request data, // for transfer to StampPageFactory and StampPage
PropDict pDict = new PropDict(request);
spBeanF.setUserName(pDict.getDef("userName"));
// User name will be used to retrieve user profile
String configFileName = "/data/spStampPage.xml";
configFileName = application.getRealPath(configFileName);
spBeanF.setConfigFile(configFileName);
// Create a StampPage instance and transfer request data to it
StampPage spBean = spBeanF.getStampPage();spBean.setDict(pDict);
Logger lg = new Logger();lg.logIt("check pDict: " + pDict);
// Obtain instance of DomSearch to retrieve XML data// and get it ready for action
DomSearch domSrch = spBean.getDomSearch();domSrch.updateFromDict(pDict);
lg.logIt("domSrch.updateFromDict complete");domSrch.doCommand("initFile");
lg.logIt("domSrch.doCommand complete");%>
<html><head><title> StampPage </title></head>
<frameset cols="20%,80%"><frame name="ctlFrame" src="spControl.jsp">
<frame name="dataFrame" src="spQuery.jsp“></frameset></html>
26
 The initialization process starts by transferring the user name from Request to
StampPageFactory spBeanF via a PropDict. We also set the property of
spBeanF to provide default configuration in case there is no XML configuration
string associated with the user name.
 With userName and configFile properties set, we call getStampPage ( ) to obtain
the StampPage bean for the session. With a SatmpPage in hand, we transfer
Request data to it via a PropDict and obtain its DomSearch object. The
DomSearch object is also initialized from Request data.
 There are three forms. Their action attributes are spQuery.jsp, spConfigure.jsp
and spLogout.jsp.
//spConfigure.jsp
<%@ page errorPage="errorpage.jsp" %>
<jsp:useBean id="spBeanF"
class="StampPageFactory" scope="session"/>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Configuration page for StampPage </title></head>
<body><form target="_parent" method="post"
action="spSaveConfig.jsp" ><input type="submit" value="Save" />
<input name="userName" type="hidden"
value="<%= spBeanF.getUserName() %>" />
<textarea name="configString" rows="20" cols="50">
<%=spBeanF.getConfigString()%></textarea></form></body></html>
27
If you click Save, the submitted string goes to spSaveConfig.jsp that invokes
the methods of SatmpPageFactory to the configuration data in the database for
future sessions and also to use it in configuring the current StampPage:
//spSaveConfig.jsp
<%@ page errorPage="errorpage.jsp" %>
<jsp:useBean id="spBeanF"
class="StampPageFactory" scope="session"/>
<%
Logger lg=new Logger();
String cfg=request.getParameter("configString");
lg.logIt("now setting new configString: " + cfg);
spBeanF.setConfigString(cfg);
// spBeanF.setConfigString(request.getParameter("configString"));
spBeanF.saveUserData();
// for next time
lg.logIt("saveUserDate completed");
spBeanF.findStampPageByString(); // for this time.
lg.logIt("findStampPageByString completed - forwarding back to frames.jsp");
%>
<jsp:forward page="frames.jsp" />
28
spQuery.jsp
 We start with a page directive and a useBean action.
<%@ page errorPage="errorpage.jsp"
contentType="application/xml" %><jsp:useBean id="spBeanF"
class="StampPageFactory" scope="session"/>
<% out.clear(); // don't send the empty lines from directives above%><?xml
version="1.0" ?>
 Next, we output the Processing Instruction for a stylesheet to go with our XML
output:
<?xml-stylesheet href="http://localhost:8080/Chapter15/styles/stamps.css"
type="text/css"?>
 We start running the application. Our first task is to transfer request data to the
StampPage bean:
<% Logger lg=new Logger();
StampPage spBean = spBeanF.getStampPage();
PropDict dict = new PropDict(request);
spBean.getDict().setDef(dict); // Adds new definitions from request
if(zipCode.length() > 0) spBean.setZipCode(zipCode);
spBean.getDomSearch().updateFromDict(dict); // Update DomSearch
String domCmd= dict.getDef("domCmd"); // Get new domCmd for DomSearch
if(domCmd.length() > 0) // Run the command with new settings from request
spBean.getDomSearch().doCommand(domCmd);%>
29
 Now we can produce output, combing template data with dynamic content
produced by bean:
<toptag><field>This is an XML page, demonstrating JSP inclusion of multiple
XML sources and minimal CSS tyling for parts.</field>
<name> Your name is <%= spBeanF.getUserName() %></name>, and
<zipcode> your zip code is <%= spBean.getZipCode() %> </zipcode>
 Next, we obtian a DomSearch, and have it produce output from stamps.xml
styled by our stylesheet stamps.css:
<%DomSearch domSrch = spBean.getDomSearch();
domSrch.doCommand("reset");domSrch.setWriter(out);
domSrch.writeData() ;%>
 The next produces database output from the ch15weather database:
<%
try {
QueryHandler qh = spBean.getQueryHandler();
ResultSetParser rsp = new ResultSetParser();
Sax2Writer s2w = new Sax2Writer(rsp, out); // Send output to JSPWriter out
rsp.setResultSet(qh.query("weather", spBean.getZipCode()));
rsp.parseLoop(); // Don't generate xml headers
} catch(Exception ex) {}%>
30
The Control Component
 The factory bean is first used in frames.jsp to initialize a session:
PropDict pDict = new PropDict(request);
spBeanF.setUserName(pDict.getDef("userName"));
// User name will be used to retrieve user profile
String configFileName = "/data/spStampPage.xml";
configFileName = application.getRealPath(configFileName);
spBeanF.setConfigFile(configFileName);
// Create a StampPage instance and transfer request data to it
StampPage spBean = spBeanF.getStampPage();
31
The code of getStampPage ( ):
public class StampPageFactory {
protected Logger lg;protected StampPage stampPage=null;
protected String configString=null, userName=null, configFile=null;
protected UserDataHandler userDataHandler=null;public StampPageFactory(){
lg=new Logger();} public StampPageFactory(String defaultFile){
this(); setConfigFile(defaultFile);
}
public StampPage getStampPage() {
if (null != stampPage) { // Not the beginning of session
return stampPage;} else if (null != configString) { // configString already set
return findStampPageByString(); } else if (null
!= getUserData()) { // Get configString from database
return findStampPageByString();} else {
return findStampPageByFile(); // No configString; use default
}
}
32
 getUserData ( ) uses the methods of UserDataHandler to retrieve the
configuration string:
public String getUserData() {
if (userName == null) {return null;}
if (null != configString) {return configString;}
if (userDataHandler == null) {
userDataHandler = UserDataHandler.getInstance();}
configString = userDataHandler.getUserData(userName);
return configString;}
 One way or another, from a string or from a file, getStampPage ( ) configures
a StampPage using XML configuration data. This is where the Control
component connects to the Configuration component, and we finally get to see
the XML configuration file for the application.
33
Configuration Methods
public String getUserName(){return userName;}
public StampPage findStampPageByString() {
try {// Create an InputSource from String, give it to Config.config()
InputSource in = new InputSource(new java.io.StringReader(configString));
stampPage = (StampPage) Config.Config(in);} catch (Exception ex) {
lg.logIt("findStampPageByString [\n" + configString + "]", ex);return null;}
return stampPage;}
public StampPage findStampPageByFile() {
try {return stampPage = (StampPage) Config.Config(configFile);
} catch (Exception ex) {
lg.logIt("findStampPageByFile [\n" + configFile + "]", ex);
return null;
}
}
34
 From spStampPage.xml, we configure three fields, a zip code, a DomSearch
and a QueryHandler. (Since DomSearch and QueryHandler are objects that
have fields of their own, they are configured recursively, from a recursive
structure.) in the case of DomSearch, we actually configure a subclass of
DomSearch and DSNextMatch. Whichever way we configure, the StampPage
comes equipped with those three fields.
 In frames.jsp, it has obtained an instance of StampPage:
spBean.setDict(pDict);
Logger lg = new Logger();
lg.logIt("check pDict: " + pDict);
// Obtain instance of DomSearch to retrieve XML data
// and get it ready for action
DomSearch domSrch = spBean.getDomSearch();
domSrch.updateFromDict(pDict);
lg.logIt("domSrch.updateFromDict complete");
domSrch.doCommand("initFile");
35
The StampPage Bean

This is an empty shell of a class. All the work is done by the objects it owns. It has four
protected fields and get/set methods for all of them.
import java.io.*;
import java.util.Properties;
public class StampPage {
protected PropDict dict;
protected DomSearch domSearch;
protected String zipCode;
protected QueryHandler qHandler;Logger lg;
// This class handles a session's data, customized to a user;
// all will be initialized from a single xml file, in nested structure
public StampPage() {lg = new Logger();}
// Set and get methods
public void setDict(PropDict defs) {dict = defs;}
public void setDomSearch(Object ds) {domSearch = (DomSearch) ds;}
public void setZipCode(String zip) {zipCode = zip;}
public void setQueryHandler(Object qh) {qHandler = (QueryHandler) qh;
}
36
public PropDict getDict() {return dict;}
public DomSearch getDomSearch() {return domSearch;}
public String getZipCode() {return zipCode;}
public QueryHandler getQueryHandler() {return qHandler;}
// to provide access to PropDict
public void setDef(String name, String val) {
if (null == dict) { dict = new PropDict();}
dict.setDef(name, val);}
public String getDef(String name) {
if (null == dict) {dict = new PropDict();}
return dict.getDef(name);
}
} // end of StampPage class
37
Chapter15\
Data\
Ch15Weather.mdb
SpStampPage.xml
Stamps.xml
Styles\
Stamp.class
WEB_INF\
Classes\
Config.class
PropDicty.class
DBConnector.class
QueryHandler$DBQuery.class
DBConnectorImpl.class QueryHandler.class
Dom2Sax.class
ResultSetParser.class
DomSearch.class
Sax2Writer.class
DSNextMatch.class
StampPage.class
Logger.class
StampPageFactory.class
UserDataHAndler.class
errorpage.jsp
frames.jsp
spConfigure.jsp
spCopntrol.jsp
spQuery.jsp
spSaveConfig.jsp
top.jsp
38
Questions?
Thank You!
39