Mark Wilson I am the creator of TopXML. I am available for international and local (Australia) contracts. I am a Solution Architect/Business Analyst. I have worked in IT in several countries (NZ, Australia, South Africa, UK) building and training teams for government and very large non-governmental organizations. I am ex-Microsoft Consulting Services. I wrote the first book on Microsoft XML published in 2000 called XML Programming with VB and ASP. Most recently I have been building tools for the SEO industry. Ask me for a 37 point SEO health-checkup for your website.
No part of this chapter may be reproduced, stored in a retrieval system or transmitted in any form or by any means -- electronic, electrostatic, mechanical, photocopying, recording or otherwise -- without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or
reviews.
So far in this book, much has been said on the subject of how
XML can be used in the design of server-side applications and
components within the J2EE context. By performing most of the
processing and transformations on the server side, our web-based
application and services can be used by a very wide audience via
even the most basic HTML browsers. This is possible because the
information that is passed between the client and the server in
these cases is standard HTML, even though the internal data
interchange format may originally have been XML. The server
application logic has transformed the data (perhaps using XSLT) to
HTML by the time the client accesses the data through a servlet or
JSP.
The protocol of choice used between the client and the server is
HTTP. In fact, much of the modern corporate intranet infrastructure
is optimized and configured for delivering HTTP-based traffic. This
architecture reflects a very 'thin' client, basically a browser
supporting simple HTML. The diagram overleaf illustrates a standard
view of a J2EE application.
In practice, however, there are frequently real-life scenarios
where a more proportional sharing of processing between the client
and server is necessary. This means that the client will share in
the data processing tasks, potentially alleviating the heavy load
on the server. In this scenario, the data interchanged between the
client and the server is more than HTML. The interchange also
contains data that the client can perform processing on. In such a
case, the protocol of choice between client and server is still
HTTP (as it is still the most widely supported protocol in
corporate intranet infrastructures).
This means that we must incorporate an extra data delivery
mechanism between the 'medium weight' client and the server. XML,
as it turns out, is perfect for satisfying this niche in an
interoperable manner. The diagram below illustrates the data flow
in these scenarios. We can see the additional transform and
processing function that is performed by the client now, something
that did not occur in the 'thin' client scenario before.
In fact, if we restrict our discussion to a purely Java-based
context, the processing and transformation occurring on the client
side will be carried out by application logic written in the Java
language. As the current processing for automatic XSLT
transformation on the browsers is rather patchy at this moment, the
client-side processing module is implemented using a Java applet in
this context. Appropriately enough, all of the major mainstream
browsers now provide full support for Java applets - with most
of them at the JDK 1.1 level. This is certainly sufficient to
perform highly sophisticated processing on the client side.
Couple this with the fact that most modern-day client machines
are actually quite high-powered (fast CPUs with lots of memory),
there is even more reason to adopt a 'medium weight' client
architecture to offload the processing from the server. The only
caveat may be bandwidth restrictions and the presence of firewalls,
especially when the client connects to the server via slow links
(such as dialup modems). This effectively limits the size of the
applet that may be downloaded.
When we combine the universal data encapsulation/expression
power of XML with the run-anywhere nature of Java, we have a widely
applicable medium weight client architecture that can be deployed
in a very wide variety of design scenarios. The diagram below
illustrates the 'medium weight' client in the Java context.
Especially noteworthy is the ability to have custom application
logic (in the form of a downloaded applet) to process XML-based
data on the client side.
In this chapter, our focus is on these client designs -
designs that involve significant processing and presentation logic
on the client side. Through the analysis of a case study, we will
see:
Why client-side
processing is often a good idea in real-world application problem
solving
The various
technologies that enable client-side XML processing
When it may be
appropriate to apply each of the client-side processing
technologies
What is involved in
enabling client-side XML processing
Along the way, we will also be covering design techniques
applicable for client-side Java XML programming. More specifically,
we will see:
How to get XML data
from the server to the client side using proven technology
How to access and
manipulate the XML data on the client side
How to modify the
appearance of the client-side XML document, with or without
Java
In short, we will see how, as professional Java programmers, we
can add value to a web-based application by moving a portion of the
presentation logic from server to client. A servlet can be used on
the server side to provide this sharing.
In order to be complete, there is one more common architecture,
somewhere between the 'thin' and the 'medium weight' client that we
must take a look at. In this case, XML data and/or mixed HTML/XML
data is sent between the client and the server, but the client
takes advantage of the built-in support on the browser for
presenting the XML data to the user. For example, IE 5 will handle
XSLT stylesheets for transforming XML data into HTML documents. The
diagram below illustrates this scenario:
We will conclude this chapter with an examination of the
built-in XML handling capabilities within the current breed of
mainstream browsers. Obviously, we can also combine these
capabilities with client-side custom applet processing
functionality.
Getting XML Data to the Client Side: Three Techniques
In order for processing to occur on the client, data must be
transferred between the server and the client. We have already
established that XML can be used to our advantage for the exchange
of structured datasets between client and server. The next question
that comes to mind is how the data should be transferred. There are
at least three very common techniques:
One shot:
Deliver the XML data encapsulated in-line with the HTML
document
The limitation of this technique is that the data is essentially
static, and cannot be changed without a complete reload of the
URL - essentially resetting the processing logic. Updating the
data must be done as a reload, and the complete dataset must be
transferred all over again.
Client pull:
Have the client make a connection back to the server
This is considerably more flexible, and the client has great
latitude in optimizing the actual data transferred. However, doing
this ad hoc can be problematic because the client needs to make a
connection back to the server - and there is no telling
whether the connection can in fact be made, due to security
restrictions, routing problems, etc.
In-band client pull:
Have the client request data through a proven HTTP channel
This is commonly known as HTTP tunneling, and is the preferred
way of implementing a client pull. It is sure to work because it
uses the same channel that the original HTML page and applet were
downloaded through to make a request back to the server for data
transfer.
In this chapter, we will be deploying the in-band client pull
technique, creating an HTTP tunnel that we can make arbitrary data
requests through. We will also be working with several examples
where the one-shot technique is used.
Our hypothetical customer is a large chain of travel agencies.
Each of the travel agents in the chain specializes in his/her own
areas. There are agents that specialize in Asia, others that are
specialists in Africa, and still others specialize in trips to
tropical destinations.
While there are hundreds of agencies in the chain, there is only
a single headquarters. Each travel agent has access to a computer
that is tied in to the network at the headquarters. Some computers
are connected via existing packet switched networks, while others
dial in using analog modems to access points provided by the
headquarters.
The entire chain of travel agencies is located in a major
market. In this market, there is a constant flow of last minute
vacancies from tour operators, wholesalers, and distributors that
are being sold off (on almost a daily basis) at a fraction of their
original cost. These trips may be cancellations, seats that are
blocked but unsold, or low season excess. The rationale from the
operators and wholesalers is that "getting something for these
packages/seats is better than getting nothing at all".
These last minute specials are commonly known as "sell-offs".
The headquarters of the travel agency chain receives notification
of these sell-offs from the major distributors and operators daily.
These notifications used to come in the form of faxes, but with the
new e-business system that is installed, they now come into the
headquarters in the format of transmitted XML documents.
The agency chain decides to enter the sell-off market. The trick
of the trade is to notify the agencies as soon as possible upon the
discovery of availability. Since many other independent travel
agencies and chains also receive this sell-off information, and are
essentially in competition for these sales, timeliness and
efficiency are of the essence here.
Our mission is to design a system that will publish information
on these sell-offs in the most economical, timely, and efficient
way possible. The diagram below lays out the data flow in the
system, and where XML may be used for data interchange.
The server in headquarters is mainly a database server,
containing an RDBMS that consolidates all the received sell-off
specials. There is a desire not to load the server with unnecessary
processing since other departments at the headquarters -
including the all-important accounting department, also use the
very same server.
A Quick Analysis
The restrictive server platform forces us to consider scenarios
where processing is off-loaded from the server, and shared by the
client machines. Since most of the agencies have acquired their
desktop computers recently, most of them have machines that have
computing power and memory to spare.
Furthermore, the restrictive budget of the project does not
warrant large-scale server-side development and testing. We must
keep the server piece of the system relatively simple. Yet, our
design must cater for the specialization of the individual travel
agents. One design alternative considered is to classify and
organize the sell-off information, and then map it to the specialty
of each of the travel agencies. Server-side application logic will
then customize the delivery of the sell-off information to each
connection agency. This approach requires significant database
table and query design, as well as server-side programming. It
would also definitely exceed the client's anticipated budget.
A large portion of the travel agents are connecting to the
headquarters via slow analog modems, and some are on a packet
switched network that charges by the data transmitted. Therefore,
there is a definite need to optimize on the quantity of data
transmitted between the client and server.
Our XML Centric Design
From our analysis, we can conclude that the design must
incorporate:
Significant
client-side processing of the information, reducing the need for
server-side development
Intelligent
optimization of the information transmitted between the client and
server; ideally only new sell-off information should be transmitted
during each update
Customization of
presentation according to each travel agency (for example, the
specialized tropical travel agencies do not want to sell trips that
have a commission of less than $300)
We will satisfy every one of the above requirements with our
design. The figure below shows the general approach at a high
level:
Each travel agency will access a specific URL that loads a web
page displaying a customizable applet. The applet is customizable
via <param> in the <applet> tag of the HTML page. This
applet will contact a servlet at the headquarters to download and
display the required information.
In more detail, the figure above shows all the components of the
system - including their class names. The following sections
include the description of the client and server classes and what
they do in the system.
Note that we use an HTTP GET transaction for information
exchange between the client and server. This is necessary in order
for the application to work through the firewalls installed at the
headquarters and at many of the travel agencies.
The Sell-offs XML Document for Data Exchange
The XML document being transferred from the server to the applet
on the client is a list of sell-offs. Here is what an instance of
the document may look like:
<?xml version="1.0" encoding="UTF-8"?>
<selloff>
<trip number="31" region="2">
<startdate>April
15</startdate>
<duration>1 week</duration>
<location>Moon Palace in
Cancun</location>
<price
commission="400">1899</price>
</trip>
<trip number="32" region="1">
<startdate>June
19</startdate>
<duration>2 weeks</duration>
<location>Princeville Hotel in
Kauai</location>
<price
commission="700">2699</price>
</trip>
<trip number="33" region="3">
<startdate>Jan 16</startdate>
<duration>1 week</duration>
<location>Calinda in
Aruba</location>
<price
commission="300">1299</price>
</trip>
<trip number="34" region="2">
<startdate>May 12</startdate>
<duration>1 week</duration>
<location>Westin Regina in Puerto
Vallarta</location>
<price
commission="300">1399</price>
</trip>
</selloff>
We can see here that a selloff element consists of multiple trip
elements. Each trip has a number and region attribute. The number
is monotonically increasing as new sell-offs arrives, and is used
by the client to download only newer trips.
The region is used by the applet for filtering trips. We can
also see that a trip has startdate, duration, location, and price
sub-elements. commission for a trip is an attribute of the price
sub-element.
Client-Side Classes
The main user interface is the VacationApplet class; this applet
will handle user interactions. It allows the user to query the
headquarters for the latest sell-off update (fetching only new trip
information since the last update). It also presents the travel
agency user with specialized filtering capabilities. For example,
it can be configured to show only those trips that offer commission
of a certain value or above, and it can also show trips by
different regions via the user interface.
When the user clicks the Update button, the applet connects to
the headquarters computer and makes a call to the servlet via an
HTTP GET request. The call sends the highest trip number that it
has already received, and the servlet returns a sell-offs XML
dataset document consisting of all the new sell-offs that have
arrived since the last update.
The customized XML parser for the disconnected dataset that is
transmitted from the servlet is called DealsParser. This parser
will parse the data and create a vector of TravelDeal objects that
the VacationApplet can use to populate its user interface
elements.
To parse the XML dataset, it makes use of the Ƭfred lightweight
SAX2-compliant parser that is obtainable from http://www.opentext.com/microstar.
TravelDeal encapsulates a single sell-off. It has a description
field that is 'human friendly' and is displayed by the
VacationApplet in its listbox. It also contains easily accessible
commission and region fields used in the intelligent filtering.
The Server-Side Classes
We start with the main driver for the server side code;
VacationServlet. The servlet does very little other than setting
handling the HTTP GET transaction and calling the methods of an
embedded up an XMLRPC call by registering an instance of a
DealsFinder object.
com.wrox.pjxml.DealsFinder is essentially a relational-to-XML
mapping class. It has one method, locateDealsXML(), that is called
by the servlet. Given a trip number, it will perform a query on the
sell-off database using JDBC to obtain all trips with greater trip
numbers (which therefore arrived later) than those in the previous
update of the client. This data is then placed into a DOM using a
DOMBuild instance. The DOM is written out to XML using a DOMWriter
instance. The output is converted into a String and returned
as the response to the servlet, which in turn send the XML as a
response to the client. There are four more classes concerned with
the data source and the two DOM classes mentioned above. We will
start with the JDBC classes:
com.wrox.pjxml.sqlBean is a JDBC helper class. It manages the
connection to and disconnection from the JDBC source. In a
production environment, we could modify this class to make use of
connection pooling support, etc., supplied by the container. The
other JDBC helper class is com.wrox.pjxml.queryBean. It has a
method that supports query by trip number. It inherits from
sqlBean.
com.wrox.pjxml.DOMBuild uses the DOM support in the Apache
Xerces library. It takes the resultset returned from a JDBC query
and builds an XML DOM tree with it. The final class,
com.wrox.pjxml.DOMWriter, is a simple modification of a sample
class supplied with Apache Xerces. It writes out a DOM tree as XML.
We use this to create the XML string of new trips that is then
shipped back to the client.
The VacationApplet class manages the user interface seen by the
travel agency customers. The screenshot below shows how this user
interface should look. It makes use of standard Java AWT widgets,
enabling it to run on browsers that support the earliest 1.x
JDKs.
The Update button will make the call across to the servlet when
clicked, and download any new sell-offs that may be available. All
sell-offs are displayed in the list box, when the All Deals radio
button is selected. Clicking any one of the other radio buttons
will display a subset of the sell-offs for that region.
A sample web page that contains the applet can be found in the
projavaxml\Chapter15\vacserv directory of the distribution. It is
called index.html:
<html>
<head>
</head>
<body>
<table width="600">
<tr><td>
<center><h1>Wrox Travel
Center</h1></center>
</td></tr>
</table>
<applet
archive="aelfred.jar"
code="VacationApplet.class"
width=600
height=400>
<param
name="mincomm" value="0">
</applet>
</body>
</html>
Note that the Ƭfred XML parser is downloaded via the archive
attribute (for more information on AElfred, see Chapter 2 -
this assumes that Ƭfred is available in the web app). This XML
parser library has a very small footprint (23k), very quick to
download even over analog modems.
The parameter called mincomm can be used to set filtering based
on minimum acceptable commission value for trips (in dollars). Here
it is set to 0. As an example, the applet will only display trips
with commission greater than (or equal to) $300 if we set it to
300.
You can locate the source of VacationApplet.java in the
\vacserc\ directory of the distribution. We will follow it line by
line below.
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import java.net.URL;
import java.io.InputStream;
import java.io.StringWriter;
We derive from Applet (not JApplet), and we implement the
ItemListener for the radio button selection, and the ActionListener
to handle the Update button click event:
public class VacationApplet extends Applet
implements ItemListener,
ActionListener {
static final int LISTWIDTH = 400;
static final int LISTHEIGHT = 200;
static final int FONTSIZE = 16;
private Font myFont;
private FontMetrics myFM;
private int numLines = 0;
private int myLineHeight = 0;
private int curLineIndex = 1;
curTripNumber contains the trip number from the last update; it
starts at 0:
private int curTripNumber = 0;
int minCommision = 0;
listNeedUpdate is used initially to determine if the list has
been initialized. This occurs if the user clicks one of the radio
buttons without first clicking the Update button.
The currentRegionFilter contains the region code for filtering
the listbox, depending on the clicked radio button. curDeals is a
Vector of TravelDeals instances that is used to populate and filter
the listbox. minCommission is obtained from the applet parameter
mincom, and is used to filter the trips in the listbox based on
minimum acceptable commission:
public int currentRegionFilter = 0;
public Vector curDeals = null;
public int minCommission = 0;
public void init() {
super.init();
The remainder of the init() methods code sets up the AWT user
interface, and obtains the
mincomm parameter:
destGroup = new CheckboxGroup();
allClick = new Checkbox("All Deals");
allClick.addItemListener(this);
allClick.setCheckboxGroup(destGroup);
mexicoClick = new Checkbox("Mexico");
mexicoClick.addItemListener(this);
mexicoClick.setCheckboxGroup(destGroup);
caribClick = new Checkbox("Caribbean");
caribClick.addItemListener(this);
caribClick.setCheckboxGroup(destGroup);
hawaiiClick = new Checkbox("Hawaii");
hawaiiClick.addItemListener(this);
hawaiiClick.setCheckboxGroup(destGroup);
destGroup.setSelectedCheckbox(allClick);
listNeedUpdate = true;
tripList = new List();
tripList.setSize(LISTWIDTH, LISTHEIGHT);
myFont = new Font("SansSerif", Font.PLAIN,
FONTSIZE);
tripList.setFont(myFont);
updateButton = new Button("Update");
updateButton.addActionListener(this);
clickPanel = new Panel();
clickPanel.setLayout(new FlowLayout());
clickPanel.add(allClick);
clickPanel.add(hawaiiClick);
clickPanel.add(mexicoClick);
clickPanel.add(caribClick);
clickPanel.add(updateButton);
setLayout(new BorderLayout());
add("Center", tripList);
add("North", clickPanel);
String tpComm = getParameter("mincomm");
minCommission = Integer.parseInt(tpComm,
10);
}
The locateDealsXML() method is a wrapper method for hiding the
details of the HTTP GET transaction. If we assume that the applet
is on the localhost at port 8080, the URL that it creates the GET
transaction is:
where nnn is the latest trip number seen by the client. The
application on Tomcat that will hold the application is called
/vacserv. The simplest way to do this is to create the following
directory structure in the webapps\ directory in Tomcat:
webapps\
vacserv\
WEB-INF\
classes\
The applet will then read the content of the URL into a String,
and return it to the caller. This effectively hides all IO
operations from the caller. We also define the methods mentioned
earlier for the applet:
private String locateDealsXML(int tripNum) {
String resp;
URL myURL;
try {
URL tpURL =
getDocumentBase();
myURL = new
URL(tpURL.getProtocol(),
tpURL.getHost(),
tpURL.getPort(),
"/vacserv/servlet/locatedeals?tnum=" + tripNum);
InputStream myStream =
myURL.openStream();
StringWriter myWriter = new
StringWriter();
int c;
while ((c =
myStream.read()) != -1)
myWriter.write(c);
myWriter.close();
resp =
myWriter.toString();
} catch (Exception e) {
resp = e.toString();
}
return resp;
}
The filterList() method performs filtering on the curDeals
vector. It clears the listbox, and repopulates it with a filtered
list of deals. The filtering is based on the currentRegionFilter
and the minCommission.
//
System.out.println("the region is " + tpDeal.getRegion());
if
(currentRegionFilter == tpDeal.getRegion()) {
if
(tpDeal.getCommission() >= minCommission) {
tripList.add(tpDeal.getDescription());
}
}
}
}
}
updateListData() is the method that actually makes the call to
the server. It passes the result of the call, containing all of the
new trips available, in a disconnected XML dataset, into an
instance of DealsParser. This instance will parse the dataset and
create a vector of TravelDeal instances.
We then add this new vector of TravelDeal instances to the
curDeals vector - thus expanding the list of available
deals:
private void updateListData() {
String dealsXML =
locateDealsXML(curTripNumber);
DealsParser myParser = new DealsParser();
if (curTripNumber == 0) {
curDeals =
myParser.parse(dealsXML);
} else { // curTripNumber !=
0
Vector tpVec =
myParser.parse(dealsXML);
for (int i = 0; i <
tpVec.size(); i++) {
curDeals.addElement(tpVec.elementAt(i));
}
}
curTripNumber =
myParser.getMaxTripNumber();
}
itemStateChanged() is called every time one of the radio
buttons is selected or deselected. On a selection, we change the
value currentRegionFilter and we then refresh the filtered
list:
public void itemStateChanged(ItemEvent evt) {
if (evt.getStateChange() ==
java.awt.event.ItemEvent.SELECTED) {
if (evt.getSource() ==
mexicoClick) {
currentRegionFilter =
TravelDeal.MEXICO;
} else {
if (evt.getSource()
== hawaiiClick) {
currentRegionFilter = TravelDeal.HAWAII;
} else {
if
(evt.getSource() == caribClick) {
currentRegionFilter = TravelDeal.CARIBBEAN;
} else
{
currentRegionFilter = 0;
}
}
}
// System.out.println("Current
filter is now " + currentRegionFilter);
if (listNeedUpdate) {
updateListData();
listNeedUpdate =
false;
}
filterList();
}
}
actionPerformed() is called whenever the Update button is
clicked. We then make a call to the remote server, and reset the
filter list:
public void actionPerformed(ActionEvent evt) {
updateListData();
filterList();
listNeedUpdate = false;
}
}
We will need to create the next class in order to compile the
applet, so we will leave that for the moment. The file should be
saved in the vacserv\ directory.
Parsing and Converting XML Data: The DealsParser Class
The DealsParser class parses an XML document containing a list
of sell-offs and creates a vector of TravelDeal instances. Below is
TravelDeals.java, which you can find it in the vacserv\ directory.
As you can see, it is a straightforward class containing easily
accessible ID, commission, region, and description information.
public class TravelDeal {
public static final int HAWAII = 1;
public static final int MEXICO = 2;
public static final int CARIBBEAN = 3;
private int Region;
private int Commission;
private int ID;
private String Description;
public TravelDeal(int inID, int inRegion, int
inCommission,
String inDesc) {
ID = inID;
Region = inRegion;
Commission = inCommission;
Description = inDesc;
}
public void setRegion(int inRegion) {
Region = inRegion;
}
public int getRegion() {
return Region;
}
public void setCommission(int inComm) {
Commission = inComm;
}
public int getCommission() {
return Commission;
}
public void setID(int inID) {
ID = inID;
}
public int getID() {
return ID;
}
public void setDescription(String inDesc) {
Description = inDesc;
}
public String getDescription() {
return Description;
}
}
DealsParser performs its work using a SAX2-based parser. In
fact, it uses the Ƭfred parser in the aelfred.jar file.
com.microstar.xml.XmlHandler is the default handler implementation.
The com.microstar.xml.XmlParser is the SAX2 parser class
itself.
The source of DealsParser.java can be found in the vacserv\
directory of the distribution.
import com.microstar.xml.XmlParser;
import com.microstar.xml.XmlHandler;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Vector;
import java.net.URL;
public class DealsParser extends com.microstar.xml.HandlerBase
{
Note that we inherit from HandlerBase to avoid implementing all
the methods of the handler (HandlerBase provides default
behavior).
During parsing, we maintain the trip number of the latest trip.
This is used by VacationApplet to determine the delta update that
it needs whenever it contacts the server. The getMaxTripNumber()
method allows the VacationApplet to obtain this number. Only trips
newer than those already sent, as indicated by the maxTripNumber,
will be included in the sell-off transmission.
public int getMaxTripNumber() {
return maxTripNumber;
}
DumpDeals() is a debug method used in testing DealsParser. It
works in conjunction with the main() method and parses a
testfile.xml URL in order to test parsing functionality:
public void DumpDeals() {
TravelDeal curDeal;
for (int i = 0; i < myDeals.size(); i++)
{
curDeal = ((TravelDeal)
myDeals.elementAt(i));
System.out.println("Deals #" + (i
+ 1) + " is "
+ curDeal.getDescription() + "(Comm: $"
+ curDeal.getCommission() + ", Region:"
+ curDeal.getRegion() + ")");
}
}
public static void main(String args[]) {
DealsParser myParser = new DealsParser();
URL myURL = null;
try {
myURL = new
URL("http://localhost:8080/vacdeal/testfile.xml");
} catch (Exception e) {
e.printStackTrace();
}
myParser.parse(myURL);
myParser.DumpDeals();
}
We have two parse() methods. The first one is used by
VacationApplet and takes the XML to be parsed as a string argument.
The second variation parses the XML from a URL. Both of them will
instantiate the Ƭfred SAX2 parser to perform parsing.
public Vector parse(String inXML) {
try {
parser = new XmlParser();
InputStream myInput = new
ByteArrayInputStream(inXML.getBytes());
parser.setHandler(this);
parser.parse(null, null, myInput,
null);
} catch (Exception se) {
se.printStackTrace();
}
return myDeals;
}
public Vector parse(URL myURL) {
try {
parser = new XmlParser();
InputStream myInput =
myURL.openStream();
parser.setHandler(this);
parser.parse(null, null, myInput,
null);
} catch (Exception se) {
se.printStackTrace();
}
return myDeals;
}
Here are the handlers for the parser. The general strategy is to
collect the subelement and attribute information of the
<trip> element, and add an entry to the myDeals vector
whenever we reach an endElement(</trip>) on the <trip>
element.
public void attribute(String name, String val, boolean
inSpec) {
if (name.compareTo("number") == 0) {
maxTripNumber =
Integer.parseInt(val, 10);
curID = maxTripNumber;
} else if (name.compareTo("region") == 0)
{
curRegion = Integer.parseInt(val,
10);
} else if (name.compareTo("commission") == 0)
{
curCommission =
Integer.parseInt(val, 10);
}
}
The charData() method is called whenever character data is
parsed by the XML parser. Here, we accumulate the character data in
the lastParsedText variable.
public void charData(char[] ch, int start, int length)
{
int limit = start + length;
String cdata = "";
for (int i = start; i < limit; i++) {
cdata += ch[i];
}
lastParsedText = cdata;
}
Note that for the handling in endElement() for </trip>,
we:
Format the collected
trip description as workstring
These are all the classes on the client. Before you can compile
them successfully, however, you must make that the aelfred.jar file
is in your classpath. You should copy the JAR file the same
directory as the applet's classes:
This completes our examination of the client-side classes. They
will all be loaded into the browser from the vacserv\ directory.
Let us turn our attention to the server-side classes.
Supplying XML Data: VacationServlet Classes
The first class we examine is the simplest: the VacationServlet
class. You can find the source code in the
projavaxml\vacserv\WEB-INF\classes directory. This servlet class
handles the HTTP GET method and supplies an XML document
(sell-offs) in return.
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletConfig;
import java.io.OutputStream;
import java.io.IOException;
import com.wrox.pjxml.*;
public class VacationServlet extends HttpServlet {
public DealsFinder myFinder;
public void init(ServletConfig conf) throws
ServletException {
myFinder = new DealsFinder();
myFinder.init();
}
Client calls are received through the HTTP GET method. Here, we
extract the tnum parameter from the request, and use it to fetch
all the trip descriptions that needs to be returned. Most of the
work is performed by the locateDealsXML() method of the
com.wrox.pjxml.DealsFinder class.
public void doGet(HttpServletRequest req, HttpServletResponse
res)
The DealsFinder class fields the remote call from the
VacationApplet, and co-ordinates the retrieval of data from the
JDBC data source, and the conversion of this data into XML format
for the return value. The main purpose of this class is to handle
the locateDealsXML() method, typically called remotely.
Other than the VacationServlet class we have just looked at, the
rest of the server-side classes are in a package called
com.wrox.pjxml. This packaging facilitates the reuse of these
classes, for the JSP example that we will be looking at later.
package com.wrox.pjxml;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
public class DealsFinder {
queryBean myQuery;
public DealsFinder() {}
The init() method makes the required JDBC connection. This is
typically called from the init() method of a servlet or JSP.
public void init() {
try {
myQuery = new queryBean();
myQuery.makeConnection();
} catch (Exception e) {}
}
The locateDealsXML() method performs all the necessary
co-ordination needed, by making the query via the getDeals() method
of queryBean. It also creates an instance of DOMBuild, and uses the
AddATrip() method to add more nodes to the DOM tree.
public String locateDealsXML(int tripno) {
DOMBuild myDOM;
ByteArrayOutputStream myOut = new
ByteArrayOutputStream();
String retVal = "";
try {
myDOM = new DOMBuild();
myQuery.getDeals(""+tripno);
myDOM.CreateRoot();
while (myQuery.getNextTrip()) {
myDOM.AddATrip(myQuery.getColumn("tripno"),
myQuery.getColumn("region"),
myQuery.getColumn("startdate"),
myQuery.getColumn("duration"),
myQuery.getColumn("location"),
myQuery.getColumn("price"),
myQuery.getColumn("commission"));
}
Finally, it asks DOMBuild to write the tree out as an XML
document in a string:
myDOM.writeDOMTree(new
PrintWriter(myOut));
retVal = myOut.toString();
} catch (Exception ex) {
retVal =
ex.toString();
}
return retVal;
}
}
RDBMS Query Result to XML: DOMBuild and DOMWriter Classes
The DOMBuild and DOMWriter classes make extensive use of the
Apache Xerces library. Here is the source code to DOMBuild.java.
This class builds the DOM tree consisting of a root
<sell-off> element, with a collection of <trip>
subelements.
package com.wrox.pjxml;
import java.io.IOException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.apache.xerces.dom.DocumentImpl;
import org.apache.xerces.parsers.DOMParser;
import java.io.PrintWriter;
public class DOMBuild {
private DocumentImpl myDoc;
private Element docElement;
public DOMBuild() {}
public void CreateRoot() {
myDoc = new DocumentImpl();
docElement =
myDoc.createElement("sell-off");
myDoc.appendChild(docElement);
}
AddATrip() is the main method in this class, it is called
by the DealsFinder class to add <trip> nodes under the
<sell-off> root element.
The writeDOMTree() method uses the DOMWriter class to create an
XML document in a String representing the tree.
public void writeDOMTree(PrintWriter out) {
try {
DOMWriter.print(out, myDoc);
} catch (Exception e) { e.printStackTrace();
}
}
}
The DOMWriter class is actually a modification of the
DOMWriter.java sample program from the Xerces distribution. The
original source can be found in the samples/dom directory of the
Xerces distribution. Here, we will detail the simple modifications
that had been made to the DomWriter.java file. You can find the
modified file in the
code/service/webapps/xmlrpc/classes/com/wrox/pjxml directory.
At the very top of the file, we added:
package com.wrox.pjxml;
We commented out the first two statements in the file to remove
further dependencies on packages within the DOMWriter sample that
we will not need:
//package dom;
//import util.Arguments;
Next, we comment out the print() method that we will not
use:
/** Prints the resulting document tree.
public static void print(String parserWrapperName, String
uri,
boolean canonical ) {
...
} // print(String,String,boolean)
*/
We added two methods to the class to enable us to use the class
on a PrintWriter:
public void setWriter(PrintWriter outlet) {
out = outlet;
}
public static void print(PrintWriter outlet, Node node)
{
DOMWriter writer;
try {
writer = new
DOMWriter(false);
writer.setWriter(outlet);
writer.print(node);
} catch ( Exception e ) {
//e.printStackTrace(System.err);
}
}
Finally, we remove the main() method from the file. This
eliminates the reference to external argument processing
classes.
Instead of modifying the source code yourself, you can use the
modified file provided in the
source code distribution.
The final source files we will look at are the
com.wrox.pjxml.sqlBean class and the com.wrox.pjxml.queryBean
class. These are simple JDBC access beans -sqlBean handles the
connection and disconnection to the JDBC datasource. We're using a
simple JDBC to ODBC bridge for our example. We have named the
database "travel"; it contains the following data:
tripno
region
startdate
duration
location
price
commission
31
2
April 15
1 week
Moon Palace in Cancun
1899
400
32
1
June 19
2 weeks
Princeville Hotel in Kauai
2699
700
33
3
Jan 16
1 week
Calinda in Aruba
1299
300
34
2
May 12
1 week
Westin Regina in Puerto Vallarta
1399
300
35
1
Sept 12
1 week
Marriott Kauai
2200
600
36
3
Feb 12
1 week
Pelican Resort in St. Martin
1700
400
We simply enter this into a table named "hotdeals" and add the
database as an ODBC source. The file is available in the code
download as traveldeals.mdb. To add a DSN entry in Windows, select
the Start | Settings | Control Panel and choose the Data Sources
(ODBC) entry, which may be in the Administrative Tools menu. Choose
the System DSN tab and select the Add... button. Choose Microsoft
Access Driver (.mdb) as the driver and select Finish.
You will be presented with the following:
enter "travel" for the Data Source Name and a short description
of the database. Now select the database from the download code (as
shown) and press OK through to the finish. This will allow us to
refer to the database using the URI jdbc:odbc:travel.
queryBean supports the single query method, getDeals().
getNextTrip() is used to iterate through the JDBC Resultset that is
returned from this query.
package com.wrox.pjxml;
public class queryBean extends sqlBean {
String myTripQuery = "select * from hotdeals where tripno
> ";
ResultSet myResultSet = null;
public queryBean() {super();}
public boolean getDeals(String tripNo) throws Exception
{
String myQuery = myTripQuery + tripNo;
Statement stmt =
myConn.createStatement();
myResultSet = stmt.executeQuery(myQuery);
return (myResultSet != null);
}
public boolean getNextTrip() throws Exception {
return myResultSet.next();
}
public String getColumn( String inCol) throws Exception
{
return myResultSet.getString(inCol);
}
}
Compiling the Servlet
In order to compile the VacationServlet, you must make sure you
have xerces.jar; it should go without saying that you will need the
servlet API jar file in your classpath. First, open a Command
Prompt window and change your working directory to
the\vacserv\web-inf\classes\ directory. You should able to compile
the servlet using the following line:
On my system <path_to> equates to c:\Xerces\Xerces.jar
Setting Up Tomcat 3.2.1
Place the files generated in the webapps\ directory for Tomcat.
The complete web application looks like this:
webapps\
vacserv\
aelfred.jar
DealsParser.class
index.html
TravelDeal.class
VacationApplet.class
WEB-INF\
web.xml
classes\
VacationServlet.class
com\
wrox\
pjxml\
DealsFinder.class
DOMBuild.class
DOMWriter.class
queryBean.class
sqlBean.class
That means both the client and server are being served from the
same web application in Tomcat. There is one file that is left for
us to write, the web.xml descriptor file that will contain the
servlet mapping:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web
Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<servlet>
<servlet-name>
locatedeals
</servlet-name>
<servlet-class>
VacationServlet
</servlet-class>
</servlet>
</web-app>
We are assuming that you have modified tomcat.bat to include
xerces.jar as the first entry in Tomcat's classpath.
Testing the End-to-End System
For the system test, start Tomcat from its bin directory:
tomcat start
Next, make sure the server portion is working OK. Start an
instance of Internet Explorer, and enter the following URL:
You should see the <selloff> XML document with all the
trips listed, as shown in the following screenshot; this is what
the applet will be working with.
Next, start another instance of Internet Explorer, and enter the
following URL:
http://localhost:8080/vacserv/
If everything is configured correctly, you should see the applet
running as shown below.
Now, here is the acid test - click the Update button!
This should set the following sequence of events into
motion:
VacationApplet
starts an HTTP GET transaction to contact VacationServlet
VacationServlet goes
through the JDBC to ODBC bridge and accesses the RDBMS sell-off
information
VacationServlet
builds a DOM tree of the RDBMS information, and creates an XML
dataset, which it returns to the VacationApplet for client-side
processing
VacationApplet
receives the disconnected XML dataset and parses it using a SAX2
parser
VacationApplet
builds an internal Vector of TravelDeal objects and populates the
AWT listbox with the list of objects
Whew! No wonder it takes a little while to start up in this
test. You should see a display similar to the one below if all goes
well.
Now, you can try client-side filtering by clicking any of the
region filters. Try clicking the Hawaii or Mexico filters and you
will see that the displayed list is filtered.
You may also want to modify index.html to filter on a different
minimum commission level. For example, the following index.html
will cause the system to only show trips with commission of $300 or
more:
You may want to repeat this test with the Netscape 4.7 and
Netscape 6 browsers to convince yourself that this is a
cross-browser compatible solution.
Finally, we need to test the optimized incremental update
mechanism that is built into the system. For this, we will need a
testing tool.
Compiling and Using the XML-to-RDBMS Data Injection Test
Tool
The testing tool we look at will allow us to perform three
simple RDBMS operations:
Read an XML document
called data1.xml containing sell-off information and add the trips
to the RDBMS
Read an XML document
called data2.xml containing sell-off information and add the trips
to the RDMBS
Clear the RDBMS of
all records
The tool provides a GUI consisting of three buttons. Clicking
each button will perform the associated task. The finished GUI
should resemble the one below:
The testing tool can be found in the projavaxml\Chapter15\tools
distribution directory. It consists of the following source
files:
TestTool.java
HandlerBase.java
databaseBean.java
sqlBean.java
We will not examine sqlBean here, since it is the file we
created earlier. The program again makes use of the Ƭfred SAX2
parser available within the aelfred.jar file.
Here is the databaseBean.java, it has two methods; the first
takes sufficient parameters about a sell-off to register it on the
database. The second cleans out the whole database. Here is the
code for it:
import java.sql.Statement;
public class databaseBean extends com.wrox.pjxml.sqlBean {
String myAppend1 = "insert into hotdeals (startdate,
region," +
Here is ParseHandler.java. The ParseHandler class handles the
parsing of the incoming XML data file. Note the inheritance from
the default HandlerBase class.
class ParseHandler extends com.microstar.xml.HandlerBase {
String lastParsedText = null;
String lastParsedStart = null;
String lastParsedLoc = null;
String lastParsedDur = null;
String lastParsedPri = null;
String lastParsedCom = null;
String lastParsedRegion = null;
Here, we make a connection to the RDBMS:
databaseBean myQuery = new databaseBean();
public void startDocument () {
try {
myQuery.makeConnection();
} catch (Exception e) { e.printStackTrace(); }
}
The general parsing strategy here is to collect all the fields
and attributes of a trip record, and then at the endElement() of
the <trip> element, we add a record to the RDBMS representing
the trip.
public void endElement (String name) {
if (name.compareTo("startdate")==0)
{
lastParsedStart =
lastParsedText;
}
if (name.compareTo("duration")==0) {
lastParsedDur =
lastParsedText;
}
if (name.compareTo("location")==0) {
lastParsedLoc =
lastParsedText;
}
if (name.compareTo("price")==0) {
lastParsedPri =
lastParsedText;
}
if (name.compareTo("commission")==0) {
lastParsedCom =
lastParsedText;
}
if (name.compareTo("region")==0) {
lastParsedRegion =
lastParsedText;
}
if (name.compareTo("trip")==0) {
try {
myQuery.addTrip(lastParsedStart,
lastParsedRegion,
lastParsedDur,
lastParsedLoc,
lastParsedPri,
lastParsedCom);
} catch (Exception e) {
e.printStackTrace(); }
}
}
endDocument() is an opportune time for taking down the RDBMS
connection:
public void endDocument() {
try {
myQuery.takeDown();
} catch (Exception e) { e.printStackTrace();
}
}
}
The actual TestTool class sets up the GUI, and handles the
button selection. It will use an instance of the ParseHandler to do
the parsing and data injection work. TestTool works with two XML
files containing sell-off information.You can press one button to
inject the first set, and another to inject the second set. The
first XML file is data1.xml:
<?xml version="1.0"?>
<selloff>
<trip>
<startdate>April
15</startdate>
<region>2</region>
<duration>1
week</duration>
<location>Moon Palace in
Cancun</location>
<price>1899</price>
<commission>400</commission>
</trip>
<trip>
<startdate>June
19</startdate>
<region>1</region>
<duration>2
weeks</duration>
<location>Princeville Hotel
in Kauai</location>
<price>2699</price>
<commission>700</commission>
</trip>
<trip>
<startdate>Jan
16</startdate>
<region>3</region>
<duration>1
week</duration>
<location>Calinda in
Aruba</location>
<price>1299</price>
<commission>300</commission>
</trip>
<trip>
<startdate>May
12</startdate>
<region>2</region>
<duration>1
week</duration>
<location>Westin Regina in
Puerto Vallarta</location>
<price>1399</price>
<commission>300</commission>
</trip>
<trip>
<startdate>Sept
12</startdate>
<region>1</region>
<duration>1
week</duration>
<location>Marriott
Kauai</location>
<price>2200</price>
<commission>600</commission>
</trip>
<trip>
<startdate>Feb
12</startdate>
<region>3</region>
<duration>1
week</duration>
<location>Pelican Resort in
St. Martin</location>
<price>1700</price>
<commission>400</commission>
</trip>
</selloff>
And data2.xml:
<?xml version="1.0"?>
<selloff>
<trip>
<startdate>Sept
3</startdate>
<region>2</region>
<duration>1
week</duration>
<location>Hyatt Caribe in
Cancun</location>
<price>2399</price>
<commission>800</commission>
</trip>
<trip>
<startdate>Jan
3</startdate>
<region>1</region>
<duration>2
weeks</duration>
<location>Pono Kai in
Kapaa</location>
<price>3099</price>
<commission>1000</commission>
</trip>
<trip>
<startdate>Oct
1</startdate>
<region>3</region>
<duration>1
week</duration>
<location>Amsha Paradise in
D.R.</location>
<price>1299</price>
<commission>300</commission>
</trip>
<trip>
<startdate>July
4</startdate>
<region>2</region>
<duration>1
week</duration>
<location>Krystal in Puerto
Vallarta</location>
<price>2499</price>
<commission>800</commission>
</trip>
<trip>
<startdate>August
12</startdate>
<region>1</region>
<duration>2
weeks</duration>
<location>Hilton Waikoloa
in Hawaii</location>
<price>4999</price>
<commission>1200</commission>
</trip>
<trip>
<startdate>Mar
3</startdate>
<region>3</region>
<duration>1
week</duration>
<location>Atlantis in
Nassau</location>
<price>2700</price>
<commission>800</commission>
</trip>
</selloff>
Here is the actual implementation of the TestTool class:
import java.awt.*;
import java.awt.event.*;
import java.io.InputStream;
import java.io.FileInputStream;
import com.microstar.xml.XmlParser;
import com.microstar.xml.XmlHandler;
public class TestTool extends Frame implements ActionListener
{
private Panel basePanel;
private Button purgeButton;
private Button inject1Button;
private Button inject2Button;
private XmlParser parser;
private ParseHandler myHandler;
public TestTool() {
super("Travel Deals Test Tool");
purgeButton = new Button("Purge Data");
inject1Button = new Button("Inject 1st
set");
inject2Button = new Button("Inject 2nd
set");
purgeButton.addActionListener(this);
inject1Button.addActionListener(this);
inject2Button.addActionListener(this);
This WindowAdapter() enables the Close button on the window
frame to work.
addWindowListener( new WindowAdapter() {
public void
windowClosing(WindowEvent ev) { System.exit(0); }
InputStream myInput = new
FileInputStream(filename);
myHandler = new
ParseHandler();
parser.setHandler(myHandler);
parser.parse(null, null,myInput,
null);
}
actionPerformed() is where the button clicks are handled.
Note the direct access to queryBean's methods to purge the RDBMS of
all records.
public void actionPerformed(ActionEvent evt) {
try {
if (evt.getSource() == purgeButton) {
databaseBean myUtil = new
databaseBean();
myUtil.makeConnection();
myUtil.clean();
myUtil.takeDown();
}
if (evt.getSource() == inject1Button) {
parseAndAddRecords("data1.xml");
}
if (evt.getSource() == inject2Button) {
parseAndAddRecords("data2.xml");
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main (String args[])
throws Exception {
TestTool myTT = new TestTool();
myTT.pack();
myTT.show();
}
}
While simple, this test tool illustrates the general framework
that any XML-to-RDBMS conversion program needs to have. It can
serve as a starting point for coding modules that involve such
functionality. We have placed all the relevant files in a folder
named projavaxml\Chapter15\tool\
To properly compile the tool, you need to make sure that you
have the aelfred.jar file in your classpath, together with sqlBean.
You can compile the files from the folder they are in using the
following commands:
set classpath=<path_to>aelfred.jar;%classpath%
set
classpath=<path_to>\vacserv\WEB-INF\classes;%classpath%
set classpath=.;%classpath%
javac TestTool.java
The first three lines add aelfred.jar, the com.wrox.pjxml
package (substitute the relevant paths to Ƭfred and the
com.wrx.pjxml package), and the current directory. The final
command should then compile the files. Here is the result on my
system:
Once the compilation is successful, close any browser with the
applet displayed - this will ensure the applet's max trip
number is consistent during our test. You can start the TestTool
using the same command-line window above:
java TestTooljava
Using this tool, we can simulate the arrival of new sell-off
information from tour operators and travel wholesalers for our
system. Follow this sequence:
Clicking the Purge
Data button to clean the database
Starting up our
end-to-end system, and clicking Update - you will see that no trips
are displayed
Clicking the Inject
1st set button to inject the first set of data from the data1.xml
file into the database
Clicking on the
Update button on the end-to-end system, to display the set of
data
Clicking the Inject
2nd set button to inject the second set of data from the data2.xml
file into the database
Clicking on the
Update button on the end-to-end system to display the new set of
data
To confirm that indeed only the newly arrived sell-off list is
transmitted between the client and the server, you may want to set
up the HTTP Tunneling Monitor program (detailed in Chapter 19).
This utility will give you an X-ray view of the XML-based traffic
that actually flows between the client and the server.
This concludes our examination of client-side XML filtering and
processing enabled by a fully-fledged Java applet and SAX2 parser.
We will now check out some of the built-in XML-handling features of
the modern day browsers - and see how they may be used for
client-side XML presentation.
For most practical intents and purposes, we are talking about
either the Netscape Communicator/Navigator or Microsoft's Internet
Explorer when we talk about client-side browsing. Surprisingly
enough, native support for XML formatting is a relatively recent
feature enhancement for both lines of browsers.
Internet Explorer 4.x and 5.x XML Presentation Support
Microsoft has supported the formatting and viewing of XML
documents since version 4.x of its browsers. More recently, full
XSLT is supported by MSXML. msxml.dll is a C++ implementation of
XML and XSLT parsing library that IE 5 depends on; it can also be
used by third party developer to perform the same tasks.
At the time of writing, the latest version of MSXML is 3.0. If
you simply load an XML document into an Internet Explorer version
that supports XML formatting, you will see a collapsible tree view
of the parsed document:
While this can act as a mechanism to check for document
well-formedness, it is not too useful for presenting the
information contained in the XML to users.
The two most popular ways of formatting XML data for display
(using Internet Explorer) are as follows:
XSLT formatting and
transformations
XML data islands
embedded within the HTML page
XSLT Formatting of XML Data
XSLT formatting of XML data allows us to associate an XSLT
stylesheet with a raw XML data file. We have created a JSP that
will illustrate how this may be used to format our sell-off
information. You can find the source code to this JSP under the
\vacserv\jsp directory. It is called IE5xslt.jsp:
This JSP uses the DealsFinder class from the VacationServlet to
display all the available sell-offs in XML format. Two things to
note, that are very important when using JSP to generate XML output
for direct consumption by browsers, are:
1. Make sure you use the contentType
attribute of the page directive to specify the MIME type
text/xml
2. Make sure that
<?xmlversion="1.0"?> is the very first line in the file with
no leading blank space whatsoever.
Failing to ensure that either one of the above is taken care of
will render your work very difficult to debug, especially when
working with multiple browser versions.
<TR><TD><H2>Client-side XSLT Formatting - JSP
Generated XML Data</H2></TD></TR>
</TABLE>
<TABLE
BORDER="1">
<TR>
<TD><b>Trip Number</b></TD>
<TD><b>Region</b></TD>
<TD><b>Location</b></TD>
<TD><b>Start</b></TD>
<TD><b>Duration</b></TD>
<TD><b>Price</b></TD>
</TR>
<xsl:for-each select="sell-off/trip">
<TR>
<TD><xsl:value-of select="@number"/></TD>
<TD><xsl:value-of select="@region"/></TD>
<TD><xsl:value-of select="location"/></TD>
<TD><xsl:value-of select="startdate"/></TD>
<TD><xsl:value-of select="duration"/></TD>
<TD><xsl:value-of select="price"/></TD>
</TR>
</xsl:for-each>
</TABLE>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>
This basically formats the XML data in a simple tabular format.
We can try it out and see the result with the URL:
http://localhost:8080/vacserv/jsp/IE5xslt.jsp
You should see a result similar to the one below:
If you try the example with any version of the Netscape browser,
you will notice that none of the XML data will be formatted
properly. In fact, if we simply want to display of the XML data on
IE 5 browsers, we do not even need to write an XSLT stylesheet.
There is built-in support for the formatting of an XML data
island.
XML Data Island Support
What Microsoft calls "XML data islands" are actually XML
document(s) embedded within the flow of HTML. There is a special
<XML> tag in Internet Explorer's HTML dialect to support
this. We have created a JSP page to demonstrate this capability.
Here is IE5island.jsp from the projavaxml\vacserv\jsp
directory:
<td><H3>Trips tabulated via XML data
island</H3></td>
</tr>
</table>
<table datasrc="#vacDeals" border="1">
<tr>
<td><div
datafld="location"></div></td>
<td><div
datafld="startdate"></div></td>
<td><div
datafld="duration"></div></td>
</tr>
</table>
<P>
<table width="600">
<tr>
<td><H3>Script code reading the XML data
island</H3></td>
</tr>
</table>
<script>
document.write("The first special has a start date of " +
vacDeals.XMLDocument
.documentElement
.childNodes.item(0)
.childNodes.item(0).text +
" and has duration of " +
vacDeals.XMLDocument
.documentElement
.childNodes.item(0)
.childNodes.item(1).text +
"<P>" );
document.write("The second special has destination of " +
vacDeals.XMLDocument.documentElement.childNodes.item(1).childNodes.item(2).text);
</script>
</BODY>
</HTML>
Note that this actually generates an HTML file, and not an XML
data set as the previous IE5xslt.jsp did. If you access the
URL:
http://localhost:8080/vacserv/jsp/IE5island.jsp
you can see how Internet Explorer supports XML data islands, as
shown below:
By embedding XML in this fashion, within the <XML> tag, we
can use the data island as a data source (via the datasrc
attribute) for any data source-aware element. It so happens that
<table> is such an element in IE 5. Therefore, the HTML code
segment below will display the XML data island in tabular form:
<table datasrc="#vacDeals" border="1">
<tr>
<td><div
datafld="location"></div></td>
<td><div
datafld="startdate"></div></td>
<td><div
datafld="duration"></div></td>
</tr>
</table>
Another potentially very powerful feature, especially for
JavaScript programmers, is that the elements in the XML data island
are actually accessible from the Dynamic HTML DOM (Document Object
Model). More specifically, JavaScript (or VBScript) code within the
HTML page can actually read the elements in the XML document as an
extension of the document object model. This enables the script
code to read, format, or interpret the XML data. One may use
Dynamic HTML to change the presentation (using scripting code) or
the user interface, based on the data.
We have a simple script segment within the IE5island.jsp file to
demonstrate reading the XML data using script:
<script>
document.write("The first special has a start date of " +
vacDeals.XMLDocument
.documentElement
.childNodes.item(0)
.childNodes.item(0)
.text + " and has duration of " +
vacDeals.XMLDocument
.documentElement
.childNodes.item(0)
.childNodes.item(1).text + "<P>" );
document.write("The second special has destination of " +
vacDeals.XMLDocument
.documentElement
.childNodes.item(1)
.childNodes.item(2).text);
</script>
This segment reads the element values from the first trip's
startdate, and the second trip's destination.
In-depth coverage of Javascript, IE Dynamic HTML DOM, or IE
Dynamic HTML programming is beyond the scope of this section.
Interested readers are encouraged to consult Professional
Javascript (ISBN 1-861002-70-X) and XML IE5 Programmer's Reference
(ISBN 1-861001-57-6)from Wrox for more information.
Internet Explorer browsers have had various levels of support
for XML since the 4.x version. Netscape, on the other hand,
provided no native support for formatting of XML-based data until
version 6.x. The rendering engine was replaced by a
high-performance open source "Gecko engine" technology and support
for XML data is now native.
The formatting support at version 6.0 of the browser (the most
recent available at the time of writing) is confined to Cascading
Style Sheets Level 1, and additionally supporting some Cascading
Style Sheets Level 2 features. Support for CSS1 in the formatting
of HTML documents has long been standard in both the Microsoft and
Netscape browsers. However, support for specific CSS2 features such
as tables, and formatting of XML documents, is new for the Netscape
6.0 browser.
Here is a JSP file that utilizes the possibilities offered,
called NScss2.jsp:
<topic>CSS2 Formatting via JSP Generated XML
Data</topic>
<%!
public DealsFinder myFinder;
public void jspInit() {
myFinder = new
DealsFinder();
myFinder.init();
}
%>
<%
String myStr = myFinder.locateDealsXML(0);
int chop = myStr.indexOf(">");
myStr = myStr.substring(chop + 1); %>
<%= myStr %>
</triplist>
Again, we are generating an XML document instead of HTML here.
Note the way we have included the <triplist> outermost
element, and added the heading as an additional element. This
highlights one of the major differences between XSLT-styled
formatting and transformation as opposed to CSS-based formatting.
With CSS, the flow and structure of the underlying XML document
must be very close to the transformed output. XSLT, on the other
hand, allows data to be extracted from the original XML document
and displayed in an arbitrary fashion. One will likely find
XSLT-based formatting support in a later version of Netscape
browser.
The associated stylesheet is now of the text/css type, and is
called trip.css. The CSS2 stylesheet trip.css is quite simple:
title, topic { display: block }
title { font-family: Times; font-size: 28pt; font-weight: bold
}