miércoles 23 de julio de 2008

Invoking Web Services via Spring

SOA SOA SOA...SOA everywhere. Right now it's difficult to work on an enterprise project and don't have the need to connect to a WebService. What would we be without these buzzwords...?

Unfortunately, for us developers that is, WebServices is not the easiest of technologies (understandable as the problem it solves is also complex) and Java's approach has always been guilty of not been the most intuitive. But the need is there so it has to be solved.

To call a Web Service there are several possibilities at the framework level. The first is to use JAX-RPC but this approach is in disuse today (for uber complexity). If we stick to the standard the next API would be JAX-WS. This is a much more polished API based heavily on annotations. JAX-WS is now part of the standard Java Standard Edition. Finally there are other alternatives in the Open Source scene. And better, Spring, in some form or another, supports all of them (Groovy also helps but not everybody has access to it).

It's important to notice that a developer has two ways to connect to a WS (I'll start talking about JAX-WS from now on), creating a SEI (Service Endpoint Interface) proxy or creating and sending a SOAP message. The first case allows the programmer to create a Java interface (mainly because actually it has to be annotated) and forget about the stub. The second (more powerful) alternative creates an XML structure from scratch. Spring facilitates the work with proxies. The later case requires the Spring Web Services subproject.

So how does Spring help? In two ways, it dynamically creates the proxy and injects it to the business beans seamlessly. For the most advanced use Spring, as of now, still requires the Java interface to be provided. All in all, it's not a big deal as there's a tool (wsimport) to generate it given a WSDL. Any good IDE will do it as well. Actually, wsimport generates more than one Java source because the parameters probably require a JAXB (read Java to XML) transformation.

I haven't talked about registries yet. JAXR is the Java API to connect to WSDL containers. They basically provide a level of indirection to a WSDL. JAXR is complicated (to some extent) and if possible I like the REST API better (but not all registries provide it!). IBM Service Registry does, for example (so you can just use Dom4J with some security). Anyway, JaxWsPortProxyFactoryBean (with minor tweaks) supports this kind of access.

That should be all for the gross of the situations. Recall that JAX-WS supports WS-I Basic Profile, MTOM and other such advanced protocols by default. That's not true for all of them though! For example, it requires some work to allow for message level security. This is overcome by the use of LogicalHandlers or SOAPHandlers (you can mix and match). In Spring or AOP jargon they would be called interceptors. They allow to modify the message prior to being wired. Another common and interesting use, for example, is logging. Spring allows handler injection grouped as HandlerResolvers.

So here's the final code:

<bean id="loggingHandler" class="mypkg.LoggingHandler" />

<bean id="defaultHandlerResolver" class="mypkg.ConfiguredHandlerResolver">
   <property name="handlers">
      <list>
         <ref bean="loggingHandler" />
      </list>
   </property>
</bean>

<bean id="wsdlRetriever" abstract="true" class="...JaxWsPortProxyFactoryBean">
   <property name="lookupServiceOnStartup" value="false" />
   <property name="handlerResolver" ref="defaultHandlerResolver" />
</bean>

<bean id="aSample" parent="wsdlRetriever">
   <property name="serviceInterface" value="..Sample" />
   <property name="wsdlDocumentUrl" value="http://...Sample?wsdl" />
   <property name="serviceName" value="Sample" />
   <property name="portName" value="SampleSOAP" />
   <property name="namespaceUri" value="..." />
</bean>

Get the logging handler source code along a good explanation from here.

2 comentarios:

Christopher Keene dijo...

This is a good writeup, but no matter how you slice it, it is pretty complicated to get started on web services with Jax-WS. One way to simplify the process is to use a visual builder tool for Spring like Wavemaker, then just look at the generated WAR file to get all the Java, Jax-WS and Spring config code done for you automatically. Once you have this you can import it into Eclipse and away you go!

Jose Noheda dijo...

Thanks for the tip. I didn't know about Wavemaker and seems interesting. I agree, JAX-WS is complicated. I find WS in general quite convoluted, a lot of specifications incompatible between them made by several different comitees..IMHO we still need a couple of years till this is mature enough