Coffee Break Sample App

 


Overview

The Coffee Break sells coffee on the Internet. Customers communicate with the Coffee Break server to order coffee online. The server consists of Java Servlets, JSP pages, and JavaBeans components. A customer enters the quantity of each coffee to order and clicks the "Submit" button to send the order.

The Coffee Break does not maintain any inventory. It handles customer and order management and billing. Each order is filled by forwarding suborders to one or more coffee distributors. This process is depicted in Figure 18-1.

Figure 18-1 Coffee Break Application Flow

The Coffee Break server obtains the coffee varieties it sells and their prices by querying distributors at startup and on demand.

  1. The Coffee Break server uses JAXM messaging to communicate with one of its distributors. It has been dealing with this distributor for some time and has previously made the necessary arrangements for doing request-response JAXM messaging. The two parties have agreed to exchange four kinds of XML messages and have set up the DTDs those messages will follow.
  2. The Coffee Break server uses JAXR to send a query searching for coffee distributors that support JAX-RPC to the Registry Server.
  3. The Coffee Break server requests price lists from each of the coffee distributors. The server makes the appropriate remote procedure calls and waits for the response, which is a JavaBeans component representing a price list. The JAXM distributor returns price lists as XML documents.
  4. Upon receiving the responses, the Coffee Break server processes the price lists from the JavaBeans components returned by calls to the distributors.
  5. The Coffee Break Server creates a local database of distributors.
  6. When an order is placed, suborders are sent to one or more distributors using the distributor's preferred protocol.

JAX-RPC Distributor Service

The Coffee Break server is also a client-- it makes remote calls on the JAX-RPC distributor service. The service code consists of the service interface, service implementation class, and several JavaBeans components that are used for method parameters and return types.

 

Service Interface

The service interface, SupplierIF, defines the methods that can be called by remote clients. The parameters and return types of these methods are JavaBeans components:

Because these components are shared by other programs, their source code resides in the $JWSDP_HOME/docs/tutorial/examples/cb/common/src directory. The source code for the SupplierIF interface, which follows, resides in the $JWSDP_HOME/docs/tutorial/examples/cb/jaxrpc/src directory.

package com.sun.cb;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface SupplierIF extends Remote {

    public ConfirmationBean placeOrder(OrderBean order) 
        throws RemoteException;
    public PriceListBean getPriceList() throws RemoteException;
}

 

Service Implementation

The SupplierImpl class implements the placeOrder and getPriceList methods, which are defined by the SupplierIF interface. So that you can focus on the code related to JAX-RPC, these methods are short and simplistic. In a real-world application, these methods would access databases and interact with other services, such as shipping, accounting, and inventory.

The placeOrder method accepts as input a coffee order and returns a confirmation for the order. To keep things simple, the placeOrder method confirms every order and sets the ship date in the confirmation to the next day. (This date is calculated by DateHelper, a utility class that resides in the cb/common subdirectory.) The source code for the placeOrder method follows:

public ConfirmationBean placeOrder(OrderBean order) {

    Date tomorrow = 
        com.sun.cb.DateHelper.addDays(new Date(), 1);
    ConfirmationBean confirmation = 
        new ConfirmationBean(order.getId(), tomorrow);
    return confirmation;
}

The getPriceList method returns a PriceListBean object, which lists the name and price of each type of coffee that can be ordered from this service. The getPriceList method creates the PriceListBean object by invoking a private method named loadPrices. In a production application, the loadPrices method would fetch the prices from a database. However, our loadPrices method takes a shortcut by getting the prices from the SupplierPrices.properties file. Here are the getPriceList and loadPrices methods:

public PriceListBean getPriceList() {

   PriceListBean priceList = loadPrices(); 
   return priceList;
}

private PriceListBean loadPrices() {

   String propsName = "com.sun.cb.SupplierPrices";
   Date today = new Date();
   Date endDate = DateHelper.addDays(today, 30);

   PriceItemBean[] priceItems =
       PriceLoader.loadItems(propsName);
   PriceListBean priceList = 
       new PriceListBean(today, endDate, priceItems);

   return priceList;
}

 

Publishing the Service in the Registry

Because we want customers to find our service, we will to publish it in a registry. The programs that publish and remove our service are called OrgPublisher and OrgRemover. These programs are not part of the service's webapp. They are stand-alone programs that are run by the ant set-up-service command. (See Building and Installing the JAX-RPC Service.) Immediately after the service is installed, it's published in the registry. And in like manner, right before the service is removed, it's removed from the registry.

The OrgPublisher program begins by loading String values from the CoffeeRegistry.properties file. Next, the program instantiates a utility class named JAXRPublisher. OrgPublisher connects to the registry by invoking the makeConnection method of JAXRPublisher. To publish the service, OrgPublisher invokes the executePublish method, which accepts as input username, password, and endpoint. The username and password values are required by the Registry Server. The endpoint value is the URL that remote clients will use to contact our JAX-RPC service. The executePublish method of JAXRPublisher returns a key that uniquely identifies the service in the registry. OrgPublisher saves this key in a text file named orgkey.txt. The OrgRemover program will read the key from orgkey.txt so that it can delete the service. (See Deleting the Service From the Registry.) The source code for the OrgPublisher program follows.

package com.sun.cb;

import javax.xml.registry.*; 
import java.util.ResourceBundle;
import java.io.*;

public class OrgPublisher {

    public static void main(String[] args) {

        ResourceBundle registryBundle =
           ResourceBundle.getBundle
           ("com.sun.cb.CoffeeRegistry");

        String queryURL = 
            registryBundle.getString("query.url");
        String publishURL =
            registryBundle.getString("publish.url");
        String username =
            registryBundle.getString("registry.username");
        String password =
            registryBundle.getString("registry.password");
        String endpoint = registryBundle.getString("endpoint");
       String keyFile = registryBundle.getString("key.file");

        JAXRPublisher publisher = new JAXRPublisher();
        publisher.makeConnection(queryURL, publishURL);
        String key = publisher.executePublish
            (username, password, endpoint);

        try {
            FileWriter out = new FileWriter(keyFile);
            out.write(key);
            out.flush();
            out.close();
        } catch  IOException(ex) {
            System.out.println(ex.getMessage());
        }
    }
    
}

The JAXRPublisher class is almost identical to the sample program JAXRPublish.java, which is described in Managing Registry Data.

First, the makeConnection method creates a connection to the Registry Server. See Establishing a Connection for more information. To do this, it first specifies a set of connection properties using the query and publish URLs passed in from the CoffeeRegistry.properties file. For the Registry Server, the query and publish URLs are actually the same.

Properties props = new Properties();
props.setProperty("javax.xml.registry.queryManagerURL",
    queryUrl);
props.setProperty("javax.xml.registry.lifeCycleManagerURL",
    publishUrl);

Next, the makeConnection method creates the connection, using the connection properties:

ConnectionFactory factory = ConnectionFactory.newInstance();
factory.setProperties(props); 
connection = factory.createConnection();

The executePublish method takes three arguments: a username, a password, and an endpoint. It begins by obtaining a RegistryService object, then a BusinessQueryManager object and a BusinessLifeCycleManager object, which enable it to perform queries and manage data:

rs = connection.getRegistryService();
blcm = rs.getBusinessLifeCycleManager();
bqm = rs.getBusinessQueryManager();

Because it needs password authentication in order to publish data, it then uses the username and password arguments to establish its security credentials:

PasswordAuthentication passwdAuth = 
    new PasswordAuthentication(username, 
        password.toCharArray());
Set creds = new HashSet();
creds.add(passwdAuth);
connection.setCredentials(creds);

It then creates an Organization object with the name "JAXRPCCoffeeDistributor," then a User object that will serve as the primary contact. It gets the data from the resource bundle instead of hardcoding it as strings, but otherwise this code is almost identical to that shown in the JAXR chapter.

ResourceBundle bundle =
    ResourceBundle.getBundle("com.sun.cb.CoffeeRegistry");

// Create organization name and description 
Organization org =
    blcm.createOrganization(bundle.getString("org.name"));
InternationalString s = 
    blcm.createInternationalString
    (bundle.getString("org.description"));
org.setDescription(s);

// Create primary contact, set name 
User primaryContact = blcm.createUser();
PersonName pName =
    blcm.createPersonName(bundle.getString("person.name"));
primaryContact.setPersonName(pName);

It adds a telephone number and email address for the user, then makes the user the primary contact:

org.setPrimaryContact(primaryContact);

It gives JAXRPCCoffeeDistributor a classification using the North American Industry Classification System (NAICS). In this case it uses the classification "Other Grocery and Related Products Wholesalers".

Classification classification = (Classification)
    blcm.createClassification(cScheme,
        bundle.getString("classification.name"), 
        bundle.getString("classification.value"));
Collection classifications = new ArrayList();
classifications.add(classification);
org.addClassifications(classifications);

Next, it adds the JAX-RPC service, called "JAXRPCCoffee Service," and its service binding. The access URI for the service binding contains the endpoint URL that remote clients will use to contact our service:


JAXR validates each URI, so an exception is thrown if the service was not installed before you ran this program.

Collection services = new ArrayList();
Service service =
    blcm.createService(bundle.getString("service.name"));
InternationalString is = 
    blcm.createInternationalString
    (bundle.getString("service.description"));
service.setDescription(is);

// Create service bindings
Collection serviceBindings = new ArrayList();
ServiceBinding binding = blcm.createServiceBinding();
is = blcm.createInternationalString
    (bundle.getString("service.binding"));
binding.setDescription(is);
try {
    binding.setAccessURI(endpoint); 
} catch (JAXRException je) {
    throw new JAXRException("Error: Publishing this " + 
    "service in the registry has failed because " + 
    "the service has not been installed on Tomcat.");
}
serviceBindings.add(binding);

// Add service bindings to service
service.addServiceBindings(serviceBindings);

// Add service to services, then add services to organization
services.add(service);
org.addServices(services);

Then it saves the organization to the registry:

Collection orgs = new ArrayList();
orgs.add(org);
BulkResponse response = blcm.saveOrganizations(orgs);

The BulkResponse object returned by saveOrganizations includes the Key object containing the unique key value for the organization. The executePublish method first checks to make sure the saveOrganizations call succeeded.

If the call succeeded, the method extracts the value from the Key object and displays it:

Collection keys = response.getCollection();
Iterator keyIter = keys.iterator();
if (keyIter.hasNext()) {
    javax.xml.registry.infomodel.Key orgKey =
        (javax.xml.registry.infomodel.Key) keyIter.next();
    id = orgKey.getId();
    System.out.println("Organization key is " + id);
}

Finally, the method returns the string id so that the OrgPublisher program can save it in a file for use by the OrgRemover program.

 

Deleting the Service From the Registry

The OrgRemover program deletes the service from the Registry Server immediately before the service is removed. Like the OrgPublisher program, the OrgRemover program starts by fetching values from the CoffeeRegistry.properties file. One these values, keyFile, is the name of the file that contains the key that uniquely identifies the service. OrgPublisher reads the key from the file, connects to the Registry Server by invoking makeConnection, and then deletes the service from the registry by calling executeRemove. Here is the source code for the OrgRemover program:

package com.sun.cb;

import java.util.ResourceBundle;
import javax.xml.registry.*; 
import javax.xml.registry.infomodel.Key;
import java.io.*;

public class OrgRemover {

    Connection connection = null;

    public static void main(String[] args) {

        String keyStr = null;

        ResourceBundle registryBundle = 
            ResourceBundle.getBundle
            ("com.sun.cb.CoffeeRegistry");

        String queryURL = 
            registryBundle.getString("query.url");
        String publishURL =
            registryBundle.getString("publish.url");
        String username =
            registryBundle.getString("registry.username");
        String password =
            registryBundle.getString("registry.password");
        String keyFile = registryBundle.getString("key.file");

        try {
            FileReader in = new FileReader(keyFile);
            char[] buf = new char[512];
            while (in.read(buf, 0, 512) >= 0) { }
            in.close();
            keyStr = new String(buf).trim();
        } catch  IOException(ex) {
            System.out.println(ex.getMessage());
        }

        JAXRRemover remover = new JAXRRemover();
        remover.makeConnection(queryURL, publishURL);
        javax.xml.registry.infomodel.Key modelKey = null;
        modelKey = remover.createOrgKey(keyStr);
        remover.executeRemove(modelKey, username, password);
    }
}

Instantiated by the OrgRemover program, the JAXRRemover class contains the makeConnection, createOrgKey, and executeRemove methods. It is almost identical to the sample program JAXRDelete.java, which is described in Removing Data from the Registry.

The makeConnection method is identical to the JAXRPublisher method of the same name.

The createOrgKey method is a utility method that takes one argument, the string value extracted from the key file. It obtains the RegistryService object and the BusinessLifeCycleManager object, then creates a Key object from the string value.

The executeRemove method takes three arguments: a username, a password, and the Key object returned by the createOrgKey method. It uses the username and password arguments to establish its security credentials with the Registry Server, just as the executePublish method does.

The method then wraps the Key object in a Collection and uses the BusinessLifeCycleManager object's deleteOrganizations method to delete the organization.

Collection keys = new ArrayList();
keys.add(key);
BulkResponse response = blcm.deleteOrganizations(keys);

The deleteOrganizations method returns the keys of the organizations it deleted, so the executeRemove method then verifies that the correct operation was performed and displays the key for the deleted organization.

Collection retKeys = response.getCollection();
Iterator keyIter = retKeys.iterator();
javax.xml.registry.infomodel.Key orgKey = null; 
if (keyIter.hasNext()) { 
    orgKey = 
        (javax.xml.registry.infomodel.Key) keyIter.next();
    id = orgKey.getId();
    System.out.println("Organization key was " + id);
}

JAXM Distributor Service

The JAXM distributor service is simply the arrangements that the distributor and the Coffee Break have made regarding their exchange of XML documents. These arrangements include what kinds of messages they will send, the form of those messages, and what kind of JAXM messaging they will do. If they had agreed to do one-way messaging, they would also have had to use messaging providers that talk to each other and had to use the same profile. In this scenario, the parties have agreed to use request-response messaging, so a messaging provider is not needed.

The Coffee Break server sends two kinds of messages:

The JAXM coffee supplier responds with two kinds of messages:

All of the messages they send conform to an agreed-upon XML structure, which is specified in a DTD for each kind of message. This allows them to exchange messages even though they use different document formats internally.

The four kinds of messages exchanged by the Coffee Break server and the JAXM distributor are specified by the following DTDs:

These DTDs may be found at

$JWSDP_HOME/docs/tutorial/examples/cb/jaxm/dtds

The dtds directory also contains a sample of what the XML documents specified in the DTDs might look like. The corresponding XML files for each of the DTDs are as follows:

Because of the DTDs, both parties know ahead of time what to expect in a particular kind of message and can therefore extract its content using the JAXM API.

Code for the client and server applications is in the following directory:

$JWSDP_HOME/docs/tutorial/examples/cb/jaxm/src/com/sun/cb

 

JAXM Client

The Coffee Break server, which is the JAXM client in this scenario, sends requests to its JAXM distributor. Because the request-response form of JAXM messaging is being used, the client applications use the SOAPConnection method call to send messages.

SOAPMessage response = con.call(request, endpoint);

Accordingly, the client code has two major tasks. The first is to create and send the request; the second is to extract the content from the response. These tasks are handled by the classes PriceListRequest and OrderRequest.

Sending the Request

This section covers the code for creating and sending the request for an updated price list. This is done in the getPriceList method of PriceListRequest, which follows the DTD price-list.dtd.

The getPriceList method begins by creating the connection that will be used to send the request. Then it gets the default MessageFactory object so that it can create the SOAPMessage object msg.

SOAPConnectionFactory scf = 
          SOAPConnectionFactory.newInstance();
SOAPConnection con = scf.createConnection();

MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();

The next step is to access the message's SOAPEnvelope object, which will be used to create a Name object for each new element that is created. It is also used to access the SOAPBody object, to which the message's content will be added.

SOAPPart part = msg.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
SOAPBody body = envelope.getBody();

The file price-list.dtd specifies that the top-most element inside the body is request-prices and that it contains the element request. The text node added to request is the text of the request being sent. Every new element that is added to the message must have a Name object to identify it, which is created by the Envelope method createName. The following lines of code create the top-level element in the SOAPBody object body. The first element created in a SOAPBody object is always a SOAPBodyElement object.

Name bodyName = envelope.createName("request-prices",
      "RequestPrices", "http://sonata.coffeebreak.com");
SOAPBodyElement requestPrices =
                body.addBodyElement(bodyName);

In the next few lines, the code adds the element request to the element request-prices (represented by the SOAPBodyElement requestPrices.) Then the code adds a text node containing the text of the request. Next, because there are no other elements in the request, the code calls the method saveChanges on the message to save what has been done.

Name requestName = envelope.createName("request");
SOAPElement request = 
            requestPrices.addChildElement(requestName);
request.addTextNode("Send updated price list.");

msg.saveChanges();

With the creation of the request message completed, the code sends the message to the JAXM coffee supplier. The message being sent is the SOAPMessage object msg, to which the elements created in the previous code snippets were added. The endpoint is the URI for the JAXM coffee supplier. The SOAPConnection object con is used to send the message, and because it is no longer needed, it is closed.

URL endpoint = new URL(
 "http://localhost:8080/jaxm-coffee-supplier/getPriceList");
SOAPMessage response = con.call(msg, endpoint);
con.close();

When the call method is executed, Tomcat executes the servlet PriceListServlet. This servlet creates and returns a SOAPMessage object whose content is the JAXM distributor's price list. (PriceListServlet is discussed in Returning the Price List.) Tomcat knows to execute PriceListServlet because the web.xml file at <JWSDP>/docs/tutorial/examples/cb/jaxm/web/ maps the given endpoint to that servlet.

Extracting the Price List

This section demonstrates (1) retrieving the price list that is contained in response, the SOAPMessage object returned by the method call, and (2) returning the price list as a PriceListBean.

The code creates an empty Vector object that will hold the coffee-name and price elements that are extracted from response. Then the code uses response to access its SOAPBody object, which holds the message's content. Notice that the SOAPEnvelope object is not accessed separately because it is not needed for creating Name objects, as it was in the previous section.

Vector list = new Vector();

SOAPBody responseBody = response.getSOAPPart().
                    getEnvelope().getBody();

The next step is to retrieve the SOAPBodyElement object. The method getChildElements returns an Iterator object that contains all of the child elements of the element on which it is called, so in the following lines of code, it1 contains the SOAPBodyElement object bodyEl, which represents the price-list element.

Iterator it1 = responseBody.getChildElements(); 
while (it1.hasNext()) {
  SOAPBodyElement bodyEl = (SOAPBodyElement)it1.next();

The Iterator object it2 holds the child elements of bodyEl, which represent coffee elements. Calling the method next on it2 retrieves the first coffee element in bodyEl. As long as it2 has another element, the method next will return the next coffee element.

  Iterator it2 = bodyEl.getChildElements();
  while (it2.hasNext()) {
    SOAPElement child2 = (SOAPElement)it2.next();

The next lines of code drill down another level to retrieve the coffee-name and price elements contained in it3. Then the message getValue retrieves the text (a coffee name or a price) that the JAXM coffee distributor added to the coffee-name and price elements when it gave content to response. The final line in the following code fragment adds the coffee name or price to the Vector object list. Note that because of the nested while loops, for each coffee element that the code retrieves, both of its child elements (the coffee-name and price elements) are retrieved.

    Iterator it3 = child2.getChildElements();
    while (it3.hasNext()) {
     SOAPElement child3 = (SOAPElement)it3.next();
     String value = child3.getValue();
     list.addElement(value);
    }
  }
}

The last code fragment adds the coffee names and their prices (as a PriceListItem) to the ArrayList priceItems, and prints each pair on a separate line. Finally it constructs and returns a PriceListBean.

ArrayList priceItems = new ArrayList();

for (int i = 0; i < list.size(); i = i + 2) {
  priceItems.add(
      new PriceItemBean(list.elementAt(i).toString(), 
      new BigDecimal(list.elementAt(i + 1).toString())));
  System.out.print(list.elementAt(i) + "        ");
  System.out.println(list.elementAt(i + 1));
}

Date today = new Date();
Date endDate = DateHelper.addDays(today, 30);
PriceListBean plb = 
        new PriceListBean(today, endDate, priceItems);

Ordering Coffee

The other kind of message that the Coffee Break server can send to the JAXM distributor is an order for coffee. This is done in the placeOrder method of OrderRequest, which follows the DTD coffee-order.dtd.

Creating the Order

As with the client code for requesting a price list, the placeOrder method starts out by creating a SOAPConnection object, creating a SOAPMessage object, and accessing the message's SOAPEnvelope and SOAPBody objects.

SOAPConnectionFactory scf =
                  SOAPConnectionFactory.newInstance();
SOAPConnection con = scf.createConnection();
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();

SOAPPart part = msg.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
SOAPBody body = envelope.getBody();

Next the code creates and adds XML elements to form the order. As is required, the first element is a SOAPBodyElement, which in this case is coffee-order.

Name bodyName = envelope.createName("coffee-order", "PO", 
               "http://sonata.coffeebreak.com"); 
SOAPBodyElement order = body.addBodyElement(bodyName);

The application then adds the next level of elements, the first of these being orderID. The value given to orderID is extracted from the OrderBean object passed to the OrderRequest.placeOrder method.

Name orderIDName = envelope.createName("orderID");
SOAPElement orderID = order.addChildElement(orderIDName);
orderID.addTextNode(orderBean.getId());

The next element, customer, has several child elements that give information about the customer. This information is also extracted from the Customer component of OrderBean.

Name childName = envelope.createName("customer");
SOAPElement customer = order.addChildElement(childName);

childName = envelope.createName("last-name");
SOAPElement lastName = customer.addChildElement(childName);    
lastName.addTextNode(orderBean.getCustomer().
  getLastName());

childName = envelope.createName("first-name"); 
SOAPElement firstName = customer.addChildElement(childName);    
firstName.addTextNode(orderBean.getCustomer().
  getFirstName());

childName = envelope.createName("phone-number"); 
SOAPElement phoneNumber = customer.addChildElement(childName);      
phoneNumber.addTextNode(orderBean.getCustomer().
  getPhoneNumber());

childName = envelope.createName("email-address"); 
SOAPElement emailAddress = 
          customer.addChildElement(childName); 
emailAddress.addTextNode(orderBean.getCustomer().
  getEmailAddress());

The address element, added next, has child elements for the street, city, state, and zip code. This information is extracted from the Address component of OrderBean.

childName = envelope.createName("address");
SOAPElement address = order.addChildElement(childName);

childName = envelope.createName("street");
SOAPElement street = address.addChildElement(childName);
street.addTextNode(orderBean.getAddress().getStreet());

childName = envelope.createName("city");   
SOAPElement city = address.addChildElement(childName); 
city.addTextNode(orderBean.getAddress().getCity());

childName = envelope.createName("state");
SOAPElement state = address.addChildElement(childName);
state.addTextNode(orderBean.getAddress().getState());

childName = envelope.createName("zip");
SOAPElement zip = address.addChildElement(childName);
zip.addTextNode(orderBean.getAddress().getZip());

The element line-item has three child elements: coffeeName, pounds, and price. This information is extracted from the LineItems list contained in OrderBean.

for  Iterator(it = orderBean.getLineItems().iterator();
                              it.hasNext(); ; ) {
  LineItemBean lib = (LineItemBean)it.next();

  childName = envelope.createName("line-item");
  SOAPElement lineItem = 
      order.addChildElement(childName);

  childName = envelope.createName("coffeeName");
  SOAPElement coffeeName = 
      lineItem.addChildElement(childName);
  coffeeName.addTextNode(lib.getCoffeeName());

  childName = envelope.createName("pounds");
  SOAPElement pounds = 
      lineItem.addChildElement(childName);
  pounds.addTextNode(lib.getPounds().toString());

  childName = envelope.createName("price");
  SOAPElement price = 
      lineItem.addChildElement(childName);
  price.addTextNode(lib.getPrice().toString());

}

  //total
  childName = envelope.createName("total");
  SOAPElement total = 
      order.addChildElement(childName);
  total.addTextNode(orderBean.getTotal().toString()); 
}

With the order complete, the application sends the message and closes the connection.

URL endpoint = new URL(
  "http://localhost:8080/jaxm-coffee-supplier/orderCoffee");
SOAPMessage reply = con.call(msg, endpoint);
con.close();

Because the web.xml file maps the given endpoint to ConfirmationServlet, Tomcat executes that servlet (discussed in Returning the Order Confirmation) to create and return the SOAPMessage object reply.

Retrieving the Order Confirmation

The rest of the placeOrder method retrieves the information returned in reply. The client knows what elements are in it because they are specified in confirm.dtd. After accessing the SOAPBody object, the code retrieves the confirmation element and gets the text of the orderID and ship-date elements. Finally, it constructs and returns a ConfirmationBean with this information.

SOAPBody sBody = reply.getSOAPPart().getEnvelope().getBody();
Iterator bodyIt = sBody.getChildElements();
SOAPBodyElement sbEl = (SOAPBodyElement)bodyIt.next();
Iterator bodyIt2 = sbEl.getChildElements();

SOAPElement ID = (SOAPElement)bodyIt2.next();
String id = ID.getValue();

SOAPElement sDate = (SOAPElement)bodyIt2.next();
String shippingDate = sDate.getValue();

SimpleDateFormat df = new 
        SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
Date date = df.parse(shippingDate);
ConfirmationBean cb = new ConfirmationBean(id, date); 

 

JAXM Service

The JAXM coffee distributor, the JAXM server in this scenario, provides the response part of the request-response paradigm. When JAXM messaging is being used, the server code is a servlet. The core part of each servlet is made up of three javax.servlet.HttpServlet methods: init, doPost, and onMessage. The init and doPost methods set up the response message, and the onMessage method gives the message its content.

Returning the Price List

This section takes you through the servlet PriceListServlet. This servlet creates the message with the current price list that is returned to the method call, invoked in PriceListRequest.

Any servlet extends a javax.servlet class. Being part of a webapp, this servlet extends HttpServlet. It first creates a static MessageFactory object that will be used later to create the SOAPMessage object that is returned. Then it declares the MessageFactory object msgFactory, which will be used to create a SOAPMessage object that has the headers and content of the original request message.

public class PriceListServlet extends