SOA adoption is often accompanied by the recognition of the need for a revamped approach for handling cross-cutting concerns such as logging, diagnostics and monitoring, security, error detection and reporting, performance etc. All the shiny new services being developed on the latest middleware stack either implement these revamped standards for cross-cutting concerns or rely on a policy based service bus to implement some of these concerns. It is generally considered a best practice that the business-method / logic should be separated out from the code and configuration that handles common cross cutting concerns.
For Java webservices developed by exposing business functionality written in Java beans via JAX-WS web-services the SOAP message handlers are a useful place to plug-in a lot of common processing associated with service invocations.
This java world article http://www.javaworld.com/javaworld/jw-02-2007/jw-02-handler.html has a very good introduction to this feature.
In this post, I will provide you some sample skeleton code which shows how SOAP message handlers can be used to cache service invocation responses on the server-side and return cached results by-passing the server invocation when available.
Assume we have a Customer getCustomerDetails(Customer customer) method which excepts a customer object with a customerId or name as input and returns a Customer object with the details of the customer fetched from a database. Assume we have a cache named CustomerCache which has static methods isAvailable(String), Customer get(String) and add(String, Customer) which can provide faster access to cached customer data. We now want to add additional caching logic on the server-side to first look for the request customer details in the cache and return it to the client and invoke the service endpoint only if the customer is not in the cache. Also whenever the service endpoint is invoked (customer details not found in cache), the returned objected by the endpoint has to be added to cache for future requests.
To achieve this we implement a logical message handler (javax.xml.ws.handler.LogicalHandler)
We implement the handleMessage method of the logical handler to delegate the request and response processing to utility methods.
In the handleRequest(LogicalMessageContext) method, we check if the requested service result is present in a custom cache. If yes, we set a message property to a key-value of the cache and return false to skip any additional processing of the request. As a result the actual service endpoint is never invoked.
In the handleResponseMessage(LogicalMessageContext) method, we check if the message property with cache key is set by the request handler. If yes, we know that the service endpoint has not been invoked. So we lookup the object from cache and modify the response payload to include this object. However if the message property was not set by the request handler, we read the object from the response message (coming from the endpoint now) and add it to the cache. So that future requests of this objects can be returned from the cache.
Thus using message handlers additional message processing logic can be added to a service and the actual service implementation only deals with the business logic.