CSCI A348/548
Lab Notes Fourteen

Spring 2001 (Second semester 2000-2001)


Parsing XML over the network.
Take this program:
import java.io.IOException;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

// Import your vendor's XMLReader implementation here
//import org.apache.xerces.parsers.SAXParser;


/**
 * <b><code>SAXParserDemo</code></b> will take an XML file and parse it
 *   using SAX, displaying the callbacks in the parsing lifecycle.
 *
 * @author
 *   <a href="mailto:brettmclaughlin@earthlink.net">Brett McLaughlin</a>
 * @version 1.0
 */
public class SAXParserDemo {

    /**
     * <p>
     *   This parses the file, using registered SAX handlers, and outputs
     *     the events in the parsing process cycle.
     * </p>
     *
     * @param uri <code>String</code> URI of file to parse.
     */
    public void performDemo(String uri) {
        System.out.println("Parsing XML File: " + uri + "\n\n");

        // Get instances of our handlers
        ContentHandler contentHandler = new MyContentHandler();
        ErrorHandler errorHandler = new MyErrorHandler();

        try {
            // Instantiate a parser
            XMLReader parser =
                XMLReaderFactory.createXMLReader(
                    "org.apache.xerces.parsers.SAXParser");

            // Register the content handler
            parser.setContentHandler(contentHandler);

            // Register the error handler
            parser.setErrorHandler(errorHandler);

            // Parse the document
            parser.parse(uri);

        } catch (IOException e) {
            System.out.println("Error reading URI: " + e.getMessage());
        } catch (SAXException e) {
            System.out.println("Error in parsing: " + e.getMessage());
        }
    }

    /**
     * <p>
     *   This provides a command-line entry point for this demo.
     * </p>
     */
    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("Usage: java SAXParserDemo [XML URI]");
            System.exit(0);
        }
        String uri = args[0];
        SAXParserDemo parserDemo = new SAXParserDemo();
        parserDemo.performDemo(uri);
    }
}

/**
 * <b><code>MyContentHandler</code></b> implements the SAX
 *   <code>ContentHandler</code> interface and defines callback
 *   behavior for the SAX callbacks associated with an XML
 *   document's content.
 */
class MyContentHandler implements ContentHandler {

    /** Hold onto the locator for location information */
    private Locator locator;

    /**
     * <p>
     * Provide reference to <code>Locator</code> which provides
     * information about where in a document callbacks occur.
     * </p>
     *
     * @param locator <code>Locator</code> object tied to callback
     * process
     */
    public void setDocumentLocator(Locator locator) {
        System.out.println(" * setDocumentLocator() called");

        // We save this for later use if desired.
        this.locator = locator;
    }

    /**
     * <p>
     * This indicates the start of a Document parse—this precedes
     * all callbacks in all SAX Handlers with the sole exception
     * of <code>{@link #setDocumentLocator}</code>.
     * </p>
     *
     * @throws <code>SAXException</code> when things go wrong
     */
    public void startDocument() throws SAXException {
        System.out.println("Parsing begins...");
    }

    /**
     * <p>
     *   This indicates the end of a Document parse—this occurs after
     *     all callbacks in all SAX Handlers.</code>.
     * </p>
     *
     * @throws <code>SAXException</code> when things go wrong
     */
    public void endDocument() throws SAXException {
        System.out.println("...Parsing ends.");
    }

    /**
     * <p>
     *   This indicates that a processing instruction (other than
     *     the XML declaration) has been encountered.
     * </p>
     *
     * @param target <code>String</code> target of PI
     * @param data <code>String</code containing all data sent to the PI.
     *               This typically looks like one or more attribute value
     *               pairs.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void processingInstruction(String target, String data)
        throws SAXException {

        System.out.println("PI: Target:" + target + " and Data:" + data);
    }

    /**
     * <p>
     *   This indicates the beginning of an XML Namespace prefix
     *     mapping. Although this typically occurs within the root element
     *     of an XML document, it can occur at any point within the
     *     document. Note that a prefix mapping on an element triggers
     *     this callback <i>before</i> the callback for the actual element
     *     itself (<code>{@link #startElement}</code>) occurs.
     * </p>
     *
     * @param prefix <code>String</code> prefix used for the namespace
     *                being reported
     * @param uri <code>String</code> URI for the namespace
     *               being reported
     * @throws <code>SAXException</code> when things go wrong
     */
    public void startPrefixMapping(String prefix, String uri) {
        System.out.println("Mapping starts for prefix " + prefix +
            " mapped to URI " + uri);
    }

    /**
     * <p>
     *   This indicates the end of a prefix mapping, when the namespace
     *     reported in a <code>{@link #startPrefixMapping}</code> callback
     *     is no longer available.
     * </p>
     *
     * @param prefix <code>String</code> of namespace being reported
     * @throws <code>SAXException</code> when things go wrong
     */
    public void endPrefixMapping(String prefix) {
        System.out.println("Mapping ends for prefix " + prefix);
    }

    /**
     * <p>
     *   This reports the occurrence of an actual element. It includes
     *     the element's attributes, with the exception of XML vocabulary
     *     specific attributes, such as
     *     <code>xmlns:[namespace prefix]</code> and
     *     <code>xsi:schemaLocation</code>.
     * </p>
     *
     * @param namespaceURI <code>String</code> namespace URI this element
     *               is associated with, or an empty <code>String</code>
     * @param localName <code>String</code> name of element (with no
     *               namespace prefix, if one is present)
     * @param rawName <code>String</code> XML 1.0 version of element name:
     *                [namespace prefix]:[localName]
     * @param atts <code>Attributes</code> list for this element
     * @throws <code>SAXException</code> when things go wrong
     */
    public void startElement(String namespaceURI, String localName,
                                           String rawName, Attributes atts)
        throws SAXException {

        System.out.print("startElement: " + localName);
        if (!namespaceURI.equals("")) {
            System.out.println(" in namespace " + namespaceURI +
                " (" + rawName + ")");
        } else {
            System.out.println(" has no associated namespace");
        }

        for (int i=0; i<atts.getLength(); i++) {
            System.out.println(" Attribute: " + atts.getLocalName(i) +
                "=" + atts.getValue(i));
        }
    }

    /**
     * <p>
     *   Indicates the end of an element
     *     (<code></[element name]></code>) is reached. Note that
     *     the parser does not distinguish between empty
     *     elements and non-empty elements, so this occurs uniformly.
     * </p>
     *
     * @param namespaceURI <code>String</code> URI of namespace this
     *                element is associated with
     * @param localName <code>String</code> name of element without prefix
     * @param rawName <code>String</code> name of element in XML 1.0 form
     * @throws <code>SAXException</code> when things go wrong
     */
    public void endElement(String namespaceURI, String localName,
                                          String rawName)
        throws SAXException {

        System.out.println("endElement: " + localName + "\n");
    }

    /**
     * <p>
     *   This reports character data (within an element).
     * </p>
     *
     * @param ch <code>char[]</code> character array with character data
     * @param start <code>int</code> index in array where data starts.
     * @param end <code>int</code> index in array where data ends.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void characters(char[] ch, int start, int end)
        throws SAXException {

        String s = new String(ch, start, end);
        System.out.println("characters: " + s);
    }

    /**
     * <p>
     * This reports whitespace that can be ignored in the
     * originating document. This is typically invoked only when
     * validation is ocurring in the parsing process.
     * </p>
     *
     * @param ch <code>char[]</code> character array with character data
     * @param start <code>int</code> index in array where data starts.
     * @param end <code>int</code> index in array where data ends.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void ignorableWhitespace(char[] ch, int start, int end)
        throws SAXException {

        String s = new String(ch, start, end);
        System.out.println("ignorableWhitespace: [" + s + "]");
    }

    /**
     * <p>
     *   This reports an entity that is skipped by the parser. This
     *     should only occur for non-validating parsers, and then is still
     *     implementation-dependent behavior.
     * </p>
     *
     * @param name <code>String</code> name of entity being skipped
     * @throws <code>SAXException</code> when things go wrong
     */
    public void skippedEntity(String name) throws SAXException {
        System.out.println("Skipping entity " + name);
    }
}

/**
 * <b><code>MyErrorHandler</code></b> implements the SAX
 *   <code>ErrorHandler</code> interface and defines callback
 *   behavior for the SAX callbacks associated with an XML
 *   document's errors.
 */
class MyErrorHandler implements ErrorHandler {

    /**
     * <p>
     *   This will report a warning that has occurred; this indicates
     *     that while no XML rules were broken, something appears
     *     to be incorrect or missing.
     * </p>
     *
     * @param exception <code>SAXParseException</code> that occurred.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void warning(SAXParseException exception)
        throws SAXException {

        System.out.println("**Parsing Warning**\n" +
            " Line: " +
            exception.getLineNumber() + "\n" +
            " URI: " +
            exception.getSystemId() + "\n" +
            " Message: " +
            exception.getMessage());
        throw new SAXException("Warning encountered");
    }

    /**
     * <p>
     *   This will report an error that has occurred; this indicates
     *     that a rule was broken, typically in validation, but that
     *     parsing can reasonably continue.
     * </p>
     *
     * @param exception <code>SAXParseException</code> that occurred.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void error(SAXParseException exception)
        throws SAXException {

        System.out.println("**Parsing Error**\n" +
            " Line: " +
            exception.getLineNumber() + "\n" +
            " URI: " +
            exception.getSystemId() + "\n" +
            " Message: " +
            exception.getMessage());
        throw new SAXException("Error encountered");
    }

    /**
     * <p>
     *   This will report a fatal error that has occurred; this indicates
     *     that a rule has been broken that makes continued parsing either
     *     impossible or an almost certain waste of time.
     * </p>
     *
     * @param exception <code>SAXParseException</code> that occurred.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void fatalError(SAXParseException exception)
        throws SAXException {

        System.out.println("**Parsing Fatal Error**\n" +
            " Line: " +
            exception.getLineNumber() + "\n" +
            " URI: " +
            exception.getSystemId() + "\n" +
            " Message: " +
            exception.getMessage());
        throw new SAXException("Fatal Error encountered");
    }
}
and place it somewhere in your directory.

I'd keep it in ~/xml or something similar.

Now compile it.

If you can't, you need some .jar files.

Take all of these files:

burrowww.cs.indiana.edu% ls /u/dgerman/xalan/*.jar
/u/dgerman/xalan/ant.jar               /u/dgerman/xalan/xalanjdoc.jar
/u/dgerman/xalan/bsf.jar               /u/dgerman/xalan/xalansamples.jar
/u/dgerman/xalan/stylebook-1.0-b2.jar  /u/dgerman/xalan/xerces.jar
burrowww.cs.indiana.edu% 
Place them where you can access them and point your CLASSPATH to them.

I'd put all of them in $TOMCAT_HOME/lib or similar.

Then change your ~/.cshrc to update your CLASSPATH.

setenv CLASSPATH   .:$TOMCAT_HOME/lib/servlet.jar

setenv CLASSPATH $TOMCAT_HOME/lib/xalan.jar:$CLASSPATH
setenv CLASSPATH $TOMCAT_HOME/lib/xerces.jar:$CLASSPATH
setenv CLASSPATH $TOMCAT_HOME/lib/ant.jar:$CLASSPATH
setenv CLASSPATH $TOMCAT_HOME/lib/bsf.jar:$CLASSPATH
setenv CLASSPATH $TOMCAT_HOME/lib/stylebook-1.0-b2.jar:$CLASSPATH
setenv CLASSPATH $TOMCAT_HOME/lib/xalanjdoc.jar:$CLASSPATH
After you compile it, try to parse the output of my supplyBooks.pl as follows:
burrowww.cs.indiana.edu% java SAXParserDemo http://burrowww.cs.indiana.edu:20006/cgi-bin/supplyBooks.pl
Parsing XML File: http://burrowww.cs.indiana.edu:20006/cgi-bin/supplyBooks.pl

^Cburrowww.cs.indiana.edu% 
Let's make a servlet out of it.

Start with:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class MTBDC extends HttpServlet {
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html><head><title>Books, books!</title></head>" +
                    "<BODY>\n" +
                    "<H1>You want books?</H1>\n" +
                    "</BODY></HTML>");
    }
}
Compile and run this, first.

Once it runs, change it a bit.

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class MTBDC extends HttpServlet {

    PrintWriter out;  

    public void doGet(HttpServletRequest request,
		      HttpServletResponse response)
	throws ServletException, IOException {
	response.setContentType("text/html");
	out = response.getWriter();

	out.println("<html><head><title>Books, yes!" + 
                    "</title></head><BODY>\n<pre>"); 



	out.println("</pre></BODY></HTML>");

    }

}
Bring SAXParserDemo in the same place, and change it.

import java.io.*;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

// Import your vendor's XMLReader implementation here
//import org.apache.xerces.parsers.SAXParser;


/**
 * <b><code>SAXParserDemo</code></b> will take an XML file and parse it
 *   using SAX, displaying the callbacks in the parsing lifecycle.
 *
 * @author
 *   <a href="mailto:brettmclaughlin@earthlink.net">Brett McLaughlin</a>
 * @version 1.0
 */
public class SAXParserDemo {

    PrintWriter out; 

    public SAXParserDemo(PrintWriter out) {
	this.out = out; 
    }

    /**
     * <p>
     *   This parses the file, using registered SAX handlers, and outputs
     *     the events in the parsing process cycle.
     * </p>
     *
     * @param uri <code>String</code> URI of file to parse.
     */
    public void performDemo(String uri) {
        out.println("Parsing XML File: " + uri + "\n\n");

        // Get instances of our handlers
        ContentHandler contentHandler = new MyContentHandler(out);
        ErrorHandler errorHandler = new MyErrorHandler(out);

        try {
            // Instantiate a parser
            XMLReader parser =
                XMLReaderFactory.createXMLReader(
                    "org.apache.xerces.parsers.SAXParser");

            // Register the content handler
            parser.setContentHandler(contentHandler);

            // Register the error handler
            parser.setErrorHandler(errorHandler);

            // Parse the document
            parser.parse(uri);

        } catch (IOException e) {
            out.println("Error reading URI: " + e.getMessage());
        } catch (SAXException e) {
            out.println("Error in parsing: " + e.getMessage());
        }
    }

    /**
     * <p>
     *   This provides a command-line entry point for this demo.
     * </p>
     */
    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("Usage: java SAXParserDemo [XML URI]");
            System.exit(0);
        }
        String uri = args[0];
        SAXParserDemo parserDemo = new SAXParserDemo(
                                         new PrintWriter(System.out)
                                       );
        parserDemo.performDemo(uri);
    }
}

/**
 * <b><code>MyContentHandler</code></b> implements the SAX
 *   <code>ContentHandler</code> interface and defines callback
 *   behavior for the SAX callbacks associated with an XML
 *   document's content.
 */
class MyContentHandler implements ContentHandler {

    PrintWriter out; 

    public MyContentHandler(PrintWriter out) {
	this.out = out; 
    } 

    /** Hold onto the locator for location information */
    private Locator locator;

    /**
     * <p>
     * Provide reference to <code>Locator</code> which provides
     * information about where in a document callbacks occur.
     * </p>
     *
     * @param locator <code>Locator</code> object tied to callback
     * process
     */
    public void setDocumentLocator(Locator locator) {
        out.println(" * setDocumentLocator() called");

        // We save this for later use if desired.
        this.locator = locator;
    }

    /**
     * <p>
     * This indicates the start of a Document parse—this precedes
     * all callbacks in all SAX Handlers with the sole exception
     * of <code>{@link #setDocumentLocator}</code>.
     * </p>
     *
     * @throws <code>SAXException</code> when things go wrong
     */
    public void startDocument() throws SAXException {
        out.println("Parsing begins...");
    }

    /**
     * <p>
     *   This indicates the end of a Document parse—this occurs after
     *     all callbacks in all SAX Handlers.</code>.
     * </p>
     *
     * @throws <code>SAXException</code> when things go wrong
     */
    public void endDocument() throws SAXException {
        out.println("...Parsing ends.");
    }

    /**
     * <p>
     *   This indicates that a processing instruction (other than
     *     the XML declaration) has been encountered.
     * </p>
     *
     * @param target <code>String</code> target of PI
     * @param data <code>String</code containing all data sent to the PI.
     *               This typically looks like one or more attribute value
     *               pairs.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void processingInstruction(String target, String data)
        throws SAXException {

        out.println("PI: Target:" + target + " and Data:" + data);
    }

    /**
     * <p>
     *   This indicates the beginning of an XML Namespace prefix
     *     mapping. Although this typically occurs within the root element
     *     of an XML document, it can occur at any point within the
     *     document. Note that a prefix mapping on an element triggers
     *     this callback <i>before</i> the callback for the actual element
     *     itself (<code>{@link #startElement}</code>) occurs.
     * </p>
     *
     * @param prefix <code>String</code> prefix used for the namespace
     *                being reported
     * @param uri <code>String</code> URI for the namespace
     *               being reported
     * @throws <code>SAXException</code> when things go wrong
     */
    public void startPrefixMapping(String prefix, String uri) {
        out.println("Mapping starts for prefix " + prefix +
            " mapped to URI " + uri);
    }

    /**
     * <p>
     *   This indicates the end of a prefix mapping, when the namespace
     *     reported in a <code>{@link #startPrefixMapping}</code> callback
     *     is no longer available.
     * </p>
     *
     * @param prefix <code>String</code> of namespace being reported
     * @throws <code>SAXException</code> when things go wrong
     */
    public void endPrefixMapping(String prefix) {
        out.println("Mapping ends for prefix " + prefix);
    }

    /**
     * <p>
     *   This reports the occurrence of an actual element. It includes
     *     the element's attributes, with the exception of XML vocabulary
     *     specific attributes, such as
     *     <code>xmlns:[namespace prefix]</code> and
     *     <code>xsi:schemaLocation</code>.
     * </p>
     *
     * @param namespaceURI <code>String</code> namespace URI this element
     *               is associated with, or an empty <code>String</code>
     * @param localName <code>String</code> name of element (with no
     *               namespace prefix, if one is present)
     * @param rawName <code>String</code> XML 1.0 version of element name:
     *                [namespace prefix]:[localName]
     * @param atts <code>Attributes</code> list for this element
     * @throws <code>SAXException</code> when things go wrong
     */
    public void startElement(String namespaceURI, String localName,
                                           String rawName, Attributes atts)
        throws SAXException {

        out.print("startElement: " + localName);
        if (!namespaceURI.equals("")) {
            out.println(" in namespace " + namespaceURI +
                " (" + rawName + ")");
        } else {
            out.println(" has no associated namespace");
        }

        for (int i=0; i<atts.getLength(); i++) {
            out.println(" Attribute: " + atts.getLocalName(i) +
                "=" + atts.getValue(i));
        }
    }

    /**
     * <p>
     *   Indicates the end of an element
     *     (<code></[element name]></code>) is reached. Note that
     *     the parser does not distinguish between empty
     *     elements and non-empty elements, so this occurs uniformly.
     * </p>
     *
     * @param namespaceURI <code>String</code> URI of namespace this
     *                element is associated with
     * @param localName <code>String</code> name of element without prefix
     * @param rawName <code>String</code> name of element in XML 1.0 form
     * @throws <code>SAXException</code> when things go wrong
     */
    public void endElement(String namespaceURI, String localName,
                                          String rawName)
        throws SAXException {

        out.println("endElement: " + localName + "\n");
    }

    /**
     * <p>
     *   This reports character data (within an element).
     * </p>
     *
     * @param ch <code>char[]</code> character array with character data
     * @param start <code>int</code> index in array where data starts.
     * @param end <code>int</code> index in array where data ends.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void characters(char[] ch, int start, int end)
        throws SAXException {

        String s = new String(ch, start, end);
        out.println("characters: " + s);
    }

    /**
     * <p>
     * This reports whitespace that can be ignored in the
     * originating document. This is typically invoked only when
     * validation is ocurring in the parsing process.
     * </p>
     *
     * @param ch <code>char[]</code> character array with character data
     * @param start <code>int</code> index in array where data starts.
     * @param end <code>int</code> index in array where data ends.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void ignorableWhitespace(char[] ch, int start, int end)
        throws SAXException {

        String s = new String(ch, start, end);
        out.println("ignorableWhitespace: [" + s + "]");
    }

    /**
     * <p>
     *   This reports an entity that is skipped by the parser. This
     *     should only occur for non-validating parsers, and then is still
     *     implementation-dependent behavior.
     * </p>
     *
     * @param name <code>String</code> name of entity being skipped
     * @throws <code>SAXException</code> when things go wrong
     */
    public void skippedEntity(String name) throws SAXException {
        out.println("Skipping entity " + name);
    }
}

/**
 * <b><code>MyErrorHandler</code></b> implements the SAX
 *   <code>ErrorHandler</code> interface and defines callback
 *   behavior for the SAX callbacks associated with an XML
 *   document's errors.
 */
class MyErrorHandler implements ErrorHandler {

    PrintWriter out; 

    public MyErrorHandler(PrintWriter out) {
	this.out = out; 
    }  

    /**
     * <p>
     *   This will report a warning that has occurred; this indicates
     *     that while no XML rules were broken, something appears
     *     to be incorrect or missing.
     * </p>
     *
     * @param exception <code>SAXParseException</code> that occurred.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void warning(SAXParseException exception)
        throws SAXException {

        out.println("**Parsing Warning**\n" +
            " Line: " +
            exception.getLineNumber() + "\n" +
            " URI: " +
            exception.getSystemId() + "\n" +
            " Message: " +
            exception.getMessage());
        throw new SAXException("Warning encountered");
    }

    /**
     * <p>
     *   This will report an error that has occurred; this indicates
     *     that a rule was broken, typically in validation, but that
     *     parsing can reasonably continue.
     * </p>
     *
     * @param exception <code>SAXParseException</code> that occurred.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void error(SAXParseException exception)
        throws SAXException {

        out.println("**Parsing Error**\n" +
            " Line: " +
            exception.getLineNumber() + "\n" +
            " URI: " +
            exception.getSystemId() + "\n" +
            " Message: " +
            exception.getMessage());
        throw new SAXException("Error encountered");
    }

    /**
     * <p>
     *   This will report a fatal error that has occurred; this indicates
     *     that a rule has been broken that makes continued parsing either
     *     impossible or an almost certain waste of time.
     * </p>
     *
     * @param exception <code>SAXParseException</code> that occurred.
     * @throws <code>SAXException</code> when things go wrong
     */
    public void fatalError(SAXParseException exception)
        throws SAXException {

        out.println("**Parsing Fatal Error**\n" +
            " Line: " +
            exception.getLineNumber() + "\n" +
            " URI: " +
            exception.getSystemId() + "\n" +
            " Message: " +
            exception.getMessage());
        throw new SAXException("Fatal Error encountered");
    }
}
Then use it in the servlet.

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class MTBDC extends HttpServlet {

    PrintWriter out; 

    public void doGet(HttpServletRequest request,
		      HttpServletResponse response)
	throws ServletException, IOException {
	response.setContentType("text/html");
	out = response.getWriter();

	out.println("<html><head><title>Books, yes!" + 
                    "</title></head><BODY>\n<pre>"); 

        String uri = "http://burrowww.cs.indiana.edu" + 
	             ":20006/cgi-bin/supplyBooks.pl"; 
        SAXParserDemo parserDemo = new SAXParserDemo(out);
        parserDemo.performDemo(uri);

	out.println("</pre></BODY></HTML>");

    }

}
The communication link is established.

Now we need to improve the output to make it like this.


Last updated on Apr 19, 2001, by Adrian German for A348/A548