Implementation Notes

The completed system will consist of two distinct programs: the GUI order-entry client and the non-interactive order processing service. Although an SMTP service plays a vital part in the overall system, it will be treated as a black-box component. The “Deployment” section later in this chapter explains the role of the SMTP server and its use.

Business Analysis

Before examining the nuts-and-bolts details of the client and server applications, we must spend some time gathering the business requirements behind the system. Before proceeding, it is necessary to answer the following two questions:

  • Where will the list of items that can be ordered come from?

  • Exactly what information needs to be collected to complete an order?

Because it is known in advance that the catalog of items for sale will change infrequently, a simple text file containing a list of items will be sufficient. Of course, this file will be written in XML.

To help tie together the ideas surrounding XML and unified business processes, our hypothetical salesman will be selling accessories for the videocassette player used for the examples in Chapter 12, “Web Content Publishing,” and Chapter 18, “Unifying Product Documentation.” Figure 14.3 shows the various publications that can be generated from the authoritative information in the product manual document.

Figure 14.3. Generating multiple views of the base XML document.


Within the product manual itself, several accessories for the videocassette player are mentioned. If our business processes dictate that the product manual is the canonical source of information about the product, it is possible to generate a definitive list of accessories from the manual.

Building the Catalog

Because the information about the items to be included in the catalog is already stored in an XML document, constructing the catalog dynamically using XSLT is an obvious solution. Before we can construct the XSLT script, however, we need to know what needs to be included in the final document.

The information included in the catalog will fall into three main areas: product information, item information, and order application configuration information.

The product information will be identical to the product information included in the product manual XML document. The XSLT stylesheet will be able to copy it verbatim from the source document.

The item information is scattered throughout the source document in <accessory> nodes, so the catalog generation stylesheet will need to extract them.

The following information needs to be included in the catalog about each item:

  • A part number

  • A description

  • The item manufacturer's name

Listing 14.1 shows the document type definition for the simple catalog file that is read by the order client application.

Listing 14.1. DTD for Simple Item Catalog Document (OrderCatalog.dtd)
<!--
  OrderCatalog.dtd

  DTD for the simple distributed ordering application example
  included in the book Strategic XML.
-->
<!ELEMENT catalog ( product_info, order_info, items ) >

<!ELEMENT company_name ( #PCDATA ) >

<!ELEMENT desc ( #PCDATA ) >

<!ELEMENT item ( desc, manufacturer ) >
<!ATTLIST item part_num NMTOKEN #REQUIRED >

<!ELEMENT items ( item+ ) >

<!ELEMENT manufacturer ( #PCDATA | company_name | web_site )* >

<!ELEMENT model_num ( #PCDATA ) >

<!ELEMENT order_info EMPTY >
<!ATTLIST order_info address CDATA #REQUIRED >
<!ATTLIST order_info method NMTOKEN #REQUIRED >

<!ELEMENT product_info ( manufacturer, product_type, model_num ) >

<!ELEMENT product_type ( #PCDATA ) >

<!ELEMENT web_site ( #PCDATA ) >
							

Any catalog conforming to this document type definition could be used by the order client application. In this case, the catalog will be generated from the product manual document using a simple XSLT transform. The stylesheet that performs this transformation is build_catalog.xsl. This same stylesheet could be used to create catalogs from other product manuals in the future.

Contents of an Order

Now that the contents of the order catalog have been defined, it is necessary to determine what information must be collected as part of a given order.

A real-world order processing system would need to collect a great deal of information about the purchaser, delivery instructions, billing requirements, and so forth. Because the purpose of this application is only to illustrate the principles involved, an order will include the following information:

  • The customer's name

  • The customer's credit-card number

  • A list of item numbers to order, with a quantity for each

This information will be encoded as an XML document that will be sent as the body of an e-mail message by the OrderClient application. The document will conform to the document type definition shown in Listing 14.2.

Listing 14.2. DTD for a Simple Order Message (OrderMessage.dtd)
<!ELEMENT order (customer_name, credit_card_num, items)>

<!ELEMENT customer_name (#PCDATA)>

<!ELEMENT credit_card_num (#PCDATA)>

<!ELEMENT items (item+)>

<!ELEMENT item EMPTY>

<!ATTLIST item
  part_num CDATA #REQUIRED
  quantity CDATA #REQUIRED
							

The OrderClient Application

The client application that is used to transmit orders is written in Java. It provides a very simple GUI for selecting which items to order, the quantities for each, and the customer's name and credit-card number. Figure 14.4 shows the client application in action.

Figure 14.4. The GUI client.


This application was built using Borland's JBuilder 3.0 IDE. The controls were created and laid out using the JBuilder designer.

The main program entry point is located in OrderClient.java. The OrderClient class creates a new instance of the OrderFrame class and passes the command-line arguments on to it. All the real work is done by the OrderFrame class.

The constructor of the OrderFrame class performs several important functions:

  • Initializes AWT and runs the control creation code that was generated by JBuilder

  • Loads the client configuration file

  • Locates and loads the catalog

  • Initializes the window controls and current order document to prepare for a new order to be placed

After the application window has been created and prepared for a new order, the user adds new items to his order by selecting an item from the product list, entering a quantity, and then clicking the Add to Order button. This causes a new <item> element to be added to the current order document. When the user is done selecting items, he clicks the Transmit Order button to send it to the order server's e-mail inbox.

Several different APIs and implementations of those APIs are available for working with XML in a Java application. This particular application uses the relatively new JDOM library. JDOM is an open-source project that was created by Jason Hunter and Brett McLaughlin and is maintained by a community of developers through the project Web site, www.jdom.org. In February 2002, JDOM was accepted by the Java Community Process (JCP) as Java Specification Request 102. This is the first step toward making JDOM part of the official Sun core Java platform.

JDOM provides a lightweight, Java-specific API for creating in-memory document trees. These trees can be created either by parsing an XML source document or by programmatically creating document, element, and attribute objects. The JDOM distribution includes different classes for creating JDOM trees using either a W3C Document Object Model parser or a Simple API for XML parser. It also includes a rich output class for serializing a document to a Java stream. The order client application uses both of these features, because it needs to both read existing documents and write new documents.

Two types of XML documents are read by the order client: the client configuration file and the product catalog.

The Client Configuration File

Most applications have various parameters and settings that need to be preserved between program invocations. Most operating systems provide some facilities for this task (such as the Windows Registry), but this solution is not appropriate for a cross-platform application.

In the past, most application developers chose to formulate their own proprietary configuration file formats. They would then write code to read and write these files. Fortunately, XML has relieved the developer from the burden of developing new and inventive file formats to solve a relatively mundane problem. XML is a good format for storing arbitrary hierarchical information in a flexible text format. The file is human-readable, so in case of corruption or misconfiguration, it is actually possible for a user to fix his settings using a text editor.

Listing 14.3 shows a sample configuration file. It is parsed when the main application frame window is created, and the resulting JDOM Document object is kept as an instance member throughout the life of the application.

Listing 14.3. A Sample Order Client Configuration File
<?xml version="1.0" encoding="utf-8"?>
<order_client_config>
  <salesman>
    <name>Scott Means</name>
    <email>[email protected]</email>
  </salesman>
  <catalog_url>file:c:/OrderClient/VCPCatalog.xml</catalog_url>
  <smtp_server>localhost</smtp_server>
</order_client_config>
							

Before it is possible to use the JDOM library to parse an XML document, it is necessary to create either a SAXBuilder or a DOMBuilder object instance. JDOM includes no XML parser of its own. Instead, it allows the registration of standard parsers that conform to one of the two major XML parsing APIs, which it then uses to create its in-memory document images. The following code appears in the OrderFrame() constructor:

m_bBuilder = new SAXBuilder(DEFAULT_SAX_DRIVER_CLASS);

The single parameter is a Java classname for the SAX parser that will be used by the builder to parse new documents. For this application, the DEFAULT_SAX_DRIVER_CLASS member is a final String that gives the fully qualified classname of the Apache XML Project's Xerces SAX parser.

The configuration document includes the current user's name and e-mail address (which is used when an order e-mail message is sent), the URL for the product catalog to display, and the hostname of the SMTP server to use. In this case, it indicates that there is an SMTP server running on the local machine.

The order client sample doesn't include any support for modifying these settings. Modifications can be made using any text editor or dedicated XML editor.

The Product Catalog

As discussed earlier in this chapter, the particular catalog used by this application is for a videocassette player that is described in Chapter 18. This catalog could just as well be for a barbecue grill or a 777 aircraft. As long as the catalog conforms to the DTD in OrderCatalog.dtd, the order client application will be able to use it to submit orders.

One of the most important parts of the order catalog is the <order_info> element:

<order_info method="email" address="[email protected]"/>

This element includes two required attributes. The first attribute (method) indicates the method to use to submit new orders. The only method understood by this sample application is e-mail. The second attribute (address) gives the e-mail address where orders are to be submitted. Later, we'll see how the order processing service monitors this mailbox for new orders.

The rest of the product catalog includes basic information about the product in question and a list of items that can be ordered. The loadCatalog() method (shown in Listing 14.4) parses the product catalog document and populates the items list box using the elements found in the <items> element.

Listing 14.4. The loadCatalog() Method
public void loadCatalog(String strCatalogURL) throws Exception
{
  // parse the product catalog
  m_docCatalog = m_bBuilder.build(strCatalogURL);

  // init UI with catalog info

  // first, get a list of all of the item elements in the catalog
  Element elCat = m_docCatalog.getRootElement();
  Element elItems = elCat.getChild("items");

  java.util.List lstItems = elItems.getChildren("item");

  // now, populate a Vector with text descriptions of each item
  Vector vItems = new Vector();
  Iterator i = lstItems.iterator();
  Element e;

  while (i.hasNext()) {
    e = (Element)i.next();
    vItems.add(e.getChildText("desc") + " ("
        + e.getAttributeValue("part_num") + ")");
  }

  // now initialize the list box with the list of items
  lstCatalog.setListData(vItems);
}
							

The loadCatalog() method is a good example of basic JDOM usage. The first call, to the build() method of the SAXBuilder class member, invokes the registered SAX parser to parse the catalog document. The resulting Document object will be kept in the m_docCatalog member, where it can be referenced later.

After the document has been parsed, the getRootElement(), getChild(), and getChildren() methods are used to get a list of all the <item> elements in the catalog. The list is then used to construct text descriptions of each item, which are added to a Java Vector object. This vector is used to initialize the JList control.

Using the Application

After the application has been initialized, the user is free to select items from the catalog list, enter a quantity to purchase, and add them to the current order table. The items and quantities to be ordered are actually stored in a JDOM XML document in real-time. The XML order document is displayed using a JTable and a custom table model class, OrderTableModel.

The current order document is created and initialized in the resetOrder() method, shown in Listing 14.5.

Listing 14.5. The resetOrder() Method
public void resetOrder()
{
  // create a new order document object
  Element elRoot = new Element("order");
  m_docOrder = new Document(elRoot);

  // create the empty structure of the order document
  elRoot.addContent(new Element("customer_name"));
  elRoot.addContent(new Element("credit_card_num"));
  elRoot.addContent(new Element("items"));

  // set up the on-screen order table with a new table model
  OrderTableModel otm = new OrderTableModel(m_docOrder);
  tabOrder.setModel(otm);

  // reset the other text controls
  txtCustName.setText("");
  txtCreditCardNum.setText("");
  txtQuantity.setText("1");
}
							

First, the empty root <order> element is created. Then, the new JDOM Document object is created using the new root element. The addContent() method of the Element interface is used to create the basic structure of the order document. As the user adds new items to the order, new <item> children will be added to the <items> element. To ensure that the order display is always current, the application uses a custom table model class that returns data from the XML document to the JTable control.

Within the OrderTableModel class, the getValueAt() method translates row and column references from the table control into elements within the attached order document. For example, take the sample order document shown in Listing 14.6.

Listing 14.6. A Sample Order Document
<?xml version="1.0" encoding="UTF-8" ?>
<order>
 <customer_name>Electro-Shack</customer_name>
 <credit_card_num>2293293923231514</credit_card_num>
 <items>
   <item part_num="KJ-1291CC-01" quantity="50" />
   <item part_num="KNU-1485" quantity="50" />
   <item part_num="SC-938" quantity="33" />
   <item part_num="PH-1412U" quantity="33" />
   <item part_num="DPX482426" quantity="200" />
 </items>
</order>

The OrderTableModel class interprets the contents of the <items> element as a table. The getRowCount() method returns the number of <item> elements in the list. The two attributes (part_num and quantity) are interpreted as columns in the table. The getValueAt() method shows how row and column references can be converted into XML document references when the underlying data is regular in structure:

public Object getValueAt(int parm1, int parm2) {
  Element elItems = m_docOrder.getRootElement().getChild("items");

First, the row number is used to find the proper <item> element:

Element elRow = (Element)elItems.getChildren().get(parm1);

Then, the column number indicates which attribute value needs to be returned:

  switch (parm2) {
  case 0: {
    return elRow.getAttributeValue("quantity");
  }

  case 1: {
    return elRow.getAttributeValue("part_num");
  }

  default: {
    return "";
  }
  }
}
							

After the order is complete, the user clicks the Transmit Order button. This fires the btnXmit_ actionPerformed() event handler, which populates the current in-memory order document with the customer name and credit-card number from the text box controls. Then, if the requested transmission method is e-mail (others could be added in the future), it calls the sendEmailOrder() method.

The sendEmailOrder() method uses a simple open-source SMTP helper class from www.gjt.org (see the Smtp.java source file for more information about usage and licensing of this object). The bulk of the method initiates an SMTP session with the server given in the application configuration document. First it sets the sender's e-mail address, and then it sets the destination address using the address from the order catalog document. Finally, the actual body of the message is transmitted using JDOM's XMLOutputter class:

XMLOutputter xo = new XMLOutputter();

xo.output(m_docOrder, pwOut);

After the smtp.sendMessage() method is called, the message is on its way to the order server for processing.

The OrderServer Service

The order processing server is a command-line Java program that periodically checks a given e-mail box for new order messages from the order client. The service continues to run until the user terminates it by pressing Enter.

This is the basic flow of the order processing service:

1.
The main() method creates a new OrderServer object instance.

2.
The service reads configuration information from the XML configuration file.

3.
The service initializes the various server settings from the values contained in the configuration file.

4.
The service listener thread starts.

5.
The listener thread checks the POP box for new messages.

6.
The service parses new messages and appends the order information to the global order database document.

7.
After waiting for a predetermined amount of time, execution continues with step 5.

The workhorse routine of the service is the processMessage() method. Given an array of strings that contain single lines from the incoming message, it reconstructs the order document that was created by the order client. It then saves various bits and pieces of information from the e-mail message itself into the new order document. After the order document is complete, it appends the new order to the master order database:

Document docDB;

try {
  docDB = m_sb.build(new FileInputStream(m_fileOrderDB));
}  catch (Exception e) {
  // either the order database document doesn't exist, or
  // there was an error while parsing it
  docDB = null;
}

If there is no order database document, or an error occurred while parsing, the service creates a new empty document:

if (docDB == null) {
  docDB = new Document(new Element("order_database"));

  docDB.getRootElement().addContent(new Element("orders"));
}

All <order> elements are children of the <orders> element. The following code fragment detaches the root element from the order document that was just constructed and appends it to the <orders> element of the order database:

Element elOrders = docDB.getRootElement().getChild("orders");

Element elRoot = docOrder.getRootElement();
docOrder.setRootElement(new Element("temp")); // set a placeholder

elOrders.addContent(elRoot);

Now it is necessary to write the order database document back to the disk. This occurs after each order is read, but before the corresponding order e-mail is deleted from the POP mailbox. This provides a primitive type of transaction safety. Here's the necessary code:

try {
  XMLOutputter xo = new XMLOutputter();

  FileOutputStream fos = new FileOutputStream(m_fileOrderDB);
  xo.output(docDB, fos);
  fos.close();
}  catch (IOException ioe) {
  System.err.println(ioe);
}

The rest of the code in the OrderServer object deals with creating and stopping the listener thread, interfacing with the POP server, and reading the server configuration.

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

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