Sending and Receiving SOAP Messages with JAXM

The Java API for XML Messaging (JAXM) provides an API for the creation, manipulation, and exchange of SOAP messages. Figure 21.4 shows the overall architecture of a JAXM application.

Figure 21.4. Overall architecture of a JAXM application.


In a full-blown JAXM application, the message sender connects to a JAXM Provider. The JAXM Provider delivers additional services above and beyond the basic SOAP transport, such as multihop routing and quality-of-service (QoS) guarantees. The sender creates a message and hands it over to the JAXM Provider. The Provider is then responsible for delivering this to the receiver at the given address, possibly via multiple intermediate nodes.

There are two example Providers that come with the JAXM reference implementation:

  • A Provider based on the ebXML Transport, Routing and Packaging (TR&P) specification— This provides for multi-hop routing and the manipulation of ebXML information in the message, such as the conversation and CPA IDs.

  • A Provider for the SOAP Routing Protocol (SOAPRP)— Again, this provides for multihop routing and manipulation of SOAPRP-specific fields in the underlying message header.

Because it uses the services of the Provider, a JAXM-based component, whether a sender or receiver of messages, is termed a JAXM client. This nomenclature can make some statements quite confusing, so the terms sender and receiver (or submitter and processor) are used in this section.

The JAXM specification defines five styles of interaction that must be supported between JAXM clients:

  • Synchronous information query (response now)

  • Asynchronous information query (response later)

  • Synchronous updates (acknowledgement now)

  • Asynchronous updates (acknowledgement later)

  • Logging (no response or acknowledgement)

Full JAXM clients that use a Provider can support all of these models. Indeed, J2EE components that do not use a provider can still send and receive messages using JAXM. In this case, SOAP messages can be delivered directly in a point-to-point fashion. Even a J2SE application can use JAXM to send (but not receive) SOAP messages. Such J2SE-based JAXM clients are referred to as standalone clients.

JAXM and J2EE

JAXM is intended to bring message-based Web Services to J2EE and is scheduled to form part of J2EE 1.4. This will allow J2EE components to send or receive SOAP-based messages and act as message-based Web Service clients or servers.

Any J2EE component can be a JAXM client, although servlets and EJBs are the primary focus. In the first release, a base servlet called JAXMServlet is provided on which message receivers can be built. The JAXMServlet handles the SOAP message in its doPost method and delivers a Java SOAPMessage object to the receiver. Two types of receiver interface are defined—one for synchronous interaction and one for asynchronous.

Under J2EE, a JAXM client will obtain a suitable Provider through JNDI. Information about the Provider and associated client endpoints will be provided as part of the J2EE container configuration.

Configuring JAXM

JAXM can be installed under either Tomcat or the J2EE RI (or any other J2EE server). Specific instructions are provided in the JAXM documentation (see jaxm-1.0/docs/jaxm-on-j2ee.html and jaxm-1.0/docs/tomcat.html, respectively). Although the specifics differ, the tasks that need to be done are as follows:

1.
Copy the library jars provided with JAXM into a central location (<J2EE_HOME>/lib/system or <CATALINA_HOME>/common/lib). This includes jaxm.jar, log4j.jar, dom4j.jar, activation.jar, mail.jar, jndi.jar, client.jar, crimson.jar, xalan.jar, and potentially provider.jar (out of provider.war).

2.
For J2EE, you will also need to put these on the J2EE_CLASSPATH in the <J2EE_HOME>/bin/userconfig script.

3.
For Tomcat, you will need to copy <JAXM-HOME>/jaxm/provider.war into <CATALINA_HOME>/webapps.

4.
Start (or restart) the server.

When creating JAXM clients, you will use servlets wrapped up as Web applications. Under Tomcat, you can simply copy these WAR files into the <CATALINA_HOME>/webapps directory and restart the server.

To deploy your Web applications under J2EE, you should add them to a J2EE application and then deploy the application (you can use deploytool for this).

To ensure that your installation is correct, deploy the simple.war WAR from the JAXM samples directory. You can then access the following URL (under Tomcat, change the port number for J2EE) to run this simple application and see it confirm a sent and received message:

http://localhost:8080/simple/index.html

Later, in “Using a JAXM Profile,” you examine how the sample JAXM Providers work. This will require you to use the JAXM Provider Admin Tool. To enable this tool, deploy the provideradmin.war file from <JAXM-HOME>/tools. Under J2EE, you will need to add the j2ee user to this Web application as an authorized role.

To run the Provider Admin Tool, use a Web browser to access the URL http://localhost:8080/provideradmin. You can log in using a username/password combination of tomcat/tomcat under Tomcat and j2ee/j2ee under J2EE.

Sending Basic SOAP Messages

Probably the simplest way to use the JAXM SOAP functionality is to send a straightforward SOAP message. You can create a command-line Java application that acts as a standalone JAXM client to create a SOAP message using the JAXM API and send it directly to a SOAP server. Figure 21.5 shows the interaction between a standalone JAXM client and a SOAP server.

Figure 21.5. A standalone JAXM client sending a message to a SOAP service.


The code for such a standalone JAXM client application is shown in Listing 21.3.

Listing 21.3. JAXMOrderServiceClient.java—A Standalone JAXM Client
 1: import java.util.Iterator;
 2:
 3: import webservices.LineItem;
 4: import webservices.Order;
 5: import webservices.Receipt;
 6:
 7: import javax.xml.soap.*;
 8:
 9: import javax.xml.messaging.URLEndpoint;
10:
11: public class JAXMOrderServiceClient
12: {
13:   public static void main(String [] args)
14:   {
15:
16:     try
17:     {
18:         MessageFactory messageFactory = MessageFactory.newInstance();
19:         SOAPMessage message = messageFactory.createMessage();
20:
21:         SOAPPart part = message.getSOAPPart();
22:         SOAPEnvelope envelope = part.getEnvelope();
23:
24:         Order order = new Order("Bargain Buys","Strangeways, Manchester");
25:         order.addLineItem(new LineItem("Levi 501", 200));
26:         order.addLineItem(new LineItem("Wibble 1000 mp3 player", 33));
27:         order.addLineItem(new LineItem("Sony Playstation", 10));
28:
29:         order.marshal(envelope);
30:
31:         SOAPConnectionFactory connectionFactory = SOAPConnectionFactory.newInstance();
32:         SOAPConnection connection = connectionFactory.createConnection();
33:
34:         URLEndpoint endPoint = new URLEndpoint("http://localhost:8080/JAXMOrderService
/order");
35:
36:         SOAPMessage msg = connection.call(message, endPoint);
37:         SOAPPart p = msg.getSOAPPart();
38:         SOAPEnvelope env = p.getEnvelope();
39:         SOAPBody body = env.getBody();
40:
41:         Receipt receipt = new Receipt();
42:
43:         Name receiptName = envelope.createName("receipt",
44:                                                "acme",
45:                                                "http://acme.com/commerce");
46:
47:         Iterator iterator = body.getChildElements(receiptName);
48:
49:         if (iterator.hasNext())
50:         {
51:           SOAPBodyElement element = (SOAPBodyElement)iterator.next();
52:           receipt.unmarshal(element, env);
53:
54:           System.out.println("Got receipt for order
	Name:		" + receipt.getName() +
55:                               "
	Address:	" + receipt.getAddress() +
56:                               "
	Number of items:	" + receipt.getNumItems() +
57:                               "
	Cost:		" + receipt.getCost());
58:         }
59:         else
60:         {
61:           throw new SOAPException( "receipt SOAPElement is missing from SOAP body");
62:         }
63:     }
64:     catch (SOAPException ex)
65:     {
66:       System.err.println("Exception: " + ex);
67:     }
68:   }
69: }
						

The first thing to do is create the SOAP message. Lines 18 and 19 of Listing 21.3 show how a javax.xml.soap.SOAPMessage is created from a javax.xml.soap.MessageFactory. In this case, a generic SOAP message is required so the factory is created simply through the newInstance method. Later, you will obtain a message factory for a specific provider profile.

You can now populate the SOAP message with your information. As shown previously in Figure 21.3, a SOAP message consists of a SOAP part and a set of optional attachments. As a result, you need to retrieve the SOAP part of the message to populate it using the getSOAPPart method (line 21). You can then retrieve the SOAP envelope from the SOAP part with the getEnvelope method (line 22). The parts of a SOAP message corresponding to the JAXM SOAPPart are shown in Figure 21.6.

Figure 21.6. The parts of a SOAP message contained in a JAXM SOAPPart.


The SOAP envelope will be populated with the message data. Lines 24–27 show the creation and population of a domain object that represents an order. It contains a name, address, and multiple line items. Each line item contains a product ID and a quantity. The marshal method on line 29 asks the Order object to populate the given SOAP envelope. This marshalling code will be examined soon, but for now, concentrate on the sending of the message.

After the message is ready, the client needs a javax.xml.soap.SOAPConnection to send the message. This is created from a javax.xml.soap.SOAPConnectionFactory, as shown in lines 31 and 32. Because the client application is not running inside a J2EE container, the connection factory is created simply through the newInstance method. Inside J2EE containers, connection factories can be obtained through JNDI. As with all such resources, if your client is long-running, you should close and release the connection when you are no longer using it (this is not done here because it will be done on application exit).

Because this is a direct SOAP client (that is, it does not use a Provider) you must specify the address of the target server using a javax.xml.messaging.URLEndpoint. The endpoint specified in line 34 is the URL of a SOAP server (this happens to be implemented using JAXM, as you will see later, but this is not essential, the server could equally well be implemented in Perl).

You now have the two things you need—a message and somewhere to send it—so you can now send the message (line 36).

Because the service you are calling is a synchronous, request/response service, you will receive a SOAP message as a response. In application terms, this message contains an XML document containing receipt information for the order you have just submitted. To process this receipt, you need to retrieve the XML document from the SOAP message. This XML document will be contained in the SOAP body, so you must retrieve the body from within the SOAP envelope (lines 37–39).

After it has retrieved the SOAP body, the application creates a Receipt domain object (line 41) that will represent this information. This object can be passed an XML <receipt> element (containing sub-elements for name, address, number of items, and cost) that it will unmarshal and use to populate its data fields. Consequently, the application must find the <receipt> element within the SOAP body. To do this, it creates a javax.xml.soap.Name based on the SOAP envelope (lines 43–45). This name represents the qualified name (including namespace) of the element to be found—in this case, (http://acme.com/commerce)receipt. This is then passed to the getChildElements method of the javax.xml.soap.SOAPBody (line 47) to return a java.util.Iterator that can be used to find all such child elements (there should be only one).

The javax.xml.soap.SOAPBodyElement representing the XML <receipt> element can then be retrieved and passed to the Receipt's unmarshal method that will populate the Receipt object from the XML document (lines 51 and 52). The information in the receipt is then printed out (lines 54–57).

Note that during all of this, javax.xml.soap.SOAPExceptions may be thrown by the various methods and constructors used. Please refer to the JAXM API documentation for specific details.

Running the Simple Client

To run the client, you will need to deploy the server. Under Tomcat, this is as simple as copying the server WAR file into the /webapps directory. The server WAR file (JAXMOrderService.war) is provided in Day 21's examples/JAXMDirect/OrderServer directory on the CD-ROM.

Note

To run the standalone client, you will need to add to your CLASSPATH all the JAR files you placed into (CATALINA_HOME)/common/lib earlier.


When you have deployed the server WAR file, start (or re-start) Tomcat and then run the client:

prompt> java JAXMOrderServiceClient
Got receipt for order
        Name:            Bargain Buys
        Address:         Strangeways, Manchester
        Number of items: 243
        Cost:            12147.57

As you can see, this submits the order as shown in Listing 21.3 to the server and prints out the receipt.

Wow. That seems like quite a lot of work to send a simple message. However, you should now have a good grasp of how it all fits together, which makes the rest of the JAXM API easier to understand.

Populating the Message

So far, you have seen how a message can be created and sent, but the client application did not give details on how a message is populated. As indicated earlier, the JAXM API reflects the SOAP containment hierarchy in that you will obtain a SOAPPart from the SOAPMessage and then a SOAPEnvelope from the SOAPPart. Within the SOAPEnvelope is a SOAPBody and a SOAPHeader. The JAXM API provides a DOM-like mechanism for populating and examining the contents of the SOAP body and header.

To examine how this is done, consider the code for the Order class shown later in Listing 21.4. The class is essentially a JavaBean that contains a name, address, and a Vector to hold the line items. To be encapsulated in the SOAP message, the order should be converted into XML similar to the following:

<acme:order xmlns:acme="http://acme.com/commerce">
  <acme:name>Fred Bloggs</acme:name>
  <acme:address>Bury New Road, Manchester</acme:address>
  <acme:items>
    <acme:item>
      <acme:productId>...</acme:productId>
      <acme:quantity>...</acme:quantity>
    </acme:item>
    ...
  </acme:items>
</acme:order>

Recall from the application code in Listing 21.3 that the marshal method is passed a SOAP envelope to populate (line 29 of Listing 21.3 and line 16 of Listing 21.4). The SOAP body is retrieved from the envelope, and a new SOAPBodyElement is created with the addBodyElement method to represent the top level XML <order> element (line 24). Note again that qualified names are required when creating new elements (lines 21–23).

Elements to represent the name and address are created below the XML <order> element (lines 26–36) by using the SOAPBodyElement's addChildElement method. These new elements are of type SOAPElement. The text for the elements is added using the addTextNode method on SOAPElement.

To generate the line items in the XML document, an XML <items> element is created in the SOAPBodyElement, and this is then populated by iterating through the Vector of LineItem objects that are asked to marshal themselves underneath the XML <items> element (lines 38–49).

Listing 21.4. Order.java Showing the Marshalling and Unmarshalling of XML Data from a SOAP Body
  1: package webservices;
  2:
  3: import java.util.*;
  4:
  5: import javax.xml.soap.*;
  6:
  7: public class Order
  8: {
  9:   private Collection _lineItems = new Vector();
 10:   private String _name = "";
 11:   private String _address = "";
 12:
 13:   // NORMAL BEAN METHODS AND CONSTRUCTOR REMOVED FOR CLARITY
 14:   ...
 15:
 16:   public void marshal(SOAPEnvelope envelope) throws SOAPException
 17:   {
 18:     SOAPBody body = envelope.getBody();
 19:
 20:     // Create a new Order element under the body
 21:     Name orderName = envelope.createName("order",
 22:                                          "acme",
 23:                                          "http://acme.com/commerce");
 24:     SOAPBodyElement order = body.addBodyElement(orderName);
 25:
 26:     Name nameName = envelope.createName("name",
 27:                                         "acme",
 28:                                         "http://acme.com/commerce");
 29:     SOAPElement name = order.addChildElement(nameName);
 30:     name.addTextNode(name);
 31:
 32:     Name addressName = envelope.createName("address",
 33:                                            "acme",
 34:                                            "http://acme.com/commerce");
 35:     SOAPElement address = order.addChildElement(addressName);
 36:     address.addTextNode(address);
 37:
 38:     Name itemsName = envelope.createName("items",
 39:                                          "acme",
 40:                                          "http://acme.com/commerce");
 41:     SOAPElement items = order.addChildElement(itemsName);
 42:
 43:     for (Iterator iterator = _lineItems.iterator();
 44:          iterator.hasNext();)
 45:     {
 46:       LineItem item = (LineItem)iterator.next();
 47:
 48:       item.marshal(items, envelope);
 49:     }
 50:    }
 51:
 52:   public void unmarshal(SOAPBodyElement order, SOAPEnvelope envelope) throws
 SOAPException
 53:   {
 54:     Name nameName = envelope.createName("name",
 55:                                         "acme",
 56:                                         "http://acme.com/commerce");
 57:     Name addressName = envelope.createName("address",
 58:                                            "acme",
 59:                                            "http://acme.com/commerce");
 60:     Name itemsName = envelope.createName("items",
 61:                                          "acme",
 62:                                          "http://acme.com/commerce");
 63:
 64:     Iterator iterator = order.getChildElements(nameName);
 65:
 66:     if (iterator.hasNext())
 67:     {
 68:       SOAPElement element = (SOAPElement)iterator.next();
 69:       _name = element.getValue();
 70:     }
 71:     else
 72:     {
 73:       throw new SOAPException("order SOAPElement is missing name");
 74:     }
 75:
 76:     iterator = order.getChildElements(addressName);
 77:
 78:     if (iterator.hasNext())
 79:     {
 80:       SOAPElement element = (SOAPElement)iterator.next();
 81:       _address = element.getValue();
 82:     }
 83:     else
 84:     {
 85:       throw new SOAPException("order SOAPElement is missing address");
 86:     }
 87:
 88:     iterator = order.getChildElements(itemsName);
 89:
 90:     if (iterator.hasNext())
 91:     {
 92:       SOAPElement element = (SOAPElement)iterator.next();
 93:
 94:       iterator = element.getChildElements();
 95:
 96:       while (iterator.hasNext())
 97:       {
 98:         SOAPElement elem = (SOAPElement)iterator.next();
 99:
100:         LineItem item = new LineItem();
101:
102:         item.unmarshal(elem, envelope);
103:
104:         addLineItem(item);
105:       }
106:     }
107:     else
108:     {
109:       throw new SOAPException("order SOAPElement is missing items");
110:     }
111:   }
112: }
						

The unmarshal method (line 52) used by the server contains the reverse code from the marshal method. The unmarshal method is passed a SOAPBodyElement representing an XML <order> element. The getChildElements method can then be used to retrieve the XML <name>, <address>, and <items> elements in turn. The method returns an Iterator that can be used to retrieve the SOAPElement itself (as shown in lines 66–68). The text contents of the SOAPElement can be retrieved with the getValue method (line 69). As each line item element is retrieved, it is passed to an instance of the LineItem class to be unmarshalled (94–105).

The marshalling and unmarshalling code for the Receipt and LineItem classes is very similar.

Although this is a perfectly adequate way of populating a SOAP message, it is somewhat unwieldy, especially if you already have the information as a Java object or DOM instance. Hopefully, simpler ways of populating JAXM messages will emerge over time, which may include use of the Java API for XML Data Binding (JAXB).

Headers and Attachments

As well as XML information in the SOAP body, JAMX allows you to add and retrieve header information and attachments.

The SOAPHeader can be obtained from the SOAPEnvelope with the getHeader method. If you are creating a message, you can populate the header using the addHeaderElement method that returns a SOAPHeaderElement. You can then add text or attributes to this header element, as shown in the following:

Name txName = envelope.createName("TransactionId", "acme",
                                  "http://acme.com/transactions");
SOAPHeaderElement headerElement = header.addHeaderElement(txName);
headerElement.addTextNode("78d2892ea8af625323c7");

There are also specific methods for associating a particular SOAP actor or the SOAP mustUnderstand attribute to a header element.

When receiving a message, an Iterator that iterates over the header elements can be retrieved with the examineHeaderElements method.

Additionally, you may want to add attachments to your SOAP messages by using SOAPMessage's addAttachmentPart method. SOAPMessage also has a createAttachment method that allows you to create an AttachmentPart object to attach to a message.

You can provide the data for your attachment directly or through a content handler (part of the JavaBeans Activation Framework—JAF). These attachments can be any form of data, so the key thing is to set the appropriate MIME type. If providing the data directly, you must specify the MIME type when creating the attachment. When using a content handler, this can supply the correct MIME type to the AttachmentPart object.

The code required to create an attachment from an image at a given URL is shown in the following code:

SOAPMessage message = ...
...
URL url = new URL("http://acme.com/acme_logo.jpg");
AttachmentPart attachment = message.createAttachmentPart(new DataHandler(url));
message.addAttachmentPart(attachment);

Receiving a SOAP Message Using JAXM

The SOAP service used by the standalone client you ran previously is implemented using JAXM. The service takes the form of a servlet that is a JAXM client. The servlet runs inside a servlet container and interacts with JAXM, as shown in Figure 21.7. The servlet consumes the SOAP message from the client containing the order, processes it, and returns a SOAP message containing a receipt.

Figure 21.7. A JAXM client acting as a SOAP service.


To implement a simple JAXM client is reasonably straightforward. The code for the service used is shown in Listing 21.5. The first thing to note is that the JAXM client implements the javax.xml.messaging.ReqRespListener interface (line 8). This interface defines a single method—onMessage—that takes a single SOAPMessage as a parameter and returns a SOAPMessage as a parameter (line 10). If a JAXM client intends to consume SOAP messages, it must implement either ReqRespListener or OnewayListener, which defines the same onMessage method but with no return value.

Listing 21.5. JAXMOrderServer.java—A Simple JAXM Client Providing a SOAP Order Service
 1: package webservices;
 2:
 3: import java.util.Iterator;
 4: import javax.xml.soap.*;
 5: import javax.xml.messaging.JAXMServlet;
 6: import javax.xml.messaging.ReqRespListener;
 7:
 8: public class JAXMOrderServer extends JAXMServlet implements ReqRespListener
 9: {
10:     public SOAPMessage onMessage(SOAPMessage message)
11:     {
12:         try
13:         {
14:             SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
15:             SOAPBody body = envelope.getBody();
16:
17:             Name orderName = envelope.createName("order",
18:                                                  "acme",
19:                                               "http://acme.com/commerce");
20:
21:             Iterator iterator = body.getChildElements(orderName);
22:
23:             Order order = new Order();
24:
25:             if (iterator.hasNext())
26:             {
27:               SOAPBodyElement element = (SOAPBodyElement)iterator.next();
28:               order.unmarshal(element, envelope);
29:             }
30:             else
31:             {
32:               throw new SOAPException( "order SOAPElement is missing from SOAP body");
33:             }
34:
35:             System.out.println("Got order from " + order.getName());
36:
37:             Receipt receipt = new Receipt(order.getName(), order.getAddress(),
38:                                order.getNumItems(), calculateCost(order));
39:
40:             MessageFactory fac = MessageFactory.newInstance();
41:
42:             SOAPMessage msg = fac.createMessage();
43:
44:             SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
45:
46:             Name receiptName = envelope.createName("receipt",
47:                                                    "acme",
48:                                               "http://acme.com/commerce");
49:
50:             SOAPBodyElement rcpt = env.getBody().addBodyElement(receiptName);
51:
52:             receipt.marshal(rcpt, env);
53:
54:             return msg;
55:         }
56:         catch(Exception ex)
57:         {
58:             System.err.println( "Error in processing or replying to a message: " + ex);
59:             return null;
60:         }
61:   }
62:
63:   private double calculateCost(Order order)
64:   {
65:     // Sale now on - everything for $49.99
66:     return order.getNumItems() * 49.99;
67:   }
68: }
						

You will know from yesterday that HTTP-based SOAP messages are delivered to the doPost method of a servlet. The question then arises of where this SOAP message is delivered when the submitter sends it. The answer lies in the fact that the JAXMOrderServer extends javax.xml.messaging.JAXMServlet. The doPost method of JAMXServlet processes the inbound SOAP message and calls the onMessage method defined by its subclass (the signature of the method called depends on which interface the subclass implements). The JAXMServlet also takes responsibility for converting any returned SOAPMessage into an appropriate HTTP/XML document and sending this back to the caller.

The code that processes the message should be fairly familiar from the standalone client. The servlet extracts the XML <order> element from the received SOAP message and then passes it to a Java Order object to unmarshal it. Based on the contents of this order, the servlet generates a Receipt object to pass back. The servlet creates a SOAPMessage and recovers its SOAPBody into which the receipt is marshalled.

To compile and deploy your JAXM service, you will need to do the following:

1.
Include jaxm.jar on your CLASSPATH when compiling.

2.
Create the correct directory structure for a Web application, including a WEB-INF directory and a web.xml file (see Day 12, “Servlets,” for more detail). The web.xml file should define a servlet mapping for the URL by which your client expects to access the service. Place your class files under the WEB-INF/classes directory.

3.
JAR the Web application into a WAR file and copy the WAR into (CATALINA_HOME)/webapps. Start (or restart) Tomcat and then run the client as before.

Note that the example used in this section has a standalone client submitting the order. It is equally possible for the submitter to be a JAXM client itself. This scenario is shown in Figure 21.8.

Figure 21.8. A JAXM client acting as a SOAP client.


Using a JAXM Profile

As noted earlier, synchronous, point-to-point message exchange is only one of the intended modes of operation for a JAXM application. To conclude this three-week tour of J2EE past, present, and future, this final section looks briefly at how you use a JAXM Provider. Recall that a Provider delivers extra messaging capabilities, such as multihop routing and quality-of-service guarantees.

The order submission application can be updated to use a Provider. The updated exchange is described in the following:

  • The processor JAXM client implements OnewayListener through which it will receive orders to process.

  • The submitter JAXM client will send a message to the processor and then continue processing without requiring a receipt to be returned.

  • The processor will receive a message and then process it. In reality, this would probably involve spawning a thread to process the message while the onMessage method returns.

If you wanted to send a receipt, the submitter would also have to implement OnewayListener. The processor could then send the receipt back to the submitter and the submitter would receive the receipt through its own onMessage method. The full exchange of order and receipt is shown in Figure 21.9. Because the sending of a receipt is the reverse of sending the order, we will just examine how to pass messages in one direction.

Figure 21.9. An asynchronous exchange between two JAXM clients.


Caution

The JAXM Provider requires additional configuration across several files and tools, which can be tricky to get right. You should ensure that you are fully comfortable with the concepts around the provider before making the changes required to deploy a provider-based JAXM client.


Because the application logic of the JAXM servlets is essentially the same (the creation and consumption of orders), we will concentrate on describing code that is different.

The updated application consists of an order submitter (SubmittingServlet.java) and an order processor (ProcessingServlet.java). The classes involved are available under the JAXMProvider directory in the Day 21 examples code on the CD-ROM.

Sending a Message Using a JAXM Profile

The first thing that the submitter must do if it is to use a Provider is to get a connection to the Provider. This is done in the servlet's init method, as shown in Listing 21.6 (lines 27–41). A Provider can also be configured in the servlet's deployment information and obtained using JNDI.

Listing 21.6. SubmittingServlet.java—A JAXM Client That Uses a Provider
 1: package soaprp.submitter;
 2:
 3: import java.net.*;
 4: import java.io.*;
 5:
 6: import javax.servlet.http.*;
 7: import javax.servlet.*;
 8:
 9: import javax.xml.messaging.*;
10: import javax.xml.soap.*;
11:
12: import javax.activation.*;
13: import com.sun.xml.messaging.soaprp.*;
14:
15: public class SubmittingServlet extends HttpServlet
16: {
17:   private String submitter ="http://www.acme.com/orderprocessor";
18:   private String processor = "http://www.acme.com/orderprocessor";
19:
20:   private ProviderConnectionFactory cFactory;
21:   private ProviderConnection connection;
22:   private MessageFactory mFactory;
23:
24:   private static final String providerURI =
25:                               "http://java.sun.com/xml/jaxm/provider";
26:
27:   public void init(ServletConfig config) throws ServletException
28:   {
29:     super.init(config);
30:
31:     try
32:     {
33:       cFactory = ProviderConnectionFactory.newInstance();
34:       connection = cFactory.createConnection();
35:     }
36:     catch (Exception ex)
37:     {
38:       System.err.println("Unable to open connection to the provider" +
39:                                                         ex.getMessage());
40:     }
41:   }
42:
43:   public void doGet(HttpServletRequest request,
44:                     HttpServletResponse response) throws ServletException
45:   {
46:     try
47:     {
48:       if (mFactory == null)
49:       {
50:         ProviderMetaData metaData = connection.getMetaData();
51:         String[] profiles = metaData.getSupportedProfiles();
52:         String profile = null;
53:
54:         for (int i=0; i < profiles.length; i++)
55:         {
56:           if (profiles[i].equals("soaprp"))
57:           {
58:             profile = profiles[i];
59:             break;
60:           }
61:         }
62:         mFactory = connection.createMessageFactory(profile);
63:       }
64:
65:       SOAPRPMessageImpl message =
66:                             (SOAPRPMessageImpl)mFactory.createMessage();
67:
68:       message.setFrom(new Endpoint(submitter));
69:       message.setTo(new Endpoint(processor));
70:
71:       String order = "http://localhost:8080/jaxm-soaprp-order/orders/" +
72:                                    "order1.xml";
73:       URL orderDocument = new URL(order);
74:       DataHandler dh = new DataHandler(orderDocument);
75:
76:       AttachmentPart attachment = message.createAttachmentPart(dh);
77:       attachment.setContentType("text/xml");
78:       message.addAttachmentPart(attachment);
79:
80:       System.out.println("SubmittingServlet: doGet: Sending message");
81:
82:       connection.send(message);
83:
84:       System.out.println("SubmittingServlet: doGet: Sent message");
85:
86:       PrintWriter writer = response.getWriter();
87:       writer.println("<html><body>Looking good...</body></html>");
88:       writer.flush();
89:       writer.close();
90:     }
91:     catch (Exception ex)
92:     {
93:       System.err.println("SubmittingServlet: doGet: " + ex.getMessage());
94:     }
95:   }
96: }
						

The doGet method handles the submit request (line 43) as follows:

  • Obtain a message factory for your chosen profile. The code (lines 48–63) looks through the profiles available and selects the SOAP routing protocol—soaprp.

  • Create a SOAPMessage representing a fixed order to send. This takes the form of a SOAPRPMessageImpl, which is a concrete form of SOAPMessage.

  • Populate and send the message (lines 68–82). In this case, for simplicity, a pre-built order, order1.xml, is loaded into an attachment of this message. Because this is a routed SOAP message, you must set the source and destination addresses using two endpoint URIs that are discussed later. If you were using the ebXML profile, you could set ebXML-specific message fields, such as the CPAid, and the sender/receiver parties. Note also that no response is expected from the message processor.

  • Displaying an HTML page to the user stating that the order has been sent (line 87).

Note

Do not try to obtain a message factory in the servlet's init because this can hang the servlet engine.


Receiving a Message Using a JAXM Profile

The submitter sets the destination address on the SOAPRP message in the form of endpoints. The processor is registered with the SOAPRP Provider under the URI http://www.acme.com/orderprocessor. The Provider must map this address to an actual servlet URL to deliver messages to this address. This mapping information is contained in an XML file—client.xml—that forms part of each JAXM client's Web application and is stored in the WEB-INF/classes directory.

To use the Provider, you must also configure router mappings for the endpoints. These mappings indicate to which router JAXM should pass messages when they are sent to the processor URI. To configure the provider under Tomcat, point your Web browser at the URL http://localhost:8080/provideradmin/index.jsp. Here you will be able to configure the SOAPRP mappings for HTTP-based JAXM clients by expanding the menu on the left side as follows: JAXM Provider, Profiles, SOAPRP, HTTP. You can now select Create New Endpoint Mapping from the Available Actions drop-down list box and set up the following router mapping:

URI: http://www.acme.com/orderprocessor
URL: http://localhost:8080/jaxm-provider/receiver/soaprp

This mapping points to the message receiver for SOAPRP messages (a servlet located at /provider/receiver/soaprp), which is part of the SOAPRP Provider. This message receiver acts as the local router for such messages. When the client servlet is initialized and creates a new connection, its endpoint-to-URL mapping is registered with the SOAPRP Provider. The Provider builds a routing table based on all of the registered endpoints. This routing table is used to route all JAXM-based SOAPRP services that arrive at the local host. When a message arrives at the message receiver, the message receiver consults this routing table to dispatch the message. If this were a full-blown application and deployment, this SOAPRP Provider may figure out that the message is not for a local service and so forward it on to the next router. Alternatively, the endpoint mapping for a particular URI can cause a locally-sent message to be delivered to a different router (possibly an “edge” router for this organization).

The order processor, shown in Listing 21.7, implements the OnewayListener interface. The order processor obtains a Provider connection, waits for a message and processes it when it arrives. In this simple case, the processing just consists of logging the message to standard output. As you can see, there are very few requirements on JAXM message recipients.

Listing 21.7. ProcessingServlet.java—A JAXM Client That Receives Messages from a Provider
 1: package soaprp.processor;
 2:
 3: import javax.xml.messaging.*;
 4: import javax.xml.soap.*;
 5: import javax.servlet.*;
 6: import javax.servlet.http.*;
 7: import com.sun.xml.messaging.soaprp.*;
 8:
 9: public class ProcessingServlet extends JAXMServlet
10:                                                implements OnewayListener
11: {
12:   private ProviderConnectionFactory factory;
13:   private ProviderConnection connection;
14:   private static final String providerURI =
15:                                 "http://java.sun.com/xml/jaxm/provider";
16:
17:   public void init(ServletConfig servletConfig) throws ServletException
18:   {
19:     super.init(servletConfig);
20:
21:     try
22:     {
23:       factory = ProviderConnectionFactory.newInstance();
24:       connection = factory.createConnection();
25:       setMessageFactory(new SOAPRPMessageFactoryImpl());
26:     }
27:     catch (Exception ex)
28:     {
29:       throw new ServletException("ProcessingServlet: init: " +
30:                                                        ex.getMessage());
31:     }
32:   }
33:
34:   public void onMessage(SOAPMessage message)
35:   {
36:     System.out.println("ProcessingServlet: onMessage: Received message:");
37:     try
38:     {
39:       message.saveChanges();
40:       // Just log the message for now...
41:       message.writeTo(System.out);
42:     }
43:     catch(Exception ex)
44:     {
45:       System.err.println("ProcessingServlet: onMessage: " +
ex.getMessage());
46:     }
47:   }
48: }
						

Tip

Should you decide to try out the JAXM Provider-based code shown here (and provided on the CD-ROM), you may find that on early versions of the reference implementation, the initial message gets “stuck” and is not delivered to the order processor. If this happens, try sending another message (just use the browser Back button and click the hyperlink again), which has the effect of flushing out the initial message.


That is a submitter and processor for JAXM using a Provider. The classes and Web applications are quite similar in many respects for both submitter and processor because they are both essentially just clients of the JAXM provider taking on the alternate roles of client and server in this particular case.

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

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