Chapter 24. Creating an XML Application

In this chapter

The Extensible Markup Language (XML) represents an important step in data representation. In the past, programs used many different formats for storing data. There have been text files with comma-delimited or pipe-delimited fields, binary files in any number of formats, and even plain ASCII text. Unfortunately, most programs use slightly different ways to store data, even if the overall format is similar. For example, when you save data using comma-separated fields, how do you specify what each field is? You might save the first name followed by the last name. Another developer might save the last name and then the first name. How do you know the format of the file, other than by someone telling you? You could put a line at the top of the file explaining what each field is, but even that might have different formats.

Another problem you encounter when storing data is that most representations tend to be tabular in nature. That is, when you write data into a file, you typically put all the information for a particular data element on a single line in the file. What happens when the data you want to write is a Java object with many nested data structures? How can you store all those data structures within a single line, especially if you must stick to a prescribed set of fields?

XML solves this problem by defining a standard way to represent data, a standard way to tag the data with its type, and a standard way to describe the overall data structure. XML is very simple, easy to read, and easy to understand. There are a few things XML is not:

  • XML is not a replacement for HTML. Although there is an XML-compliant version of HTML, XML does not define how to represent data on a Web browser.

  • XML is not a cure for all the data format ills. Various software vendors and the business sector must still agree on a common representation for data. For example, the travel industry needs a standard format to represent a reservation. The banking industry needs a standard format to represent account information.

  • XML is not a programming language. XML lets you describe data, but it doesn't let you describe how to process the data.

A "Hello World" XML Page

In a minimal XML page you need two things: a heading identifying the page as an XML page, and a single pair of tags that represents the root of the data. Think of the root tags as being like the <HTML> </HTML> tags in an HTML document. They enclose everything else. An XML document might have only one pair of root tags. Everything else in the document must be within those two tags.

Listing 24.1 shows a "Hello World" XML page.

Example 24.1. Source Code for HelloWorld.xml

<?xml version=_1.0_?><greeting>   Hello World_</greeting>

The <?xml version="1.0"?> tag must be present in any XML file. The <greeting> </greeting> tag pair is the root of the document.

Sending XML from a Java Server Page

You can create XML Java Server Pages the same way you create HTML pages. The only difference is that you must set the content type of your page to text/xml. To set the content type, use the<%@page%> tag, like this:

<%@ page contentType="text/xml" %>

Listing 24.2 shows the "Hello World" XML page as a Java Server Page.

Example 24.2. Source Code for XMLHelloWorld.jsp

<%@ page contentType="text/xml" %>

<?xml version="1.0"?>
<greeting>
    Hello World!
</greeting>

Figure 24.1 shows the XMLHelloWorld Java Server Page from Internet Explorer 5. As you can see, Internet Explorer shows you the XML source code.

Internet Explorer shows you XML source code.

Figure 24.1. Internet Explorer shows you XML source code.

Sending XML from a Servlet

Sending XML from a servlet is just as easy as sending HTML. The only difference is that you must set the content type to text/xml as you did with the JSP. Because you always need to set the content type in a servlet (unless you don't send a response at all), sending XML is just as easy as sending HTML.

Listing 24.3 shows a servlet that generates the "Hello World" XML page.

Example 24.3. Source Code for XMLHelloWorldServlet.java

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

public class XMLHelloWorldServlet extends HttpServlet
{
    public void service(HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType(_text/xml_);

        PrintWriter out = response.getWriter();

        out.println(_<?xml version=\_1.0 _?>_);
        out.println(_<greeting>_);
        out.println(_    Hello World__);
        out.println(_</greeting>_);
    }
}

A Few Simple Rules for XML

You already know that an XML page must start with the <?xml?> tag and must contain a single root tag. There are a few additional rules that dictate how you create an XML page.

First, when you create an XML page, you have the option of specifying a Document Type Definition (DTD) that defines what tags are permitted in the XML page. DTDs let you create standard definitions of XML pages. You can use an XML validator to check an XML page to make sure it conforms to its DTD. Use the <!DOCTYPE> tag to specify the DTD for your page. Listing 24.4 shows an example DTD.

Example 24.4. Source Code for Simple.dtd

<!ELEMENT person (first-name? | middle-name? | last-name?)+>
<!ELEMENT first-name (#PCDATA)>
<!ELEMENT middle-name (#PCDATA)>
<!ELEMENT last-name (#PCDATA)>
<!ELEMENT number-name (#PCDATA)>

Creating a DTD is more complex than creating an XML page. You should consult the W3C Web site athttp://www.w3c.org for pointers on how to create a DTD. Listing 24.5 shows an XML page that conforms to the DTD in Listing 24.4.

Example 24.5. Source Code for Simple.xml

<?xml version="1.0"?>
<!DOCTYPE Simple SYSTEM "http://localhost/Simple.dtd">
<person>
<first-name>Samantha</first-name>
   <middle-name>Lauren</middle-name>
    <last-name>Tippin</last-name>
</person>

A DTD is optional, of course. You can create an XML page with any set of tags you want. For each opening tag you must have a closing tag. The exception to this rule is that you can end a tag with /> to indicate that it doesn't need a closing tag. Remember the <jsp:include/> and <jsp:forward/> tags end with /> and thus take no closing tag. You can't interleave tags, either. In other words, if tag B starts within tag A, then tag B must be closed before tag A closes. The following combination of tags is illegal in XML:

<foo>
   <bar>
</foo>
    </bar>

Because the <bar> tag starts inside the <foo> tag, it must also close within the <foo> tag.

You can also specify attributes within a tag. Although HTML is lenient about quotes in attributes, XML requires that the value of every attribute is enclosed in quotes. In other words, you might have an HTML tag like this:

<img src="katy.jpg" width=140 height=150>

A valid XML version of the <IMG> tag would look like this:

<img src="katy.jpg" width="140" height="150"/>

Notice that the XML version of <IMG> ends with /> because it doesn't need a closing tag.

Why Use XML with JSP and Servlets?

If you are new to the XML world, you are probably wondering why you would care about using XML from servlets and JSPs. After all, you are sending output to a browser and the browser understands HTML, not XML. One thing you often find when you create applications that need to send data to other applications is that there are often firewalls sitting between the applications. This is especially true when one business sends data to another business. You would think that RMI or CORBA would be the technologies of choice for exchanging data between businesses, but when it comes to firewalls, many developers take the path of least resistance: HTTP. There are products that allow you to send CORBA over a firewall, and RMI even supports HTTP tunneling, but because most firewalls already support HTTP and HTTPS (HTTP over SSL), you can use the URL and URLConnection classes to communicate with servlets and JSPs with little or no extra work.

When you pass XML data via HTTP, it makes sense to use servlets to handle incoming XML and Java Server Pages to generate outgoing XML. Java has several good XML parsers for handling incoming XML. You might need to perform additional work to copy the XML values into Java classes, however.

Automatically Generated XML

The simple, uniform structure of XML makes it very computer-friendly. The nested structure of XML makes it an ideal format for storing complex data structures. In fact, XML makes a nice format for serializing Java objects. There are several different ways to serialize Java objects into XML. Some approaches choose to define an XML DTD that describes a Java object. For example, you might have XML that looks like this:

<class>
    <class-name>usingjsp.TestClass</class-name>
    <attribute>
        <attribute-name>myAttribute</attribute-name>
        <attribute-value>Foo</attribute-value>
    </attribute
</class>

Although this method is good, it is very Java-centric. XML allows you to describe a huge variety of data structures, so wouldn't it be nice if you could easily map those data structures into Java as well? Conversely, it would be nice to generate XML from these Java classes.

You can use a package called JOX (Java Objects in XML), available at http://www.wutka.com/jox, to serialize Java objects into XML. Listing 24.6 shows an example Java bean suitable for serialization.

Example 24.6. Source Code for TestBean.java

package com.wutka.jox.test;

import com.wutka.jox.*;
import java.util.*;

public class TestBean implements java.io.Serializable
{
    protected int foo;
    protected String bar;
    protected java.util.Date baz;
    protected Vector thingies;
    protected TestSubbean subbean;

    public TestBean()
    {
        bar = "";
        baz = new Date();
        thingies = new Vector();
    }

    public int getFoo() { return foo; }
    public void setFoo(int aFoo) { foo = aFoo; }

    public String getBar() { return bar; }
    public void setBar(String aBar) { bar = aBar; }

    public java.util.Date getBaz() { return baz; }
    public void setBaz(java.util.Date aBaz) { baz = aBaz; }

    public TestSubbean getSub() { return subbean; }
    public void setSub(TestSubbean aSub) { subbean = aSub; }

    public String[] getThingies()
    {
        String[] retThingies = new String[thingies.size()];
        if (thingies.size() > 0) thingies.copyInto(retThingies);

        return retThingies; 
    }

    public void setThingies(String[] newThingies)
    {
        thingies = new Vector(newThingies.length);
        for (int i=0; i < newThingies.length; i++)
        {
            thingies.addElement(newThingies[i]);
        }
    }

    public String getThingies(int i)
    {
        return (String) thingies.elementAt(i);
    }

    public void setThingies(int i, String thingy)
    {
        thingies.setElementAt(thingy, i);
    }

    public String toString()
    {
        StringBuffer ret = new StringBuffer(
            "foo="+foo+";bar="+bar+";baz="+baz.toString()+
            ";thingies=");
        for (int i=0; i < thingies.size(); i++)
        {
            if (i > 0) ret.append(",");
            ret.append((String) thingies.elementAt(i));
        }

        ret.append(";sub=");
        ret.append(subbean.toString());

        return ret.toString();
    }
}

Listing 24.7 shows the XML generated by JOX.

Example 24.7. Source Code for TestBean.xml

<?xml version="1.0"?>
<MarkTest>
<thingies>Moe</thingies>
<thingies>Larry</thingies>
<thingies>Curly</thingies>
<thingies>Shemp</thingies>
<thingies>Curly Joe</thingies>
<foo>5</foo>
<baz>5/15/00 5:59 PM</baz>
<bar>This is the bar value</bar>
<sub>
<age>35</age>
<name>Mark</name>
</sub>
</MarkTest>

Listing 24.8 shows a servlet that displays the contents of a bean using the JOX library.

Example 24.8. Source Code for BeanXMLServlet.java

package usingjsp.xml;import com.wutka.jox.*;
import com.wutka.jox.test.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class BeanXMLServlet extends HttpServlet
{
    public void service(HttpServletRequest request,
        HttpServletResponse response)
        throws IOException, ServletException
    {

// Create the bean and populate it
        TestBean bean = new TestBean();

        bean.setThingies(
            new String[] { "Moe", "Larry", "Curly", "Shemp",
                "Curly Joe" } );

        bean.setFoo(5);
        bean.setBar("This is the bar value");
        bean.setBaz(new java.util.Date());

        TestSubbean sub = new TestSubbean();
        sub.setName("Mark");
        sub.setAge(35);

        bean.setSub(sub);

// Set the content type for the response
        response.setContentType("text/xml");

// Get the Writer for sending the response
        Writer out = response.getWriter();

// Wrap a JOXBeanWriter around the output writer
        JOXBeanWriter beanOut = new JOXBeanWriter(out);

// Write out the object as XML with a root tag of <MarkTest>
         beanOut.writeObject("MarkTest", bean);
    }
}

Parsing XML with SAX and DOM

When you use servlets and Java Server Pages to generate XML for application-to- application communication, the client on the other end must be able to interpret the XML and turn it into useful data structures. More importantly, if a client sends XML to your JSP or servlet, you must be able to parse the XML document. You can use any of several different parsers to turn XML documents into Java data structures.

There are two different approaches for parsing XML in Java: SAX and DOM. SAX stands for Simple API for XML and allows you to handle XML tags in the data as the parser encounters them. In other words, when the parser locates an XML tag, it calls a Java method to handle the tag. It's up to you to decide what to do with it. DOM, or Document Object Model, isn't strictly an API; it's an object model describing how an XML document is organized. When you parse an XML document using a DOM parser, the parser reads the entire document and passes you back a Document object containing everything that was defined in the XML document.

Each of these approaches has its advantages and disadvantages, and you certainly don't need to choose one over the other. You can use whichever one makes sense for your situation. For example, if you are parsing very large files (in the 10-15MB range and higher), you probably want to use SAX, because a DOM parser will first read the entire file into memory before you can begin processing it. The Java XML API from Sun supports both SAX and DOM.

Parsing XML Using SAX

SAX uses an event-driven model for parsing. The SAX parser reads the XML and when it finds something interesting, it calls a method in a handler class. The handler class is something that you must write, although there is a skeleton base class that you can start with. SAX will tell you when it finds the beginning of a document, the end of a document, an opening tag, a closing tag, or character data within an element. It will also tell you when it finds an error.

SAX is most useful when you need to read through a very large XML file but you might not need much of the data in the file. If you need to search through a file for a particular tag or data value, SAX is generally much quicker.

Listing 24.9 shows a servlet that reads an XML file sent to it and searches for a particular tag. After it finds the tag, it looks for character data.

Example 24.9. Source Code for SaxParseServlet.java

package usingjsp.xml;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

import javax.xml.parsers.*;
import org.xml.sax.*;

public class SaxParseServlet extends HttpServlet
{
    public void doPost(HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException
    {
        try
        {
// Create a parser factory
            SAXParserFactory factory = SAXParserFactory.newInstance();

// Ask the parser factory to create a new parser
            SAXParser parser = factory.newSAXParser();

// This servlet just sends a plain text response
            response.setContentType("text/plain");

// Create an input source around the request reader, ask the parser
// to parse the input source and invoke methods in the XMLHandler class
// when it finds XML elements
            parser.parse(new InputSource(request.getReader()),
                new XMLHandler(request, response));
        }
        catch (ParserConfigurationException exc)
        {
            throw new ServletException(exc.toString());
        }
        catch (SAXException exc)
        {
            throw new ServletException(exc.toString());
        }
     }

     class XMLHandler extends HandlerBase
     {
         protected HttpServletRequest request;
         protected HttpServletResponse response;

         protected boolean handlingFirstName;
         protected boolean handlingLastName;
         protected boolean inName;

         protected String firstName;
         protected String lastName;

        public XMLHandler(HttpServletRequest aRequest,
            HttpServletResponse aResponse)
        {
            request = aRequest;
            response = aResponse;

            inName = false;
            handlingFirstName = false;
            handlingLastName = false;
        }

        public void startElement(String name, AttributeList attributes)
        {
// Look for a <name> element
            if (name.equals("name"))
            {
                inName = true;
                firstName = null;
                lastName = null;
            }
// If inside a <name> element, look for <first>
            else if (name.equals("first"))
            {
                if (!inName) return;

                handlingFirstName = true;
            }
// If inside a <name> element, look for <last>
            else if (name.equals("last"))
            {
                if (!inName) return;

                handlingLastName = true; 
            }
        }
        public void characters(char[] chars, int start, int length)
        {
// If these characters are occurring inside a <first> element, save them
            if (handlingFirstName)
            {
                firstName = new String(chars, start, length);
            }
// If these characters are occurring inside a <last> element, save them
            else if (handlingLastName)
            {
                lastName = new String(chars, start, length);
            }
            else
            {
                return;
            }
        }

        public void endElement(String name)
            throws SAXException
        {
            if (name.equals("name"))
            {
// After the end of the name element, if there's a first and a last name,
// print them separated by a space
                if ((firstName != null) && (lastName != null))
                {
                    try
                    {
                        PrintWriter out = response.getWriter();

                        out.println(firstName+" "+lastName);
                    }
                    catch (IOException ioExc)
                    {
                        throw new SAXException(ioExc.toString());
                    }
                }
                inName = false;
            }
            else if (name.equals("first"))
            {
                if (!inName) return;
                handlingFirstName = false;
            }
            else if (name.equals("last"))
            {
                if (!inName) return;
                handlingLastName = false;
            }
        }
    }
}

Listing 24.10 shows a test client program that sends the XML file to the servlet from Listing 24.9.

Example 24.10. Source Code for XMLTestClient.java

import java.io.*;
import java.net.*;
public class XMLTestClient
{
    public static void main(String[] args)
    {
        try
        {
// args[1] is the name of the file to sendFile f = new
File(args[1]);
            int contentLength = (int) f.length();

// args[0] is the URL to send the file to
            URL url = new URL(args[0]);
            URLConnection conn = url.openConnection();

// Tell the URLConnection that this is an XML file
            conn.setDoOutput(true);
            conn.setRequestProperty("content-type", "text/xml");
            conn.setRequestProperty("content-length", ""+contentLength);

            FileInputStream in = new FileInputStream(f);

            byte[] buffer = new byte[4096];
            int len;

            OutputStream out = conn.getOutputStream();

// Send the XML file to the servlet
            while ((len = in.read(buffer)) > 0)
            {
                out.write(buffer, 0, len);
            }

            InputStream resp = conn.getInputStream();

// Read the response back from the servlet
            while ((len = resp.read(buffer)) > 0)
            {
                System.out.write(buffer, 0, len);
            }
        }
        catch (Exception exc)
        {
            exc.printStackTrace();
        }
    }
}

Parsing XML Using DOM

A DOM parser reads an XML file in its entirety before passing any information to you. It creates a representation of the XML contents using a set of Java classes. An XML file is structured like a tree. The main document tag is the base of the tree, and each nested tag is a branch of the tree. The document model used by a DOM parser is also structured like a tree. You receive a Document object, which returns a list of Node objects.

The Node class is really an interface, not a class. DOM has a number of classes that implement the Node interface. The one you deal with most often is the Element class, which represents a tag or tag pair from an XML document. You might also find Comment nodes, Text nodes, CDATASection nodes, Character nodes, and several others. The Element class might contain a list of child nodes representing the tags and data contained between the element's opening and closing tags.

Listing 24.11 shows a servlet that uses a DOM parser to parse through the same file as the servlet in Listing 24.9. You can see how different a DOM parser is from a SAX parser. Although SAX is a bit faster, DOM tends to be a bit easier to use when you need to preserve the structure of the document.

Example 24.11. Source Code for DomParseServlet.java

package usingjsp.xml;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

import javax.xml.parsers.*;
import org.xml.sax.*;
import org.w3c.dom.*;

public class DomParseServlet extends HttpServlet
{
    public void doPost(HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException
    {
        try
        {
// Create a parser factory
            DocumentBuilderFactory factory = DocumentBuilderFactory.
                newInstance();

// Ask the parser factory to create a new parser
            DocumentBuilder parser = factory.newDocumentBuilder();

// This servlet just sends a plain text response
            response.setContentType("text/plain");

            PrintWriter out = response.getWriter();

// Create an input source around the request reader, ask the parser
// to parse the input source
            Document doc = parser.parse(new InputSource(request.getReader()));

 // Get all the Name elements
             NodeList names = doc.getElementsByTagName("name");

             int numNames = names.getLength();

             for (int i=0; i < numNames; i++)
             {
                 Element e = (Element) names.item(i);
              
                 String firstName = null; 

// See if there is a first name
                NodeList firstNameList = e.getElementsByTagName("first");
                if (firstNameList.getLength() > 0)
                {
                    Element firstNameNode = (Element) firstNameList.item(0);

 // Make the really bold assumption that <first> has a child and that
 // it is text. You really should check first, though.
                     CharacterData nameText = (CharacterData)
                         firstNameNode.getFirstChild();

                         firstName = nameText.getData();
                 }

                 String lastName = null;

 // See if there is a last name
                 NodeList lastNameList = e.getElementsByTagName("last");
                 if (lastNameList.getLength() > 0)
                 {
                    Element lastNameNode = (Element) lastNameList.item(0);

// Make the really bold assumption that <last> has a child and that
// it is text. You really should check first, though.
                    CharacterData nameText = (CharacterData)
                        lastNameNode.getFirstChild();

                    lastName = nameText.getData();
                }

                if ((firstName != null) && (lastName != null))
                {
                    out.println(firstName+" "+lastName);
                }
            }
        }
        catch (ParserConfigurationException exc)
        {
            throw new ServletException(exc.toString());
        }
        catch (SAXException exc)
        {
            throw new ServletException(exc.toString());
        }
    }
}

Parsing XML Using JOX

The JOX library uses a DOM parser to parse through XML. The main reason to use JOX instead of using DOM directly is that JOX automatically copies values from the XML document into a Java bean. Also, JOX is geared towards reading and writing files and fits very well within the servlet framework.

Listing 24.12 shows a servlet that uses JOX to read an XML file and copy its values into a Java bean.

Example 24.12. Source Code for JOXParseServlet.java

package usingjsp.xml;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import com.wutka.jox.*;
import com.wutka.jox.test.*;

public class JOXParseServlet extends HttpServlet
{
    public void doPost(HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException
    {
        TestBean newBean = new TestBean();

        JOXBeanReader reader = new JOXBeanReader(request.getReader());

        reader.readObject(newBean);

 // This servlet just sends a plain text response
         response.setContentType("text/plain");

         PrintWriter out = response.getWriter();

         out.println(newBean.toString());
   }
}

Troubleshooting

Generating XML from a JSP

Q1:

Why doesn't the browser recognize my JSP as an XML file?

A1:

You probably forgot to change the content type to "text/xml".

Using the Sun XML Libraries

Q1:

Why can't the Java compiler find the XML libraries?

A1:

Make sure that both jaxp.jar and parser.jar are in your classpath.

Q2:

My program compiles okay, why does it tell me it can't find the parser library?

A2:

You must have both jaxp.jar and parser.jar in your classpath. The jaxp.jar file defines the standard interfaces for SAX and DOM but doesn't contain any of the implementation classes. You can compile a program using only jaxp.jar but when you run the program, you need to include an actual XML parser in the classpath.

Using JOX

Q1:

Why do I get a ClassNotFoundException when I use the JOX library?

A1:

JOX also requires the Sun XML libraries, so you must also include jaxp.jar and parser.jar in your classpath. Make sure that the JOX jar file is also in your classpath.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.71.72