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.
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.
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.
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.
Get Java & XML Data Binding now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.