This is the second of a three-part series on the "JAX Pack," Sun's Java APIs for XML. In the first article, I introduced the Java API for XML Processessing (JAXP) and the Java API for XML Binding (JAXB). These two mechanisms provide the underlying framework to access XML data and encapsulate that XML data within Java objects. Let's move on and examine the Java API for XML Messaging (JAXM) and see how it provides underlying support for accessing various messaging formats.
JAXM (JSR67) is currently at version 0.93 (public review) with early access reference implementation version 2. JAXM is expected to be an optional part of J2SE and a standard part of J2EE.
JAXP and JAXB help us access XML data, but how does XML actually get between two cooperating applications? JAXM provides a specialized API for just this purpose. Effectively, JAXM is a specification for a Service Provicer Interface (SPI) to access standard XML messaging protocols, such as SOAP. JAXM allows for the transfer of complete business-level documents between two separate Web services. What JAXM does not do is attempt to define messaging standards of the so-called vertical markets; instead, it allows direct access to the various fields without regard to payload type. JAXM looks to provide an infrastructure for supporting whatever standard emerges, whether it be SOAP, ebXML or something else.
As I discussed in my article on Java and Web services, Simple Object Access Protocol (SOAP) actually describes the underlying format of messages transfered between applications. It's a reasonable enough task to write a SOAP- based servlet that uses JAXP to access the various parts of a message and work with it. This begs the question, why do we need JAXM at all? The problem with what I've proposed is the level of complexity and understanding that a developer requires to actually process and manipulate a SOAP message.
As much as I hate to admit it, I programmed long ago using raw Unix socket calls. At first it was really cool, but as we developed FTP and SMTP clients, the coding became tedious and error-prone. We went through a similar growth period as people developed the first Web sites. We are now seeing those same issues with Web services. JAXM is designed to be a lightweight API that abstracts away the underlying messaging infrastructure. In theory, we should be able to develop JAXMServlets, extending HttpServlets, which allow us to access SOAP (or whatever protocol follows SOAP) messages quickly and easily. Figure 1 below shows just such a conceptual picture.
|
Before we move on, a word of caution. JAXM is in reality a fairly low-level or SPI-level interface. Effectively, JAXM is an optional J2EE package supporting the sending and receiving of XML messages using a pure Java API. JAXM allows developers to focus on the processing aspects of their applications rather than on issues associated with composing and decomposing XML messages using SOAP, ebXML, or something else.
JAXM version 1.0 is targeted at providing support for:
JAXM applications fall into two major catagories: synchronous and asynchronous, each taking advantage of various capabilities, as shown above. JAXM, in fact, supports five major catagories of client-client interactions.
A fair amount of SOAP knowledge is implied in processing a SOAP message. Developers with absolutely no SOAP knowledge are advised to poke around in the SOAP areas of W3C. For those who simply need a SOAP refresher, Figure 2 shows the JAXM SOAP packaging conceptual model.
|
JAXM is, for the most part, concentrated on accessing the various parts of a SOAP 1.1 packet today, although provisions exist for other messaging styles. JAXM base applications come in one of two flavors: message consumers or message producers. Rather than go into any more detail, lets look at a JAXM message consumer example.
|
|
Related Reading
|
The first, and perhaps simplest, method for taking advantage of JAXM functionality
is by developing a servlet that extends the JAXMServlet
interface and consumes XML messages packaged via SOAP 1.1. JAXMServlets
look surprisingly like message-driven Enterprise Java Beans, in that they have a single onMessage(Message
msg) method that needs to be implemented. The reality, however, is that they
are traditional servlets and that the onMessage
method gets called whenever a servlet POST or GET action occurs. When onMessage()
is called we can then decompose the underlying SOAP message, as required by the
application. Let's look at a complete example, adapted from public draft 1, that
takes a SOAP trade message and dumps it to System.out.
Listing 1: SOAPListener.java
00 /*
01 * adapted from a sun example
02 */
03 import javax.xml.messaging.*;
04 import javax.xml.soap.*;
05 import javax.xml.parsers.*;
06 import javax.xml.transform.*;
07 import javax.activation.DataHandler;
08 import java.io.*;
09 import org.w3c.dom.*;
10 import org.xml.sax.SAXException;
11 public class SOAPListener extends JAXMServlet implements AsyncListener {
12 // implement the required onMessage method
13 public void onMessage(Message message) {
14 try {
15 //Get the soap part of the message (ignore attachments)
16 SOAPPart soapPart = message.getSOAPPart( );
17
18 // tunnel in and get the envelope
19
20 SOAPEnvelope soapEnvelope = soapPart.getSOAPEnvelope( );
21
22 // from the envelope get the DOM tree representing the content.
23 DOMSource domSrc = (DOMSource) soapEnvelope.getContentAs(DOMSource.FEATURE );
24
25 //Now use the dom tree to traverse the info.
26 //get some of the fields from it.
27 Document doc = (Document) domSrc.getNode();
28 Element root = doc.getDocumentElement();
29 NodeList list = root.getElementsByTagName("GetLastTradePriceDetailed" );
30 Element element;
31 Element childElement;
32 for(int i = ; i < list.getLength(); i++){
33 if (!(list.item(i) instanceof Element )) continue;
34 element = (Element list.item(i) );
35 NodeList list2 = element.getChildNodes();
36 for(int j = ; j < list2.getLength( ; j++ {
37 if(!(list2.item(j instanceof Element) continue;
38 childElement = (Element list2.item(j ;
39 System.out.println(childElement.getTagName() ;
40 System.out.println("\t" ;
41 System.out.println( ((Text) childElement.getFirstChild()).getData()) ;
42 System.out.println("\n" );
43 }
44 }
45 } // end try
46 catch(Exception jxe ) {
47 jxe.printStackTrace();
48 }
49 } // end onMessage
50 } // end SOAPListener
Breaking the example down, we see that lines 3-10 simply import the required
packages to support JAXM (see the JAXM specification for details). On line
11 we've defined an AsyncListener
listener. Note that SynchListeners
are expected to return a SOAPmessage
object. Servlets must implement either the AsyncListener
or SyncListener, depending on
what behavior you want. Line 13 shows the first real line of code, the definition
of the onMessage() method with
its single message argument, defined in the javax.xml.messaging
package. It's interesting to note that the specification provides classes and methods
that closely parallel the SOAP 1.1 specification. Specifically, messages contain
a SOAP part, which contains a SOAP Envelope, containing a SOAP Header and Body,
etc.
Lines 16 and 20, respectively, show how we obtain the object representing the
SOAP envelope, as shown in figure 3. The various SOAP message class information
is detailed in the javax.xml.soap
package. Note that we could have just as easily obtained the attachment part
of the message by writing:
AttachmentPart attachmentPart = message.getAttachmentPart( );
|
A word of caution: the EA2 version of the JAXM contains documentation for the JAXM packages that is unfortunately sketchy. I have tried to be as accurate as possible, but some of the information on JAXM was obtained by decompiling and examining source! |
Once we have the SOAPEnvelope
we can obtain the data representing the encapsulated XML using the getContentAs()
method. GetContentAs returns any of a number of
javax.xml.transform.Source types, of which we chose DOMSource.FEATURE,
but could have chosen SAXSource.FEATURE
or STREAMSource.FEATURE, depending
on how we wanted to process the result.
Since we choose to process the content as a DOM Tree, we use the DOMSource
object to get the DOM Document
representing the XML content; then we can then use the various DOM APIs to process
the data.
The steps we went through to develop our JAXM Servlet were:
AsynchListener
or SyncListener. onMessage method
(other parts may be available in the future).SyncListeners,
return an appropriate SOAPMessage. Before we close our discussion of JAXM, let's spend a few moments looking at message producers as well.
|
Building a message consumer is a much more straightforward process than building a message producer. However, the concepts as a whole are simple. Sending a SOAP 1.1 message with JAXM is a three-step process:
There are a number of ways to achieve these three steps using either direct connections or a messaging provider. Let's look more closely at how to develop message producers.
The first step in developing a messaging producer is to create a connection. When developing applications to produce SOAP 1.1 messages, we would normally build a message and send it over some connection. JAXM, however, defines the concept of a messaging provider. Those readers familar with JMS will be quite at home with messaging providers. A messaging provider is a sort of intermediary that collects, caches, and ultimately sends messages on behalf of an application. We can create one connection in one of two ways; using a messaging provider or not.
Creating a direct connection, without a messaging producer, is done using a
SOAPConnection object that has
a static newInstance() method
for creating connection instances. SOAP Connections are created as shown below:
java.xml.soap.SOAPConnection connection = SOAPConnection.newInstance();
Creating a connection using a messaging provider is a somewhat more difficult process. Like JMS connections, using a messaging provider requires you request the connection from the messaging provider:
private static final String providerURI = "http://java.sun.com/xml/jaxm/provider";
Context ctx = new InitialContext();
ProviderConnectionFactory cf = (ProviderConnectionFactory)ctx.lookup( providerURI);
SOAPConnection connection = connectionFactory.createConnection();
The code above assumes that the connection factory was registered in JNDI (again,
much like JMS) and fully implements the ProviderConnectionFactory
interface.
Once we have a connection, we need a message to send. Again, much like JMS, we use a factory object to create the message.
Using a direct connection, we can use a simple MessageFactory
object to generate an instance of a SOAPMessage
as shown below:
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage mg = mf.createMessage();
If we were using a messaging provider we would need to obtain a MessageFactory from the connection, as the messages might use common profiles and are bound to the provider behind the scenes. A code snippet for obtaining messages from a messaging provider-based connection is shown below.
MessageFactory mf = connection.createMessageFactory(); // Create messgages from the message factory.
SOAPMessage message = mf.createMessage( );
Once we have a message, we populate it the same way for both connection types. For example:
//Get the SOAP part of the message. We could have gotten the attachment part as well.
SOAPPart sp = message.getSOAPPart( );
// Get the soap envelope
SOAPEnvelope se = sp.getSOAPEnvelope( );
// populate the soap envelope using a DOMSource object
DOMSource domsrc = null;
try {
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance( );
DocumentBuilder db = dbf.newDocumentBuilder( ;
Document doc = db.parse("file:///somefiles.xml" );
domsrc = new DOMSource(doc);
} catch(Exception e ) {
System.err.println("Error in creating DOMSource" +
e.getMessage( );
}
// add the DOMSource object to the soap envelop
se.setContent(domsrc);
Obviously, you will need to adhere to the SOAP rules, perhaps adding SOAP Header elements with appropriate content. See the SOAP specification for complete details on how to comply with the SOAP specification.
|
We then send a message and close the connection as shown below:
private static final String endpointURL ="http://somewebservice:7001";
URLEndPoiunt ep = new URLEndPoint(endpointURL);
SOAPMessage reply = connection.send (message,ep);
connection.close();
Alternatively, if we were sending many messages to the same end point, we might set the default message end point as follows:
private static final String endpointURL ="http://somewebservice:7001";
message.setTo(new URLEndPoint(endpointURL);
connection.send(message);
We could then update the message content as required, keep sending the same message, and later close the connection as required.
A complete message provider example is shown below. Note that this example is for illustration purposes and may not conform to the SOAP specification.
Listing 2: SOAPProducer.java
import javax.xml.messaging.*;
import javax.xml.soap.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.parsers.*;
import javax.servlet.http.*;
import java.io.IOException;
import org.w3c.dom.Document;
public class SOAPServlet extends HttpServlet {
private static final String providerURI =
"http://java.sun.com/xml/jaxm/provider";
private ProviderConnectionFactory connectionFactory;
public void init(ServletConfig servletConfig) throws ServletException {
Context ctx = new InitialContext();
connectionFactory = (ProviderConnectionFactory)ctx.lookup( providerURI);
}
public void doGet(HttpServletRequest request,HttpServletResponse response )
throws ServletException, IOException
{
try {
// A request comes in to send a message. So we create a connection from the factory.
// factories are used when we have an intermediate which caches and send messages on our behalf
// much like JMS.
SOAPConnection connection = connectionFactory.createConnection();
// Create a message factory from the Connection to produce messages.
MessageFactory mf = connection.createMessageFactory();
// Create messgages from the message factory.
Message message = mf.createMessage( );
// Get the SOAPPart from the message.
// Note the infrastructure takes care of creating the SOAPPart.
// The user needs to get the SOAPPart and get the SOAPEnvelope and populate it appropriately.
SOAPPart soapPart = message.getSOAPPart( );
// Get the SOAPEnvelope from the header container obtained above.
SOAPEnvelope soapEnvelope = soapPart.getSOAPEnvelope( );
// Create a SOAPHeader from the SOAPEnvelope
SOAPHeader soapHeader = soapEnvelope.createSOAPHeader() ;
// Create a SOAPHeaderElement that will be appended to the SOAPHeader.
SOAPHeaderElement she = soapHeader.createSOAPHeaderElement( );
she.setName("from", "http://foo.bar/", "m" );
she.addContent("foo@bar.com" ;
soapHeader.addContent(she);
soapEnvelope.setSOAPHeader(soapHeader);
// Add a DOMSource object as the actual message content
DOMSource domsrc = null;
try {
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance( );
DocumentBuilder db = dbf.newDocumentBuilder( ;
Document doc = db.parse("file:///foo.bar/soap.xml" );
domsrc = new DOMSource(doc);
} catch(Exception e ) {
System.err.println("Error in creating DOMSource" +
e.getMessage( );
}
soapEnvelope.setContent(domsrc);
soapEnvelope.setSOAPBody(soapBody );
URLEndpoint ep = new URLEndpoint("http://foo.bar/Service" );
connection.send(message, endPoint );
response.setStatus(HttpServletResponse.SC_OK );
} catch(Exception jxe {
jxe.printStackTrace();
}
} // end doGet
}
The JAX Pack APIs add an exciting element to Web services development in a J2EE environment. In this article, we saw how to process XML messages using JAXM -- an admittedly low-level API, but all in all, a very powerful set of tools for writing robust Web services. In part three of this series we will look at the remaining JAXPack APIs -- JAXR and JAX-RPC.
Al Saganich is BEA Systems' senior developer and engineer for enterprise Java technologies, focused on Java integration and application with XML and Web services.
Return to ONJava.com.
Copyright © 2007 O'Reilly Media, Inc.