BizTalk Utilities CV ,   Jobs ,   Code library  
 
Home Page
Uncategorized
Learn XML
The Understanding XML Game
Dynamic functions: Functional combination, partial application and lambda expressions
An exploration of XML in database management systems
Code Samples 1
Code Samples 2
Using XML to manage a wizard
An enhancement of the Microsoft XML Class Generator
Format XML
Sending Binary Data in XML to server
Retrieving a Registry subtree as XML
An example of creating practical and efficient client-side, offline, standalone, server-independent
Using custom XML Namespaces in a VB application from an XML stylesheet
An XML Chat room
Base64 Encoder
A very simple way to generate multiple HTML combos from XML.
Retrieving a Registry subtree as XML
Retrieve Records
VB XML Parser
ITSE Namespace Navigator
<< System.XML
WCF, WS, SOAP >>

By :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.
First posted :12/04/2001
Times viewed :366

 
  

Client-Side Presentation Logic

Copyright and Authorship Notice

This chapter was taken from "Professional Java XML" by Kal Ahmed, Sudhir Ancha, Andrei Cioroianu, Jay Cousins, Jeremy Crosbie, John Davies, Kyle Gabhart, Steve Gould, James Hart, Ramnivas Laddad, Sing Li, Brendan Macmillan, Daniel Rivers-Moore, Judy Skubal, Karli Watson and Scott Williams published by Wrox Press Limited in April 2001; ISBN 186100401x; copyright © Wrox Press Limited 2001; all rights reserved.

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.

Client-Side XML Processing

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.

Case Study for a Specialized Travel Agency Chain

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.

The High-Level System View

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.

Filtering Disconnected XML Dataset: VacationApplet

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.

AElfred is a freely available, lightweight SAX parser specifically built for Java applet use. Originally created by Microstar, it is now maintained by OpenText. For more information on the AElfred parser, and to obtain documentation, development kit and samples, visit: http://www.opentext.com/services/content_management_services/xml_sgml_solutions.html#aelfred_and_sax 

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.

  private boolean listNeedUpdate;

  private CheckboxGroup destGroup;

  private Checkbox allClick, mexicoClick, caribClick, hawaiiClick;

  private Button updateButton;

  private Panel clickPanel;

  private List tripList;

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:

http://localhost:8080/vacserv/servlet/locatedeals?tnum=nnn

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.

  private void filterList() {

    tripList.removeAll();

    if (currentRegionFilter == 0) {

      for (int i = 0; i < curDeals.size(); i++) {

        TravelDeal tpDeal = (TravelDeal) curDeals.elementAt(i);

        if (tpDeal.getCommission() >= minCommission) {

          tripList.add(tpDeal.getDescription());

        }

      }

    } else {

      for (int i = 0; i < curDeals.size(); i++) {

        TravelDeal tpDeal = (TravelDeal) curDeals.elementAt(i);

        // 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).

  private Vector myDeals;

  private int maxTripNumber = 0;

  private XmlParser parser;

  private int curID = 0;

  private int curCommission = 0;

  private int curRegion = 0;

  private String lastParsedText = "";

  private String workString = "";

  private String curPrice, curLocation, curStartdate, curDuration;

  public DealsParser() {

    myDeals = new Vector();

  }

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
  • Create a new <TravelDeal> element
  • Add the new element to myDeals

  public void endElement(String name) {

    if (name.compareTo("startdate") == 0) {

      curStartdate = lastParsedText;

    }

    if (name.compareTo("duration") == 0) {

      curDuration = lastParsedText;

    }

    if (name.compareTo("location") == 0) {

      curLocation = lastParsedText;

    }

    if (name.compareTo("price") == 0) {

      curPrice = lastParsedText;

    }

    if (name.compareTo("trip") == 0) {

      workString = curLocation + "--" + curStartdate + " for "

                   + curDuration + "--**$" + curPrice + "**";

      myDeals.addElement(new TravelDeal(curID, curRegion, curCommission,

                                        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:

>javac -classpath .;.\aelfred.jar VacationApplet.java

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)

     throws ServletException, IOException  {

  String tripno = req.getParameter("tnum");

  int tripNumber = 0;

  if (tripno != null) {

    try {

      tripNumber = Integer.parseInt(tripno);

    } catch (Exception ex) {

      tripNumber = 0;

    }

  }

  String tpXML = myFinder.locateDealsXML(tripNumber);

  byte[] byteAry = tpXML.getBytes();     

  res.setContentLength (byteAry.length);

  OutputStream output = res.getOutputStream();

  output.write (byteAry);

  output.flush ();

}

The RDBMS to XML Mapper: DealsFinder Class

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.

  public void AddATrip(String tripNum,

                       String region,

                       String startDate,

                       String duration,

                       String location,

                       String price,

                       String commission) {

    Element trip = myDoc.createElement("trip");

    trip.setAttribute("number", tripNum);

    trip.setAttribute("region", region);

    Element sdate = myDoc.createElement("startdate");

    sdate.appendChild(myDoc.createTextNode(startDate));

    Element dur = myDoc.createElement("duration");

    dur.appendChild(myDoc.createTextNode(duration));

    Element loc = myDoc.createElement("location");

    loc.appendChild(myDoc.createTextNode(location));

    Element pr = myDoc.createElement("price");

    pr.setAttribute("commission", commission);

    pr.appendChild(myDoc.createTextNode(price));

    trip.appendChild(sdate);

    trip.appendChild(dur);

    trip.appendChild(loc);

    trip.appendChild(pr);

    docElement.appendChild(trip);

  }

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.

JDBC Access: SqlBean and QueryBean Classes

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.

Here is sqlBean.java that uses it:

package com.wrox.pjxml;

import java.sql.Connection;

import java.sql.DriverManager;

public class sqlBean  {

  private String myDriver = "sun.jdbc.odbc.JdbcOdbcDriver";

  private String myURL = "jdbc:odbc:travel";

  protected Connection myConn;

  public sqlBean() {}

  public void makeConnection() throws  Exception   {

   Class.forName( myDriver);

   myConn = DriverManager.getConnection(myURL);

  }

  public void takeDown() throws Exception {

   myConn.close();

  }

}

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:

javac -classpath .;<path_to>\xerces.jar VacationServlet.java

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:

http://localhost:8080/vacserv/servlet/locatedeals?tnum=0

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:

<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="300">

    </applet>

  </body>

</html>

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," +

                     " duration, location, price, commission) values ('";

  String myAppend2 = "','";

  String myAppend3 = "')";

  public queryBean() {super();}

  public boolean addTrip(String startdate,

                         String region,

                         String duration,

                         String location,

                         String price,

                         String commission)

     throws Exception  {

   String myQuery = myAppend1 + startdate + myAppend2 + region + myAppend2 +

                    duration + myAppend2 + location + myAppend2 + price +

                    myAppend2 + commission + myAppend3;

    Statement stmt = myConn.createStatement();

    return (stmt.executeUpdate(myQuery) == 1);

  }

  public void clean()throws Exception {

    Statement stmt = myConn.createStatement();

    stmt.execute("delete from hotdeals");

  }

}

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); }

    });

    basePanel = new Panel();

    basePanel.setLayout(new FlowLayout());

    basePanel.add(purgeButton);

    basePanel.add(inject1Button);

    basePanel.add(inject2Button);

    add(basePanel);

   }

  private void parseAndAddRecords(String filename) throws Exception {

      parser = new XmlParser();

      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.

Built-in XML Presentation Capabilities of Modern Browsers

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:

<?xml version="1.0" ?>

<%@ page language="java"

         import="com.wrox.pjxml.DealsFinder"

         contentType="text/xml" %>

<?xml-stylesheet type="text/xsl" href="tripdeals.xsl" ?>

<%!

      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 %>

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.

The XSLT stylesheet, tripdeals.xsl, contains:

<?xml version="1.0" ?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" version="1.0">

  <xsl:template match="/">

    <HTML>

      <BODY>

        <TABLE WIDTH="600">

         <TR><TD><H1>Wrox Vacations Center</H1></TD></TR>

         <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:

<%@ page language="java" import="com.wrox.pjxml.DealsFinder" %>

<%!

public DealsFinder myFinder;

public void jspInit() {

         myFinder = new DealsFinder();

         myFinder.init();

}

%>

<HTML><HEAD></HEAD><TITLE></TITLE>

<BODY>

<XML ID="vacDeals">

<%

   String myStr = myFinder.locateDealsXML(0);

   int chop = myStr.indexOf(">"); 

   myStr = myStr.substring(chop + 1); %>

<%= myStr %>

</XML>

<table width="600">

<tr>

<td><H1>Wrox Vacations Center</H1></td>

</tr>

<tr>

<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.

Netscape 6 Support for Cascading Style Sheet XML Formatting

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:

<?xml version="1.0" ?>

<%@ page language="java" import="com.wrox.pjxml.*" contentType="text/xml" %>

<?xml-stylesheet type="text/css" href="trip.css" ?>

<triplist>

<title>Wrox Travel Center</title>

<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 }

topic { font-family: Times; font-size: 18pt; margin:20px; }

trip { display: table-row; font-family: Helvetica; font-size: 12pt }

location { display: table-cell }

startdate