Modeling Databases in XML
In this section, you’ll learn how to model a database in XML using Java.
When we model a database, we provide an external representation of the database
contents. For our sample program, we’ll utilize a database that contains
information on rental properties. We’ll model the rental property database as
an XML document. Figure 10.3 shows the desired output.
One possible solution is to use Java servlets and JDBC. Java servlets
are server-side components that reside in a Web server or application server.
Java servlets are com-monly used to handle requests from Web browsers using the
HTTP protocol.
A key advantage to using servlets is the thin-client interface. The
servlets handle the request on the server side and respond by generating an
HTML page dynamically. This lowers the requirement on the client browser. The
browser only has to provide support of HTML. As a result, there is zero
client-side administration.
In contrast, Java applets require the browser to support the correct
version of the Java Virtual Machine (JVM). This has been a thorny issue with
the Java community since the early days of applet development. If the browser
doesn’t support Java, the applet will not execute. Of course, there are a
number of workarounds, such as the Java Plug-In and Java Web Start. However,
these technologies still require an initial installation on the client machine—which
can prove to be time consuming and error prone.
We can develop a servlet that uses JDBC. The servlet will make the
appropriate query to the database and use Java Database Connectivity (JDBC) API
result set metadata to cre-ate the elements. In fact, a servlet that performs
this operation is presented in Chapter 7, “Parsing XML Using Document Object
Model.” This is a simple and elegant solution because it leverages the result
set metadata.
In this section, we’ll leverage the XML data binding features of Java
Architecture for XML Binding (JAXB). JAXB provides a framework for representing
XML documents as Java objects. Using the JAXB framework, we can guarantee that
the documents processed by our system are well formed. Also, we have the option
of validating the XML data against a schema.
In the JAXB framework, we can parse XML documents into a suitable Java
object. This technique is referred to as unmarshaling.
The JAXB framework also provides the capa-bility to generate XML documents from
Java objects, which is referred to as marshaling.
The process is illustrated in the Figure 10.4.
JAXB is easier to use and a more efficient
technique for processing XML documents than the SAX or DOM API. Using the SAX
API, you have to create a custom content handler for each XML document
structure. Also, during the development of the content, you have to create and
manage your own state machine to keep track of your place in the document. For
very complex XML documents, the development process is very cumber-some. Using
JAXB, an application can parse an XML document by simply unmarshaling the data
from an input stream.
JAXB is similar to DOM in that we can create XML
documents programmatically and perform validation. However, the hindrance with
DOM is the complex API. If we have an XML tree, using the DOM API, we have to
traverse through the tree to retrieve ele-ments. However, with JAXB, we
retrieve the data from the XML document by simply calling a method on an
object. JAXB allows us to define Java objects that map to XML documents, so we
can easily retrieve data. The JAXB framework also ensures the type safety of
the data.
See Chapter 7 for an introduction to JAXB. Also,
detailed information on JAXB is avail-able in the JAXB specification at java.sun.com/xml. This chapter assumes you
under-stand the basics of JAXB. We’ll apply JAXB in an enterprise application
later in this chapter.
JAXB Solution
In the JAXB solution, we will model the rental property database as an
XML document. First we need to review the database schema. After reviewing the
schema, we will develop our desired XML document based on an XML schema. After
we have the XML schema developed, we can create the JAXB binding schema. The
JAXB binding schema contains instructions on how to bind the XML schema to a
Java class. We’ll take the JAXB binding schema and generate the appropriate
Java classes.
To summarize, we’ll follow these steps:
Review the database schema.
Construct the desired XML
document.
Define a schema for the XML
document.
Create the JAXB binding schema.
Generate the JAXB classes based
on the schema.
Develop a Data Access Object
(DAO).
Develop a servlet for HTTP
access.
Figure 10.5 illustrates the application architecture. RentalXMLServlet communicates with RentalDAO to retrieve information from the
database. Once the information is retrieved by RentalDAO, RentalXMLServlet generates an XML document.
Reviewing the Database Schema
We have an existing database for the rental properties. Table 10.3 contains
the database schema.
TABLE 10.3 Rental Property Database
Schema
Field : Type
prop_num : NUMBER
name : VARCHAR2
street_address VARCHAR2
city : VARCHAR2
state : VARCHAR2
zip_code : VARCHAR2
size_sq : NUMBER
bed_count : NUMBER
bath_count : NUMBER
monthly_rent : NUMBER
voice_phone : VARCHAR2
fax_phone : VARCHAR2
The source code for this chapter includes a sample
MS Access database. The file is located at <install_dir>\ch10_xmldb\data\rental_property.mdb.
Constructing the Desired XML Document
The desired output XML document describes the
rental property. However, the XML document does not use the exact field names
listed in the database schema. Instead, the XML document provides a custom
mapping of the database fields to XML element names. Table 10.4 contains the
mapping.
TABLE 10.4 XML Database Mapping
A rental property is described with a root element of <rental_property>, as shown in the following code:
<rental_property>
<prop_id>1</prop_id> <name>The Meadows</name>
<address>
<street>251 Eisenhower Blvd</street>
<city>Houston</city> <state>TX</state>
<postal_code>77033</postal_code>
</address>
<square_footage>500.0</square_footage>
<bedrooms>1.0</bedrooms> <bath>1.0</bath>
<price>600</price>
<contact> <phone>555-555-1212</phone>
<fax>555-555-1414</fax>
</contact> </rental_property>
Notice how the <address> element contains the subelements <street>, <city>, <state>, and <postal_code>. A similar approach is taken for the contact information. The <contact> element contains the <phone> and <fax> elements for the voice number
and fax number, respectively.
In our system, we’ll normally work with a collection of rental
properties. This collection is modeled using a <rental_property_list> element, as shown here:
<rental_property_list>
<rental_property> … </rental_property>
<rental_property> … </rental_property>
… …
</rental_property_list>
Defining a Schema for the XML Document
Based on the desired document format, we can create a schema definition.
In this sec-tion, we will define the Document Type Definition (DTD). The DTD
schema format was chosen because JAXB 1.0 (early access) only supports DTDs. In
the future, JAXB is sup-posed to support the formal XML Schema definition.
Listing 10.1 contains the DTD for our rental property list.
LISTING 10.1 <install_dir>\ch10_xmldb\rental_property.dtd
<!ELEMENT
rental_property_list
(rental_property)*>
<!ELEMENT rental_property (prop_id, name, address, square_footage, ➥ bedrooms, bath, price, contact)>
<!ELEMENT prop_id (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT address (street, city, state, postal_code)>
<!ELEMENT street (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT postal_code (#PCDATA)>
<!ELEMENT square_footage (#PCDATA)>
<!ELEMENT bedrooms (#PCDATA)>
<!ELEMENT bath (#PCDATA)>
<!ELEMENT price (#PCDATA)>
<!ELEMENT contact (phone, fax)>
<!ELEMENT phone (#PCDATA)>
<!ELEMENT fax (#PCDATA)>
Creating the JAXB Binding Schema
Now that the DTD is defined for our document, we need to define the JAXB
binding schema. The JAXB binding schema is an XML document that contains
instructions on how to bind a DTD to a Java class.
Using the JAXB binding schema, we can define the names of the generated
Java classes, map element names to specific properties in the Java class, and
provide the mapping rules for attributes. The following code example informs
the JAXB system that the ele-ment <rental_property_list> should be mapped to a Java class and that it is the root element for
the XML document:
<element
name=”rental_property_list”
type=”class” root=”true”/>
There’s no requirement to define a mapping for every element in the XML
document. JAXB uses a default binding schema that will create properties in the
Java class based on the XML element name.
The binding schema also allows us to define a conversion rule for
elements. For example, the numerical data for the rental property, such as
price, square footage, and number of rooms, is always represented in the DTD as
text data (#PCDATA). This is one of the limita-tions of the DTD format. However, by using
JAXB, we can specify that a given element should be converted to a Java
primitive type or class. In the following code example, we inform JAXB to
convert the values of <square_footage>, <bedrooms>, and
<bath> to the double type; also, <price> is converted to an instance of
the java.math.
BigDecimal class:
<element name=”square_footage” type=”value” convert=”double”/>
<element name=”bedrooms” type=”value” convert=”double”/> <element
name=”bath” type=”value” convert=”double”/>
<element name=”price” type=”value” convert=”BigDecimal”/>
<conversion name=”BigDecimal” type=”java.math.BigDecimal”/>
We can also use the binding schema to define enumerated types,
constructors, and interfaces. However, in the JAXB 1.0 early access version,
constructors are not yet implemented.
The binding schema includes a section for controlling the output of the
generated Java source code. For example, we can inform the system to use a
given package name. The following code defines the package name as xmlunleashed.ch10.jaxb:
<options
package=”xmlunleashed.ch10.jaxb”/>
See the JAXB specification for details on the binding schema file
format.
Now, let’s look at the JAXB binding schema file for our rental property
example. The schema files normally use the filename extension .xjs (for XML Java schema). Listing
10.2 contains the complete code for our JAXB binding schema, rental_property.xjs.
LISTING 10.2 <install_dir>\ch10_xmldb\rental_property.xjs
<?xml version=”1.0” encoding=”ISO-8859-1” ?> <!DOCTYPE
xml-java-binding-schema SYSTEM
➥ ”http://java.sun.com/dtd/jaxb/1.0-ea/xjs.dtd”>
<xml-java-binding-schema
version=”1.0-ea”> <options package=”xmlunleashed.ch10.jaxb”/>
<element
name=”rental_property_list” type=”class” root=”true”> <content
property=”list”/>
</element>
<element name=”square_footage” type=”value”
convert=”double”/>
<element name=”bedrooms” type=”value”
convert=”double”/>
<element name=”bath” type=”value”
convert=”double”/>
<element
name=”price” type=”value” convert=”BigDecimal”/>
<conversion name=”BigDecimal” type=”java.math.BigDecimal”/>
</xml-java-binding-schema>
Generating the JAXB Classes Based on Schemas
Now we are ready to generate the Java source files based on our schemas.
JAXB pro-vides a schema compiler for generating the Java source files. The
schema compiler takes as input the DTD and the JAXB binding schema. Figure 10.6
illustrates the process.
We pass our DTD (rental_property.dtd) and binding schema (rental_property.xjs) to the JAXB schema compiler with the xjc command. The command should be entered in the directory <install_dir>\ch10_xmldb\. Type
everything on one line:
java com.sun.tools.xjc.Main rental_property.dtd ➥ rental_property.xjs -d source_code
This command generates source code in the source_code directory. The following files
are generated:
• RentalPropertyList.java. This file models the <rental_property_list> ele-
ment.
RentalProperty.java. This file models the <rental_property> element.
Address.java. This file models the <address> subelement.
Contact.java. This file models the <contact> subelement.
Figure 10.7 contains the Unified Modeling Language
(UML) diagram for the generated Java classes.
Using the default schema-binding definition, the
JAXB schema compiler generates a property in the Java class for each XML
element. In the event the XML element contains subelements, the schema compiler
will create a new class.
Listing 10.3 contains the partial source code for RentalProperty.java. Some of the code and methods
are not listed to preserve space.
LISTING 10.3 <install_dir>\ch10_xmldb\source_code\xmlunleashed\ch10\jaxb\
RentalProperty.java
package xmlunleashed.ch10.jaxb;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import javax.xml.bind.ConversionException;
import javax.xml.bind.Dispatcher;
import javax.xml.bind.Element;
import javax.xml.bind.InvalidAttributeException;
import javax.xml.bind.LocalValidationException;
import javax.xml.bind.MarshallableObject;
import javax.xml.bind.Marshaller;
import javax.xml.bind.MissingContentException;
import javax.xml.bind.NoValueException;
import javax.xml.bind.StructureValidationException;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.Validator; import javax.xml.marshal.XMLScanner;
import javax.xml.marshal.XMLWriter; import
xmlunleashed.ch10.jaxb.Address;
import xmlunleashed.ch10.jaxb.Contact;
public class RentalProperty
extends MarshallableObject implements Element
{
private String _PropId; private String _Name;
private Address _Address;
private
double _SquareFootage;
private boolean has_SquareFootage = false; private
double _Bedrooms;
private boolean has_Bedrooms = false; private
double _Bath;
private boolean has_Bath = false; private
BigDecimal _Price; private Contact _Contact;
public String getPropId() {
return _PropId;
}
public void setPropId(String
_PropId) { this._PropId = _PropId;
if (_PropId == null) {
invalidate();
}
}
public String getName() { return
_Name;
}
public void setName(String _Name)
{ this._Name = _Name;
if (_Name == null) {
invalidate();
}
}
public Address getAddress() {
return _Address;
}
public void setAddress(Address
_Address) { this._Address = _Address;
if (_Address == null) {
invalidate();
}
}
public
void validateThis()
throws LocalValidationException
{
… …
}
public void marshal(Marshaller m)
throws IOException
{
// code to
output the XML
document
}
public void
unmarshal(Unmarshaller u) throws UnmarshalException
{
// code to
read in the
XML document
}
… …
}
The source code for RentalProperty.java contains private data members for the ele-ments defined in <rental_property>. The public get/set methods provide access to the properties. For example, to retrieve the
name of the rental property from the Java object, we call the getName() method. In the case of a nested
element, such as address, we call the getContact() method, which returns a Contact object. The Contact class is defined in a similar manner with get/set methods for the properties.
Also, we can validate the object by calling the validateThis() method. Recall that this source
code was generated based on the DTD, so we can verify that the contents of the
object adheres to the grammar rules of the DTD. The validate() method is very useful if the
contents of the RentalProperty object are modified using the setter methods. It is also useful if we
construct a RentalProperty object from scratch.
Finally, the RentalProperty class contains methods to marshal and unmarshal the content.
The source code for the remaining files is available in the source code
download in the directory <install_dir>\ch10_xmldb\source_code\xmlunleashed\ch10\jaxb. Feel free to investigate these files’ contents.
Developing a Data Access Object (DAO)
A Data Access Object (DAO) provides access to the backend database. The
goal of the DAO design pattern is to provide a higher level of abstraction for database
access. The DAO encapsulates the complex JDBC and SQL calls. The DAO provides
access to the backend database via public methods. The DAO converts a result
set to a collection of objects. The objects model the data stored in the
database. The application interaction with a DAO is shown in Figure 10.8.
By using a DAO, the implementation details of the
database are hidden from the applica-tion clients. The implementation details
include the database schema and database ven-dor. This follows closely with the
design principle of encapsulation. A benefit of using the DAO is improved
application maintenance. If the database schema changes, such as a column name
being modified, we only have to update the DAO. No modifications are required
to the client programs. Also, if we decide to change the database
implementa-tion from Sybase to Oracle, modifications are only required to the
DAO. The clients can continue to use the DAO without any modification. The DAO
design pattern is widely used in the industry and is documented in Sun’s J2EE
Patterns Catalog, found at java.sun.com/j2ee.
In our solution, we’ll create a DAO called RentalPropertyDAO. This version of the DAO will
only provide the method getRentalProperties(). Later in the chapter, we’ll pro-vide additional methods. The getRentalProperties() method submits a SQL query to
the database and converts the result set to a collection of JAXB RentalProperty objects. This process is
illustrated in Figure 10.9.
Let’s examine the components of the RentalPropertyDAO source code. The class
defini-tion and constructor for RentalPropertyDAO are shown here:
public class RentalPropertyDAO {
*/
protected
Connection myConn;
/**
Constructor for DAO. Setup the
database connection. ➥ * Use the default properties.
*/
public RentalPropertyDAO() throws
DAOException {
this(“sun.jdbc.odbc.JdbOdbcDriver”,
➥ ”jdbc:odbc:RentalPropertyDSN”, ➥ ”test”, “test”);
}
/**
* Constructor for DAO. Setup the database
connection. */
public RentalPropertyDAO(String driverName, String
dbUrl, ➥ String user, String pass)
throws DAOException {
try {
// Load the
driver
log(“Loading driver: “ + driverName);
Class.forName(driverName);
// Get a
connection
log(“Connecting to the database: “ + dbUrl);
log(“User id: “ + user);
myConn
=
DriverManager.getConnection
(dbUrl, user, pass);
log(“DB
connection successful at
“ + new
java.util.Date());
}
catch (Exception exc) {
throw
new DAOException(exc);
}
}
…
}
The class RentalPropertyDAO defines a data member for a java.sql.Connection object. This will serve as our connection to the database. For the sake
of simplicity, we’re using a single connection. We could also utilize a
database connection pool to increase the scalability of the application.
RentalPropertyDAO can be constructed by using the
default constructor. In this case, the DAO will use default properties for the JDBC driver name, the JDBC
database URL, and the user ID and password. This constructor shields the client
code for knowing the details of the database implementation. The typical JDBC
steps are followed to load a database driver and to obtain a connection to the
database.
RentalPropertyDAO also provides a constructor,
where the client program supplies the appropriate JDBC parameters. This constructor provides flexibility and
would be typi-cally used in a distributed computing environment such as Remote
Method Invocation (RMI) or Enterprise JavaBeans (EJB). A remote server object
would instantiate the DAO using server-side parameters. The DAO would then be
available remotely via a server-side proxy interface. In this scenario, the
client application would simply look up the remote object and invoke its
methods. The client object is not involved with the construc-tion of the DAO
and therefore is shielded from the implementation details of JDBC dri-ver name,
URL, and so on.
Once the RentalPropertyDAO Data Access Object is constructed, clients can retrieve data from the
database by calling the getRentalProperties()method. The code for getRentalProperties() is shown here:
/**
Get a
list of rental
properties from the
database
@return a
list of RentalProperty objects
@exception SQLException
thrown for SQL
errors
*/
public
RentalPropertyList
getRentalProperties() throws DAOException
{
RentalPropertyList theRentalPropertyList = new RentalPropertyList();
List theList = theRentalPropertyList.getList();
try {
Statement
myStmt = myConn.createStatement();
String
rentalSql = “SELECT
prop_num, name, street_address, “
➥ + “city, state,
zip_code, “
➥ + “size, sq, bed_count, bath_count, “ ➥ + “monthly_rent, voice_phone, “
“fax_phone FROM
rental_properties”;
ResultSet
myRs = myStmt.executeQuery(rentalSql);
RentalProperty
tempProperty = null;
// build a collection of JAXB RentalProperty
objects while (myRs.next()) {
tempProperty = createRentalProperty(myRs);
theList.add(tempProperty);
}
// be sure
to validate the
new list
theRentalPropertyList.validate();
myRs.close();
myStmt.close();
}
catch (Exception exc) {
throw
new DAOException(exc);
}
return theRentalPropertyList;
}
This method queries the database and returns a collection of RentalProperty objects.
After the SQL is executed, the method processes the result set to build
a collection of RentalProperty objects. Because the early access version of JAXB does not support the code generation of constructors,
we’ve created a simple constructor method in
RentalPropertyDAO. The code for the createRentalProperty()method is shown here:
/**
Create a
JAXB RentalProperty object
based on the
result set.
This method
provides the mapping
between database schema
and object
*/
protected RentalProperty createRentalProperty( ➥ ResultSet theRs) throws DAOException {
RentalProperty theProperty =
new RentalProperty();
Address theAddress =
new Address();
Contact theContact =
new Contact();
try {
//set the rental property number and name
theProperty.setPropId(theRs.getString(“prop_num”));
theProperty.setName(theRs.getString(“name”));
//set the address
theAddress.setStreet(theRs.getString(“street_address”));
theAddress.setCity(theRs.getString(“city”));
theAddress.setState(theRs.getString(“state”));
theAddress.setPostalCode(theRs.getString(“zip_code”));
theProperty.setAddress(theAddress);
//set the square footage, bedrooms, bath count and rent
theProperty.setSquareFootage(theRs.getDouble(“size_sq”));
theProperty.setBedrooms(theRs.getDouble(“bed_count”));
theProperty.setBath(theRs.getDouble(“bath_count”));
theProperty.setPrice(new BigDecimal(
➥ theRs.getDouble(“monthly_rent”)));
// set the contact information
theContact.setPhone(theRs.getString(“voice_phone”));
theContact.setFax(theRs.getString(“fax_phone”));
theProperty.setContact(theContact);
}
catch (SQLException exc) {
throw
new DAOException(exc);
}
return theProperty;
}
The createRentalProperty()method creates an instance of a RentalProperty object using the default constructor. It then populates the object
based on information from the result set. This method actually handles the
mapping between the database fields and the XML elements.
Listing 10.4 contains the complete code for RentalPropertyDAO.java.
LISTING 10.4 <install_dir>\ch10_xmldb\source_code\xmlunleashed\ch10\
RentalPropertyDAO.java
package xmlunleashed.ch10;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.math.BigDecimal;
import xmlunleashed.ch10.jaxb.RentalProperty;
import xmlunleashed.ch10.jaxb.RentalPropertyList;
import xmlunleashed.ch10.jaxb.Contact;
import
xmlunleashed.ch10.jaxb.Address;
/**
* Data Access
Object (DAO) for
the rental_property database.
<br>
*
*/
public class RentalPropertyDAO {
/**
* The database connection */
protected
Connection myConn;
/**
* Constructor for DAO. Setup the database
connection. */
public RentalPropertyDAO(String driverName, String
dbUrl, ➥ String user, String pass)
throws DAOException {
try {
// Load the
driver
log(“Loading driver: “ + driverName);
Class.forName(driverName);
// Get a
connection
log(“Connecting to the database: “ + dbUrl);
log(“User id: “ + user);
myConn = DriverManager.getConnection (dbUrl,
user, pass);
log(“DB
connection successful at
“ + new
java.util.Date());
}
catch (Exception exc) {
throw
new DAOException(exc);
}
}
/**
Get a
list of rental
properties from the
database
@return a
list of RentalProperty objects
@exception SQLException
thrown for SQL
errors
*/
public
RentalPropertyList
getRentalProperties() throws DAOException
{
RentalPropertyList theRentalPropertyList = new RentalPropertyList();
java.util.List theList = theRentalPropertyList.getList();
try {
Statement
myStmt = myConn.createStatement();
String
rentalSql = “SELECT
prop_num, name, street_address, “
➥ + “city, state,
zip_code, “
➥ + “size, sq, bed_count, bath_count, “ ➥ + “monthly_rent, voice_phone, “
➥ + “fax_phone FROM
rental_properties”;
ResultSet
myRs = myStmt.executeQuery(rentalSql);
RentalProperty
tempProperty = null;
// build a collection of JAXB
RentalProperty objects
while (myRs.next()) {
tempProperty = createRentalProperty(myRs);
theList.add(tempProperty);
}
// be sure to validate the new list
theRentalPropertyList.validate();
myRs.close();
myStmt.close();
}
catch
(Exception exc) {
throw
new DAOException(exc);
}
return
theRentalPropertyList;
}
/**
Create a
JAXB RentalProperty object
based on the
result set.
This method
provides the mapping
between database schema
and object
*/
protected RentalProperty
createRentalProperty( ➥ ResultSet theRs) throws
DAOException {
RentalProperty
theProperty = new
RentalProperty();
Address
theAddress = new
Address();
Contact
theContact = new
Contact();
try {
//set the rental property number and name
theProperty.setPropId(theRs.getString(“prop_num”));
theProperty.setName(theRs.getString(“name”));
//set the address
theAddress.setStreet(theRs.getString(“street_address”));
theAddress.setCity(theRs.getString(“city”));
theAddress.setState(theRs.getString(“state”));
theAddress.setPostalCode(theRs.getString(“zip_code”));
theProperty.setAddress(theAddress);
//set the square footage, bedrooms, bath count and rent
theProperty.setSquareFootage(theRs.getDouble(“size_sq”));
theProperty.setBedrooms(theRs.getDouble(“bed_count”));
theProperty.setBath(theRs.getDouble(“bath_count”));
theProperty.setPrice(new BigDecimal(
➥ theRs.getDouble(“monthly_rent”)));
// set the contact information
theContact.setPhone(theRs.getString(“voice_phone”));
theContact.setFax(theRs.getString(“fax_phone”));
theProperty.setContact(theContact);
}
catch (SQLException exc) {
throw
new DAOException(exc);
}
return theProperty;
}
/**
* Utility method for logging */
protected void log(Object
message) { System.out.println(“RentalPropertyDAO: “ + message);
}
}
Now that we have the DAO in place, a client program can easily retrieve
information from the database. The RentalPropertyList collection contains JAXB RentalProperty objects. These objects are capable of producing an XML representation of
their data thanks to the JAXB support. The XML data is available by calling the
marshal() method.
Creating a Test Harness for RentalPropertyDAO
Before we move to the next section, let’s create a test harness for RentalPropertyDAO. A test harness is a small program that tests the basic functionality
of the application. If designed properly, The test harness provides a way of
producing predictable results from an application.
The TestApp program will construct the RentalPropertyDAO Data Access Object and then retrieve a list of RentalProperty objects by calling the method getRentalPropertyList(). The XML data is displayed by
calling the marshal() method on RentalPropertyList.
Listing 10.5 contains the code for TestApp.java.
LISTING 10.5 <install_dir>\ch10_xmldb\source_code\TestApp.java
import xmlunleashed.ch10.RentalPropertyDAO;
import xmlunleashed.ch10.DAOException;
import
xmlunleashed.ch10.jaxb.RentalPropertyList;
import java.io. IOException;
/**
Test harness
for the RentalPropertyDAO.
➥ * Retrieves a
rental property list
and displays the
results to
standard out.
*/
public class TestApp
{
/**
* DAO data member */
protected
RentalPropertyDAO myRentalDAO;
/**
* Constructs the RentalPropertyDAO */
public TestApp() throws
DAOException { myRentalDAO = new RentalPropertyDAO();
}
/**
* Retrieves a rental property list and displays the
results to standard out */
public void process() throws
DAOException, IOException {
// Get the list of rental
properties
RentalPropertyList theList =
myRentalDAO.getRentalProperties();
// Send the XML data to standard out. theList.marshal(System.out);
}
/**
* Main routine. Constructs the test app and runs
the process. */
public static void main(String[]
args) { try {
TestApp myApp =
new TestApp();
myApp.process();
}
catch (Exception exc) {
exc.printStackTrace();
}
}
}
When we run the test harness, it retrieves a collection of rental
properties from the DAO. The test harness then displays an XML document similar
to this one:
<?xml version=”1.0” encoding=”UTF-8”?>
<rental_property_list>
<rental_property> <prop_id>1</prop_id> <name>The
Meadows</name> <address>
<street>251 Eisenhower Blvd</street>
<city>Houston</city> <state>TX</state>
<postal_code>77033</postal_code>
</address>
<square_footage>500.0</square_footage>
<bedrooms>1.0</bedrooms> <bath>1.0</bath>
<price>600</price>
<contact>
<phone>555-555-1212</phone> <fax>555-555-1414</fax>
</contact> </rental_property>
<rental_property>
…
</rental_property> </rental_property_list>
Developing a Servlet for HTTP Access
At this point, we have constructed the RentalPropertyDAO Data Access Object. This DAO is capable of retrieving information from
a database and providing a collection of objects. Thanks to the JAXB framework,
these objects can be marshaled into XML.
Now we need to provide an HTTP interface for RentalPropertyDAO so that a Web browser can
interact with our system. Java servlets provides support for the HTTP
proto-col. If you are interested in Web-based interaction, you should also read
about the Web Services technology, which is covered later in this book.
In our solution, we’ll use a servlet to handle the
requests to the DAO. In the servlet, we’ll call the appropriate method and
return the result as an XML document. Figure 10.10 depicts the application
interaction.
The servlet is responsible for creating an instance
of RentalPropertyDAO. The
servlet reads JDBC parameters from the web.xml configuration file and constructs RentalPropertyDAO
accordingly. An excerpt from the web.xml file is shown here (make note of the definitions for the
parameters driverName, dbUrl, user, and pass) :
<servlet>
<servlet-name>RentalXMLServlet</servlet-name>
<servlet-class>xmlunleashed.ch10.RentalXMLServlet</servlet-class>
<init-param>
<param-name>driverName</param-name>
<param-value>sun.jdbc.odbc.JdbcOdbcDriver</param-value>
</init-param> <init-param>
<param-name>dbUrl</param-name>
<param-value>jdbc:odbc:RentalPropertyDSN</param-value>
</init-param> <init-param>
<param-name>user</param-name>
<param-value>test</param-value>
</init-param> <init-param>
<param-name>pass</param-name>
<param-value>test</param-value>
</init-param> <load-on-startup/>
</servlet>
The servlet reads the parameters and constructs the
RentalPropertyDAO Data
Access Object in the init() method. The code for the init() method is shown here:
/**
* Create an instance of the RentalPropertyDAO */
public
void init() throws
ServletException {
retrieve database connection parameters String dbUrl =
getInitParameter(“dbUrl”);
String driverName = getInitParameter(“driverName”);
String user = getInitParameter(“user”);
String
pass = getInitParameter(“pass”);
create an
instance of the
RentalPropertyDAO
try {
myRentalDAO
= new RentalPropertyDAO(driverName, dbUrl,
user, pass);
}
catch (Exception exc) {
log(exc.toString());
throw
new ServletException(exc);
}
}
The servlet handles HTTP GET requests, so we have to override the doGet() method. In this method, we set the content type of the response to text/xml. This informs the client that we
are returning XML-formatted text data. Next, we set up ServletOutputStream.
Then we retrieve a list of rental properties from RentalPropertyDAO. The list is then marshaled to
the ServletOutputStream object, out. Finally, the output stream is
closed. The code for the doGet() method is shown here:
/**
Perform the
following steps for
GET requests.
<ol>
<li>Retrieve a
list of rental
properties from RentalPropertyDAO<li>
<li>Marshal the
list as an
XML document<li>
</ol>
*/
public void
doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
ServletOutputStream out =
null;
RentalPropertyList theList =
null;
try {
Set the content type to text/xml response.setContentType(“text/xml”);
Retrieve the servlet output stream out = response.getOutputStream();
Retrieve a list of rental properties theList =
myRentalDAO.getRentalProperties();
Marshal the list as an XML document theList.marshal(out);
}
catch (DAOException exc) {
exc.getRootCause().printStackTrace();
throw
new
ServletException(exc.getRootCause());
}
catch (Exception exc) {
log(exc.toString()); exc.printStackTrace();
throw new
ServletException(exc);
}
finally { out.close();
}
}
Testing the Application
In order to test the application, you need to download and install the
following important components.
Here’s what’s required to test the application:
Sun Microsystems’ Software Development Kit (SDK) 1.3 (or higher). The SDK is available at
Sun’s Web site, http://java.sun.com/j2se. Follow the installation instructions provided with the SDK.
Apache Tomcat Server 4. Apache
Tomcat 4 is the official reference implementation for JSP 1.2 and Java Servlets 2.3. If your application server already supports JSP 1.1 or higher,
there’s no requirement to install Tomcat. Apache Tomcat 4 is avail-able from
the Apache Web site, http://jakarta.apache.org/tomcat. Follow the installation instructions provided with the Tomcat server.
Microsoft
Internet Explorer 6 or Netscape 6. In this section, you will need a browser that has client-side XML
support. This is useful for viewing the raw XML output of our servlet.
Once Tomcat 4 is installed, we need to add a new
Web application that points to the source code directory. This is accomplished by editing the file <tomcat_install_dir>\ conf\server.xml. Move to the section where the <Context> elements are listed and then add the following entry:
<Context path=”/bookch10” docBase=”<install_dir>/ch10_xmldb/public_html”
debug=”0”
reloadable=”true” />
Be sure to update <install_dir> with the installation directory for the book’s source code. This configuration allows us to access
the Web application named bookch10. This Web application’s document base is located at <install_dir>\ch10_xmldb\public_html.
Now, restart the Tomcat server to pick up the new configuration. By
default, the Tomcat server is listening on port 8080. You can access files for the bookch10 Web application using the following URL:
http://localhost:8080/bookch10/test.jsp
The file test.jsp should display a welcome message and the current date.
Next, follow these steps to compile the source code:
Open a Microsoft command prompt
window.
Move to the source code directory
by typing this:
cd
<install_dir>\ch10_xmldb
This directory includes the batch file setpaths.bat. This file will place the
follow-ing JAR files in the classpath: lib\servlet.jar, lib\jaxb-rt-1.0-ea.jar, and lib\jaxb-xjc-1.0-ea.jar.
3. Set up the classpath by typing the following:
setpaths.bat
4. Move to the source code directory by typing this:
cd
source_code
5. Compile the code by typing this:
javac
-d
..\public_html\WEB-INF\classes
*.java
If you are accustomed to using ANT, note that this directory
also contains a
build.xml file.
Now we need to set up an ODBC Data Source Name (DSN) for Rent
This DSN should point to the file <install_dir>\ch10_xmldb\data\rental_ property.mdb.
Now we need to test RentalXMLServlet. In a Web browser, open http://localhost: 8080/bookch10/RentalXMLServlet.
If you are using Microsoft Internet Explorer 6, you will see the XML
content shown in Figure 10.11.
If you are using Netscape 6, select the menu option View, Page Source.
This will display the raw XML output as shown in Figure 10.12.
Great! We’ve developed RentalXMLServlet to provide an HTTP interface to RentalPropertyDAO. The end product is an XML model of the data stored in the database.
Now we can take this one step further by applying a style sheet to the
data.
Converting the XML Data to HTML with XSLT
We can leverage the functionality of XSLT to convert the XML data to
HTML. In partic-ular, we will convert the rental property list to an HTML
table, as shown in Figure 10.13.
This section assumes you are familiar with XSLT. If
not, then read Chapter 9.
The XSLT style sheet contains the HTML template
along with the XSLT constructs to retrieve the data. Our style sheet defines an
HTML table with instructions to create a table row for each rental property in
the list. Listing 10.6 contains the code for rental_view.xsl.
LISTING 10.6 <install_dir>\ch10_xmldb\public_html\rental_view.xsl
<?xml version=”1.0”?> <xsl:stylesheet
xmlns:xsl=
➥ ”http://www.w3.org/1999/XSL/Transform”
version=”1.0”>
<xsl:template match=”/rental_property_list”>
<html><body>
<h3>Rental Properties</h3> <hr></hr>
<table border=”1”
cellpadding=”5”> <tr>
<th>Name</th>
<th>Street</th> <th>City, State</th>
<th>Square Footage</th> <th>Bedrooms</th>
<th>Bath</th> <th>Price</th>
</tr>
<!— Perform loop for each rental property in the
list —> <xsl:for-each select=”rental_property” >
<tr>
<td> <xsl:value-of
select=”name” /> </td>
<td> <xsl:value-of
select=”address/street” />
</td>
<td> <xsl:value-of
select=”address/city” />,
➥ <xsl:value-of select=”address/state” />
</td>
<td> <xsl:value-of
select=”square_footage”
/> </td>
<td> <xsl:value-of
select=”bedrooms” /> </td>
<td> <xsl:value-of
select=”bath” /> </td>
<td> $
<xsl:value-of
select=”price” /> </td>
</tr>
</xsl:for-each> </table>
</body></html>
</xsl:template>
</xsl:stylesheet>
We’ll use the Apache-Jakarta custom tag, <jakarta:apply>, for XLST processing. Instead of specifying an XML document by
filename, we need to refer to RentalXMLServlet. The <jakarta:apply> element supports the subelement
<jakarta:include>, which refers to an XML resource
to include. This includes the output of
RentalXMLServlet. Listing
10.7 shows the code for rental_test.jsp.
LISTING 10.7 <install_dir>\ch10_xmldb\public_html\rental_view.jsp
<%@taglib
uri=”http://jakarta.apache.org/taglibs/xsl-1.0” prefix=”jakarta” %>
<jakarta:apply
xsl=”rental_view.xsl”> <jakarta:include page=”/RentalXMLServlet”/>
</jakarta:apply>
Testing the JSP Page
To test this example, make sure the Tomcat server is running. In a Web
browser, access the JSP page with the URL http://localhost:8080/bookch10/rental_view.jsp.
Your browser should resemble what’s shown in Figure 10.13 shown
previously.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.