To use your Web Service you need a client. For the purposes of this exercise, this client will be a standard Java class with a main method. However, it could also be a J2EE component such as a JSP or EJB or a non-Java client. The client will create a SOAP message containing a given location, send the service a request for a list of jobs at that location and print out the list when it returns.
The SAAJ API provides a simple way to send a straightforward SOAP message. An SAAJ client will create a SOAP message using the SAAJ API and send that message to a SOAP server at a specified URL.
The first thing to do is create the SOAP message. To do this, you need an instance of the MessageFactory class that you saw earlier. From this you obtain a SOAPMessage to populate with your request. This is identical to the way you created the response SOAPMessage in the servlet earlier.
You now have an empty SOAP envelope you can populate. The message to be sent in this case is a request for a list of jobs. You have the same marshaling issues as on the server-side and these are discussed shortly from the client perspective. The client class, SAAJAgencyClient, uses a ClientMarshaler helper class to marshal between Java and XML. The method addRequestToMessage() takes the message type (“jobsAtLocation”) together with the location itself and creates a request document in the given SOAPMessage. Listing 21.11 shows the client creating a SOAPMessage and populating it.
public class SAAJAgencyClient { private static String location = "%"; public static void main(String[] args) { // Obtain location from command line arguments ... MessageFactory messageFactory = MessageFactory.newInstance(); // Populate message with request type of "jobsAtLocation" // and location SOAPMessage soapRequest = messageFactory.createMessage(); ClientMarshaler.addRequestToMessage("jobsAtLocation", location, soapRequest); ... } } |
Now that 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 with the MessageFactory, the SOAPConnectionFactory can be instantiated directly using the newInstance() method or can be obtained through JNDI if one is registered with the J2EE container in which the component is running. The createConnection() method of the SOAPConnectionFactory returns a SOAPConnection.
As SAAJ is used to make direct SOAP calls to servers, you must specify the address (or endpoint) of the target server using a java.net.URL. In this case, the target URL is that of the server you deployed earlier, namely http://localhost:8000/wsagency/LocationServices.
The SOAP server happens to be implemented using Java and SAAJ but this is not essential. The server could equally well be implemented in Perl or as an ASP.NET components written in C#. Note that the URL for the Web Service looks just like any other servlet address and contains no special indications that it is a Web Service rather than an HTML interface.
You now have the two things you need—a message and somewhere to send it—so you can now send the message using the SOAPConnection's call() method. The call() method takes as parameters the message itself and the target endpoint to which it should be delivered. The sequence of obtaining the connection and making the call is shown in Listing 21.12.
public class SAAJAgencyClient { ... public static void main(String[] args) { ... SOAPConnectionFactory connectionFactory = SOAPConnectionFactory. newInstance(); SOAPConnection connection = connectionFactory.createConnection(); URL endpoint = new URL("http://localhost:8000/wsagency/LocationServices"); // Send request soapResponse = connection.call(soapRequest, endpoint); ... } } |
Message-Style Web Services and Web Service Registries 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 listing the jobs at the location you provided. To process this list, you need to retrieve the XML document from the SOAP message. This XML document will be contained in the SOAP envelope. Once again, the ClientMarshaler class encapsulates the conversion from XML to Java and provides a getJobsFromMessage() method that creates an array of Java Strings. These strings can then be printed out as shown in Listing 21.13.
public class SAAJAgencyClient { ... public static void main(String[] args) { ... // Retrieve job list from response System.out.println("Job list: "); String[] jobs = ClientMarshaler.getJobsFromMessage(soapResponse); for(int i = 0; i < jobs.length; i++) { System.out.println(jobs[i]); } System.out.println(" Done "); ... } } |
The client-side marshaling is very similar to the server-side marshaling except that it performs the actions in reverse. The addRequestToMessage() and getJobsFromMessage() methods create instances of the Name class and manipulate instances of SOAPEnvlope, SOAPBody and SOAPEnvelope. The ClientMarshaler is shown in Listing 21.14.
package client; import java.util.Iterator; import javax.xml.soap.Name; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPBodyElement; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPMessage; public class ClientMarshaler { static final String nsAgency = "http://J2EE21/agency"; static final String prefixAgency = "agency"; public static void addRequestToMessage(String type, String location, SOAPMessage soapMessage) throws SOAPException { SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope(); SOAPBody soapBody = soapEnvelope.getBody(); // Create a request element under the body Name requestName = soapEnvelope.createName("request", prefixAgency, nsAgency); SOAPBodyElement soapRequest = soapBody.addBodyElement(requestName); // Create a type element under the agencyRequest Name typeName = soapEnvelope.createName("type", prefixAgency, nsAgency); SOAPElement soapType = soapRequest.addChildElement(typeName); soapType.addTextNode(type); // Create a location element under the agencyRequest Name locationName = soapEnvelope.createName("location", prefixAgency, nsAgency); SOAPElement soapLocation = soapRequest.addChildElement(locationName); soapLocation.addTextNode(location); } public static String[] getJobsFromMessage(SOAPMessage soapMessage) throws SOAPException { SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope(); SOAPBody soapBody = soapEnvelope.getBody(); // Retrieve the response element from under the body Name responseName = soapEnvelope.createName("response", prefixAgency, nsAgency); SOAPElement soapResponse = getChildElementByName(responseName, soapBody); // Retrieve the numJobs element from under the response Name numJobsName = soapEnvelope.createName("numJobs", prefixAgency, nsAgency); SOAPElement soapNumJobs = getChildElementByName(numJobsName, soapResponse); int numJobs = Integer.parseInt(soapNumJobs.getValue()); String[] jobs = new String[numJobs]; // Retrieve the jobs element from under the response Name jobsName = soapEnvelope.createName("jobs", prefixAgency, nsAgency); SOAPElement soapJobs = getChildElementByName(jobsName, soapResponse); // Retrieve the jobs and put them in an array Iterator jobIterator = soapJobs.getChildElements(); for (int i = 0; i < numJobs && jobIterator.hasNext(); i++) { SOAPElement jobElement = (SOAPElement)jobIterator.next(); jobs[i] = jobElement.getValue(); } return jobs; } private static SOAPElement getChildElementByName(Name name, SOAPElement element) throws SOAPException { Iterator childIterator = element.getChildElements(name); if (childIterator.hasNext()) { SOAPElement childElement = (SOAPElement)childIterator.next(); return childElement; } else { throw new SOAPException("element " + name.getQualifiedName() + " is missing"); } } } |
As with the server-side marshaling, SOAPExceptions might be thrown by the various methods and constructors used. Please refer to the SAAJ API documentation for specific details.
You compile and run your client as you would any other Java application. The client is built as part of the following Ant directive:
asant build
You can run the client program using
asant run
This will prompt you for a location and then send a SOAP message to the SAAJ web service you deployed earlier. Provide the location “London” and you should see the following message printed out (depending on how many jobs you have registered for London):
[java] Using Location: London [java] Job list: [java] winston/Cigar trimmer [java] Done
So, now you have created, deployed, and invoked a simple Web Service under J2EE using SAAJ.
18.119.19.174