This page details some of the implementation decisions we made, problems we had to overcome and some gotcha's we found along the way. There is also a page that details some of the interoperability problems we encountered.
We already have a set of services (registered as Spring beans) which expose Java interfaces (ideally suited to local process interaction). On top of those, we're developing a further set Spring beans which provide the remote facade, whose implementation is backed by our current set of services. As such, it should also be possible to expose the same remote facade via other protocols such as:
Other Spring supported exporters such as Hessian & Burlap
Axis and Spring
Out of the box Spring does not provide any integration with Axis i.e. you can not use a Spring configured bean as your web service implementation.
Based on an idea that has been contributed to the Spring framework (and will probably appear in Spring 1.3) we implemented a custom RPC provider class for Axis that will retrieve Spring beans instead of POJOs.
From the contribution above we knew we had to extend Axis's RPCProvider class and override the getServiceClass(), makeNewServiceObject() and getServiceClassNameOptionName() methods. The first two basically need to do the lookup in the Spring context for the bean you specify. The bean is specified in the server-config.wsdd file via a parameter with the name defined in getServiceClassNameOptionName(). We chose to use 'springBean' as the parameter name.
The tricky bit was incorporating this custom provider into the Axis framework as unfortunately there is no documentation on how to do this on the Axis site. The only documentation we found was in the code after a little debugging!
Axis uses another provider class (WSDDProvider) to create an instance of the RPCProvider. We therefore also extended WSDDProvider to create an instance of the new SpringBeanRPCProvider. This class also defines the name of the provider you use in the server-config.wsdd (see example below), we chose 'SpringRPC' as the provider name.
In turn, this class is loaded by Axis at startup using the Java Service Provider mechanism. We added a file called 'org.apache.axis.deployment.wsdd.Provider' (the name of the Axis class that does the loading) to the META-INF/services directory inside our JAR file. This file contains the fully qualified class name of the custom WSDDProvider we created.
To see how it all works take a look at loadPluggableProviders() in org.apache.axis.deployment.wsdd.WSDDProvider.
With all this in place you can now configure a bean in Spring and reference it in your Axis config file. Take a look at the example below:
We obviously need to provide a way for users of our web services to authenticate themselves. We have spent some time looking at the WS-Security spec and it's support across the clients we need to provide access to.
WS-Security is the base for a lot of other WS- specifications and as such provides the framework for securing SOAP messages, for example encrypting and signing the message sent over the wire. For our purposes initially, the UsernameToken profile is all we need.
There is support for Java in the WSS4J project and .NET has support within it's WSE offering. There doesn't seem to be any support for PHP clients currently, so we will generate the appropriate headers manually for now.
The initial authenticate call takes the username and password as normal parameters and returns a ticket that can be used for subsequent calls. As mentioned above the ticket will be passed using the Password field of the UsernameToken SOAP header. The UsernameToken profile says that 'Passwords of type wsseasswordText and wsseasswordDigest are not limited to actual passwords, although this is a common case', so it appears that this is a legal thing to do. This also provides a simple form of Single Sign On (SSO) as a user can obtain a valid ticket by other means and call services without going through the AuthenticationService.
A pattern called 'WSDL First' is generally considered the best approach for developing interoperable web services. We have defined the schemas for all the types we'll use in the web services and are in the process of defining the WSDL for each of the services.
Once the WSDL is defined we use Axis's WSDL2Java tool to generate the classes required to support the service. We then write a class implementing the generated interface for the service and add setters for all the services that need to be injected by Spring. This class is then referenced in web-services-application-context.xml so that it can be referenced by server-config.wsdd using the Axis/Spring integration.