HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Part 1 of this series appeared in JDJ June, Part 2 in JDJ July, Part 3 in JDJ September

This is the fourth in a series of articles focused on using Java and ColdFusion technologies to develop an Online Ticket Store application. As JDJ's September issue had an XML focus, we went with the flow and discussed data formatting aspects of our store and developed XML objects to pass date structures between the Merchant Server and Service Access tiers.

This month's article focuses on the online store portion of our application, beginning with a description of the business offerings of the store. This is followed by a use-case analysis of the store, a methodology similar to the one in the July issue. We then design the classes for implementing the use cases and discuss the code needed to make our store a "reality." We'll also make use of the XML objects developed in the September issue to facilitate data transfer for the online store transactions.

The Online Airline Store Business Model
In addition to serving as a virtual travel agent, our application has an online store through which it sells merchandise. This includes the products offered via airline catalogs as well as unique items offered exclusively through the travel agency - books, magazines, clothing, gifts, souvenirs and computer equipment, for example.

So far, our store is like any other virtual store on the Internet that also sells airline tickets. But here's where it gets interesting. A unique feature our store offers is the ability for passengers to lease equipment for the duration of the flight.
This equipment includes:

  • Books and magazines
  • Laptops for business or pleasure
  • Portable CD players and music CDs
  • Portable cassette players and audiotapes

The idea is that someone who uses our Online Store to book a flight and purchase a ticket can instruct the store to have the leased equipment available on the flight he or she will be taking. Although the scenario is fictitious, I'd like to develop it further. I can think of several different options for our leasing operation:

  1. The airline could keep an inventory of the equipment at the various airports and the passenger could pick up and drop off the equipment at the gate. The equipment may even be directly available on the flight. This, of course, would put a burden on the airlines to create the infrastructure and organization to support such a model.
  2. The online store could be affiliated with a leasing store at the airport that holds the inventory. The passenger could pick up the inventory from the physical store.
  3. The online store could have personnel who would be responsible for making the equipment available at the gate for the flight and picking it up at the other end of the flight.
If this kind of business becomes a reality, I can foresee airlines (Option 1) and independent agents (Options 2 and 3) competing for the business. However, one assumption we'll make for our application is that the equipment should always be leased via the online store. Thus, similar to a ticket purchase, payment for leased equipment would be made at the Merchant Server tier. The modules for this interaction (Shopping Cart, Catalog, Personal Profile Manager, Payment Manager, etc.) are thus implemented in ColdFusion and will be discussed in the December issue of ColdFusion Developer's Journal. For an explanation of the tiers of this application and the software modules, please refer to the previous three articles. Figure 1 shows the four tiers in our application for the current reference. This is the same as Figure 2 in Part 1 of this series.

You may wonder what modules of the store will be housed in the Service Access tier. After all, that's the Java middleware tier in this application. Well, though the merchandise will be ordered and paid for at the Merchant Server tier, this tier doesn't have any knowledge of the local inventory and availability of the merchandise. The Service Access tier is responsible for this function - and for making sure that the order, whether it's a merchandise purchase or lease, gets "delivered" to the customer. Delivery may constitute shipping the actual product, making it available at the gate or keeping it ready for the customer at the physical store in the airport.

The UI design for this application isn't a major part of our Java-based design. A sophisticated UI will be developed in parallel using ColdFusion in the corresponding issues of ColdFusion Developer's Journal (Vol. 1, issues 4 and 6, and Vol. 2, issues 1 and 2).

Online Store Requirements
The Online Store entertains two types of transactions - purchase and lease. Once the customer selects the merchandise (this is handled by the Merchant Server tier), the order is sent to the Service Access tier. This leads to a check for availability against the Application Services tier. Note that the function of this tier is different from that in the previous articles. In this article the tier is responsible for satisfying a merchandise order instead of booking an airline ticket. In both cases, however, the Application Service tier acts as the back office for services offered by the middle tiers. If the purchase or lease were made directly against the airline, the Application Services tier modules would reside in the airline's back offices.

The application modules involved in this transaction are described in Table 1 and illustrated in Figure 2.

Once the "order" is placed, a confirmation is sent back to the Merchant Server tier, which is then responsible for getting the payment for the purchase. To keep the workflow simple, we're assuming a synchronous workflow through the system. In reality, part of this workflow will be asynchronous. The simplified workflow for this interaction is shown below:

  1. Accept the merchandise order from the Merchant Server tier.
  2. Determine whether it's a PURCHASE or LEASE.
  3. In the case of LEASE, search for availability of the merchandise.
  4. If not available, send back an exception.
  5. If available, place an order with the Application Services tier. For a PURCHASE operation, this would be an order for a shipment. For a LEASE operation, this would be the order to make the equipment available at the airport.
  6. Return the confirmation to the Merchant Server tier.

This is a simple workflow that runs through the system in sequential fashion. We'll implement this workflow in our system, ignoring other functionality such as canceling an order. The idea is to demonstrate how data flows through our Online Store from the end customer to the back office and back. Figure 3 illustrates the use cases for the Broker. Workflows are illustrated in Figure 4. The main difference between the two flows is that the Leasing Order workflow has a constraint on time because the items need to be booked for the flight and thus need to be at the airport at a specific time. Note also that the Leasing Order workflow follows in sequence from the Ticket Booking workflow described in the July JDJ article - that is, the items are leased only after the tickets have been reserved. The Purchase Order workflow is independent of any interaction the customer may have with the ticket reservation system. We assume that for a Purchase Order the customer will accept delivery whenever the merchandise can be sent by the system. This is similar to the regular catalog orders that state "Allow 4-6 weeks for delivery."

Data Interchange Formats
The following data objects were introduced in the September JDJ. Some of the description is repeated here to put things in the appropriate context:

  • Lease Order
  • Lease Confirmation
  • Purchase Order
  • Purchase Confirmation
Let's go ahead and define the attributes of goods sold in the store. An item that may be offered by the store will have the following fields. Example values are assigned to the fields:

ITEM_NAME="CD Player"
ITEM_ID ="cd30056"
QUANTITY="2"

An order for the purchase of an item (or items) will also need information about the person making the purchase. This will consist of the following fields:

NAME="Clark Kent"
ADDRESS="123 Tiny Lane, Smallville, Kansas, 12345"

The fields listed above are common for both purchase and lease options. The lease operation requires one additional field. This is the reference number of the confirmed flight, and is required because it's the reference for the store to make the equipment available at the airport.

REFERENCE_NO="SA123456"

The hierarchy of the XML documents in DOM for the PURCHASE/lease was illustrated in the September JDJ. The XML files are shown in Listing 1. I won't go into a detailed description of the code for the XML parsing for these objects as it's similar to the mechanism described for the ticket reservation part of our application as shown in September.

Class Design
Now let's define the classes involved in this set of transactions. For now, we'll forgo discussion on the UI and assume that the user's input somehow arrives at the Broker. In designing the store I'm restricting the discussion to an RMI-based connection to the back office. The previous articles have discussed the flexibility of using different transport protocols like CORBA, TCP/IP, etc. That design can easily be applied to our store. The classes for the store are described in the following sections. Figure 5 illustrates the class relationships.

StoreServlet
Listing 1 shows the StoreServlet class. This class receives a merchandise order request ("PURCHASE" or "LEASE") from the Merchant Server, packages it into a LeaseOrder (LEASE) or PurchaseOrder (PURCHASE) object, passes it on to the Order Manager and gets a confirmation (or an exception if it's a LEASE operation), and returns the result to the Merchant Server. The input to the servlet is in the form of an HTTP POST that carries one of the following XML data structures:

<LEASE>
<PURCHASE>

The LEASE structure is used only after the customer has purchased a ticket. The StoreServlet first checks to see whether it has a reference to the clientManager_ object. If not, it constructs a new StoreClientManager object. The request and response streams are copied into request_ and response_ variables for future use. Next, the method processParameters() is called. This method parses the input parameters and creates the appropriate Java object. The XML processing for this application is done in the XMLProcessor class. The methods for the LEASE and PURCHASE operations are shown in Listing 1. (The code for the ticket reservation objects is stubbed out to conserve space.) The StoreServlet first creates a new instance of the XMLProcessor object. The complete listing may be obtained from www.JavaDevelopersJournal.com. It then calls the initParser() method on the XMLProcessor class. The servlet's input stream is passed in as a parameter for parsing the XML document that the servlet received in its input stream. The XMLProcessor class is shown in Listing 2. The code for XMLParser.java is not discussed here, as it's similar to the code in the September article.

Next, the method getOperationType() is called on the XMLProcessor reference (xp) to check what kind of an operation was invoked on the StoreServlet. If the type is a "Purchase," the method processPurchase() is called on the object xp. This method returns a PurchaseOrder object from the XML document. This object is passed to the doPurchase() method, which in turn calls the doPurchase() method on the clientManager_. If the type is "Lease," the method processLease() is called on the object xp. Following this, the local method doLease() is called, which in turn calls the method doLease() on the clientManager_.

This is a very simplified version of an order. A real order would have greater detail, including telephone number and method of shipment.

XMLProcessor
The following methods were added to this class as compared to the September listing:
processPurchase()
processLease()
processConfirmation()

The complete code for these methods is given in Listing 2. The remainder of the listing was provided in September and thus is not repeated here. One new operation used in the parsing is the processListTag() utility method, which is used to obtain an array of item objects from the XML input file (submitted via the POST). This is used in both processLease() and processPurchase() methods. The processConfirmation() method is relatively simple in comparison to the processTicketQuote() method described in September. The complete listing for this file is available at www.JavaDevelopersJournal.com.

The next four classes - Item, PurchaseOrder, LeaseOrder and Confirmation - are shown in Listing 3.

Item
This class encapsulates an item. Basically, it consists of the following data fields and the getter and setter methods for them:

  • name  
  • sku  
  • quantity

    PurchaseOrder
    This class encapsulates an item purchase order. It consists of the following fields and the corresponding getter and setter methods:

  • list of Items  
  • customerName  
  • address

    LeaseOrder
    This class encapsulates an item leasing order. It consists of the following fields and the corresponding getter and setter methods:

  • list of Items  
  • customerName
  • address  
  • referenceNo

    The LeaseOrder class inherits its first three fields from PurchaseOrder. The last field is the reference number of the associated tickets purchased.

    Confirmation
    This class encapsulates the result of an order for a purchase or a lease. It consists of the following fields and the corresponding getter and setter methods:

  • status (confirm/reject)
  • reference number

    The last four classes - StoreClientManager, RMIStoreService, RMIStoreClient and RMIStoreServer - are shown in Listing 4.

    StoreClientManager
    This class receives a PurchaseOrder or a LeaseOrder from the StoreServlet, forwards it to the RMIStoreClient, gets back a Confirmation object and sends it back to the StoreServlet. It's also responsible for actually placing the order or arranging for the lease. This functionality is stubbed out in the application. It has two main methods - doPurchase() and doLease(). The doLease() method delegates the operation to the RMIStoreClient's doLease() method.

    RMIStoreService
    This is the interface for the services offered by the Application Services tier. It provides a single method, getLeaseAvailability().

    RMIStoreClient
    This is accessed by the StoreServlet for submitting the request to a Ticket Server. It provides the stub for the method getLeaseAvailability().

    RMIStoreServer
    This class processes a PurchaseOrder (or LeaseOrder) object and sends back a Confirmation object. It implements the method getLeaseAvailability(). The code always returns a "true" for availability. In a real application this would go against a local availability engine that would check whether the item was available in the store.

    Running the Programs
    The StoreServlet can be accessed via any client that can post the corresponding file for either the LEASE or the PURCHASE operation. I've included the files required to access the servlet from ColdFusion. Readers may use HTML, Java clients or VB clients. The code for this article is available at www.JavaDevelopersJournal.com. The code was compiled and tested on a Windows NT 4.0 workstation. To run the programs you'll need the following:

    • JDK 1.1.x
    • JSDK 2.0 (Java Servlet Development Kit)
    • Your servlet engine and Web server

    Conclusion
    Developing this application has been a fun task for me. My intention was to provide a template for building n-tier applications in e-commerce. In essence, the idea was to decouple the Web storefront from the back-office services. If you undertake the development of such applications, the business logic for accessing back-office services may reside in middleware components written in EJB or COM. In that case you may end up using two categories of application servers - the Web storefront application server like ColdFusion and a middleware application server such as Netscape Application Server or WebLogic's application server.

    As mentioned earlier, this article series has generated a lot of feedback from readers - so much so that I plan to include the concepts discussed here in a book I'm currently writing. I hope it's been a useful exercise for all of you too.

    ASIDE FROM THE AUTHOR
    I'd like to bring up a few interesting issues related to this article series. For one thing, the first article generated more feedback than I expected. It's been a pleasant surprise to know that some companies actually have airline store sites developed in ColdFusion similar to the fictitious one we've developed here. I also received some e-mail that indicated folks took this application more seriously than I thought they would. These readers concluded that this is a real-world application. While the design of the application outlines a real-world architecture for a business problem and serves as a template for a real-world solution, it's not a production-level system.

    Another interesting development was that LiveSoftware (the makers of JRun) was acquired by Allaire Corporation (the makers of ColdFusion). This happened during the week of June 14, when JavaOne was in progress. It was my pleasure to talk to Jeremy Allaire (ColdFusion) and Paul Colton (JRun) at JavaOne regarding this development. In my opinion this has been a very smart move on Allaire's part. A few months ago Allaire acquired Bright Tiger Technologies, a company that specializes in clustering technology. The acquisition of JRun makes Allaire's app server story complete. ColdFusion is a great product and I've always felt they needed a story on the Java side. That was part of the motivation for building the Online Ticket Store. Coincidentally, the August issue of ColdFusion (Vol. 1, issue 4) has two features with two individual CF_SERVLET tags - the professional one from LiveSoftware and my own humble contribution.

    Author Bio
    Ajit Sagar, a member of the technical staff at i2 Technologies in Dallas, Texas, focuses on Web-based e-commerce applications and architectures. Ajit is a Sun-certified Java programmer with nine years of programming experience, including two and a half in Java. He holds an MS in computer science and a BS in electrical engineering.
    He can be reached at: [email protected]

    	
    
    Listing 1: StoreServlet.java
     
    import java.io.*; 
    import java.util.*; 
    import java.text.*; 
    import java.rmi.*; 
    import javax.servlet.http.*; 
    import javax.servlet.*; 
    
    public class StoreServlet extends HttpServlet 
    { 
    
      protected HttpServletRequest request_ = null; 
      protected HttpServletResponse response_ = null; 
      protected StringBuffer result_ = null; 
    
      XMLProcessor xp_ = null; 
      PrintWriter toClient_ = null; 
      StoreClientManager clientManager_ = null; 
      Confirmation confirmation_ = null; 
    
      // handle POST for servlet 
      public void doPost (HttpServletRequest request, 
              HttpServletResponse response) 
        throws ServletException, IOException { 
    
        request_ = request; 
        response_ = response; 
    
        toClient_ = new PrintWriter(response_.getOutputStream()); 
    
        // Begin HTML 
        toClient_.println("<HTML>"); 
    
        if (clientManager_ == null) { 
            clientManager_ = new StoreClientManager(); } 
    
            result_ = new StringBuffer(); 
    
            // Extract the arguments into local 
            // variables 
            processParameters(); 
            toClient_.println(result_.toString()); 
            toClient_.println("<HTML>"); 
            toClient_.flush(); } 
      
    
      private void processParameters () { 
        //XMLProcessor for converting XML 
        //to Java 
        xp_ = new XMLProcessor(); 
    
        try { 
          InputStream in = request_.get 
          InputStream(); 
          xp_.initParser(request_.getInput 
          Stream(), toClient_); } 
    
        catch (IOException ioe) { ioe.printStackTrace(); } 
    
        LeaseOrder leaseOrder = null; 
        PurchaseOrder purchaseOrder = null; 
        String queryType = xp_.getQueryType(); 
    
        if (queryType.equals("Lease")) { 
           leaseOrder = xp_.processLease(); 
            confirmation_ = doLease(leaseOrder); } 
    
        else if (queryType.equals("Purchase")) { 
            purchaseOrder = xp_.processPurchase(); 
            confirmation_ = doPurchase(purchaseOrder); } 
    
        else { 
            result_.append("<HI>ERROR:: Unknown query type"); 
                return; } 
    
        String xmlConfirmation = 
         xp_.processConfirmation(confirmation_); 
        result_.append(xmlConfirmation); 
      } 
    
      private Confirmation doLease(LeaseOrder leaseOrder) { 
            try { return clientManager_.doLease(leaseOrder); } 
            catch (Exception e) { return null; } 
      } 
    
      private Confirmation doPurchase(PurchaseOrder purchaseOrder) { 
            try { return clientManager_.doPurchase(purchaseOrder); } 
            catch (Exception e) { return null; } 
      } 
      
      
    
    Listing 2: .java: XML processor for the Online Store
     
    <!-- Confirm.txt --> 
    
    <?xml version="1.0"?> 
    <Confirmation Type="Ticket"> 
      <ConfirmationNo>"SA2345678"</ConfirmationNo> 
    </Confirmation> 
      
    
    <!-- Lease.txt> 
    
    <?xml version="1.0"?> 
    <Lease> 
      <Passenger> 
        <Name> 
          <LastName>"Kent"</LastName> 
          <FirstName>"Clark"</FirstName> 
        </Name> 
      </Passenger> 
      <Flight> 
       <ReferenceNo>"SA123456"</ReferenceNo> 
       </Flight> 
        <ItemList> 
          <Item> 
           <ItemName>"CD Player"</ItemName> 
           <ItemId>cd00321</ItemId> 
           <Quantity>1</Quantity> 
          </Item> 
          <Item> 
           <ItemName>"Book"</ItemName> 
           <ItemId>bookx453</ItemId> 
           <Quantity>1</Quantity> 
         </Item> 
       </ItemList> 
     </Lease> 
      
    
    <!-- Purchase.txt> 
    
    <?xml version="1.0"?> 
    <Purchase> 
     <Passenger> 
      <Name> 
        <LastName>"Kent"</LastName> 
        <FirstName>"Clark"</FirstName> 
      </Name> 
      <Address> 
        <Street>"123 Tiny Lane"</Street> 
        <City>"Smallville"</City> 
        <State>"Kansas"</State> 
        <Country>USA</Country> 
        <ZIP>12345</ZIP> 
       </Address> 
      </Passenger> 
        <ItemList> 
          <Item> 
           <ItemName>"CD Player"</ItemName> 
              <ItemId>cd00321</ItemId> 
              <Quantity>1</Quantity> 
          </Item> 
          <Item> 
              <ItemName>"Book"</ItemName> 
              <ItemId>bookx453</ItemId> 
              <Quantity>1</Quantity> 
          </Item> 
        </ItemList> 
    </Purchase> 
    
    } 
      
      
    
    Listing 3: Item.java, PurchaseOrder.java, LeaseOrder.java, Confirmation.java 
    import java.io.*; 
    
    public class Item implements Serializable { 
    
     private String name_ = null; 
     private String sku_ = null; 
     private int quantity_ = 0; 
    
     public Item (String name, String sku, 
     int quantity) { 
         name_ = name; 
         sku_ = sku; 
         quantity_ = quantity; } 
    
     public String getName () { return name_; } 
     public String getSKU () { return sku_; } 
     public int getQuantity () { return quantity_; } 
    } 
      
    
    // PurchaseOrder.java 
    import java.io.*; 
    
    public class PurchaseOrder implements Serializable { 
    private Item[] items_ = null; 
    private String customerName_ = null; 
    private String address_ = null; 
    
    public PurchaseOrder (Item[] items, 
                          String customerName, 
                          String address) { 
              items_ = items; 
              customerName_ = customerName; 
              address_ = address; 
            } 
            public Item[] getItems () { return 
     items_; } 
            public Item getItem(int index) { return 
     items_[index]; } 
            public String getCustomerName () { 
     return customerName_; } 
            public String getAddress () { return 
     address_; } 
    } 
      
    
    // LeaseOrder.java 
    import java.io.*; 
    
    public class LeaseOrder extends PurchaseOrder implements Serializable { 
    
            private String referenceNo_ = null; 
    
            public LeaseOrder (Item[] items, 
                                               String customerName, 
                                               String address, 
                                               String referenceNo) { 
    
                    super(items, customerName, address); 
                    referenceNo_ = referenceNo; 
            } 
            public String getReferenceNo () { return referenceNo_; } 
    } 
      
    
    // Confirmation.java 
    public class Confirmation { 
            String type_ = null; 
            String confirmationNo_ = null; 
    
            public Confirmation (String type, String confirmationNo) { 
                    type_ = type; 
                    confirmationNo_ = confirmationNo; 
            } 
    
            public String getType () { return type_; } 
            public String getConfirmationNo () { return confirmationNo_; } 
    
    } 
      
      
    
    Listing 4: StoreClientManager.java, RMIStoreService.java, RMIStoreClient.java, RMIStoreServer.java 
    
    public class StoreClientManager { 
    
            RMIStoreClient rmiClient_ = null; 
    
            public StoreClientManager () { 
                    if (rmiClient_ == null) 
                  rmiClient_ = new RMIStoreClient(); 
            } 
    
            public Confirmation doLease (LeaseOrder leaseOrder) 
              throws Exception { 
                    Boolean result = rmiClient_.getLeaseAvailibility(leaseOrder); 
    
                    if (result.booleanValue()) 
                      return new Confirmation("Lease", "AS1234"); 
                    else 
                      return new Confirmation("Lease", "NIL"); 
            } 
      
    
            public Confirmation doPurchase (PurchaseOrder purchaseOrder) 
              throws Exception { 
                    return new Confirmation("Purchase", "AS1234"); 
            } 
    } 
      
    
    // RMIStoreService.java 
    import java.rmi.*; 
    
    public interface RMIStoreService extends Remote { 
      public static final String SERVER_NAME = "rmi://t8000x321/servlet/rmiStoreServer"; 
      public Boolean getLeaseAvailibility (LeaseOrder leaseOrder) throws 
    RemoteException; 
    } 
      
    
    // RMIStoreClient.java 
    import java.rmi.*; 
    
    public class RMIStoreClient implements RMIStoreService { 
    
      private RMIStoreService server_ = null; 
    
      public RMIStoreClient() { connect ToServer(); } 
    
      public Boolean getLeaseAvailibility (LeaseOrder leaseOrder) throws 
    RemoteException { 
            Boolean result = new Boolean(false); 
            try { result = server_.getLeaseAvailibility(leaseOrder); } 
            catch (Exception re) { re.printStackTrace(); } 
        System.out.println("result = " + result); 
            return result; 
      } 
    
      public void connectToServer () { 
            if (System.getSecurityManager() == null) 
              System.setSecurityManager(new RMISecurityManager()); 
    
        try { server_ = 
    (RMIStoreService)(Naming.lookup(RMIStoreService.SERVER_NAME)); } 
            catch (Exception e) { e.printStack Trace(); } 
      } 
    
    } 
      
    
    // RMIStoreServer.java 
    import java.rmi.server.*; 
    
    public class RMIStoreServer 
      extends UnicastRemoteObject implements 
      RMIStoreService { 
    
      public String RMI_SERVER_NAME = "rmi://t8000x321/servlet/rmiStoreServer"; 
    
      public RMIStoreServer() throws RemoteException { 
            super(); 
      } 
    
      public Boolean getLeaseAvailibility (LeaseOrder leaseOrder) throws 
    RemoteException { 
            return new Boolean(true); 
    
      } 
    
     public static void main (String[] args) { 
            System.setSecurityManager(new RMISecurityManager()); 
            try { 
          RMIStoreServer server = new RMIStoreServer(); 
              Naming.rebind(server.RMI_SERVER_NAME, server); 
              System.out.println("RMIStoreServer ready ..."); } 
            catch (Exception e) { e.printStackTrace(); } 
      } 
    } 
      
     
          
    
  •  

    All Rights Reserved
    Copyright ©  2004 SYS-CON Media, Inc.
      E-mail: [email protected]

    Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.