A SOAP Example Using Java

Because SOAP relies on Web services, this Java example is going to be significantly more advanced than the Java examples we've already seen. You can use Java on the Internet in the form of Java servlets or JavaServer Pages, and it'll be easier to use servlets here.

SOAP in Java Versus .NET

Another major SOAP user is Microsoft's .NET initiative, of course. In fact, I wrote three complete SOAP examples using Visual Basic .NET for this chapter, but then I realized that the Java way of doing things provides us far greater insight into actually working with SOAP. In .NET, the details are handled behind the scenes (and there's a great deal of .NET-specific code); in Java, you do it yourself (including constructing and unpacking the SOAP messages themselves ). For that reason, I'm sticking to Java here. If you want to see SOAP in .NET, however, just take a look at the excellent SOAP examples that come with VB .NETthey're ready to run.

A servlet is a Java application that runs on a Web serversort of the analog of an applet that runs in the client browser. Our example will have two servlets, one that sends the SOAP message and one that receives that message. The receiving servlet will decode the data in the SOAP message and return a new SOAP message indicating that it has understood . Usually you don't see the actual SOAP message directly, but in this case, both the sent and returned SOAP messages will be stored to files so we can take a look at them directly.

You'll need a Web server that can run Java servlets for this example. You might not have access to such a Web server on the Internet, but that's not a problemyou can run one right on your own computer: the Tomcat server. Tomcat is the premier Web server for JavaServer Pages and servlets, and you can download it from http://jakarta.apache.org/tomcat/. The current version is version 4. Downloading and installing Tomcat is really not as hard as it sounds, even though you're installing a full Web server on your computer. Take a look at the installation directions that come with Tomcat. After you've gotten Tomcat installed and running (all the information you need comes with the Tomcat documentation), navigate to http://localhost:8080/index.html . (Tomcat uses port 8080, so it won't conflict with other local servers.) You should see Tomcat running, something like in Figure 18-1.

Figure 18-1. Running the Tomcat server.

graphics/18fig01.gif

In addition, you'll need some added support from Java. As we've seen, Java 1.4 comes with considerable XML support. However, the standard edition doesn't come with support to create XML Web services. A considerable amount of Java support exists for Web services, howevertake a look at http://java.sun.com/ webservices / for the details.

You have two options to start using SOAP with Java: You can download the Java XML Pack, which is currently at http://java.sun.com/xml/downloads/javaxmlpack.html, or you can download the Java Web Services Developer's Pack, which is currently at http://java.sun.com/webservices/webservicespack.html. The Java XML Pack is the smaller of these two and is a simple zipped file that holds the JAR files you'll need: jaxm-api.jar, saaj-api.jar, and activation.jar. You'll also need servlet.jar to create servlets, and it comes with Tomcat (in the Tomcat lib directory).

You'll need to set up Tomcat to work with Java XML Messaging, or JAXM. Stop Tomcat if it's running and copy jaxm-docs.war, which comes with the Java XML Pack or the Java Web Services Developer's Pack, to the Tomcat webapps directory. Then restart Tomcat and navigate to http://localhost:8080/jaxm-docs/tomcat.html for directions (which just involves copying Java Archive [JAR] and Web Archive [WAR] files).

And that's it. You've set up the needed environment; it's time to start writing some code. There will be two servlets here: the ch18_01 servlet will create a SOAP message and send it to a servlet named ch18_02, which will read it and send another SOAP message back to ch18_01.

I'll start with the ch18_01 servlet. This is the servlet that creates and sends the SOAP message in the first place. It begins by declaring itself part of a Java package that I'll call soapExample . It includes the other packages we'll need:

 package soapExample;  import java.io.*; import java.net.*; import javax.servlet.*; import javax.xml.soap.*; import javax.activation.*; import javax.servlet.http.*;         .         .         . 

Next , we'll create the actual SOAP connection that we'll use to send the SOAP message. The base class for servlets is HttpServlet , so this new class, ch18_01 , is based on that class. When a new servlet starts, its init method is called first. We can use a SOAPConnectionFactory object to create a SOAPConnection object like this (the call to super.init(servletConfig) makes sure that the HttpServlet class's constructor is called):

 public class ch18_01 extends HttpServlet  {  private SOAPConnection connection;  public void init(ServletConfig servletConfig) throws ServletException     {         super.init(servletConfig);  try {   SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();   connection = factory.createConnection();   } catch(Exception e) {}   }  .         .         . 

This new SOAPConnection object, connection , is the object we'll use to send a SOAP message to the other servlet, ch18_02. You can see the methods of the SOAPConnectionFactory class in Table 18-1 and the methods of the SOAPConnection class in Table 18-2.

Table 18-1. Methods of the javax.xml. soap.SOAP Connection Factory Class
Method Does This
SOAPConnectionFactory() The default constructor
abstract SOAPConnection createConnection() Creates a new SOAPConnection object
static SOAPConnectionFactory newInstance() Creates an object of the default SOAPConnectionFactory class
Table 18-2. Methods of the javax.xml. soap.SOAP Connection Class
Method Does This
SOAPConnection() The default constructor
abstract SOAPMessage call (SOAPMessage request, java. lang.Object to) Sends the specified message to the indicated object and waits until there is a response
abstract void close() Closes the SOAPConnection object

When you call this servlet using the GET Http method, the servlet's doGet method is called. In that method, we want to create the new SOAP message, which you can do with a MessageFactory object. You use this object's createMessage method to create a new SOAPMessage object. Note that we're also creating the text string that will display the progress of this servlet to the user:

 public void doGet(HttpServletRequest request, HttpServletResponse response)  throws ServletException {  String outString ="<HTML><H1>Sent and Received SOAP Message</H1><P>";    try {     MessageFactory messageFactory = MessageFactory.newInstance();     SOAPMessage message = messageFactory.createMessage();   .     .     . 

You can see the methods of the MessageFactory class in Table 18-3 and the methods of the SOAPMessage class in Table 18-4.

Table 18-3. Methods of the javax.xml. soap.MessageFactory Class
Method Does This
MessageFactory() The default constructor
abstract SOAPMessage createMessage() Creates a new SOAPMessage object using default SOAPPart , SOAPEnvelope , SOAPBody , and SOAPHeader objects
abstract SOAPMessage createMessage(MimeHeaders headers, java.io.InputStream in) Creates a SOAP message and returns that object
static MessageFactory newInstance() Creates a new MessageFactory object
Table 18-4. Methods of the javax.xml. soap.SOAP Message Class
Method Does This
SOAPMessage() The default constructor
abstract void addAttachmentPart (AttachmentPart AttachmentPart) Adds the specified AttachmentPart object to this SOAPMessage object
abstract int countAttachments() Returns the number of attachments in this message
abstract AttachmentPart createAttachmentPart() Creates an empty AttachmentPart object
AttachmentPart createAttachmentPart (javax.activation.DataHandler dataHandler) Creates an AttachmentPart object using the specified DataHandler object
AttachmentPart createAttachmentPart (java.lang.Object content, java.lang.String contentType) Creates an AttachmentPart object using data of the specified type
abstract java.util.Iterator getAttachments() Returns an iterator for all the AttachmentPart objects that are attached to this SOAPMessage object
abstract java.util.Iterator getAttachments(MimeHeaders headers) Returns an iterator over the AttachmentPart objects that have header entries that match the given headers
abstract java.lang.String getContentDescription() Returns a description of this SOAPMessage object's content
abstract MimeHeaders getMimeHeaders() Returns the MIME headers for this SOAPMessage object
abstract SOAPPart getSOAPPart() Returns the SOAP part of this SOAPMessage object
abstract void removeAllAttachments() Removes all AttachmentPart objects attached to this SOAPMessage object
abstract void saveChanges() Saves this SOAPMessage object, including all changes that have been made to it
abstract boolean saveRequired() Returns true if this SOAPMessage object has had the saveChanges method called
abstract void setContentDescription (java.lang.String description) Sets the description of this SOAPMessage object's content
abstract void writeTo (java.io.OutputStream out) Writes this SOAPMessage object to the specified output stream

Now that we have a SOAPMessage object, we need to customize the message we want to send. You do that by getting access to the SOAP message's body. In this case, we might send a SOAP message informing the other servlet that there are 125 writing pens in stock at the current time, for example. To get access to the SOAP message's body, you first use the SOAPMessage object's getSOAPPart method to get the SOAP part of the message (as opposed to attachments). Then you can use the getEnvelope method to get the SOAP envelope. Finally, you can use the envelope's getHeader and getBody methods to get the message's header and body (we won't use the header herethis code just shows how to get access to it):

 public void doGet(HttpServletRequest request, HttpServletResponse response)  throws ServletException {     String outString ="<HTML><H1>Sent and Received SOAP Message</H1><P>";     try {         MessageFactory messageFactory = MessageFactory.newInstance();         SOAPMessage message = messageFactory.createMessage();  SOAPPart soappart = message.getSOAPPart();   SOAPEnvelope envelope = soappart.getEnvelope();   SOAPHeader header = envelope.getHeader();   SOAPBody body = envelope.getBody();  .     .     . 

You can see the methods of the SOAPPart class in Table 18-5, the methods of the SOAPEnvelope interface in Table 18-6, the methods of the SOAPHeader interface in Table 18-7, and the methods of the SOAPBody interface in Table 18-8.

Table 18-5. Methods of the javax.xml. soap.SOAP Part Class
Method Does This
SOAPPart() The default constructor
abstract void addMimeHeader (java.lang.String name , java.lang.String value) Adds a MimeHeader object with the given name and value to this SOAPPart object
abstract java.util.Iterator getAllMimeHeaders() Returns an iterator over all the headers for this SOAPPart object
abstract Source getContent() Returns the content of the SOAPEnvelope as a Source object
java.lang.String getContentId() Returns the value of the MIME header with the name "Content-Id"
java.lang.String getContentLocation() Returns the value of the MIME header with the name "Content-Location"
abstract SOAPEnvelope getEnvelope() Returns the SOAPEnvelope object associated with this SOAPPart object
abstract java.util.Iterator getMatchingMimeHeaders (java.lang.String[] names ) Returns an iterator over all MimeHeader objects with names matching those in the specified array
abstract java.lang.String[]getMimeHeader(java.lang.String name) Returns the values of the MimeHeader object in this SOAPPart object identified by the specified String
abstract java.util.Iterator getNonMatchingMimeHeaders (java.lang.String[] names) Returns all MimeHeader objects whose name is not in the specified array
abstract void removeAllMimeHeaders() Removes all the MimeHeader objects in this SOAPEnvelope object
abstract void removeMimeHeader (java.lang.String header) Removes all MIME headers that match the specified name
abstract void setContent (Source source) Sets the content of the SOAPEnvelope object using the given Source object
void setContentId(java.lang. String contentId) Sets the MIME header named "Content-Id" to the given String
void setContentLocation(java. lang.String contentLocation) Sets the MIME header "Content-Location" to the given String
abstract void setMimeHeader (java.lang.String name, java. lang.String value) Sets the header entry that matches the specified header name
Table 18-6. Methods of the javax.xml. soap.SOAP Envelope Interface
Method Does This
SOAPBody addBody() Adds a SOAPBody object to this SOAPEnvelope object
SOAPHeader addHeader() Adds a SOAPHeader object to this SOAPEnvelope object
Name createName(java.lang. String localName) Creates a new Name object using the indicated local name
Name createName(java.lang.String localName, java.lang.String ) prefix, java.lang.String uri Creates a new Name object using the indicated local name, namespace prefix, and namespace URI
SOAPBody getBody() Returns the SOAPBody object of this SOAPEnvelope object
SOAPHeader getHeader() Returns the SOAPHeader object of this SOAPEnvelope object
Table 18-7. Methods of the javax.xml. soap.SOAP Header Interface
Method Does This
SOAPHeaderElement addHeaderElement(Name name) Creates a new SOAPHeaderElement object
java.util.Iterator examineHeaderElements(java.lang. String actor) Returns an iterator over all the SOAPHeaderElement objects in this SOAPHeader object with the given actor
java.util.Iterator extractHeaderElements(java.lang. String actor) Returns an iterator over all the SOAPHeaderElement objects in this SOAPHeader object with the given actor
Table 18-8. Methods of the javax.xml. soap.SOAP Body Interface
Method Does This
SOAPBody addBody() Adds a SOAPBody object to this SOAPEnvelope object
SOAPBodyElement addBodyElement (Name name) Adds a new SOAPBodyElement object with the given name to this SOAPBody object
SOAPFault addFault() Adds a new SOAPFault object to this SOAPBody object
SOAPFault getFault() Returns the SOAPFault object if there is one
boolean hasFault() Returns true if a SOAPFault object exists in this SOAPBody object

We can insert a new element into the body of our SOAP message, creating an element named <pens:NumberInStock> with the value 125 like this:

 <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/  envelope/">     <soap-env:Header/>     <soap-env:Body>  <pens:NumberInStock xmlns:pens="http://www.starpowder.com">125</pens: graphics/ccc.gif NumberInStock>  </soap-env:Body> </soap-env:Envelope> 

Here's how you create that element and add it to the SOAP message's bodywith the SOAPBody object's addBodyElement method and the addText method to add the text 125 :

 public void doGet(HttpServletRequest request, HttpServletResponse response)  throws ServletException {     String outString ="<HTML><H1>Sent and Received SOAP Message</H1><P>";     try {         MessageFactory messageFactory = MessageFactory.newInstance();         SOAPMessage message = messageFactory.createMessage();         SOAPPart soappart = message.getSOAPPart();         SOAPEnvelope envelope = soappart.getEnvelope();         SOAPHeader header = envelope.getHeader();         SOAPBody body = envelope.getBody();  body.addBodyElement(envelope.createName("NumberInStock", "pens",   "http://www.starpowder.com")).addTextNode("125");  .     .     . 

We can also add an attachment to our SOAP message. Attachments hold the text of a file that you want to pass along with the SOAP message (and a receiving servlet can read a SOAP message's attachments with the getAttachments method). In this example, we'll use an HTML page, ch18_03.html (which will be developed in a few pages), to call the ch18_01 servlet and make that servlet run. Let's attach the text of that HTML page to our SOAP message as an example attachment. We start by going through a little work to find the actual URL of that document:

 public void doGet(HttpServletRequest request, HttpServletResponse response)  throws ServletException {     String outString ="<HTML><H1>Sent and Received SOAP Message</H1><P>";     try {         MessageFactory messageFactory = MessageFactory.newInstance();         SOAPMessage message = messageFactory.createMessage();         SOAPPart soappart = message.getSOAPPart();         SOAPEnvelope envelope = soappart.getEnvelope();         SOAPHeader header = envelope.getHeader();         SOAPBody body = envelope.getBody();         body.addBodyElement(envelope.createName("NumberInStock", "pens",         "http://www.starpowder.com")).addTextNode("125");  StringBuffer urlServer = new StringBuffer();   urlServer.append(request.getScheme()).append("://") .append(request.getServerName());   urlServer.append(":").append(request.getServerPort()).append\(request.getContextPath());   String requestBase = urlServer.toString();   URL url = new URL(requestBase + "/ch18_03.html");  .     .     . 

Then we can add that document as an attachment to our SOAP message with an AttachmentPart object, like this:

 public void doGet(HttpServletRequest request, HttpServletResponse response)  throws ServletException {     String outString ="<HTML><H1>Sent and Received SOAP Message</H1><P>";     try {         MessageFactory messageFactory = MessageFactory.newInstance();         SOAPMessage message = messageFactory.createMessage();         SOAPPart soappart = message.getSOAPPart();         SOAPEnvelope envelope = soappart.getEnvelope();         SOAPHeader header = envelope.getHeader();         SOAPBody body = envelope.getBody();         body.addBodyElement(envelope.createName("NumberInStock", "pens",         "http://www.starpowder.com")).addTextNode("125");           StringBuffer urlServer = new StringBuffer(); urlServer.append(request.getScheme()).append("://").append (request.getServerName()); urlServer.append(":").append(request.getServerPort()).append (request.getContextPath());         String requestBase = urlServer.toString();         URL url = new URL(requestBase + "/ch18_03.html");  AttachmentPart attachmentpart = message.createAttachmentPart (new DataHandler( graphics/ccc.gif url));   attachmentpart.setContentType("text/html");   message.addAttachmentPart(attachmentpart);  .     .     . 

Now ch18_03.html will be sent to the target servlet as an attachment to our SOAP message. You can see the methods of the AttachmentPart class in Table 18-9.

Table 18-9. Methods of the javax.xml. soap. Attachment Part Class
Method Does This
AttachmentPart() The default constructor
abstract void addMimeHeader (java.lang.String name, java.lang.String value) Adds a MIME header with the given name and value to this AttachmentPart object
abstract void clearContent() Clears this AttachmentPart object's content
abstract java.util.Iterator getAllMimeHeaders() Returns an iterator over the headers for this AttachmentPart
abstract java.lang.Object getContent() Returns the content of this AttachmentPart object
java.lang.String getContentId() Returns the value of the MIME header named "Content-Id"
java.lang.String getContentLocation() Returns the value of the MIME header named "Content-Location"
java.lang.String getContentType() Returns the value of the MIME header "Content-Type"
abstract javax.activation. DataHandler getDataHandler() Returns the DataHandler object for this AttachmentPart object
abstract java.util.Iterator getMatchingMimeHeaders(java. lang.String[] names) Returns all MimeHeader objects that match a name in the indicated array
abstract java.lang.String[]getMimeHeader(java.lang.String name) Returns all values of the header specified by the passed String
abstract java.util.Iterator ) getNonMatchingMimeHeaders (java.lang.String[] names Returns all MimeHeader objects whose name is not in the given array
abstract int getSize() Returns the number of bytes in this AttachmentPart object
abstract void removeAllMimeHeaders() Removes all the MIME headers
abstract void removeMimeHeader) (java.lang.String header Removes all MIME headers that match the specified name
abstract void setContent (java.lang.Object object, java. lang.String contentType) Sets the content of this attachment part to that of the given Object and sets the value of the Content-Type header to the given type
void setContentId(java.lang. String contentId) Sets the MIME header "Content-Id" with the given value
void setContentLocation(java. lang.String contentLocation) Sets the MIME header "Content-Location" with the given value
void setContentType(java.lang. String contentType) Sets the MIME header "Content-Type" with the given value
abstract void setDataHandler (javax.activation.DataHandler dataHandler) Makes the passed DataHandler object the data handler for this AttachmentPart object
abstract void setMimeHeader (java.lang.String name, java. lang.String value) Sets the header entry matching the given name to the given value

Now that we have the base location of the application, we can get the URL of the target servlet, which I'll call getter :

 StringBuffer urlServer = new StringBuffer();          urlServer.append(request.getScheme()).append("://").append(request. getServerName( graphics/ccc.gif ));         urlServer.append(":").append(request.getServerPort()).append (request. graphics/ccc.gif getContextPath());         String requestBase = urlServer.toString();         URL url = new URL(requestBase + "/ch18_03.html");         AttachmentPart attachmentpart = message.createAttachmentPart(new DataHandler( graphics/ccc.gif url));         attachmentpart.setContentType("text/html");         message.addAttachmentPart(attachmentpart);  URL getter = new URL(requestBase + "/ch18_02");  .         .         . 

This URL is the URL we'll send the SOAP message to. Before sending that message, let's store it in a file so we can take a look at it afterward; in this case, I'll store our SOAP message in a file named put.msg. You can do that with the Java FileOutputStream class and the SOAP message's writeTo method this way:

 StringBuffer urlServer = new StringBuffer();  urlServer.append(request.getScheme()).append("://").append (request.getServerName()); urlServer.append(":").append(request.getServerPort()).append (request.getContextPath());         String requestBase = urlServer.toString();         URL url = new URL(requestBase + "/ch18_03.html");         AttachmentPart attachmentpart = message.createAttachmentPart (new DataHandler( graphics/ccc.gif url));         attachmentpart.setContentType("text/html");         message.addAttachmentPart(attachmentpart);         URL getter = new URL(requestBase + "/ch18_02");  FileOutputStream sentFile = new FileOutputStream("put.msg");   message.writeTo(sentFile);   sentFile.close();  .         .         . 

Now we'll be able to examine the SOAP message even after it has been sent. You can send the message using the connection object's call method, passing that method the message and the target URL like this (note that I also add the message " Sent SOAP message stored in put.msg. <p> " to the text this servlet will display in the user's browser):

 StringBuffer urlServer = new StringBuffer();  urlServer.append(request.getScheme()).append("://").append (request.getServerName()); urlServer.append(":").append(request.getServerPort()).append (request.getContextPath());         String requestBase = urlServer.toString();         URL url = new URL(requestBase + "/ch18_03.html");         .         .         .         URL getter = new URL(requestBase + "/ch18_02");         .         .         .  outString += "Sent SOAP message stored in put.msg. <p>";   SOAPMessage receivedMessage = connection.call(message, getter);  .         .         . 

The call method returns the SOAP message that the other servlet sent back to us. We can save that SOAP message in a new file, get.msg:

 SOAPMessage receivedMessage = connection.call(message, getter);  if (receivedMessage != null) {   FileOutputStream receivedFile = new FileOutputStream("get.msg");   receivedMessage.writeTo(receivedFile);   receivedFile.close();   outString += "Received SOAP message stored in get.msg.</HTML>";   }  

And that's it. We've sent a SOAP message, gotten one back, and stored them both in files for later examination. All that remains is to send the notification text stored in the outString variable (which tells the user Sent SOAP message stored in put.msg . and Received SOAP message stored in get.msg .) back to the user's browser. You can do that in a servlet using an OutputStream object. You can create this using the response object's getOutputStream method (the response object is passed to the servlet's doGet method):

 SOAPMessage receivedMessage = connection.call(message, getter);          if (receivedMessage != null) {             FileOutputStream receivedFile = new FileOutputStream("get.msg");             receivedMessage.writeTo(receivedFile);             receivedFile.close();             outString += "Received SOAP message stored in get.msg.</HTML>";         }     } catch(Throwable e) {}  try {   OutputStream outputStream = response.getOutputStream();   outputStream.write(outString.getBytes());   outputStream.flush();   outputStream.close();   } catch (IOException e) {}  

And that's it. We've passed the text back to the user's browser, and that completes the ch18_01 servlet. Here's the full code:

Listing ch18_01.java
 package soapExample; import java.io.*; import java.net.*; import javax.servlet.*; import javax.xml.soap.*; import javax.activation.*; import javax.servlet.http.*; public class ch18_01 extends HttpServlet {     private SOAPConnection connection;     public void init(ServletConfig servletConfig) throws ServletException     {         super.init(servletConfig);           try {           SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();             connection = factory.createConnection();         } catch(Exception e) {}     }     public void doGet(HttpServletRequest request, HttpServletResponse response)     throws ServletException     {         String outString ="<HTML><H1>Sent and Received SOAP Message</H1><P>";         try {             MessageFactory messageFactory = MessageFactory.newInstance();             SOAPMessage message = messageFactory.createMessage();             SOAPPart soappart = message.getSOAPPart();             SOAPEnvelope envelope = soappart.getEnvelope();             SOAPHeader header = envelope.getHeader();             SOAPBody body = envelope.getBody();             body.addBodyElement(envelope.createName("NumberInStock", "pens",             "http://www.starpowder.com")).addTextNode("125");             StringBuffer urlServer = new StringBuffer(); urlServer.append(request.getScheme()).append("://").append (request.getServerName()); urlServer.append(":").append(request.getServerPort()).append (request.getContextPath());             String requestBase = urlServer.toString();             URL url = new URL(requestBase + "/ch18_03.html");             AttachmentPart attachmentpart = message.createAttachmentPart (new DataHandler( graphics/ccc.gif url));             attachmentpart.setContentType("text/html");             message.addAttachmentPart(attachmentpart);                   URL getter = new URL(requestBase + "/ch18_02");                   FileOutputStream sentFile = new FileOutputStream("put.msg");             message.writeTo(sentFile);             sentFile.close();                   outString += "Sent SOAP message stored in put.msg. <p>";                   SOAPMessage receivedMessage = connection.call(message, getter);                   if (receivedMessage != null) {                 FileOutputStream receivedFile = new                 FileOutputStream("get.msg");                 receivedMessage.writeTo(receivedFile);                 receivedFile.close();                 outString += "Received SOAP message stored in                 get.msg.</HTML>";             }               } catch(Throwable e) {}           try {             OutputStream outputStream = response.getOutputStream();             outputStream.write(outString.getBytes());             outputStream.flush();             outputStream.close();         } catch (IOException e) {}     } } 

The next step is to develop the ch18_02 servlet, which is the target of our SOAP message. This servlet will read the incoming SOAP message, interpret it, and send a message back indicating that it has understood that incoming message. This servlet, which "listens" for incoming SOAP messages, is based on the JAXMServlet class and implements the ReqRespListener interface:

 public class ch18_02 extends JAXMServlet implements ReqRespListener  {         .         .         . 

The JAXMServlet class lets you handle XML messages like SOAP messages. The ReqRespListener interface has only one method, the onMessage method that is called when a message is read. When that message is read, we will create and send back a new SOAP message, which means we'll need a new message factory object. I'll create that object when the servlet is first initialized :

 public class ch18_02 extends JAXMServlet implements ReqRespListener  {  static MessageFactory factory = null;  public void init(ServletConfig servletConfig) throws ServletException     {         super.init(servletConfig);  try {   factory = MessageFactory.newInstance();   } catch (Exception ex) {}  }         .         .         . 

Now, in the onMessage method, we can handle the incoming SOAP message, which is passed to this method. We handle that message by getting the data in it (that there are 125 pens in stock). The code begins by getting the SOAP message's body:

 public SOAPMessage onMessage(SOAPMessage msg)  {     try {  SOAPPart soappart = msg.getSOAPPart();   SOAPEnvelope incomingEnvelope = soappart.getEnvelope();   SOAPBody body = incomingEnvelope.getBody();  .     .     . 

To access the elements in the SOAP message's body, you can use the body object's getChildElements method. You pass this method a Name object for the element you want, which includes the name of the element as well as the element's namespace and namespace URI. You can create that Name object with the SOAP envelope object's createName method like this:

 public SOAPMessage onMessage(SOAPMessage msg)  {     try {         SOAPPart soappart = msg.getSOAPPart();         SOAPEnvelope incomingEnvelope = soappart.getEnvelope();         SOAPBody body = incomingEnvelope.getBody();  Iterator it = body.getChildElements(   incomingEnvelope.createName("NumberInStock", "pens","http://www.starpowder.com"));  .     .     . 

The getChildElements method returns a Java Iterator object, which lets you access all the returned elements with Iterator 's next method. To get the body element's first (and only, in this case), elementthe <pens:NumberInStock> elementyou can use the Iterator 's next method like this:

 public SOAPMessage onMessage(SOAPMessage msg)  {     try {         SOAPPart soappart = msg.getSOAPPart();         SOAPEnvelope incomingEnvelope = soappart.getEnvelope();         SOAPBody body = incomingEnvelope.getBody();         Iterator it = body.getChildElements(         incomingEnvelope.createName("NumberInStock", "pens", "http://www.starpowder. graphics/ccc.gif com"));  SOAPElement element;   element = (SOAPElement) it.next();  .     .     . 

Now we have the <pens:NumberInStock> element in a SOAPElement object. You can see the methods of the SOAPElement interface in Table 18-10. This interface is based on the SOAP Node interface, and you can see the methods of the Node interface in Table 18-11. Note in particular the getValue method of the Node interface in Table 18-11that's the method we'll use to extract the value of the <pens:NumberInStock> element.

Table 18-10. Methods of the javax.xml. soap.SOAP Element Interface
Method Does This
SOAPElement addAttribute (Name name,java.lang.String value) Adds an attribute with the given name and value to this SOAPElement object
SOAPElement addChildElement (Name name) Adds a new SOAPElement object with the given name to this SOAPElement object
SOAPElement addChildElement (SOAPElement element) Adds a SOAPElement object as a child of the current SOAPElement object
SOAPElement addChildElement (java.lang.String localName) Adds a new SOAPElement object with the given local name to this SOAPElement object
SOAPElement addChildElement (java.lang.String localName , java.lang.String prefix) Adds a new SOAPElement object with the given local name and prefix to this SOAPElement object
SOAPElement addChildElement (java.lang.String localName , java.lang.String prefix, java . lang.String uri) Adds a new SOAPElement object with the given local name, prefix, and URI to this SOAPElement object
SOAPElement addNamespaceDeclaration (java.lang.String prefix , java.lang.String uri) Adds a namespace declaration with the given prefix and URI to this SOAPElement object
SOAPElement addTextNode (java.lang.String text) Adds a new Text object holding the given String to this SOAPElement object
java.util.Iterator getAllAttributes() Returns an iterator over all the attribute names in this SOAPElement object
java.lang.String getAttributeValue(Name name) Returns the value of the attribute with the specified name
java.util.Iterator getChildElements() Returns an iterator over child elements of this element
java.util.Iterator getChildElements(Name name) Returns an iterator over all the child elements with the indicated name
Name getElementName() Returns the name of this SOAPElement object
java.lang.String getEncodingStyle() Returns the encoding style for this SOAPElement object
java.util.Iterator getNamespacePrefixes() Returns an iterator over namespace prefixes
java.lang.String getNamespaceURI (java.lang.String prefix) Returns the URI of the namespace that matches the specified prefix
boolean removeAttribute (Name name) Removes the attribute with the given name
boolean removeNamespaceDeclaration (java.lang.String prefix) Removes the namespace declaration corresponding to the specified prefix
void setEncodingStyle (java.lang.String encodingStyle) Sets the encoding style for this SOAPElement object
Table 18-11. Methods of the javax.xml. soap.Node Interface
Method Does This
void detachNode() Removes this Node object
SOAPElement getParentElement() Returns the parent element of this Node object
java.lang.String getValue() Returns the text value of the immediate child of this Node object
void recycleNode() Indicates that you're done with the node and lets Java reuse it
void setParentElement (SOAPElement parent) Makes the parent of this Node object the specified SOAPElement object

To send a SOAP message back to the ch18_01 servlet, we can use the message factory's createMessage method. Then we can use the new message's getEnvelope method to get the message's envelope, which is the first step in customizing that message:

 public SOAPMessage onMessage(SOAPMessage msg)  {     try {         SOAPPart soappart = msg.getSOAPPart();         SOAPEnvelope incomingEnvelope = soappart.getEnvelope();         SOAPBody body = incomingEnvelope.getBody();         Iterator it = body.getChildElements(         incomingEnvelope.createName("NumberInStock", "pens", "http://www.starpowder. graphics/ccc.gif com"));         SOAPElement element;         element = (SOAPElement) it.next();  SOAPMessage message = factory.createMessage();   SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();  .     .     . 

All that's left is to add a new element to hold the data we want to send to the return message. I'll call this new element <Response> , and I'll add a text node to that element with text indicating that we've read how many pens are left in stock (the element.getValue method here returns the number of pens from the incoming SOAP message):

 public SOAPMessage onMessage(SOAPMessage msg)  {     try {         SOAPPart soappart = msg.getSOAPPart();         SOAPEnvelope incomingEnvelope = soappart.getEnvelope();         SOAPBody body = incomingEnvelope.getBody();         Iterator it = body.getChildElements(         incomingEnvelope.createName("NumberInStock", "pens", "http://www.starpowder. graphics/ccc.gif com"));         SOAPElement element;         element = (SOAPElement) it.next();         SOAPMessage message = factory.createMessage();         SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();  envelope.getBody().addChildElement(envelope.createName("Response")).addTextNode(   "Thanks for the SOAP message telling me there are " + element.getValue() +   " pens in stock"   );  return message;     } catch(Exception e) {         return null;     } 

At the end of this code, we return the completed message, which automatically sends it back to the original servlet, ch18_01. Here's the full code for the ch18_02 servlet:

Listing ch18_02.java
 package soapExample; import java.util.*; import javax.servlet.*; import javax.xml.soap.*; import javax.servlet.http.*; import javax.xml.messaging.*; public class ch18_02 extends JAXMServlet implements ReqRespListener {     static MessageFactory factory = null;     public void init(ServletConfig servletConfig) throws ServletException     {         super.init(servletConfig);         try {             factory = MessageFactory.newInstance();         } catch (Exception ex) {}     }     public SOAPMessage onMessage(SOAPMessage msg)     {         try {             SOAPPart soappart = msg.getSOAPPart();             SOAPEnvelope incomingEnvelope = soappart.getEnvelope();             SOAPBody body = incomingEnvelope.getBody();             Iterator it = body.getChildElements(             incomingEnvelope.createName("NumberInStock", "pens", "http://www.starpowder. graphics/ccc.gif com"));             SOAPElement element;             element = (SOAPElement) it.next();             SOAPMessage message = factory.createMessage();             SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();                   envelope.getBody().addChildElement(envelope.createName ("Response")). graphics/ccc.gif addTextNode(                 "Thanks for the SOAP message telling me there are " + element.getValue() +                 " pens in stock"             );             return message;         } catch(Exception e) {             return null;         }     } } 

And that's it. To compile these servlets, ch18_01.java and ch18_02.java, you'll need servlet.jar, jaxm-api.jar, saaj-api.jar, and activation.jar in your classpath environment variable. Here's what that looks like (this assumes that the .jar files are in the same directory as the .java files; if that's not so, make sure you preface every .jar filename with its correct path ):

 %set classpath=servlet.jar;jaxm-api.jar;saaj-api.jar;activation.jar  %javac ch18_01.java %javac ch18_02.java 

How do we get the first servlet, ch18_01, to run? In this example, I'll use an HTML page, ch18_03.html, to call that servlet. The user can open this HTML page and click a hyperlink to run ch18_01, which will send a SOAP message to ch18_02:

Listing ch18_03.html
 <HTML>     <HEAD>         <TITLE>Using SOAP</TITLE>     </HEAD>     <BODY>         <CENTER>             <H1>Using SOAP</H1>         </CENTER>  <A HREF="ch18_01">Click me</a> to send and receive a SOAP message.  </BODY> </HTML> 

And we're almost set. We also need to create a file named web.xml that will let Tomcat connect the class files ch18_01.class and ch18_02.class to URLs that you can enter into a browser. Here's what web.xml looks like (this file is included in the downloadable code for this book):

 <?xml version="1.0" encoding="ISO-8859-1"?>  <!DOCTYPE web-app     PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"     "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app>     <servlet>         <servlet-name>             ch18_01         </servlet-name>         <servlet-class>             soapExample.ch18_01         </servlet-class>     </servlet>     <servlet>         <servlet-name>             ch18_02         </servlet-name>         <servlet-class>             soapExample.ch18_02         </servlet-class>     </servlet>     <servlet-mapping>         <servlet-name>             ch18_01         </servlet-name>         <url-pattern>             /ch18_01         </url-pattern>     </servlet-mapping>     <servlet-mapping>         <servlet-name>             ch18_02         </servlet-name>         <url-pattern>             /ch18_02         </url-pattern>     </servlet-mapping> </web-app> 

And that's it. Now we have the files we'll need, ch18_01.class, ch18_02.class, ch18_03.html, and web.xml. To install them in Tomcat, stop Tomcat and add a directory named soap to the Tomcat webapps directory with these files and subdirectories:

 webapps (directory)  __soap (directory)    __ch18_03.html    __WEB-INF (directory)       __web.xml       __classes (directory)          __soapExample (directory)             __ch18_01.class             __ch18_02.class 

Then (re)start Tomcat. The whole application is ready to go. Navigate to http://localhost:8080/soap/ch18_03.html in your browser, as you see in Figure 18-2.

Figure 18-2. Starting our SOAP example.

graphics/18fig02.gif

Click the link you see in Figure 18-2 to call the ch18_01 servlet, which will send a SOAP message to the ch18_02 servlet. This servlet, in turn , sends another SOAP message back to ch18_01. You can see the results that ch18_01 reports back to the user in Figure 18-3.

Figure 18-3. The status report by the SOAP example.

graphics/18fig03.gif

As you can see in Figure 18-3, a SOAP message was sent and another SOAP message was returned. What did those messages look like? We wrote this application to store the actual SOAP messages in the files put.msg and get.msg, which will be stored in the Tomcat bin directory. Here's the first SOAP message, as sent by ch18_01 and stored in put.msg. You can see the SOAP message here, including the <pens:NumberInStock> element that holds the number of pens in stock, as well as the attachment that holds ch18_03.html:

 ------=_Part_0_13526686.1026338101718  Content-Type: text/xml <?xml version="1.0" encoding="UTF-8"?> <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/ envelope/">     <soap-env:Header/>     <soap-env:Body>  <pens:NumberInStock xmlns:pens="http://www.starpowder.com">125< /pens: graphics/ccc.gif NumberInStock>  </soap-env:Body> </soap-env:Envelope> ------=_Part_0_13526686.1026338101718 Content-Type: text/html  <HTML>   <HEAD>   <TITLE>Using SOAP</TITLE>   </HEAD>   <BODY>   <CENTER>   <H1>Using SOAP</H1>   </CENTER>   <A HREF="ch18_01">Click me</a> to send and receive a SOAP message.   </BODY>   </HTML>  ------=_Part_0_13526686.1026338101718-- 

Here's the returned SOAP message, as stored in get.msg, indicating that the second servlet, ch18_02, understood that 125 pens were in stock:

 <?xml version="1.0" encoding="UTF-8"?>  <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/ envelope/">     <soap-env:Header/>     <soap-env:Body>  <Response>Thanks for the SOAP message telling me there are 125 pens in stock</ graphics/ccc.gif Response>  </soap-env:Body> </soap-env:Envelope> 

Congratulations, you've sent and received SOAP messages in code now. As you can see, SOAP can form the communications backbone for a distributed application. And you don't need to program SOAP applications in Java, either. There are many other options these days, including Microsoft's Visual Studio .NET, which creates Web services that use SOAP. In fact, because SOAP is language- and platform-independent, you can use SOAP to communicate among various parts of a Web application that are written in different languages.



Real World XML
Real World XML (2nd Edition)
ISBN: 0735712867
EAN: 2147483647
Year: 2005
Pages: 440
Authors: Steve Holzner

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net