Sun has released the Java API for XML Messaging (JAXM) to combine messaging and XML processing capabilities into one API. It enables both synchronous and asynchronous communication (refer to the discussion related to synchronous and asynchronous communications in the section "Messaging Versus Request-Response" earlier in this chapter). JAXM follows the standard SOAP 1.1 and SOAP with Attachments specifications. It can generate SOAP-compatible messages and work in any of the following ways:
Note Note that a JAXM provider may support multiple profiles, but an application can use only one at a time. We will now see the details of how the three types of operations are accomplished using JAXM. JAXM As a Simple SOAP ClientWe will now discuss the first and simplest case, in which JAXM acts as a SOAP client. In this case, we will use JAXM to perform the following tasks:
Let's see how these tasks are accomplished using JAXM. The following lines of code author a complete SOAP request: MessageFactory messageFactory = MessageFactory.newInstance(); SOAPMessage request = messageFactory.createMessage(); SOAPPart soapPartOfRequest = request.getSOAPPart(); SOAPEnvelope soapEnvelope = soapPartOfRequest.getEnvelope(); SOAPBody soapBody = soapEnvelope.getBody(); Name methodName = soapEnvelope.createName( "invoke" , "jaxm", "urn:P2PCarRental"); soapBody.addChildElement(methodName); The first step is to instantiate a MessageFactory that will be used to author a SOAP request message using the MessageFactory.createMessage method. The createMessage method authors a default SOAPMessage object, which contains a SOAP Envelope. The SOAP Envelope in turn contains an empty SOAP Header and an empty SOAP Body. The following is the XML representation of a default SOAPMessage object created by createMessage method: <soap-env:Envelope xmlns:soap-env=http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Header/> <soap-env:Body/> </soap-env:Envelope> SOAP messages can also optionally contain non-SOAP (or even non-XML) parts. They are treated as attachments with the SOAP part of the message. Your interest lies with the SOAP part of the message, so you can add SOAP service invocation details to the SOAP Body. The getSOAPPart method of the SOAPMessage class will extract the SOAP part of the message (a SOAPPart object). Next you will call the SOAPPart.getEnvelope method that returns the SOAPEnvelope object. The SOAPEnvelope class has a method named getSOAPBody. The getSOAPBody method will eventually bring you the SOAPBody object that you will manipulate. You will now author the application-specific XML content of your SOAP request, and then add the content to the SOAPBody object. XML authoring with JAXM is generally a two-step procedure. You first create a new element, and then add the newly created element to the appropriate place. A new element is created by using the createName method of the SOAPEnvelope class. This returns a Name object. A newly created element is empty, and therefore just carries an XML name (which might include an element name, a namespace prefix, and a namespace URI). That's probably the reason why JAXM calls a new element a Name object. You will need to specify three parameters while creating a new element for the XML contents of SOAP Body: the name of the element, the namespace prefix, and the namespace URI. The name of the element in this case is invoke; the namespace prefix is jaxm; and the namespace URI is urn:P2PcarRental. The following is how this new element looks like in XML format: <jaxm:invoke xmlns:jaxm="urn:P2PCarRental"/> You have created the new element. Now just add the new element to the SOAP Body. The addChildElement method of the SOAPBody class does this job for you. The following is the complete XML structure that you have just authored: <soap-env:Envelope xmlns:soap-env=http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Header/> <soap-env:Body> <jaxm:invoke xmlns:jaxm="urn:P2PCarRental"/> </soap-env:Body> </soap-env:Envelope> We have authored the SOAP request that we want to send to the remote SOAP server. It is time to connect to the SOAP server. Connecting to a SOAP server using JAXM is a three-step procedure:
You are now ready to send the SOAP request over this connection. The following code sends your request synchronously to the remote server: SOAPMessage soapResponse = connection.call(request, soapServerAddress); Notice that it is a synchronous request, so SOAPConnection.call method will return only when the SOAP server responds. The SOAP response is a SOAPMessage object. Recall that our request was also a SOAPMessage object. Therefore, to process this SOAP response, you can follow the same sequence of getSOAPPart, getEnvelope, and getBody methods that we explained earlier while authoring the SOAP request. We have summed up all this code in Listing 12.10 as a simple Java program named JAXMSOAPClient, which is a simple JAXML-based SOAP client. To compile and execute this application , you will need the following JAR files in your classpath: jaxm.jar, dom4j.jar, mail.jar, and client.jar. Listing 12.10 JAXMSOAPClientimport java.io.*; import javax.xml.soap.*; import javax.xml.messaging.*; import java.net.URL; import javax.mail.internet.*; import javax.xml.transform.*; import javax.xml.transform.stream.*; import org.dom4j.*; public class JAXMSOAPClient{ public static void main(String args[]) { try { //Author a SOAP request. MessageFactory messageFactory = MessageFactory.newInstance(); SOAPMessage request = messageFactory.createMessage(); SOAPPart soapPartOfRequest = request.getSOAPPart(); SOAPEnvelope soapEnvelope = soapPartOfRequest.getEnvelope(); SOAPBody soapBody = soapEnvelope.getBody(); Name methodName = soapEnvelope.createName( "invoke" , "jaxm", "urn: P2PCarRental"); soapBody.addChildElement(methodName); //Add code for SOAP Header authoring here. //Establish the SOAP connection. URLEndpoint soapServerAddress = new URLEndpoint( "http://localhost:8080/soap/ servlet/rpcrouter"); SOAPConnectionFactory connectionFactory = SOAPConnectionFactory.newInstance(); SOAPConnection connection = connectionFactory.createConnection(); //Display the request. System.out.println(); System.out.println("Sending SOAP request:"); request.writeTo(System.out); System.out.println(); //Send the request and wait for the response. SOAPMessage soapResponse = connection.call(request, soapServerAddress); //We have received the response. Display it. System.out.println(); System.out.println("Received reply from: "+soapServerAddress); soapResponse.writeTo(System.out); //Job done. Close the connection. connection.close(); } catch(Throwable e) { e.printStackTrace(); }//catch }//main }//class Using JAXM for SOAP Header AuthoringWe'll now show how to author a SOAP Header and add it to a SOAP message. The following is how the completed SOAP message will look after authoring is complete: <soap-env:Envelope xmlns:soap-env=http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Header> <sh:sampleHeader xmlns:sh=http://sampleNamespace.com/> </soap-env:Header> <soap-env:Body> <jaxm:invoke xmlns:jaxm="urn:P2PCarRental"/> </soap-env:Body> </soap-env:Envelope> The following lines of JAXM code will author the preceding SOAP message: SOAPHeader soapHeader = soapEnvelope.getHeader(); Name soapHeaderName = soapEnvelope.createName( "sampleHeader", "sh", "http:// sampleNamespace.com"); soapHeader.addHeaderElement(soapHeaderName); You can add the lines of code shown here just after authoring the SOAP request message and before establishing the SOAP connection in Listing 12.10. The first line in this code obtains a reference to the SOAP Header element. Recall that you used the getBody method of the SOAPEnvelope class to obtain a reference to the SOAP Body. Similarly, you will now use the getHeader method of the SOAPEnvelope class to obtain a reference to the SOAP Header. The second line creates a new sampleHeader element with the fictitious namespace http://sampleHeaderName.com. The third line of code simply adds the sampleHeader element to the SOAP Header. This is a generic procedure to author any Header. The actual element names and namespace declaration will depend on the protocol that you wish to support through the SOAP Header. Using JAXM Providers and ProfilesThis section describes the use of JAXM providers and profiles. The use of a JAXM provider enables asynchronous communication, which means you will hand your message over to the provider and will not wait for the response. The provider will take care of routing and delivery and will inform you upon receipt of the response. The JAXM profile is used to author preconfigured SOAP Headers confirming to a specific SOAP-based protocol (such as the ebXML Message Service Specification). The JAXM providers only work inside J2EE or servlet containers. They cannot work alone, so if you are designing a standalone client, you cannot use a JAXM provider. Your application will use the following steps to make use of the JAXM providers and profiles:
You might have noticed that these steps do not include any action about receiving messages. How will you implement the message receiving logic in your application? We'll make use of the OnewayListener interface to receive messages asynchronously. Let's see how all of this is accomplished. We will use a sample application called soaprp to demonstrate these steps. The soaprp sample application comes bundled with the JAXM version 1.0 Reference Implementation. It's a WAR file that can be deployed directly on a J2EE or servlet container such as Tomcat. You might want to see what this application does before going into the technical details. Deploying this application on Tomcat involves nothing more than copying the soaprp.war file in the webapps directory after you have successfully installed JAXM, for which you will find comprehensive instructions in the documentation accompanying the Reference Implementation. If you deploy the soaprp application on your container and access it through a normal Web browser, you will see the following message on the browser window: This is an example of a roundtrip JAXM message exchange via the remote provider.. Click here to send the message The message will contain a hyperlink. Click on the hyperlink and the message will be delivered to the provider, and in turn sent to its destination. You will also notice that the JAXM-based recipient of the message has some message-receiving logic that reported the receipt of the incoming message on the System.out console of your container. How soaprp Uses JAXMYou can unzip the soaprp.war file using any unzipping tool. Look for the src folder that contains the source code for this application. You will find two JavaBeans inside: SendingServlet.java and ReceivingServlet.java. SendingServlet is responsible for authoring messages and handing them over to the provider. ReceivingServlet demonstrates the use of the OnewayListener interface that's responsible for receiving messages. This is a simple and generic arrangement that you can use to build your own messaging logic. Look at the SendingServlet JavaBean, which extends the HttpServlet class. The most interesting part is in the doGet method, where you will look for the profile of your interest and create the message factory with it. Look at the following code extract from the sample application (mf is a MessageFactory instance, and pc is a ProviderConnection instance in the following code): if (mf == null) { ProviderMetaData metaData = pc.getMetaData(); String[] supportedProfiles = metaData.getSupportedProfiles(); String profile = null; for(int i=0; i < supportedProfiles.length; i++) { if(supportedProfiles[i].equals("soaprp")) { profile = supportedProfiles[i]; break; }//if }//for mf = pc.createMessageFactory(profile); }//if // Create a message from the message factory. SOAPRPMessageImpl soaprpMsg = (SOAPRPMessageImpl)mf.createMessage(); The if (mf==null) block is only meant to create the required message factory by selecting the correct profile. When you have the message factory, you can create the message the same way you created it in Listing 12.10. However, this time you will store the message in a SOAPRPMessageImpl object, which represents the profile-specific message format with the correct SOAP Header. The rest is logically the same as in Listing 12.10, except that you will use the ProviderConnection.send method instead of the SOAPConnection.call method to send the message. The send and call methods differ from each other in the sense that send is asynchronous, or one-way (meaning it returns immediately without waiting for the response), whereas call is synchronous (meaning it waits and remains blocked until it gets a response). Now look at the ReceivingServlet.java file. The ReceivingServlet class implements the OnewayListener interface, which consists of only one method, onMessage. This method assumes control upon receipt of an incoming message. |