Monkeys at Keyboards: Java-Fu
© Michael James Heron
Topic: Java Programming
Level: 3
Version: beta

There are no facts, only interpretations.
Fredrich Nietzsche

11 - Washing Your Mouth Out With SOAP

PreviousTable of ContentsNext
Forum


Chapter Objectives

By the end of this chapter, the reader will be able to:



    11.1

    Introduction

    Now that we've gorged ourselves silly on XML and SAX parsing, let's look at how it all fits into the central theme of the text - component based programming. XML in itself is a side-issue, and will be covered in much more depth in another text on the website. We've only touched on its capabilities, because we don't want to get too side-tracked.

    Our introduction to XML will be useful for us when we discuss SOAP (Simple Object Access Protocol), which is an XML-based protocol that allows for information to be exchanged in a distributed environment - like the Internet, for example. The real power of SOAP is that it allows for information to be exchanged over an HTTP connection, which allows us to ignore the tricky issues of socket-based programming (an issue that will also be discussed in a later text).

    We'll be looking primarily at SOAP and Java in this chapter, but SOAP is also a key underlying technology in Microsoft's .NET architecture.

    So, enough setting the scene - let's get started!

    11.2

    The SOAP Envelope

    SOAP allows for messages to be passed between distributed objects over the HTTP protocol... it does this through the use of SOAP messages, which are encoded within an XML document. The root element of the XML document is called the envelope.

    The SOAP standard requires a certain name-space to be defined to ensure that naming collisions are avoided:

    http://schemas.xmlsoap.org/soap/envelope/

    <?xml version="1.0"?>
    <Envelope xmlns ="http://schemas.xmlsoap.org/soap/envelope/">
    ...
    Stuff goes in here
    ...
    </Envelope>

    With our Envelope root element, we can have a Header element that contains supplementary information about our SOAP message. This does not have to be present, but if it is present it must be the first child element of the Envelope element. We'll talk more about that later in this chapter.

    We also need a Body element which will contain all of the information we wish to transmit within the envelope. For example, let's consider a simple envelope that contains a name and an address:

    <?xml version="1.0"?>
    <Envelope xmlns ="http://schemas.xmlsoap.org/soap/envelope/">
    <Body>
    <name>Michael Heron</name>
    <address>666 Fake Street</address>
    </Body>
    </Envelope>

    We won't be spending too much time on the envelope at the moment - all you need to understand is that SOAP works via the HTTP protocol, which sends SOAP messages contained within XML envelopes. We'll have more cause to dissect the envelope protocol a little further when we start to actually do something constructive with the protocol.

    11.3

    SOAP and Water

    We have to make a distinction here between a SOAP client (which we will be writing in this chapter) and a SOAP server (which is sometimes known as a web service). There's no reason that a particular object cannot act as both a client and a server, but we'll assume a strict distinction between them for now because it suits our purposes.

    We build a connection between two SOAP objects through the use of the SOAP envelope. The client builds up an envelope containing information as to what methods are going to be invoked, and what parameters are to be passed to that method. The server intercepts this envelope, pulls out all the relevant information and does the appropriate computation before sending the results back to the client in another envelope.

    Java will handle all of this communication for you - there's no need for you to manually create and parse XML files before you can make use of SOAP. However, there is much useful information contained within the envelope, which we'll discuss later in this chapter.

    So, we're going to create a SOAP client that will connect to a SOAP server - we'll pick a suitable server from the internet (there are many of these available at www.xmethods.com) and build up an object that lets us make use of it.

    11.4

    Covering Yourself in SOAP

    Here we hit the first snag - Java doesn't come pre-shipped with any packages for handling all the neat SOAP stuff. What a dilemma!

    Luckily, there is a set of freeware libraries available for our use - we'll be making use of them. Download the quickstart.zip file from the book website, and unzip it - inside you'll find a number of .JAR files. You know what to do with these by now, I'm sure.

    It can be quite complex to setup an environment suitable for developing SOAP programming components... details of this can be found in the appendices to this text. We'll look more at this in the next chapter - for now, let's just concentrate on making use of SOAP.

    There are a number of packages you need to import before you can make use of the SOAP protocol - these are contained with the JAR files indicated above... the most important of these (at least in the short term) are:

    import org.apache.soap.*; 
    import org.apache.soap.rpc.*;
    import org.apache.soap.util.xml.*;

    These packages define the various classes we're going to be making use of in this chapter - really, there are only three that we're especially interested in: Call, Response and Parameter.

    We're going to make use of the Synonym web service that is available at www.xmethods.com. It's a simple service that takes a string parameter and returns an HTML document that lists its synonyms. We'll be making use of the IceBrowser JavaBean for this application, just like we did for the RSS parser bean.

    First of all, let's set ourselves up with a simple interface. We need a JTextField for entering the word we want to search for. We need a JButton to press, and we need a Browser to render the HTML that comes our way... we can easily slot these into a suitable class:

    import java.util.*; 
    import org.apache.soap.*;
    import org.apache.soap.rpc.*;
    import org.apache.soap.util.xml.*;
    import java.net.*;
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    import ice.htmlbrowser.Browser;


    public class SoapClient extends JFrame implements ActionListener {
    JButton myButton;
    JTextField myText;
    Browser myBrowser;

    public SoapClient() {
    Container c = getContentPane();
    myButton = new JButton ("Get Synonym");
    myButton.addActionListener (this);
    myBrowser = new Browser();
    myText = new JTextField (10);
    c.add (myButton, BorderLayout.SOUTH);
    c.add (myBrowser, BorderLayout.CENTER);
    c.add (myText, BorderLayout.NORTH);
    }


    public static void main (String args[]) {
    SoapClient myMain = new SoapClient();
    myMain.setSize (500, 500);
    myMain.setVisible (true);
    }


    public void actionPerformed (ActionEvent e) {
    }

    }

    Note here that we use the Browser bean in the same way we would any other GUI component - by adding it to the container of a JFrame. This demonstrates in part of the flexibility of the bean specification.

    Whenever the button is pressed, we want to take the string that is contained in the JTextField (easy), and send that as a parameter to the web service using SOAP (que?).

    The process seems quite complex, but it resolves to a few simple method calls (as do most things in Java).

    First of all, we need to create a new instance of the Call class - this handles all the hard work of building a SOAP envelope and making the network connection:

    Call myCall = new Call(); 

    Then, we call the setTargetObjectURI method on this Call object. We pass in a String parameter that represents a particular namespace within the web service object - for now, we won't worry too much about this and concentrate primarily on just making the connection. The web services provided on the XMethods websites document what value should be passed as a parameter. For our Synonym service, it is urn:MBWS-SoapServices:

    myCall.setTargetObjectURI ("urn:MBWS-SoapServices"); 

    Then, we choose the method we want to call on the web service - again, the website documents what methods are available, but we won't worry about that at the moment. The method we want is called getServiceResponsePublic:

    myCall.setMethodName ("getServiceResponsePublic"); 

    Next, we set the encoding details... ignore what this means for now:

    myCall.setEncodingStyleURI (Constants.NS_URI_SOAP_ENC); 

    And then finally we need to build up a list of parameters - these are passed to the method in the web service. We do this through a method called setParams in the Call object, but first we need to create them.

    The setParams object requires an object of type Vector to be passed as its parameter - this is an older version of the ArrayList class, so its usage should be familiar to you. Every element of the Vector will be an instance of the Parameter class.

    First, we create an empty Vector:

    Vector params = new Vector(); 

    The web-service we are using requires two parameters, both of type String - the first is called serviceName, and we set this to Synonym. The second is inputText, and we set this to whatever word we want to get the synonyms for.

    We setup a parameter by creating a new instance of the Parameter class - the constructor for this class takes four parameters:

    1. The name to give the parameter
    2. The Type of the parameter
    3. The value of the parameter
    4. The encoding style (we'll ignore this).

    So first, we create a parameter for the serviceName:

    Parameter temp; 
    temp = new Parameter ("serviceName", String.class, "Synonym", null);

    And then we add it to the Vector:

    params.addElement (temp); 

    And then we create a Parameter for the inputText, setting the value to whatever is in our JTextField:

    temp = new Parameter ("inputText", String.class, myText.getText(), null); 
    params.addElement (temp);

    Once we've set up our parameters, we call setParams on our call object:

    myCall.setParams (params); 

    And with that, we've configured our SOAP method call... all that's left to do after that is make the actual connection.

    The Call object has a method called invoke, and this method creates the SOAP Envelope, passes it to the web service, and obtains a response - the response goes into an object of type Response, as can be imagined. The invoke method takes two parameters - one is a URL object that points to where the web service may be located, and the second is an action to be performed (we'll ignore this, and pass an empty string in its place).

    The invoke call carries with it a mandatory acknowledgement exception of type SOAPException - this must be tried and caught in the usual way:

    Response res = null; 
    try {
    res = myCall.invoke (new URL (http:
    // www.extensio.com:
    8080/ExtensioInfoServer/”“servlet/rpcrouter"), "");
    }

    catch (MalformedURLException ex) {
    System.out.println ("Bad URL!");
    }

    catch (SOAPException ex) {
    System.out.println ("SOAP Exception.");
    }

    With these little flutters of our Java-encrusted fingers, we've managed to create a connection to a web service and store its response for our edification. Huzzah for us!

    In the case of this particular web service, it just returns a string of HTML - we don't need to worry about parsing out numerous different elements. The getReturnValue method of Response will return an object of type Parameter that indicates the return value. The getValue method of Parameter will give us the actual contents of the parameter - we'll need to cast this to a String since it is stored as an Object:

    Parameter returnVal = null; 
    String html;
    returnVal = res.getReturnValue();
    html = (String) returnVal.getValue();

    All that's left to do now is display the HTML on the browser. First we call the method htmlClear on the browser to clear its display:

    And then we call the method htmlAppend to set it up with our HTML string:

    And voila! We have ourselves a SOAP based thesaurus!

    SOAP Based Thesaurus
    Fig 11.1: SOAP Based Thesaurus

    As you can see, it's not as hard as you might think to make use of SOAP in your applications - primarily because Java itself does all the hard work. We're making use of some powerful classes to handle all of this for us, but really all we need is some File IO and an ability to make a network connection. Obviously though, there is no point in re-inventing the wheel when we have such fine, pre-rolled functionality available at our fingertips.

    11.5

    Dealing with Errors

    Sometimes, things will go wrong with a SOAP request, but it won't throw up an exception - instead it will handle the problem internally through the SOAP envelope, which allows for a fault string to be recorded along with all the other data. For example, specifying the wrong target URI will cause an error to be generated, but not exception - a good SOAP client should anticipate this kind of problem and provide error handling code to deal with it.

    The Response object that comes back from the invoke call has a method called generatedFault - this will return true if a fault was encountered and false if one was not... we can put this within an if structure to print out the error message if something goes wrong.

    The Response object also contains a method called getFault, which returns an object of type Fault. The Fault object has a method called getFaultString which returns a string containing the error that was encountered... so for our application:

    if (res.generatedFault() == true) { 
    html = "<b>" + res.getFault() .getFaultString() + "</b>";
    }

    else {
    returnVal = res.getReturnValue();
    html = (String) returnVal.getValue();
    }

    myBrowser.htmlClear();
    myBrowser.htmlAppend (html);

    Now if a fault is generated by the web service, our client will let us know exactly what happened. This particular framework is not as useful as the generic exception handler structure (it's more difficult to provide meaningful error messages to the user for one thing), but it's still important that we understand how it works.

    11.6

    Bean Serialization

    Back in chapters two and three we made a point of ensuring that all of our JavaBeans implemented the Serializable interface, even though there seemed to be little need for it. In this section, we'll look at why serialization is so important to proper Bean design.

    Our example above took a simple web-service and built an application around that - we passed data as a String, and we got data back as a String. In the real world, things are rarely that 'neat' - often, we need to pass specific objects to and from SOAP based services, and the only way to do that is via a serialization routine - the objects are condensed into a string of bytes and then transmitted to the server or the client, which is then responsible for reassembling them into the appropriate format.

    The SOAP packages we are using provide a number of Serialization classes for some basic (and not so basic) data types:

    Serialization Classes
    Fig 11.2: Serialization Classes

    There is also another Serialization handler class, and this is the BeanSerializer - as long as an object follows the Bean standard (as discussed in chapter two), it can be used as a valid object for SOAP envelopes.

    The SOAP framework we've been using makes use of introspection (remember that from chapter two?) to find out which properties are available on a bean. It then serializes the state of each of these properties, and stores them in an XML tag.

    Sending Beans through the soap protocol involves a degree of additional complexity, since it is not really possible for the classes we have been using so far to automate the mapping of XML elements to class types - we need to do a bit of that ourselves. The SOAP layer we are using makes use of a mapping to make sense of the data in terms of XML.

    In order to set up our JavaBean, we need to manipulate this mapping to add in all the relevant data - for simple JavaBeans (one composed of primitive values), we need to register only one class. For JavaBeans that are composed of any of the supported data types above, we likewise register only one class in the mapping.

    For complex JavaBeans that are constructed from several objects, we need to create a mapping for each object contained within the JavaBean. We won't look at that during this chapter - it's left as an exercise for the interested reader.

    The class that deals with all this is called SOAPMappingRegistry... we create an instance of it in the normal way, with new:

    SOAPMappingRegistry myReg = new SOAPMappingRegistry(); 

    To create our mapping for a JavaBean, we also need an instance of the BeanSerializer:

    BeanSerializer myBeanSerializer = new BeanSerializer(); 

    And then we need to map a specific element name to this serializer. The method in the SOAPMappingRegistry class that handles this is called mapTypes, and it takes five parameters.

    The first parameter is the encoding style - we'll just use the default for this, as above. The second parameter is an instance of the QName class, which is a descriptor for a qualified name (a combination of the namespace and the element name - see chapter four). The third parameter is the class specification of the Bean. The fourth parameter is the serialization handler class that handles the serialization, the fifth is the handler class that handles the deserialization. We'll be using the same one for our Bean.

    So, let's imagine the following simple Bean:

    import java.io.*; 

    public class SimpleBean implements Serializable {
    String message;
    String target;

    public SimpleBean() {
    }


    public void setMessage (String value) {
    message = value;
    }


    public String getMessage() {
    return message;
    }


    public void setTarget (String value) {
    target = value;
    }


    public String getTarget() {
    return target;
    }

    }

    We're not going to connect to an actual service at the moment (because there is no service that makes use of our SimpleBean) - we're just looking at the process. Let's also imagine an XML document that references this bean in an element called mySimpleBean, with a namespace of example. Our mapTypes method call would look like this:

    myReg.mapTypes (Constants.NS_URI_SOAP_ENC, new QName ("example", "mySimpleBean") 
    , mySimpleBean.getClass(), myBeanSerializer, myBeanSerializer);

    Consider for example if we were building an XML envelope for sending to a server, just as we did earlier... we could now create Parameters based on our new mapping and send them in the same way we can send a String:

    SimpleBean example = new SimpleBean(); 
    example.setMessage ("hello");
    example.setTarget ("Michael");
    params.addElement (new Parameter ("name", example.getClass(), example
    , null));

    Before we can actually make use of our new mapping, we need to ensure that our Call object knows that it has been updated - we do this through the setSOAPMappingRegistry method, passing as a parameter our modified registry:

    myCall.setSOAPMappingRegistry (myReg); 

    That's all it takes to encode a custom object, ready for sending it to a waiting SOAP server.

    Well... that's not quite true. We'll look more at this in the last chapter of this book.

    That's the real power of SOAP - the fact that objects can be sent as XML elements. It's not difficult to send a string of text to even a standard server - and it's not difficult to capture that data at the receiving end. In fact, it's quite a lot simpler than setting up a proper SOAP connection... however, standard client/server architectures require formal protocols to be developed, and complex parsing routines to be implemented before they can be more responsive. We'll look more at this idea of complex data types when we start writing our own SOAP servers later in the module.

    11.7

    What's Out There?

    Most publicly available web-services are toys at best - there are only a handful that offer anything approaching real functionality. That's okay though - we're just learning about a powerful framework at the moment. Those web-services that are useful (such as the ones provided by Amazon, Google and EBay) are wrapped up in an API and abstracted away from the 'bare bones' approach we've discussed here. The API downloads themselves will document how they are to be used.

    Although the potential is there for SOAP services to be very powerful, most of the development goes into very specific servers designed to meet very particular (and usually proprietary) purposes... it's not an architecture that solves a lot of your problems for you, although there is no reason that it couldn't be.

    The site mentioned above (www.xmethods.com) has a list of services, most of which are just fun little toys (like the Synonym service). The difficult part is working out how to interface with a particular service - some give detailed instructions, but most don't - instead they provide what is known as a wsdl file (Web Service Definition Language). The wsdl file provides all the information we need to make use of a service.

    We won't talk about how to read or write these files just yet (in fact, we'll never talk about how to write them - just how to generate them), but you should at least know how to understand the analysed WSDL provided on the website, so here's a quick tutorial:

    1. Pick a service, any service. At least, any service that indicates that it is SOAP based. (For example, we'll pick the Barnes and Noble Price Quote service.)
    2. There is a link at the top of the page that says 'Analyze WSDL' - clicking on this will bring up a table of the web service, like so:

    WSDL Analysed
    Fig 11.3: WSDL Analysed

    The first column lists the services available, the second lists the number of operations provided by that service - for our purposes, an operation is pretty much just a method. We don't care about the other columns particularly... clicking on the operations link will bring up a list of methods available in another table:

    Available Methods
    Fig 11.4: Available Methods

    There is a link for input msg and output msg - these indicate the forms of the SOAP envelope that the client sends, and the information contained within the response message respectively. For example, for input msg:

    Message Detail
    Fig 11.5: Message Detail

    The Message is the name of the method that we set on the Call object with setMethodName.

    The Namespace is what we pass as a parameter to setTargetObjectURI.

    The parts table indicates the data that we set up as a parameter - in this case, we would create a valid parameter like so:

    temp = new Parameter ("isbn", String.class, myText.getText(), null); 

    That's all we need to change to make a connection to this particular web-service.

    Clicking on the output msg link show us the following information:

    We can tell from this that what we get back out of the server is a floating point number that indicates the price of the given book - we can parse this out in the same way that we did with the Synonym service above.

    11.8

    Conclusion

    Soap is a very powerful object access protocol that offers a simple and compatible way of sending messages between decentralised elements of functionality. However, the range of services that are generally available are pretty poor, and useful only for demonstration purposes. If we want really useful services, we need to write them ourselves (which of course, we don't yet know how to do).

    An understanding of XML is valuable in making sense of how a SOAP client communicates with a server, but all of the hard work of generating the XML file and parsing the response is done by the packages we've installed for that purpose. Different packages will have different levels of functionality, but the Apache SOAP tools are powerful and open source, meaning that we are not tied into the adoption of a proprietary set of classes.

    We'll return to the subject of SOAP in a later chapter when we discuss how we can write and deploy our own SOAP servers.

    Further Reading

    The following table details further reading on the topic in this chapter, and also any external resources that you may find useful.

    ResourceDescription
    NothingThere's no links as yet, so go find your own you shiftless wasters.

    PreviousTable of ContentsNext

    © 2004-2006 Michael James Heron