The Java EE 5 Tutorial

SAAJ Client

The Coffee Break server, which is a SAAJ client in this scenario, sends requests to the SAAJ supplier. The SAAJ client application uses 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 to be used for creating the SOAPMessage object msg.

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

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

The next step is to access the message’s SOAPBody object, to which the message’s content will be added.

SOAPBody body = msg.getSOAPBody();

The file price-list.dtd specifies that the topmost 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 QName object to identify it. 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 = new QName("http://sonata.coffeebreak.com",
    "request-prices", "RequestPrices");
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.

QName requestName = new QName("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 SAAJ 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 SAAJ coffee supplier, http://localhost:8080/saaj-coffee-supplier/getPriceList. The SOAPConnection object con is used to send the message, and because it is no longer needed, it is closed.

URL endpoint = new URL(url);
SOAPMessage response = con.call(msg, endpoint);
con.close();

When the call method is executed, the Application Server executes the servlet PriceListServlet. This servlet creates and returns a SOAPMessage object whose content is the SAAJ supplier’s price list. (PriceListServlet is discussed in Returning the Price List.) The Application Server knows to execute PriceListServlet because the given endpoint is mapped 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.

Vector<String> list = new Vector<String>();

SOAPBody responseBody = response.getSOAPBody();

The next step is to retrieve the SOAPBodyElement object. The method getChildElements returns an Iterator object that contains all 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 SAAJ coffee supplier 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 final 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<PriceItemBean> items = new ArrayList<PriceItemBean>();
for (int i = 0; i < list.size(); i = i + 2) {
    PriceItemBean pib = new PriceItemBean();
    pib.setCoffeeName(list.elementAt(i).toString());
    pib.setPricePerPound(new BigDecimal(list.elementAt(i + 1).toString()));
    items.add(pib);
    System.out.print(list.elementAt(i) + "        ");
    System.out.println(list.elementAt(i + 1));
}

Date today = new Date();
Date endDate = DateHelper.addDays(today, 30);
GregorianCalendar todayCal = new GregorianCalendar();
todayCal.setTime(today);
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(endDate);
plb = new PriceListBean();
plb.setStartDate(DatatypeFactory.newInstance().newXMLGregorianCalendar(todayCal));

List<PriceItemBean> priceItems = new ArrayList<PriceItemBean>();
Iterator<PriceItemBean> i = items.iterator();
while (i.hasNext()) {
    PriceItemBean pib = i.next();
    plb.getPriceItems().add(pib);
}

plb.setEndDate(DatatypeFactory.newInstance().newXMLGregorianCalendar(cal));

Ordering Coffee

The other kind of message that the Coffee Break servers can send to the SAAJ supplier 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 by creating a SOAPConnection object and a SOAPMessage object and accessing the message’s SOAPBody object.

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

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

SOAPBody body = msg.getSOAPBody();

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.

QName bodyName = new QName("http://sonata.coffeebreak.com",
    "coffee-order", "PO");
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.

QName orderIDName = new QName("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.

QName childName = new QName("customer");
SOAPElement customer = order.addChildElement(childName);

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

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

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

childName = new QName("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 = new QName("address");
SOAPElement address = order.addChildElement(childName);

childName = new QName("street");
SOAPElement street = address.addChildElement(childName);
street.addTextNode(orderBean.getAddress().getStreet());

childName = new QName("city");
SOAPElement city = address.addChildElement(childName);
city.addTextNode(orderBean.getAddress().getCity());

childName = new QName("state");
SOAPElement state = address.addChildElement(childName);
state.addTextNode(orderBean.getAddress().getState());

childName = new QName("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.

List<LineItemBean> lineItems = orderBean.getLineItems();
Iterator<LineItemBean> i = lineItems.iterator();
while (i.hasNext()) {
    LineItemBean lib = i.next();

    childName = new QName("line-item");
    SOAPElement lineItem = order.addChildElement(childName);

    childName = new QName("coffeeName");
    SOAPElement coffeeName = lineItem.addChildElement(childName);
    coffeeName.addTextNode(lib.getCoffeeName());

    childName = new QName("pounds");
    SOAPElement pounds = lineItem.addChildElement(childName);
    pounds.addTextNode(lib.getPounds().toString());

    childName = new QName("price");
    SOAPElement price = lineItem.addChildElement(childName);
    price.addTextNode(lib.getPrice().toString());
}

// total
childName = new QName("total");
SOAPElement total = order.addChildElement(childName);
total.addTextNode(orderBean.getTotal().toString());

With the order complete, the application sends the message to the endpoint http://localhost:8080/saaj-coffee-supplier/orderCoffee and closes the connection.

URL endpoint = new URL(url);
SOAPMessage reply = con.call(msg, endpoint);
con.close();

Because the given endpoint is mapped to ConfirmationServlet, the Application Server 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.getSOAPBody();
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);
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(date);
cb = new ConfirmationBean();
cb.setOrderId(id);
cb.setShippingDate(DatatypeFactory.newInstance().newXMLGregorianCalendar(cal));