Chapter 7. Successfully Using JSP and XML in an Application

CONTENTS

Chapter 7. Successfully Using JSP and XML in an Application

IN THIS CHAPTER

  •  Using a Java Representation of an XML Document
  •  Why Not Just Use SAX or DOM?
  •  Installing JDOM and dom4j
  •  Why Both JDOM and dom4j?
  •  Common Ways to Use XML
  •  Using a Database with XML
  •  Pulling in XML Files
  •  Summary

This chapter has two goals. The first is to help you begin walking through using XML in a natural way within your JSP application. The second is to show you how to create and use a Java representation of an XML document.

The chapter will start by introducing JDOM and dom4j. These two APIs enable a programmer to create and use a Java representation of an XML document. Once the XML data is conveniently stashed as a Java object, it then becomes simple to use standard Java techniques to manipulate the data. The other benefit of these APIs is that they have convenient helper classes that make performing common tasks easy with Java and XML.

After we've introduced these two APIs, we'll go through several examples of XML usage with a JSP application.

Using a Java Representation of an XML Document

One of the best features of Java is the object-oriented nature of the language. Objects are containers within which it is easy to store and then later access data. The first trick then is to get the data into the Java objects. In this book, we have shown how to use both SAX and DOM to read data into Java. However, these APIs have limitations. With SAX, you can quickly read the data, but you still need to customize and build your own special classes to handle the data. DOM provides classes to handle the data within Java; however, the data representation given in DOM is bulky and not written with an eye towards Java. This means that the next problem to solve is developing a standard way to represent the XML data within Java. Because XML is a standard method of describing data, it makes sense to create a repeatable method to represent an XML document within Java. The two APIs that have been developed to turn XML into a Java representation are JDOM and dom4j.

At this point, a quick explanation of the names JDOM and dom4j is in order. Because of trademark issues, neither stands for anything. The names are not acronyms. Rather, these are the full names of the two products.

Why Not Just Use SAX or DOM?

There are several different ways to read, write, and manipulate an XML document. Up to this point in the book, we have covered two major APIs: SAX and DOM. With these APIs, a Java programmer can do quite a bit with XML. However, each of these APIs has its weaknesses.

SAX is a very fast way to read in and process an XML document. However, SAX doesn't create a memory representation of the XML document. It instead permits us to stream through an XML document. SAX is a forward-only read process. This means that SAX is perfect when only a single pass through a document is required. However, it isn't always the right solution if the program needs to repeatedly access the same XML document. In cases where the data needs to be used more than once, it makes sense to use SAX to read in the data once and then use another process to work with the data. Finally, many programmers prefer to work against an internal document rather than against a stream process.

It turns out that when JDOM and dom4j are used, programmers will often still use SAX. This is because JDOM and dom4j are not XML parsers. The XML document still needs to be parsed and read into memory. SAX is a fast way to do this. Both JDOM and dom4j have builder classes that enable you to use SAX to quickly read an XML file into memory.

Now let's look at DOM for a moment. As shown in Chapter 5, "Using DOM," DOM is very complete; however, it isn't optimized from a Java coding viewpoint. This means that the Java interface to DOM can be awkward to a Java programmer. In addition, DOM is an extremely memory-intensive model. The large memory footprint of DOM is related to the fact that it's such a complete representation of the XML document. The final representation ends up consuming a very large amount of internal memory. JDOM and dom4j were created as a solution to these and other problems encountered when using DOM within Java.

Both JDOM and dom4j create a Java-optimized representation of an XML document. You can use either to represent the XML document in terms of a Java Collection. Thus, you can use the standard List, Map, and Iterator Java classes when working with the XML document. Note that Java-optimized doesn't necessarily refer to performance. In this case, we are referring to the ability to write and read the Java code in a simpler way. The best tool to use in terms of performance cannot be defined here. Performance depends on internal optimizations within the APIs, which are constantly improving between versions, and the manner in which an API is used in the code. Because these APIs are updated on almost a monthly basis, you have to test for yourself to ensure that the performance is acceptable relative to your project needs.

Installing JDOM and dom4j

This section will briefly cover downloading and installing the JDOM and dom4j APIs. Doing so will enable you to follow along with the examples in this chapter.

JDOM

The JDOM site is found at http://www.jdom.org/.

This book uses beta version 7 of JDOM. Once you have downloaded the JDOM zip file and installed the software, place the JDOM.jar file into your Tomcat's lib directory.

dom4j

The dom4j site is found at http://www.dom4j.org/.

This book uses version 1.1 of dom4j. Once you have downloaded the dom4j zip file and installed the software, place the dom4j.jar file into your Tomcat's lib directory. Of course, it should be noted that dom4j comes with several JAR files. Since we already have JAXP and Xerces installed to run with Tomcat, we only need to use the dom4j.jar file.

Notes

After installing both JDOM and dom4j, stop and restart Tomcat. Also, if you are using NetBeans, don't forget to add the dom4j and JDOM JAR files to the development environment.

Why Both JDOM and dom4j?

You don't really need both JDOM and dom4j since these APIs perform the same task. They both create a Java representation of an XML document. They differ, however, in the approach they take to creating the XML representation. JDOM is based on abstract classes, while dom4j is based on interfaces. In this chapter, we will discuss and use both APIs and will leave it to you to decide which Java XML representation you prefer. I personally use both APIs on the JSP Insider Web site. I just match each API to the particular task I am performing and choose the one that best fits my current needs.

Both JDOM and dom4j are good systems and each is an excellent choice for building a Java representation. The examples in this chapter will show how simple it is to use either API.

JDOM and dom4j: A Quick Comparison

While these APIs do the same thing, there are quite a few differences between the implementations. However, the APIs have the same roots. JDOM is an open source project, and the original dom4j code base is a fork from JDOM. A fork occurs in an open source environment when a project splits into two different projects due to differences in philosophy among its users. The developers of dom4j felt that there was a need for an interface-based version of JDOM, so they started their own project.

One sign that both of these APIs are well established is their inclusion in Sun Microsystems products. The next version of JAXP is slated to have a JDOM builder. JDOM is an active JSR within Java and is slated to be rolled into the Java package in late summer 2002. Meanwhile, JAXM is currently built around dom4j.

We can't give you a clear indication of which API is best to use for your own projects. The choice really just boils down to which API is closest to your needs. To determine this, visit each API's Web site and examine the current features.

Choose the API that comes closer to your needs in terms of its prebuilt features and general performance. At the time of this writing, the only published benchmark showed dom4j to be the faster of the two tools. In addition, dom4j has a few extra helper classes built in for XPath features. However, by the time this book is published, the reverse might be true. The two tools will dance around each other for the next few years. Each tool is growing and changing very rapidly. A feature-by-feature comparison list would be useless here, because such a list would be hopelessly out-of-date by the time the book is published. This means that each new build of one API will offer slightly different performance benefits over the other API. JDOM generally changes less rapidly than dom4j, but it has a larger following than dom4j. Because this book will use both APIs, you will have a chance to judge for yourself which one you prefer while working through the examples.

Common Ways to Use XML

Now it's time to begin mixing XML and JSP together. We will do this by building two main examples. While it's possible to show how to use XML piecemeal, the best way to demonstrate XML use is through larger integrated examples. The two examples in this chapter will show you how to use a database with XML and how to incorporate data from XML files into JSP pages.

Using a Database with XML

This first example is a series of smaller examples tied together. The goal will be to take the data from the BannerAds table discussed in Chapter 1, "Integrating JSP and Data," and store the results in a Java XML representation. Once we've stored the data as an XML representation, we can reuse the same data and JavaBeans for different processes throughout the book. For example, this chapter will show how easy it is to apply XSLT and XPath to create formatted output against data from the database. It would be a good idea to add some additional data to this table and to enter a few values for the starttext and endtext fields. Refer to Table 7.1 for the data entry values used in this example.

Table 7.1. Data Within the BannerAds Database Table
Name Link linktext starttext endtext
Sun http://www.sun.com The Home of Java    
SAMS http://www.samspublishing.com Java books Find great at SAMS
JSP Information http://www.jspinsider.com JSP Insider Get inside your code.  
Jakarta http://jakarta.apache.org Kewl Tools If you need some great JSP software, check out: Jakarta is one of the best sites for JSP code around!

This first set of examples will use dom4j. We will walk through the following steps:

  1. Use an XML initialization file to define the database parameters.

  2. Design a special object to store and retrieve the database connectivity data.

  3. Build a servlet listener file to import the database information to application memory.

  4. Build a Java class to create a JDBC ResultSet and convert it to a dom4j Document.

  5. Create a helper class to perform some common dom4j functionality. This class will be used to apply XSL stylesheets to the Document.

  6. Construct a Java class to create and handle the actual banner ads.

  7. Build a test JSP page to demonstrate everything we've created.

XML Initialization Files

Initialization files provide a time-honored method of data storage. Using an XML file is the best way to build an initialization file. Using XML ensures that the data in the file is in the correct format and is accessible to anyone editing the file.

A standard initialization file is typically used to store database connectivity information for a project. Such a file, named database.xml, is shown in Listing 7.1. Create this file and save it as webapps/xmlbook/WEB-INF/database.xml.

Listing 7.1 XML Database Initialization File
<?xml version="1.0" encoding="UTF-8"?> <database>     <databasename>xmlbook</databasename>     <driver>org.gjt.mm.mysql.Driver</driver>     <url>jdbc:mysql://localhost/xmlbook</url>     <username></username>     <password></password> </database>

Note that the file is stored in the WEB-INF directory, so only the Web application and administrators with direct access to the server machine can access it.

Storing the Initialization Data

To use data from the XML file, you will need to parse it into memory. To do this, just build a special Java class to store the parameters. Listing 7.2 shows a class file named DatabaseParameter.java. Save this file as webapps/xmlbook/WEB-INF/classes/xmlbook/chapter7/DatabaseParameter.java.

Listing 7.2 Database Initialization Object
package xmlbook.chapter7; import java.io.File; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; public class DatabaseParameter extends Object implements java.io.Serializable {     private String databaseName = "";     private String driver       = "";     private String url          = "";     private String userName     = "";     private String password     = "";     public DatabaseParameter() {}     public DatabaseParameter(String as_db,   String as_driver, String as_url,                              String as_name, String as_password)     {setDatabaseName(as_db) ;      setDriver      (as_driver) ;      setUrl         (as_url) ;      setUserName    (as_name);      setPassword    (as_password);     }     public DatabaseParameter(String as_xmlfile) throws Exception     {   try         {   SAXReader xmlReader = new SAXReader();             Document  doc       = xmlReader.read(new File(as_xmlfile));             Element root = doc.getRootElement();             setDatabaseName ( root.element("databasename").getText());             setDriver       ( root.element("driver").getText());             setUrl          ( root.element("url").getText());             setUserName     ( root.element("username").getText());             setPassword     ( root.element("password").getText());         }         catch(Exception e)         {throw e;}     }     public String getDatabaseName ()  {   return databaseName;   }     public String getDriver ()        {   return driver;   }     public String getUrl ()           {   return url;   }     public String getUserName ()      {   return userName;   }     public String getPassword ()      {   return password;   }     public void setDatabaseName (String as_data)  {databaseName = as_data; }     public void setDriver (String as_data)        {driver = as_data; }     public void setUrl (String as_data)           {url = as_data;}     public void setUserName (String as_data)      {userName = as_data; }     public void setPassword (String as_data)      {password = as_data;} }

This class does more than just store the database parameters. It comes equipped with its own reader methods. Pass in the XML file and it can initialize itself:

public DatabaseParameter(String as_xmlfile) throws Exception

The code first creates a SAX reader. This object reads in the XML file and creates a dom4j Document object:

SAXReader xmlReader = new SAXReader(); Document  doc       = xmlReader.read(new File(as_xmlfile));

The Document object is the Java representation of the XML file. Once the code has the Document, it becomes simple to navigate around the XML data. Each tag within the XML document representation is represented using an Element object. The code will need access to the root element of the Document:

Element root = doc.getRootElement();

Once the root element is at hand, it's easy to drill down further into the dom4j Document. This example only requires two methods to query the Document elements. The getText() method returns the text data of an Element. The second method is element, which gives a handle to the first subelement that matches a given name:

setDatabaseName ( root.element("databasename").getText()); setDriver       ( root.element("driver").getText()); setUrl          ( root.element("url").getText()); setUserName     ( root.element("username").getText()); setPassword     ( root.element("password").getText());

This simple code snippet is all we need to use to read the data in from the XML file.

In these few lines of code, it's easy to forget that there is so much happening. This is a multi-step process that entails reading the file, building a Document object, and then parsing through the Java representation. The code here is very simple because dom4j is doing all the work for us. For pure speed, it would be faster just to use SAX and build the events to deal with the data. However, in most cases, especially with smaller XML files, the runtime difference is very small, and the use of dom4j enables a programmer to achieve large time savings in design and programming. Just keep in mind that this isn't always the case. When each millisecond counts, it's time to build your own SAX handler, as discussed in Chapter 6, "Programming SAX."

Of course, this is only the start. Throughout this example, the code will illustrate other ways to use dom4j Documents.

Using a Listener to Store the DatabaseParameter Object

Two common options exist as to how to use the XML database file. The code can read the file as required, or read it once and reuse a cached version of the data. Since the information in this example will not change very often, it makes sense to read the data once and then cache the results.

The best way to do this is to build a listener class to initialize everything upon startup of the JSP application. The servlet specification supports several events within a JSP container against which a programmer can hang a listener event. A listener can be defined against state changes in the application (ServletContext) or the session (HttpSession) objects. In practice, this means that it's possible to listen for the creation or destruction of the application or session objects (known as life cycle events). It's also possible to listen for the creation, removal, or updating of attributes (attribute events) stored in the application or session. To create a listener, a class must implement an appropriate interface, as described in Table 7.2.

Table 7.2. Servlet Listener Interfaces
Interface Purpose
javax.servlet.ServletContextListener application life cycle
javax.servlet.ServletContextAttributesListener application attributes
javax.servlet.http.HttpSessionListener session life cycle
javax.servlet.HttpSessionAttributesListener session attributes

Note that these interfaces are abstract, so a programmer must create all the methods declared within the interface. This example requires an event to be triggered when the application starts up. This means that the code will implement ServletContextListener.

This part of the example is all about building a listener. Such a file is shown in Listing 7.3. Save it as webapps/xmlbook/WEB-INF/classes/xmlbook/chapter7/DatabaseInitialization.java.

Listing 7.3 Creating an Application Startup Listener
package xmlbook.chapter7; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public final class DatabaseInitialization implements ServletContextListener {    public void contextInitialized(ServletContextEvent event)     { ServletContext application = event.getServletContext();       try       { String  ls_xml  = application.getRealPath("WEB-INF/database.xml");          DatabaseParameter dbdata = new DatabaseParameter(ls_xml);          application.setAttribute("DatabaseParameter",dbdata);       }       catch (Exception e)       { application.log("Problem encountered at Startup:" + e.toString());       }    }    public void contextDestroyed(ServletContextEvent event)    {} }

The code in the listener is basic. The class determines the actual path to the database initialization XML file. Then it creates a DatabaseParameter object. The DatabaseParameter object is stashed in a ServletContext attribute. Once it is in the ServletContext, the object is accessible to every JSP page through the application implicit object.

The next step in building a listener object is to tell the JSPContainer about the listener. Listing 7.4 shows how to update the web.xml file located at webapps/xmlbook/WEB-INF/web.xml.

Listing 7.4 Updating the web.xml File
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app   PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"   "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"> <web-app>     <listener>         <listener-class>          xmlbook.chapter7.DatabaseInitialization         </listener-class>     </listener>     ... Your original web-app contents here! </web-app>

NOTE

Make sure that the web.xml file is defined using version 2.3 of the DTD. (Listeners are a servlet 2.3 feature.)

The JSP container will register listeners in the order of definition within the web.xml file. Thus, the listeners are executed in order of placement. The listeners need to be defined before any servlets are declared within the web.xml file. No servlets are currently defined, so this isn't an issue for this example. Finally, the entries don't tell the JSP container the style of listener being deployed in the web.xml file. The Web container determines this from the interface the listener implements.

At this point, we must repeat our normal routine; it's time to restart Tomcat. A word of warning: An error in the listener class will prevent the Web application from being loaded as a Web application. If this happens, it will not be available for access. The error will be a general The requested resource (/URL) is not available message. In case of such a failure, double-check the log file. Within the Tomcat log file, you'll find the following message:

Error configuring application listener

Examine and solve the error posted in the log file. This error shouldn't happen, but if it does occur, it's disconcerting enough to earn this little warning.

The code we've built up to this point runs behind the scenes. It's time now to build the database portion of the example.

Using a Java XML Model

Let's expand the banner ad example from Chapter 1. This example will read the data from the database and generate a Java XML object representation of the data. Then the Java XML representation will be stored in memory for easy access to the data.

Now we'll build the files needed for the automated text banner system. The first class that we will create, shown in Listing 7.5, will translate a JDBC ResultSet to a dom4j Document object. Save this file as webapps/xmlbook/WEB-INF/classes/xmlbook/chapter7/XMLFromResult.java.

Listing 7.5 XMLFromResult.java
package xmlbook.chapter7; import java.sql.*; import org.dom4j.Document; import org.dom4j.DocumentFactory; import org.dom4j.util.NonLazyDocumentFactory; import org.dom4j.Element; public class XMLFromResult {   public XMLFromResult() {}     private String rootName = "Statement";     private String rowName  = "row";     public String getRootName ()      {   return rootName;   }     public String getRowName  ()      {   return rowName;    }     public void setRootName (String as_data)  {rootName = as_data; }     public void setRowName  (String as_data)  {rowName = as_data;  }     public XMLFromResult(String as_root, String as_row)     { setRootName (as_root);       setRowName  (as_row);     }     public Document createXML (DatabaseParameter dbdata, String as_query)     throws Exception     {  return createXML ( dbdata, as_query, false);  }     public Document createXML (DatabaseParameter    dbdata,                                String               as_query,                                boolean              ab_embedmetadata)     throws Exception     {   String ls_dburl     = dbdata.getUrl();         String ls_dbdriver  = dbdata.getDriver();         Connection dbconn   = null;         Document   resultxml= null;         try         {   Class.forName(ls_dbdriver);             dbconn = DriverManager.getConnection(ls_dburl);             Statement statement = dbconn.createStatement();             if (statement.execute(as_query))             {   ResultSet  results   = statement.getResultSet();                 resultxml =  CreateDocument(results,ab_embedmetadata);             }         }         catch (ClassNotFoundException e)         {   throw e;  }         catch (SQLException e)         {   throw e;  }         finally         {   try             {   if (dbconn != null)                 { dbconn.close(); }             }             catch (SQLException e)             {   throw e;  }         }        return resultxml;     }     public Document CreateDocument(ResultSet a_result)     { return CreateDocument(a_result, false);  }     public Document CreateDocument(ResultSet a_result,boolean ab_embedmetadata)     {DocumentFactory factory  = NonLazyDocumentFactory.getInstance();      Document xmldoc          = factory.createDocument();      Element root = xmldoc.addElement(getRootName());         if(a_result != null )         {   try             {              ResultSetMetaData  metadata  = a_result.getMetaData();              int li_columns = metadata.getColumnCount();              int li_rows    = 0;                 while(a_result.next())                 {   li_rows++;                      Element row = root.addElement( getRowName())                     .addAttribute( "rownum", String.valueOf(li_rows));                     for ( int i = 1; i <= li_columns; i++)                     {   if(a_result.getObject(i) == null)                         {row.addElement( metadata.getColumnLabel(i) )                             .addText("");                         }                         else                         {row.addElement( metadata.getColumnLabel(i) )                             .addText( a_result.getObject(i).toString() );                         }                     }                 }              /* embed metadata if asked for */              if (ab_embedmetadata)              { Element mrow = root.addElement("metadata");                /* embed row count */                mrow.addElement( "rowcount" )                       .addText( String.valueOf(li_rows) );                /* embed column datatypes */                for ( int i = 1; i <= li_columns; i++)                {    mrow.addElement(metadata.getColumnLabel(i))                            .addText(metadata.getColumnTypeName(i));                }              }             }             catch(Exception e)             { root.addElement( "error" ).addText(e.toString());  }        }     return xmldoc;     } }

Let's examine the points of interest within the XMLFromResult class. The first thing to notice is the rootName and rowName properties. These properties are important because the data being returned from the database isn't in XML format. Most importantly, the SQL results are missing a root element and the row element containing each row of data. When the logic goes to build the Document object, the code will need a name for the root element and the element containing data from each row.

The code for accessing the database is similar to the code described in Chapter 1. The new aspect of this object is the CreateDocument method. This method will loop through a ResultSet and create the actual Document object. Creating the actual Document only takes a few lines. The first step is to get a DocumentFactory; with this, it's possible to create an initial Document:

DocumentFactory factory  = NonLazyDocumentFactory.getInstance(); Document xmldoc          = factory.createDocument();

Once the Document object is created, it's a simple matter to add elements using the addElement method:

Element root = xmldoc.addElement(getRootName());

Then the code loops through and appends one row element at a time to the Document:

Element row = root.addElement( getRowName())                   .addAttribute( "rownum", String.valueOf(li_rows));

Notice that this code will also append an attribute with the addAttribute method to each row to indicate the row number the element represents. While not necessary, this will ensure that the Document can return the data in the same order as the original SQL. Within each row element, the code will then add each column of data. The actual element name will be created to match the column name returned by the SQL statement. The only new method of note here is addText, which appends a string to serve as the text data within the element:

for ( int i = 1; i <= li_columns; i++)                     {   row.addElement( metadata.getColumnLabel(i) )                            .addText( a_result.getObject(i).toString() );                     }

Note that after the data from the SQL statement is processed, the code has one additional step:

 if (ab_embedmetadata) {Element mrow = root.addElement("metadata");   /* embed row count */   mrow.addElement( "rowcount" )          .addText( String.valueOf(li_rows) );   /* embed column datatypes */   for ( int i = 1; i <= li_columns; i++)   {    mrow.addElement(metadata.getColumnLabel(i))                .addText(metadata.getColumnTypeName(i));   }

The code creates an additional optional element called metadata to store important metadata. In this example, the code stores the row count and the column data types. This example could be easily expanded to store other metadata information. It's our way to embed additional data into the XML representation. The XML data is all string based; once it is placed within the Document, the data is stored as a String. The information from the metadata element makes it possible to recall the original data type and perform special logic when the need arises.

Threading Issues

In dom4j, the default implementation of Element (DefaultElement) can use lazy instantiation to avoid creating List instances. This can cause problems when a document object is shared among threads without being traversed. In a JSP application, it's likely that the code will share a Document object across threads, so we must put some thought into this potential problem. In the case of Listing 7.5, we don't know whether the document object will be accessed by multiple threads. The reason? This is a generic object for use in different processes. This means that the object needs to be designed with thread safety in mind. The code will use the NonLazyDocumentFactory to ensure that no lazy evaluation occurs. Thus, the Document object will be safe for read-only access across concurrent threads.

Keep in mind, additional logic will have to be implemented when this object is within a process that will use the Document object for concurrent modifications. For our purposes, concurrent modifications will not be an issue, as the code is used in a read-only capacity.

Getting the Row Count

One of the our goals is to display a random entry. This means that the code will need to get the count of banners. In this case, the code builds an XML file to match the RecordSet, so it's simple to count the rows while building the XML document.

While getting the row count is simple in our example, this wasn't always the case. Within JDBC, this seemingly simple task turns out to be more difficult than it appears. There isn't a method to directly count rows in JDBC. The reason? Databases can start returning rows as soon as they are available to output. This means that a count function could return inaccurate numbers as rows stream into the resultset.

A programmer has access to two standard methods to retrieve a row count from the ResultSet. One method is to use a Count within the SQL statement. Of course, this method has the problem that the count could change between the time of the count and the time of the SQL statement operation.

The second method is to perform the count once you've obtained the recordset. The problem here is one of speed. First, move to the last row using the last() method. Then, ask for the current row number using the getRow() method. Then, move back to the first row using the first() method. At this point, the code can continue its normal processing. This method is slow and at times can be extremely resource intensive.

Ironically, it's easier to perform the count once the data is stored in the Document object. Using dom4j, you can either perform an XPath statement (not the fastest way to count the data) or obtain a List of the elements from which you can get an element count.

Why get into the gory details of counting? This example illustrates that dom4j and JDOM introduce new tools. Programmers accustomed to logic using JDBC now have additional ways to view, examine, and modify data within a Java XML representation. Code and logic that used to be written exclusively for dealing with a JDBC ResultSet might at times be better written to work against the dom4j Document object.

XML and the WebRowSet

In Java 1.4, JDBC 3.0 will introduce the WebRowSet. This Java class is based on the CachedRowSet class. The nice thing about this class is that it can describe the data as an XML document. This means that we could simplify the code in Listing 7.5 using the WebRowSet. The WebRowSet isn't covered in this book, as the code is written using the Java 1.3 specification.

Building a dom4j Helper Class

Listing 7.6 contains a class that handles the dom4j Document object we created in the XMLFromResult.java class. Save this new class as webapps/xmlbook/WEB-INF/classes/xmlbook/chapter7/ProcessDom4J.java.

Listing 7.6 ProcessDom4J.java, a Class to Process a dom4j Document
package xmlbook.chapter7; import org.dom4j.Document; import org.dom4j.io.DocumentResult; import org.dom4j.io.DocumentSource; import org.dom4j.io.*; import java.io.*; import javax.servlet.jsp.JspWriter; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.*; public class ProcessDom4J { public ProcessDom4J() {} public void produceXML (JspWriter out, Document a_xmlresult) throws Exception {   try     {   OutputFormat format = OutputFormat.createPrettyPrint();         XMLWriter writer = new XMLWriter( out, format );         writer.write(a_xmlresult);         writer.close();     }     catch (Exception e)     {  throw e; } } public void applyXSL (JspWriter out,    Document a_xmlresult,                       String    as_xsl) throws Exception {       StreamSource xsl    = new StreamSource(new File(as_xsl));         StreamResult result = new StreamResult(out);         Transformer transformer;         TransformerFactory factory = TransformerFactory.newInstance();         transformer = factory.newTransformer( xsl );         // Creates a JAXP Source from the dom4j document         DocumentSource source = new DocumentSource( a_xmlresult );         // create the output stream from the xsl / document combo         transformer.transform(source, result); } }

Listing 7.6 represents a helper class that makes it easy to manipulate a dom4j Document. Only a few functions are included here to demonstrate some basic capabilities.

The first method of note is produceXML:

public void produceXML (JspWriter out, Document a_xmlresult) throws Exception {   try     {   OutputFormat format = OutputFormat.createPrettyPrint();         XMLWriter writer = new XMLWriter( out, format );         writer.write(a_xmlresult);         writer.close();     }     catch (Exception e)     {  throw e; } }

This method takes a Document object and applies the dom4j OutputFormat object against the Document. More specifically, the createPrettyPrint method is used to take the Document and transform it into a simple XML document.

The second method is called applyXSL. This method first reads in an XSL file:

{       StreamSource xsl    = new StreamSource(new File(as_xsl));         StreamResult result = new StreamResult(out);

It then creates a Transformer object with which to apply the XSL stylesheet against a document:

Transformer transformer; TransformerFactory factory = TransformerFactory.newInstance(); transformer = factory.newTransformer( xsl );

However, the XSL isn't applied directly to the dom4j Document object. Rather, the code creates a JAXP data source against the Document object through which to apply the XSL:

DocumentSource source = new DocumentSource( a_xmlresult );

Then, the code applies the source against the XSL and sends the data back to the output buffer:

transformer.transform(source, result);

Creating a Banner Handler

Listing 7.7 shows a class that handles the actual banners. Save this new class as webapps/xmlbook/WEB-INF/classes/xmlbook/chapter7/BannerAdStore.java.

Listing 7.7 BannerAdStore.java, a Class to Create Banner Ads
package xmlbook.chapter7; import java.util.Iterator; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.Node; public class BannerAdStore  implements java.io.Serializable { private  Document bannerData = null; public void setBannerData(Document data) {bannerData = data;} public Document getBannerData() {return bannerData;} public BannerAdStore() {} public BannerAdStore(Document data) {setBannerData(data);} public BannerAdStore(DatabaseParameter dbdata) {   XMLFromResult result= new XMLFromResult("BANNERS","BANNERAD");     try     {  String ls_sql = "select name as NAME," +                        "       link as LINK," +                        "       linktext as LINKTEXT," +                        "       starttext as STARTTEXT," +                        "       endtext as ENDTEXT" +                        "       from BannerAds";        setBannerData(result.createXML (dbdata, ls_sql,true));     }     catch(Exception e)     {  setBannerData(null);     } } public String randomBannerLink() { int li_row = 1 + (int) (Math.random() * getCount());   return produceBannerLink(li_row) ; } public String produceBannerLink (int bannerid) {    String xpath = "//BANNERAD[" + bannerid + "]";      Node bannernode  = bannerData.selectSingleNode( xpath );      String link      = bannernode.valueOf( "LINK" );      String linktext  = bannernode.valueOf( "LINKTEXT" );      String start     = bannernode.valueOf( "STARTTEXT" );      String end       = bannernode.valueOf( "ENDTEXT" );      String ls_banner = " <a href=\"" + link + "\">" + linktext + "</a> ";      return start + ls_banner + end; } public int getCount () {    Element root = bannerData.getRootElement();      String ls_count = root.element("metadata").element("rowcount").getText();      return Integer.parseInt(ls_count.trim()); } public String[] bannerArray () {   String links[] = new String[getCount()];     Element root = bannerData.getRootElement();     Iterator banner_iterator = root.elementIterator("BANNERAD");     int li_count = 0;     while(banner_iterator.hasNext())     {        Element bannerad = (Element)banner_iterator.next();        String link      = bannerad.element("LINK").getText();        String linktext  = bannerad.element("LINKTEXT").getText();        String start     = bannerad.element("STARTTEXT").getText();        String end       = bannerad.element("ENDTEXT").getText();        String banner = " <a href=\"" + link + "\">" + linktext + "</a> " ;        links[li_count]  = start + banner + end;        li_count++;     }     return links; } }

The purpose of this class is to create and store a banner document. The banner document is a dom4j Document representation of every banner stored in the database. The class also has several access methods to randomly extract a banner and to pull out specifically requested banners.

The first property to examine is the current Document object containing all the latest banners from the database:

private  Document bannerData = null;

The class also has a constructor class to automatically create and store the bannerData Document object:

public BannerAdStore(DatabaseParameter dbdata) {   XMLFromResult result= new XMLFromResult("BANNERS","BANNERAD");     try     {  String ls_sql = "select name as NAME," +                        "       link as LINK," +                        "       linktext as LINKTEXT," +                        "       starttext as STARTTEXT," +                        "       endtext as ENDTEXT" +                        "       from BannerAds";        setBannerData(result.createXML (dbdata, ls_sql,true));     }

In this constructor, the code is geared to create an XML representation that will be compatible with the XSL stylesheet BannerAds.xsl created in Listing 4.2 of Chapter 4, "A Quick Start to JSP and XML Together."

Once this object is created, the code has several functions that produce a banner ad to be displayed. Notice the actual use of XPath. One advantage of using dom4j is the ability to use XPath statements to query the Document object. Using the selectSingleNode and selectNodes() methods, you can obtain a Node and List sorted relative to an XPath expression. This is a very powerful search technique for finding data within the Document object:

public String produceBannerLink (int bannerid) {    String xpath = "//BANNERAD[" + bannerid + "]";      Node bannernode  = bannerData.selectSingleNode( xpath );      String link      = bannernode.valueOf( "LINK" );      String linktext  = bannernode.valueOf( "LINKTEXT" );      String start     = bannernode.valueOf( "STARTTEXT" );      String end       = bannernode.valueOf( "ENDTEXT" );      String ls_banner = " <a href=\"" + link + "\">" + linktext + "</a> ";      return start + ls_banner + end; }

In produceBannerLink, the code uses XPath to search for a particular banner within the Document. Once the node with the matching banner is obtained, the valueOf method can be used to extract the text data from the subelements within the node.

The class also has a function to produce a String array containing completed text banners of each banner. The bannerArray function will be used in the next chapter for the Web service methods. The bannerArray function differs from produceBannerLink in that it doesn't use XPath. Rather, it uses an Iterator to loop through the entire Document:

Element root = bannerData.getRootElement(); Iterator banner_iterator = root.elementIterator("BANNERAD"); while(banner_iterator.hasNext()) {

Once a row Element is obtained, the next step is simply to use the element and getText methods to get the banner data:

Element bannerad = (Element)banner_iterator.next(); String link      = bannerad.element("LINK").getText(); String linktext  = bannerad.element("LINKTEXT").getText(); String start     = bannerad.element("STARTTEXT").getText(); String end       = bannerad.element("ENDTEXT").getText();

The data is then used to build a link with surrounding text. This data is used to build the String array containing the text-based banners:

String banner = " <a href=\"" + link + "\">" + linktext + "</a> "; links[li_count]  = start + banner + end;

The nice thing about BannerAdStore is that all of the code is specifically geared towards generating and retrieving a final banner. The actual object doesn't have any special methods or properties for dealing with data manipulation of the individual banner elements. By storing the data within a Document object, the class receives the benefit of pushing all of the assessor functions used for data control back towards the simple dom4j methods. As developers, we don't have to spend much time customizing the class for it to handle the data intelligently. The dom4j API takes care of this aspect of data manipulation for us.

Creating a Test JSP Page

The last step of this example is to build a JSP page to access a banner. In Appendix C,"Tag Library," we will show you how to build a tag library interface for these classes. In addition, Chapter 8, "Integrating JSP and Web Services," will show you how to build a Web service interface for this same code. Listing 7.8 builds a simple JSP page, which you should save as webapps/xmlbook/chapter7/DisplayBannerText.jsp.

Listing 7.8 DisplayBannerText.jsp, Used to Display a Banner
<%@page contentType="text/html"         import="xmlbook.chapter7.*,                 org.dom4j.Document"%> <%DatabaseParameter dbdata;   dbdata = (DatabaseParameter)  application.getAttribute("DatabaseParameter");   BannerAdStore banners = new BannerAdStore(dbdata); %> <html><head><title>Using a complete banner</title></head><body> Calling the Banner 5 times for kicks <div align="center"> <% for (int i = 0 ; i < 5 ; i++) {     out.print("<p>" + banners.randomBannerLink() +  "</p>" ); } %> </div> </body></html>

The code in this page is basic; it merely retrieves our database parameters stored in the application space, creates a BannerAdStore object, and has it return a few random banners from the XML document representation of the banner ad list from the database. The page will produce the result shown in Figure 7.1.

Figure 7.1. Results of running DisplayBannerText.jsp.

graphics/07fig01.gif

This example is basic. Using the XML representation hasn't gained us very much compared to using a straight database resultset. It all depends on how the code needs to use the data. The nicest aspect of using the XML representation is that the code is clean and easy to use. This makes these objects easy to expand and use for other purposes. In fact, in later chapters we will take this example and further demonstrate other concepts, such as how to build a tag library and implement a Web service.

You should cache BannerAdStore into memory to allow it to be reused by multiple pages. You can store the object in either the application or session space. Re-creating the Document object would be inefficient it can be reused since the banners don't change frequently. The strength of this example comes from its use of a Document object. Let's examine what this will gain for us.

Using a Java Representation of an XML Document

One powerful technique of Java is to create an object representation of data used within an application. Any object that defines and stores data in this fashion is referred to as a datastore. One advantage of placing the data into a JDOM or dom4j Document object is that doing so gives us a standard way to describe data and create a datastore. This permits the use of the same methods and techniques across many different data objects created as Documents. The Java XML API becomes a common interface for most of the data objects needed within our code. This is an incredible boon, as it makes our life much easier by reducing the number of data interfaces we need to learn and use. Also, when a data object changes with time (which it will; that's the natural law of code entropy at work), the actual data interface doesn't change. This reduces the maintenance cost of the data object over the long term.

This technique doesn't replace the use of business objects to encapsulate data. Instead, it is presented as another method in our toolkit of practices for encapsulating data within a Java project. The benefits of using Document-based datastores are discussed in the "HashMap Versus Java XML Representation" section of this chapter.

Using JAXB

Several options are open to us when we build an XML-based datastore. In this chapter, the code uses dom4j and JDOM. However, JAXB is another consideration. If the data source were an XML file with a defined schema, JAXB could be an interesting choice for building a datastore. This API would be very fast and would automate most of the work involved in building our JavaBean object representation (JAXB could take the XML file and quickly create our Java object based on the XML schema). However, at the time of this writing, JAXB is still too young and incomplete. By time this book is published, JAXB should be close to final release. After you work through this chapter, we encourage you to also consider JAXB as another option in building the XML data blocks used within Java.

HashMap Versus Java XML Representation

Moving every piece of data into an XML datastore isn't always an appropriate solution. Some programmers think of using an XML representation as another layer of work. This extra layer is costly in terms of extra programming and data processing. For example, a very common method to store the data from the JDBC ResultSet involves pushing the data into a HashMap. This is a fast and reliable method for storing data within a Java application.

So the question now becomes Why use a Document representation when it's faster to use a HashMap? The advantage of using an XML datastore is that it offers some flexibility. The layer of XML abstraction makes it easy to reuse a common XML style interface over the data. This in itself isn't a compelling reason, as the HashMap also introduces a level of abstraction in storing the data. However, the XML Document introduces different ways to handle and manipulate the data. Once the data is stored in a JDOM or dom4j Document, we gain functionality, such as the ability to use XPath and XSLT. In addition, the data is very easy to translate to different XML formats. JDOM and dom4j offer direct access to XML data sources. This enables us to easily aggregate data from many sources. Also, when performing a task that requires the manipulation of data over time, using the XML representation is easier. The XML representation can be easily stored in memory and written out as a permanent XML file for long-term storage. Finally, a HashMap doesn't allow for duplicate keys, while XML allows for multiple elements of the same name.

An XML representation only allows the storage of String-based data. If you need to store Java objects, a HashMap is the way to go. You should also use a HashMap when the code needs speed. On the other hand, you should use a Java XML representation when you need long-term data access or flexibility for data access and manipulation.

Pulling in XML Files

Our second example will use JDOM. This example shows how to drive a JSP page from a collection of XML files. In this particular case, the code is based on the XML files of the JSPBuzz newsletter. The JSPBuzz is a bimonthly newsletter stored within an XML file. The goal of the example is to build an archive page that lists the contents of each issue of the JSPBuzz.

As more data is stored within XML files, it will become a common practice to mix and match various XML sources to produce different views to display to the user. The first thing that needs to be mentioned is that storing data within XML files is not as efficient as using a database. Another reality to consider is that XML files are like dust over time, they will collect and gather into XML "file piles" around a Web application. For situations where the files are small and the usage load isn't overwhelming, it's quite acceptable to use the XML files directly. In these situations, the XML files are accessed directly as a data source.

If speed is of utmost importance, or if user load is beginning to drag the server down, you should quickly import the XML into a database.

Defining an XML File

First, we'll define an XML file for the example to use. Listing 7.9 uses a shortened version of an October issue of the JSPBuzz. Save the file as webapps/xmlbook/chapter7/buzz_10_09_2001.xml.

Listing 7.9 Shortened Version of buzz_10_09_2001.xml
<?xml version="1.0" encoding="UTF-8"?> <jspbuzz volumn="II" issue="19">     <title>JSP Buzz - October 9th, 2001 - Misc Tidbits</title>     <description>A Java Current Event Newsletter. </description>     <buzzdate>10/9/2001</buzzdate>     <sponsor>Amberjack Software LLC</sponsor>     <sponsor_text> Sponsored by Amberjack Software LLC. </sponsor_text>     <ramble position="1">         <author>Casey Kochmer</author>         <title>More on Web Services</title>         <topicbody position="1">                    <body>In continuing to play with Web services, I am amazed                          at the simplicity of building a Web service. With                          little experience and existing tools any programmer                          can implement a Web service. The problems with Web                          services isn't in building a Web service but with                          everything else surrounding Web services. The problems                          I see are:</body>         </topicbody>         <topicbody position="2">                     <body>-The overall marketplace is in confusion with general                            economic conditions.</body>         </topicbody>         <topicbody position="3">                     <body>... The article continues on in the full version                     </body>         </topicbody>     </ramble>     <buzzlink type="news" numericdate="20011007" position="1">         <reference>Microsoft</reference>         <title>Microsoft Plans Java Counterpunch for .NET</title>         <link>http://www.theregister.co.uk/content/4/22088.html</link>         <author>The Register</author>         <date>October 7th, 2001</date>         <body>Rumors of Java.Net (J#) from the land of Microsoft</body>     </buzzlink>     <buzzlink type="link" numericdate="20010925" position="1">         <reference>Discussion</reference>         <title>Succeeding as a Developer In Todays Economy </title>         <link> http://theserverside.com/discussion/thread.jsp?thread_id=9215                </link>         <author>The ServerSide</author>         <date>September 25th, 2001</date>         <body>A discussion and article about the current marketplace and                       the skills required to survive in the current and future                       chaos. Well worth the time to read if you are nervous                       about your job situation.</body>     </buzzlink>     <buzzlink type="link" numericdate="20011003" position="2">         <reference>Article</reference>         <title>Where are Web Services Today?</title>         <link> http://www.webservicesarchitect.com/content/articles/mark01.asp                </link>         <author>Mark Waterhouse</author>         <date>October 3rd, 2001</date>         <body>A relatively positive article on Web services.  It reviews                       some strengths and weaknesses of Web services.</body>     </buzzlink>     <buzzlink type="link" numericdate="20011001" position="3">         <reference>Site</reference>         <title>XMethods</title>         <link>http://www.xmethods.com/</link>         <author/>         <date/>         <body>A decent site with a listing of Web services to access and                       some basic Web service information.</body>     </buzzlink>     <buzzlink type="link" numericdate="20010901" position="4">         <reference>Article</reference>         <title>XML in Java : Document Models, Part 1 </title>         <link> http://www-106.ibm.com/developerworks/xml/library/x-injava/                </link>         <author>Dennis M. Sosnoski</author>         <date>September 2001</date>         <body>A must read article for anyone playing around with Java                       and XML. It's both a review of current XML parsers and                       comparison of performance results. Great article.</body>     </buzzlink>     <buzzlink type="product" numericdate="20011007" position="1">         <reference>JSP Container</reference>         <title>Tomcat 4.0 Maintenance Release #1</title>         <link>http://jakarta.apache.org/</link>         <author>Jakarta</author>         <date>October 7th, 2001</date>         <body>Tomcat 4.0 maintenance release #1 is now available.</body>     </buzzlink>     <buzzlink type="product" numericdate="20011001" position="2">         <reference>Build Tool</reference>         <title>Ant Beta 1 Released</title>         <link>http://jakarta.apache.org/ant/index.html</link>         <author>Jakarta</author>         <date>October 1st, 2001</date>         <body>Various bug fixes for Ant 1.4 are in this release.</body>      </buzzlink>      <copyright> JSP Buzz Copyright 2001 Amberjack Software LLC.                  All rights reserved.</copyright>      <trademarks>Sun, Sun Microsystems, JSP and Java are registered                  trademarks of Sun Microsystems, Inc. in the United States                  and other countries. JSP Insider and JSP Buzz are                  independent of Sun Microsystems, Inc. </trademarks> </jspbuzz>

This XML file is unremarkable. It contains the links and data needed to create a JSPBuzz newsletter. The file contains no HTML markup. All presentation logic is performed through either XSL or Java programming logic. The JSPBuzz XML format uses attributes within elements to store metadata that aids in displaying the data. For example, any element for which display order is important has a position attribute to indicate the order in which the data is to be displayed. The other pieces of metadata stored within the XML file are the element names themselves. For example, header elements are always displayed before body elements and footer elements are displayed after body elements.

Pondering XML Design

One lesson learned from building the XML version of the JSPBuzz is the importance of a solid XML layout. The XML file layout is as important as any database design. Time and careful planning are required to build a suitable XML layout. The final design should meet three needs: storage of data, data design that complements coding requirements, and creation of metadata for special presentation needs.

The first purpose of the XML document is holding the data in an efficient manner. This idea is basic, since an XML file stores data. However, while data storage is the primary purpose of an XML document, it represents only one third of the design picture.

The second aspect of XML design is the consideration of how the data gets placed into the XML file. An XML schema with poor design can have a negative impact on how easy it is to create and maintain an XML document. This impact is due to the fact that XML layout is very flexible. It is possible to create many different variations of an XML file design to hold the same data. For example, data can be stored either as an attribute or as an element. Simple differences like this can impact how XSLT and Java processes are written to process an XML file. So in simple terms, make sure an XML design supports the way users and programmers will enter and extract data from the XML files.

The third consideration of XML design is producing a schema that supports a process using the XML-based data. It's important to remember that XML is not a database. Rather, XML is a bridge between processes. This means an XML dataset might need metadata to support the processes that use the XML dataset. Oftentimes XML data is used to drive presentations with the help of XSLT. This translates into a requirement to include some metadata to support presentation needs.

The JSPBuzz newsletter provides a quick example. Within the XML file, the JSPBuzz stores the story position. Without this information, the XSLT stylesheet would have no way to properly order the stories from the newsletter. This positional information has nothing to do with the news story data; it is purely for presentation purposes. This simple example shows that building an XML file can indeed be different from building a database dataset. XML serves different purposes, and driving presentation of data is one of those purposes. Be warned, the design of an XML file should minimize how much presentation data is stored within the XML file. The reason for this is that presentation data can be very arbitrary; it is subject to interpretation and changes over time. Building an XML file is at times a fine art that takes practice and time to learn.

Now, there will be XML programmers who disagree with the second and third statements. That's fine; there's plenty of room for debate. We make the points here to get you thinking about XML design. You should walk away with the understanding that XML is not a database, which also means that the design of an XML file doesn't necessarily follow the same rules as database design. Unfortunately, many programmers who have database experience automatically assume that XML file design should match database design. This assumption can cause problems.

Typically, you should plan and design XML files towards the end of the database design phase of a project. Just as changing a database design in mid-project necessitates reprogramming and extra work, the same is true for XML file formats. In fact, we would go so far as to say that a bad XML file design is harder to fix than a bad database design. The reason is one of scope. XML files typically are used to communicate between projects. An XML specification is released to many programmers outside the project. Releasing a bad design early on affects every related project. Sadly, we have seen projects delayed by months because of the premature release of XML specifications. Remember, not only does releasing a bad XML file make a project look bad, but chasing down everyone affected by a design change is very time-consuming.

A well-designed XML schema is an important first step in successfully using JSP and XML together within an application.

Reading XML Files and Creating New Output

Now let's build a class to take the XML data and convert it to a JSPBuzz table of contents listing. In building this example, we considered two options for generating the output:

  • Use an XSL stylesheet and XPath. This has the advantage of being modular. Using a stylesheet would make it a simple matter to swap XPath statements around to change the processing of the JDOM Document. In addition, most of the logic already existed in the JSPBuzz stylesheet used to build the current newsletter page. The disadvantage is that the XPath solution generally would be the slower of the two options. In addition, a stylesheet doesn't have access to other data stored in the Web application, or the Java class libraries.

  • Use Java logic to work through the JDOM Document. The advantage of this solution is that it uses the Java language directly. Our class would have all the resources available to a Java program and servlet.

The choice of an approach was a difficult one to make. In this case, speed was not an issue and most of the logic existed in a stylesheet already in use. However, the XML file didn't contain the location of the various JSP pages displaying the JSPBuzz newsletter. It was simpler to build the actual HTML links within a Java class. For this reason, we decided to use Java logic to work with the JDOM Document.

The class we used to convert the XML data to the JSPBuzz table of contents appears in Listing 7.10. Save this file as webapps/xmlbook/WEB-INF/classes/xmlbook/chapter7/ProduceListing.java.

Listing 7.10 ProduceListing.java, Used to List the Contents of a Newsletter
package xmlbook.chapter7; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.List; import javax.servlet.jsp.JspWriter; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; public class ProduceListing {   public ProduceListing() {}     public void listContent(String    as_site,                             String    as_file,                             String    as_path,                             JspWriter out) throws JDOMException, IOException     { try       { // Use Sax to Read the File, Uses Builder as Defined in JAXP.         SAXBuilder builder = new SAXBuilder();         // Build the JDOM Document         Document doc = builder.build(new File(as_path + "/" + as_file));         // Get the Root Element         Element root = doc.getRootElement();        // Build a table to list all items.        out.print("<table><tr>");        out.print("<td align=\"center\" valign=\"bottom\" class=\"GreenBox\">");        out.print("<a href=\"" + as_site + as_file + "\"/>");        out.print(root.getChild("title").getTextTrim());        out.print("</a></td></tr><tr>");        out.print("<td align=\"left\" valign=\"top\" class=\"OffWhiteBox\">");        out.print("<ul>");        /* first gather the links and re-sort them to the proper order*/        List buzzlinks = root.getChildren("buzzlink");        int  li_count = buzzlinks.size();        String[] news_links    = new String[li_count];        String[] general_links = new String[li_count];        String[] product_links = new String[li_count];        Iterator i = buzzlinks.iterator();        while (i.hasNext())        {   Element link = (Element) i.next();            if (link.getAttributeValue("type").equals("news"))            {int position =Integer.parseInt(link.getAttributeValue("position"));             news_links[position] = link.getChild("title").getTextTrim();            }            if (link.getAttributeValue("type").equals("link"))            {int position =Integer.parseInt(link.getAttributeValue("position"));             general_links[position] = link.getChild("title").getTextTrim();            }            if (link.getAttributeValue("type").equals("product"))            {int position =Integer.parseInt(link.getAttributeValue("position"));             product_links[position] = link.getChild("title").getTextTrim();            }        }         /*first print out the news items */         for (int loop = 0; loop < li_count; loop++)         { if (news_links[loop] != null)           out.print("<li>" + news_links[loop] +"</li>");         }         /* Then list the Ramble links */         List buzztopics = root.getChildren("ramble");         i = buzztopics.iterator();         while (i.hasNext())         {   Element link = (Element) i.next();             out.print("<li>" + link.getChild("title").getTextTrim() +"</li>");         }         /*Then print out the Links items */         for (int loop = 0; loop < li_count; loop++)         { if (general_links[loop] != null)           out.print("<li>" + general_links[loop] +"</li>");         }         /*Then print out the products */         for (int loop = 0; loop < li_count; loop++)         { if (product_links[loop]!= null)           out.print("<li>" + product_links[loop] +"</li>");         }         /* Then list the main topic links */         buzztopics = root.getChildren("topic");         i = buzztopics.iterator();         while (i.hasNext())         {   Element link = (Element) i.next();             out.print("<li>" + link.getChild("title").getTextTrim() +"</li>");         }         out.print("</ul></td></tr></table>");         }         catch(Exception e)         {   /* output nothing if something goes wrong */             //out.print(e.toString());             out.print("Archived copy of JSPBuzz not available.");         }     } }

This class wasn't built to be reusable. It has one purpose, and that is to build a table of contents for a specified JSPBuzz newsletter. In addition, this class is only being used by a single JSP page, so no tag library interface will be built and the presentation logic will be embedded within the class. We have no reason to make this class any more complicated. If we ever want to use the code for other processes, we can re-factor it to be more efficient. However, it's very doubtful this will ever be the case.

Using JDOM

Let's examine what the code in Listing 7.10 does.

First, it uses SAX to read in the XML file and then has JDOM build a Document representation of the XML file:

SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(new File(as_path + "/" + as_file));

JDOM comes with a variety of builders. A builder is a helper class used to create the actual JDOM Document. In this example, the code uses the SAXBuilder since this is currently the fastest way to read in the file. Over time, expect to see other builders emerge in JDOM that are faster than SAX. JDOM also has builders to support DOM and even an experimental builder to support translating a JDBC ResultSet. Chapter 11, "Using XML in Reporting Systems," will discuss using the JDOM ResultSetBuilder. As an interesting side note, it's easy for us as programmers to create JDOM builder classes. If JDOM doesn't support a builder class that meets your project needs (for example, a parser that translates comma-separated files into JDOM), it's a simple matter to create your own.

Once the code has the JDOM Document, the next step involves working with the data. In this example, the code will programmatically walk its way through the Document. To do this, the code will need to get a handle on the root Element of the Document:

Element root = doc.getRootElement();

The class then outputs the initial HTML to surround the XML data. After this, the code obtains a List containing all the buzzlink XML elements:

List buzzlinks = root.getChildren("buzzlink");

Now the code encounters an interesting problem. A buzzlink contains an attribute called type that defines the location in which to place the link within the newsletter. Also, the buzzlink element contains a position attribute to define placement relative to links of the same type. The XML document doesn't guarantee the placement of elements. The code must use these attributes to place the actual data correctly. It's a simple matter to use XPath to sort the data relative to these elements. However, XPath is not an option in this case. The code will need to perform an initial sorting phase to re-sort the data correctly:

int  li_count = buzzlinks.size(); String[] news_links    = new String[li_count]; String[] general_links = new String[li_count]; String[] product_links = new String[li_count];

The code uses three arrays to store the data in the proper order. As the logic works its way through the List, it sorts the data into the appropriate array:

while (i.hasNext()) {  Element link = (Element) i.next();     if (link.getAttributeValue("type").equals("news"))     {int position =Integer.parseInt(link.getAttributeValue("position"));     news_links[position] = link.getChild("title").getTextTrim();     }

Then, once the data has been processed, it is a simple matter to print out the sorted links:

/*first print out the news items */ for (int loop = 0; loop < li_count; loop++) { if (news_links[loop] != null)    out.print("<li>" + news_links[loop] +"</li>"); }

This code demonstrates the ease with which you can navigate a JDOM Document. The actual XML data is represented using JDOM Java classes such as Element. Extracting the information is a simple matter of calling methods such as getAttributeValue (which returns data from an element's attribute) or getTextTrim (which returns the text of an element trimmed of all whitespace).

Building the Final JSP Page

We're ready to build the JSP page that will display the contents of the JSPBuzz newsletter (see Listing 7.11). Save this file as webapps/xmlbook/chapter7/NewsletterArchive.jsp.

Listing 7.11 NewsletterArchive.jsp, Used to Display Newsletter Contents
<%@page contentType="text/html"         import="xmlbook.chapter7.*"%> <html> <head><title>JSP Page</title> <style>     td { font-family: verdana, Arial, ; font-size: 10pt; border-style: groove;          border-width:2px; border-color:#ffffff; padding:2px;} .Clear{background:#ffffff;border-width:0px;padding-left:0px;padding-right:0px;} .OffWhiteBox {background: #f5f5f5;} .GreenBox {background: #aabbbb;} </style> </head> <body> <% ProduceListing listing = new ProduceListing();    String path = request.getServletPath();           path = path.substring(0,path.indexOf("NewsletterArchive.jsp")) ;           path = application.getRealPath(path); %> <table width="100%">     <tr>      <td width="33%" align="center" valign="bottom" class="clear">         <% listing.listContent("http://www.jspinsider.com/jspbuzz/2001/",                                "buzz_10_09_2001.xml",                                 path,                                 out); %>      </td>      <td width="33%" align="center" valign="bottom" class="clear">         <% listing.listContent("http://www.jspinsider.com/jspbuzz/2001/",                                "buzz_11_13_2001.xml",                                 path,                                 out); %>      </td>      </tr> </table> </body> </html>

As with the ProduceListing.java file, NewsletterArchive.jsp has only one purpose and will not be reused. Thus, it's easiest and fastest to simply access the ProduceListing class directly on the page.

The listContent content method is passed the URL, file, and path of the XML file to be handled. Also, the out implicit object is given to allow the ProduceListing object to send the output directly to the buffer.

This page, when run, will produce the output shown in Figure 7.2.

Figure 7.2. Results of running NewsletterArchive.jsp.

graphics/07fig02.gif

This example shows how easy it is to take data from a collection of XML files and add data to a JSP page. A slightly modified version of the code shown here drives the JSPBuzz section of the JSP Insider Web site (http://www.jspinsider.com/jspbuzz/list.view). Web traffic is light enough that nothing fancy is needed. If Web traffic were to become an issue, we have a simple solution to improve performance.

Back in Chapter 4, we discussed the ability to generate a new page from data. (Refer to Listing 4.8 and the CreateNewsLetter.jsp page.) The solution involves a two-step process. The first step is to set up a JSP page that the system calls on a need-only basis. This page polls the XML files and then creates an access JSP page that enables users to view the final collated results. The second step is to show the user the newly created page with the final data. In Chapter 15, "Advanced Application Design," we will show a fully integrated example of this type of processing.

Summary

In this chapter, we examined the use of JDOM and dom4j as APIs when working with XML. These APIs offer an interface that is easy to use with XML documents. More importantly, they give us a simple way to describe any data with an XML-centric set of Java objects.

We then examined several examples of using XML mixed into a JSP site. XML doesn't change the way JSP is used, but rather introduces new ways to handle data and import it into a JSP page.

The next chapter will jump back into Web services. We will get into the actual implementation of a Web service within a JSP application.

CONTENTS


JSP and XML[c] Integrating XML and Web Services in Your JSP Application
JSP and XML[c] Integrating XML and Web Services in Your JSP Application
ISBN: 672323540
EAN: N/A
Year: 2005
Pages: 26

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net