I have recently been investigating the Spring Framework and turned my attention towards Spring's offerings in the field of web services. I have had several previous encounters with web services and have even tried hand coding SOAP messages (an experience not to be recommended). The Jakarta IO-taglib is very useful for knocking up simple stuff (like XML-RPC pings) and Apache Axis has come a long way since Apache SOAP in making things much simpler especially with its JWS instant web services. As you would expect from Spring there is also a nice way to create web services (ServletEndpointSupport) but as the Axis method is so simple already it probably doesn't really add that much to this area.
Almost paradoxically it seems to me that since Axis introduced JWS it has always been much easier to produce web services than it is to consume them. The real effort is needed on the client side rather than at the server. Granted Axis has some great tools like WSDL2Java that can generate most of the Java I need to talk to a web service but despite peoples best efforts to persuade me that this is all quite simple, once I've actually looked at the source code produced it makes me recoil in horror. SoapBindings, Stubs, Skeletons and ServiceLocators, yuck! I know my horror is based on a fundamental lack of understanding but I don't really want to be bothered dealing with stuff like that, I'd rather things like that were done by someone or something else. WSDL2Java wouldn't exist at all if this stuff was easy to produce now would it?
Enter Spring...
It isn't the simplest thing in the world to consume web services with Spring but IMHO it is much easier than wiring the WSDL2Java generated Axis code together.
Now let us look a example, I am using the Google Web API which gives me enough functionality to play with (simple and more advanced) and hopefully there are enough examples of its use already out there for you to make a good comparison. It has been my experience that although Spring seems to provide tools for many diverse areas it can be quite difficult to find good explanatory documentation with examples, such was my finding in the area of web services (I know I should go out and buy Spring in Action). Granted there is the Spring Documentation chapter Chapter 16. Remoting and web services using Spring but initially I found that I had to resort to finding an example on a blog. The blog example I found first was on Petrik de Heus's blog. I downloaded the code, added the necessary libraries and an Ant build script and dug out my Google Web API key and got it working. However, I couldn't understand the example, the code went to great effort to create a GoogleMessageBean and register this bean in some mapping gizmo but at no point did I actually see the bean being used, I found the example quite confusing but at least I was now armed with the right class name (JaxRpcPortProxyFactoryBean). Petrik's example all seemed a little too magic and although Spring does magic sometimes, I felt that something was not right about this. I found my answer after a little Googling around, browsing the Spring's API docs and especially in Bill Siggelkow's Weblog and ultimately I found a great example using Babelfish from the Spring in Action source code (available for free from the Manning site, I know I'm a cheapskate for downloading the source code for a book I haven't yet bought but as they say Talent Borrows, Genius Steals). For something as simple as the Google Spelling Suggestion that returns a String it turns out that you do not need even need to extend JaxRpcPortProxyFactoryBean, you just need to create port and service interfaces (and even the service interface is optional).
applicationContext.xml
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="googleService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean"> <property name="serviceFactoryClass"> <value>org.apache.axis.client.ServiceFactory</value> </property> <property name="wsdlDocumentUrl"> <value>http://api.google.com/GoogleSearch.wsdl</value> </property> <property name="namespaceUri"> <value>urn:GoogleSearch</value> </property> <property name="serviceName"> <value>GoogleSearchService</value> </property> <property name="portName"> <value>GoogleSearchPort</value> </property> <property name="portInterface"> <value>spellsuggest.SpellingSuggestRemote</value> </property> <property name="serviceInterface"> <value>spellsuggest.SpellingSuggestService</value> </property> </bean> </beans>
SpellingSuggestRemote interface
package spellsuggest; import java.rmi.Remote; import java.rmi.RemoteException; public interface SpellingSuggestRemote extends Remote { public String doSpellingSuggestion(String key, String input) throws RemoteException; /* The following methods are here but are not to be used. * They only here in order to match the endpoint methods in the WSDL. */ public String doGoogleSearch() throws RemoteException; public String doGetCachedPage() throws RemoteException; }
SpellingSuggestService interface
package spellsuggest; public interface SpellingSuggestService { public String doSpellingSuggestion(String key, String input); }
Test class
package spellsuggest; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { private static ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); private static SpellingSuggestService service = (SpellingSuggestService) context.getBean("googleService"); public static void main(String[] args) { String key = "<Insert Google Web API key here>"; String test = "webservie"; System.out.println(service.doSpellingSuggestion(key,test)); } }
...and that is all there is to it. In my next blog entry I will explain how to access everything that the Google Web API has to offer in a Spring enabled way.
In short, I'm not sure Anand. I ran the above code again this morning and it appears to be still working (although I did see a few Bad Gateway 502 errors but these pass). 502 errors are more to do with the Google web service failing to handle the demand placed on upon it than it is with any error with my coding (granted if my code was intended for a production system I'd need to add exception handling to cope with 502 errors).
The above code can be executed from the command line - it doesn't need Tomcat. If I were you, I'd first check to see if you can run it standalone before putting it onto a Tomcat server.
Also something else to check would be your library versions, I got the above code to work using Axis 1.3 and Spring 1.2.6. There may be minor changes needed for it to work with other library versions (I don't actually know).
Note also that the above code only exercises the spelling suggest part of the Google API. For details of how to do a full Google search see Part II:Using Spring to consume web services; advanced example using Google Web APIs doGoogleSearch.
I have a second blog entry which talks about using Axis to generate the beans necessary to consume complex types. I hope this could be useful to you.
The above example uses a ClassPathXmlApplicationContext. This means that the applicationContext.xml should be located somewhere in your classpath. The above example expects the file to be in the root of the classpath but if you want to put it inside a package you have to prefix the filename with the package directory. e.g. "com/somecompany/applicationContext.xml".
HTH
MarkI am pleased that people still find this tutorial useful even when Google SOAP search API has long since closed its doors to the public. Perhaps as developers we should learn from this that you cannot always rely on free services!
Mark





