![]() | Monkeys at Keyboards: Java-Fu © Michael James Heron | ||||
| Topic: Java Programming Level: 3 Version: beta | |||||
12 - Case Study 4 - SOAP Client | |||||
| Previous | Table of Contents | Next |
| Forum |
| Chapter Objectives |
By the end of this chapter, the reader will be able to: |
In the past two chapters we've had an opportunity to look at the SOAP protocol and how we can build connections to web-services, and we've also looked at a few useful design patterns, including the one most central to the idea of components - the facade. The problem with the SOAP architecture is that it's somewhat unwieldy to use - there are three new classes (the Call, the Response, and the Parameter), as well as any number of methods that must be called before we can possibly make a connection to a service and get a response back. SOAP is a component architecture, but the classes that we require in order to make use of it are not components in themselves. It would be very helpful if we had a component that abstracted all of this messy functionality away... in technical terms, we could benefit greatly from providing a facade that sits between any application we write and the SOAP classes and method calls required. That's exactly what we're going to do in this case study - build a component that allows for SOAP access with minimal fuss. We're going to build this component as a Javabean, but it's not going to be a graphical component - there's really very little to be gained by giving this a user interface since it's purely functional with no presentation at all.
First of all, we need the traditional Javabean structure - a class that implements Serializable, and a default constructor method. We have no properties yet, so we won't worry about those for now
Notice this Javabean does not extend JPanel - for our GUI Javabeans we've always made sure that they do, but this is a Javabean that contains only functionality. There's no way we can know in advance what service people are going to access, and how they're going to want return values presented... so we don't concern ourselves with that. Our Javabean is going to be a facade... it will abstract away the relationship between all the SOAP classes, and provide a mechanism for connecting to a web service via a neat, self-contained component:
As users of a component, we don't care how it works internally - the idea of the facade is that we don't need to know about the Call, or the Parameter, or the Response object. All we need to know is the data that is pertinent to our web-service... where it is, how to access it, what the methods we want to call are, the parameters we need to send, and what we get back out of it. As much as is possible, our facade should hide the rest of the implementation detail.
We need to think about what properties we are going to need to implement this functionality - these are the elements of data that are changeable from context to context. At the base, we need to be able to configure the following:
An argument could be made that we should also allow for the encoding style to be exposed as a property, but we're not going to in this case study - it's a trivial exercise if anyone wants to attempt it as an extra exercise, however. We expose our properties to flesh out the bones of our Javabean:
It's not especially good for our Facade if, in order to set up a parameter list, people need to pass Vectors full of Parameter objects... so we'll also provide a utility method, addParam, that allows developers to add parameters one at a time, without worrying about wrapping them in a Parameter:
Obviously, to get access to the Parameter object we need to import all the Usual Suspect packages... to summarise, they are:
Our addParam method is useful - this will be the primary method people use to populate a list of parameters. One might be tempted to hide the get and set methods for params since they do not aid in the development of a Facade - alas, this is a bad idea because it would leave substantial aspects of the state of a Javabean unexposed and unavailable to introspection. We'll see why this is a bad thing when we talk about serialization of Javabeans in chapter fifteen.
Once we have all of our properties, we need to write a method that makes an appropriate connection. We already know how to do this from our discussions on SOAP clients... the only difference is we won't be hard-coding parameter values. Instead we'll use our property accessors to set the values of the Call object:
Now our Javabean can be configured to connect to whatever service we wish - neato. We do however need to make a decision here about how we're going to handle the return value of our Call. The easiest way to do this is to store the Response object as an internal attribute, but not provide property accessor methods for it. Instead, we provide an accessor for an abstract property called ReturnValue - this property will actually parse out the return value from the Response object and send it back to the calling application as an Object:
Now we have the opportunity to setup a simple SOAP connection via this component. Let's try with a very simple web service - one that simply returns a string containing "Hello World". This is the first web service we'll write in chapter fifteen, but it also comes in handy here:
That's our container application - our SoapBean is the facade, and it does all the hard work of setting up the connections between all the various classes required to setup a SOAP request. Our container application simply makes use of this SoapBean, and sets the appropriate values - without needing to know anything of how it works, it makes the connection and gives us out the response value - in this case, a string that says "Hello World". Neat! We can test out our parameter code as well by making a call to a second method on this web service - a method called sumNumbers. This method takes an array of integers as a parameter, and returns the sum of all those numbers. The code for this is trivial (within the service):
By changing only a few lines in our container application, we can make use of this method with ease:
Stunningly easy, isn't it? As a developer with absolutely no knowledge as to how Java works SOAP requests internally, we can still use a web service if we know some simple facts about it. Pretty nifty! At the moment however, if someone wants to make multiple requests to a server using the same SoapBean, things get a little iffy. We should easily be able to do something like this:
The problem is that this won't work - it won't work because our parameter list still contains the array of integers. We could clear it by using setParams and passing in an empty Vector, but that seems inelegant. Our facade should be opaque - we shouldn't have to resort to resetting the state of properties ourselves. Instead, we change the setMethodName property in our SoapBean so that whenever the method name changes, the parameter list is emptied:
And with that, our simple SOAP component can be used to make multiple calls to different methods. We should also provide a clearParams method that does a similar thing - just in case people want to make repeated calls to the same method (but use different parameters):
Not bad... not bad at all! If we're serious about this component (and we should be, as it makes things so much easier when connecting to web services), then we should make sure it's more than just an exemplar - it should provide all of the functionality that we're likely to need when developing SOAP applications. We're missing one critical thing at the moment - support for Javabeans. We'll see a web service later in this book that uses a Javabean we write - we don't need to worry about how the server does this, we've already seen what we must do at the client end. We create an instance of SOAPMappingRegistry and an instance of BeanSerializer. We register a relationship between a bean and an XML element via the mapTypes method of the SOAPMappingRegistry, and then we set it on the Call object via the setSOAPMappingRegistry method. Our SoapBean should support this too, and it's fairly easy to do. We maintain an internal SOAPMappingRegistry object (and provide accessor methods for it), and we provide a simplified method that takes only a Javabean and an XML element name as a pair of parameters. It then does the work of setting up the mapping between the two:
We also need to change the makeConnection method a little bit:
Now we can add a bean mapping by calling the registerBean method on our SoapBean - again, hiding all of the details of the SOAPMappingRegistry from the poor, innocent developers. The only thing left to do now is deal with error handling. Here we'll get a chance to fix one of the problems with the way SOAP deals with errors - rather than simply returning a string of the error, we'll actually throw a proper exception. In this way, the benefits of exception handling can be bundled into our facade. First, let's create a suitable exception... we'll call it SOAPAccessException. We don't even need to provide any code to go with it... we just want the name: public class SOAPAccessException extends Exception {} Whenever our SoapBean encounters a fault when making an exception, it'll throw one of these babies... the default Exception class has a constructor that accepts a single String parameter - that parameter becomes the error message for the exception. In our makeConnection method, if we encounter a generated fault, we'll throw an exception for the container application to deal with:
If we were feeling really generous, we could manually parse out the string based error message obtained from getFaultString(), and throw different exceptions depending on what the error was:
Once again, it is us as developers who are, within our facade, abstracting away the implementation details in favour of a single, consistent set of interfaces and methods that arrive as one single footprint (the essence of a good component). The user of our Javabean need never know that SOAP doesn't generate exceptions at the server side - instead it transmits back a string based error code. The user need never know this because we have hidden these details and transformed this non-standard approach into something that should be familiar to all Java users.
In our chapter on Web Services, we look at a service that transmits a simple Javabean (a Javabean of type Quote) to a client application. Our component can step into the shoes of a custom written client application to make a connection to this service (called MySecondWebService) . The server returns one of a number of random quotes... the method we use for this is called getRandomQuote. The namespace of the XML element passed through the SOAP envelope in this case is http://SecondWebService/, and the proper name is Quote. We make use of our registerBean method to setup this mapping:
Two extra lines of code is all we need for our component to setup a Javabean transmission - this should demonstrate that design patterns help you to develop code that is much more than the sum of its parts.
Our voyage into the depths of SOAP transmission is very useful from an academic perspective - it gives us a real chance to understand what happens when we make a connection to a web service. That very understanding is what allows us to develop a useful component like this - the fact we learned the hard way. The problem with using a component is that if Developer X comes along and says 'Wow, I wish I knew how to use a web service', and we come along and say 'You don't need to! Here's a component that does it for you!', then Developer X will have no incentive to learn what's going on under the hood. That's not a problem if our component is flexible enough for Developer X to use... but what happens if X needs a little extra functionality? Our Javabean has no support for custom serializers... it could be added, but it's doubtful that anyone who knows how to write a custom serializer will need (or want) our component in the first place - their specific demands are beyond what our generic Javabean can provide. The other side of the coin - what happens if our component is no longer available? Perhaps X has moved to another programming language, or perhaps X is in an environment where custom components cannot be deployed - what if X thinks that our component is actually SOAP itself, and then goes around saying at job interviews that SOAP is a doddle. What if we start charging for our component? The facade hides the complexity - this can be a very good thing. However, learning to use a facade is not the same as learning to use the underlying tools. Many people find it difficult to write Java on a text editor after having learned how to use an IDE like JBuilder - the tool obscures the underlying understanding. It would have been easy for me to provide you with this component when we started looking at SOAP, and instead of learning the underlying framework we could have just danced off through the publicly available services swapping functionality into and out of our programs. The subtleties of web services would have been lost, and it would have made the understanding of writing SOAP services much more complicated since the relationship between cause and effect is obscured. Developing a facade requires understanding of the underlying architecture. Using a facade requires knowledge only of the facade. It is important to realise there is a tradeoff - what you gain in ease of use, you lose in flexibility. What you gain in the speed of learning, you lose in the low-level understanding. My advice to anyone is to avoid using these kind of abstractions until you understand how the underlying mechanics work. By all means, use them to speed up your development, but don't let them seduce you with their beguiling simplicity.
We've never seen a need for this yet, but the invoke method of Call has two parameters... we've just never used the second one. However, in order to maintain compatability between a .NET web-service and a Java web-service, we need to provide a way to set this second parameter. Don't worry about why... not yet. We'll see why we need to use this second parameter in the last chapter of the module book when we build a cross-platform web-service chat system:
We also need to change our makeConnection method a little bit to take into account this new property:
Now we'll be able to use this component to connect to both Java and .NET services. We'll see this in action in chapter fifteen.
The development of a SOAP client is an ideal example of how complex the relationship between underlying classes can be. The correct interrelationship of these classes is what complicates the connection of a client to a service. It's also an ideal place to employ a facade to manage this interrelationship... SOAP is a component architecture, but the tools we make use of in this module are not components... they're more like lego bricks. What we do within the facade is take these lego bricks and build an actual construct... nobody needs to know how we put the bricks together. The facade here is much more than the sum of its parts - it doesn't just cut down on the syntax, it actually manages the whole experience of connecting to a service. It also adds in consistency with the rest of the Java API by ensuring errors are dealt with by exceptions rather than strings of text. An instance of this bean can be used to easily build an application that involves multiple method calls to a service. Many services are provided as a set of related methods. Creating a client to access one of these services requires careful thought... our facade provides a way to slot a 'good' structure for this into any application that may require it. Further ReadingThe following table details further reading on the topic in this chapter, and also any external resources that you may find useful.
|
| Previous | Table of Contents | Next |
© 2004-2006 Michael James Heron