O'Reilly logo

Java & XML Data Binding by Brett McLaughlin

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Using the Results

So far, the discussions have been technical, but I really haven’t shown you how to put it all together. In this section, I will try to show you a couple of interesting uses of data binding and how they can serve as models for your own applications that could benefit from data binding. Hopefully this will finally satisfy your desire to see data binding in practical action.

Business Objects

The most common use of data binding is to turn XML directly into business objects. These objects are given contextual meaning, as in the case of the movie database. The application uses this data as a set of movies, and that use applies meaning to the data. This is quite different from the normal use case for XML (without data binding); in those cases, data has to be extracted and then placed into existing business objects. With data binding, that process is turned into a simple step (the unmarshal( ) method invocation).

As a practical example of this, Example 4-3 introduces the MovieServlet class. This class provides web access, through a GET request, to the data in the current movie database. I won’t spend time covering the semantics of servlet code; if you aren’t comfortable with servlets, check out Jason Hunter’s Java Servlet Programming (O’Reilly). In any case, look at the example code, and I’ll discuss how the data-bound classes are used.

Example 4-3. The MoviesServlet class

package javajaxb;
  
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
  
// Servlet imports
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
  
// Movie database generated classes
import javajaxb.generated.movies.*;
  
public class MoviesServlet extends HttpServlet {
  
    /** The Movies database object */
    private Movies movies = null;
  
    /** Any error that occurred. */
    private String errorMessage = null;
  
    /** The XML document storing the movie database */
    private static final String MOVIES_XML_DOCUMENT =
        "/dev/javajaxb/ch04/src/xml/movies.xml";
  
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
  
        // Load the database using JAXB
        try {
            // Load the XML
            File xmlFile = new File(MOVIES_XML_DOCUMENT);
            FileInputStream inputStream = new FileInputStream(xmlFile);
  
            // Unmarshal
            movies = Movies.unmarshal(inputStream);
        } catch (Exception e) {
            errorMessage = e.getMessage(  );
        }
    }
  
    public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws IOException, ServletException {
  
        // Handle any error conditions that might have occurred.
        if (movies == null) {
            error(res);
        }
  
        // Get output stream
        PrintWriter out = res.getWriter(  );
        res.setContentType("text/html");
  
        // Write out movie database
        out.println("<HTML><HEAD><TITLE>Movie Database</TITLE></HEAD>");
        out.println("<BODY>");
        out.println("<H2 ALIGN='center'>Movie Database</H2>");
 
        List movieList = movies.getMovie(  );
        for (Iterator i = movieList.iterator(); i.hasNext(  ); ) {
            Movie movie = (Movie)i.next(  );
  
            // Title
            out.print("<B><FONT SIZE='+1'>");
            out.print(movie.getTitle(  ));
            out.println("</FONT></B><BR />");
  
            // Director
            String director = movie.getDirector(  );
            if (director != null) {
                out.print("Director: ");
                out.print(director);
                out.println("<BR />");
            }
  
            // Producer
            out.println("Producers:<BR /><UL>");
            List producerList = movie.getProducer(  );
            for (Iterator j = producerList.iterator(); j.hasNext(  ); ) {
                out.print("<LI>");
                out.print((String)j.next(  ));
                out.println("</LI>");
            }
            out.println("</UL>");
  
            // Cast
            out.println("Starring:<BR /><UL>");
            Cast cast = movie.getCast(  );
            List actorList = cast.getActor(  );
            for (Iterator j = actorList.iterator(); j.hasNext(  ); ) {
                Actor actor = (Actor)j.next(  );
                out.print("<LI>");
                out.print(actor.getContent(  ));
                if (actor.getHeadliner(  ).equalsIgnoreCase("true")) {
                    out.print(" (Headliner)");
                }
                out.println("</LI>");
            }
            out.println("</UL>");
  
            out.println("<HR WIDTH='80%' />");
        }
  
        out.println("</BODY></HTML>");
        
        out.close(  );
    }
  
    private void error(HttpServletResponse res) throws IOException {
        PrintWriter out = res.getWriter(  );
        res.setContentType("text/plain");
  
        out.write(" ************* ERROR OCCURRED ***************\n\n");
        out.write("Error: " + errorMessage + "\n");
        out.close(  );
    }
}

Here, a constant is defined with the location of the movies database XML file. You should change this location to match the file location on your system.[10] In the init( ) method of the servlet, the movie database is read into memory for all servlet instances. If an error occurs, it is recorded. Of course, this is the single line that makes all the “magic” happen; the XML is converted into business objects, and the top-level Movies instance is stored for later use.

In the doGet( ) method, this object is used to print out the current movie listings. Simple list manipulation and printing is used here, which is the beauty of data binding. Once the unmarshalling process is complete, only normal Java programming techniques are needed to work with the data. I won’t bore you with explanations of the iteration and output code; it’s basic Java 101 material. If you load this servlet up in your web browser, you should get output that looks like Figure 4-5.

The MoviesServlet viewing the database

Figure 4-5. The MoviesServlet viewing the database

Tip

You will need to make sure that your servlet has access to the generated Java classes from the last chapter (the javajaxb.generated.movies package), as well as the JAXB runtime jar file (jaxb-rt-1.0.jar). The easiest way to do this, per the servlet 2.3 specification, is to add the classes into your context’s WEB-INF/classes/ directory and the jar file into the context’s WEB-INF/lib/ directory. In my setup (Tomcat 4.0.1), I’ve called my context javajaxb, as you can see in the URL of the web browser in Figure 4-5.

As you can see, there was no data manipulation required to move the data-bound information from XML to business objects; the conversion was direct, which is why data binding is so popular. Business data can be treated as such.

Data Objects

Additionally, it’s possible to use data binding to make dealing with data easier. This is most common for configuration data; this information has no business meaning, as did the movie database, but is often easier to work with using data binding than traditional APIs. Building on the movie database servlet, I’d like to show you how to create a standalone Java client to access this information. This client uses XML configuration information, accessed through data binding, to determine how to connect to the servlet and request data.

First, you’ll need to set up a DTD and generated classes for this new data set. Example 4-4 is a DTD I saved as connection.dtd that will serve as the constraints for this new data. It’s a simple DTD that allows a document to specify the host the servlet engine is running on, as well as the URL for the servlet to access.

Example 4-4. The connection DTD

<!ELEMENT connection (host, url)>
  
<!ELEMENT host EMPTY>
<!ATTLIST host
    hostname      CDATA    #REQUIRED
    port          CDATA    #REQUIRED
>
  
<!ELEMENT url EMPTY>
<!ATTLIST url
          context       CDATA    #REQUIRED
          servletPrefix CDATA    #REQUIRED
          servletName   CDATA    #REQUIRED
>

Once you’ve got Example 4-4 in place, you’ll need a simple binding schema to use for the class generation. Example 4-5 is this schema and it specifies only the root element and package for the generated classes.

Example 4-5. The connection binding schema

<?xml version="1.0"?>
  
<xml-java-binding-schema version="1.0-ea">
  <options package="javajaxb.generated.config" />
  
  <element name="connection" type="class" root="true"/>
</xml-java-binding-schema>

With these two documents, you can now generate Java classes and compile those classes:

C:\dev\javajaxb\ch04\src>xjc xml\connection.dtd 
                             bindingSchema\connection.xjs 
                             -d generated
Starting JAXB Schema Compiler...
generated\javajaxb\generated\config\Connection.java
generated\javajaxb\generated\config\Host.java
generated\javajaxb\generated\config\Url.java
  
C:\dev\javajaxb>javac -d build ch04\src\generated\javajaxb\generated\config\*.java

Your directory structure may be different, but the results should be the same: three new compiled classes ready for use in your application programming. Be sure to add these classes to your classpath environment variable, as you’ll be using them for the next example.

Next, you need to create an XML instance document with your configuration and connection data in it. Example 4-6 shows my document, which indicates a connection to the servlet running on my local machine, using port 8080 and in the javajaxb context.

Example 4-6. My connection data

<?xml version="1.0"?>
<!DOCTYPE connection SYSTEM "connection.dtd">
  
<connection>
  <host hostname="localhost"
        port="8080" />
  <url context="javajaxb" 
       servletPrefix="servlet"
       servletName="javajaxb.MoviesServlet" />
</connection>

With all of this in place, you’re ready to get started with the client. The complete source for the client is shown in Example 4-7.

Example 4-7. The MovieClient class

package javajaxb;
  
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Properties;
  
// Connection data binding classes
import javajaxb.generated.config.*;
  
// Jason Hunter's HttpMessage class
import com.oreilly.servlet.HttpMessage;
  
public class MovieClient {
  
    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("Usage: java javajaxb.MovieClient " +
                "[XML configuration file]");
            return;
        }
  
        try {
            File configFile = new File(args[0]);
            FileInputStream inputStream = 
                new FileInputStream(configFile);
  
            // Unmarshal the connection information
            Connection connection = Connection.unmarshal(inputStream);
  
            // Determine the data needed
            Host host = connection.getHost(  );
            Url configURL = connection.getUrl(  );
            String filename = new StringBuffer("/")
                .append(configURL.getContext(  ))
                .append("/")
                .append(configURL.getServletPrefix(  ))
                .append("/")
                .append(configURL.getServletName(  ))
                .toString(  );
  
            // Connect to the servlet
            URL url = new URL("http", 
                              host.getHostname(  ),
                              Integer.parseInt(host.getPort(  )),
                              filename);
            HttpMessage msg = new HttpMessage(url);
  
            // Indicate we want a listing
            Properties props = new Properties(  );
            props.put("action", "list");
  
            // Get response
            InputStream in = msg.sendPostMessage(props);
            BufferedReader reader = new BufferedReader(
                new InputStreamReader(in));
  
            // Output response to screen

            String line = null;
            while ((line = reader.readLine(  )) != null) {
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace(  );
        }
    }
}

In this class, I’m using the com.oreilly.servlet.HttpMessage class introduced in Jason Hunter’s servlet book. You can download the class from http://www.servlets.com/cos/index.html. Add the entire jar file, or just the HttpMessage class, to your classpath and compile the MovieClient source file. This makes sending messages to the movie database servlet very simple. The response from this servlet is obtained as an InputStream, which is buffered and then echoed to the command line.

You’ll also see that I’m sending a POST message; a GET message would return an HTML response, which isn’t very helpful to a command-line client. That, of course, means you need to go back to the MoviesServlet class and add code that accepts POST requests. This is handy, as I’ll revisit this servlet and the doPost( ) method in the next chapter. For now, the method needs to check the supplied action parameter, and if the value is list, simply return a textual representation of the movies database. Here’s the method to add to your servlet:

public void doPost(HttpServletRequest req, HttpServletResponse res)
    throws IOException, ServletException {

    // Get action paramater; default is "list"
    String[] actionValues = req.getParameterValues("action");
    String action = null;
    if ((actionValues == null) || (actionValues[0] == null)) {
        action = "list";
    } else {
        action = actionValues[0];
    }

    // Handle different actions
    PrintWriter out = res.getWriter(  );
    res.setContentType("text/plain");

    /* **** List current movies **** */        
    if (action.equalsIgnoreCase("list")) {

        out.write(" ***** Movies Database *****\n\n");

        // Print out each movie
        List movieList = movies.getMovie(  );
        for (Iterator i = movieList.iterator(); i.hasNext(  ); ) {
            Movie movie = (Movie)i.next(  );

            // Title
            out.print(" Movie: ");
            out.println(movie.getTitle(  ));

            // Director
            String director = movie.getDirector(  );
            if (director != null) {
                out.print("   Director: ");
                out.println(director);
                out.println(  );
            }

            // Producer
            out.println("   Producers:");
            List producerList = movie.getProducer(  );
            for (Iterator j = producerList.iterator(); j.hasNext(  ); ) {
                out.print("     * ");
                out.print((String)j.next(  ));
                out.println(  );
            }
            out.println(  );

            // Cast
            out.println("   Starring:");
            Cast cast = movie.getCast(  );
            List actorList = cast.getActor(  );
            for (Iterator j = actorList.iterator(); j.hasNext(  ); ) {
                Actor actor = (Actor)j.next(  );
                out.print("     * ");
                out.print(actor.getContent(  ));
                if (actor.getHeadliner(  ).equalsIgnoreCase("true")) {
                    out.print(" (Headliner)");
                }
                out.println(  );
            }

           out.println(" -------------------------------- ");
        }
   } else {
       out.write("The action supplied, '");
       out.write(action);
       out.write("', is not currently supported.\n");
   }
   out.close(  );
}

Once you’ve added this method, recompile the servlet, restart your servlet engine (if needed), and execute the command-line client. There’s nothing complex here; it essentially does what the doGet( ) method does, except in plain text form rather than HTML. In the next chapter, you’ll add handling of various other actions to this method, as marshalling will allow addition, deletion, and editing of the movies in the database. Once you’ve got the servlet compiled and running, and your MovieClient class compiled with the required components on the classpath (JAXB runtime classes, your connection data-bound classes, and the HttpMessage class), you can run the client. You should get output like this:

bmclaugh@FRODO ~/dev/javajaxb
$ java javajaxb.MovieClient ch04\src\xml\connection.xml
 ***** Movies Database *****

 Movie: Pitch Black
   Producers:
     * Tom Engelman

   Starring:
     * Vin Diesel (Headliner)
     * Radha Mitchell (Headliner)
     * Vic Wilson
 --------------------------------
 Movie: Memento
   Director: Christopher Nolan

   Producers:
     * Suzanne Todd
     * Jennifer Todd

   Starring:
     * Guy Pearce (Headliner)
     * Carrie-Anne Moss (Headliner)
 --------------------------------

After working through the examples presented here, you’re ready to move on to marshalling. If there’s anything in this chapter you aren’t clear on, take a moment to get things straight; the pace only picks up from here. You may also want to experiment with your own applications, using unmarshalling in some real-world cases, to get familiar with the process. Once you’ve got a grip on the conversion from XML to Java, it’s time to turn the process around and convert Java back into XML.



[10] I used an absolute path, which isn’t such a great idea, but is simple to understand. In your applications, it’s better to put the XML in the same context of your servlet’s engine as the servlet itself. This makes security and similar issues much easier to handle.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required