Friday 21 August 2015

SOAP web services without JAXB

What if you wanted to develop a SOAP web service without using JAXB? Now, as a Java programmer, why in the world would you not use JAXB? After all, it makes it so easy to map classes to XML representations.

Maybe I am a bit old-fashioned and you have a boss like me, who insists on the good old stuff. Like the closeness to data in XML handling that I enjoy. Automatic transmission is good but manual transmission using shift stick gives you more fine-grained control over your car acceleration / deceleration.

Or maybe just out of curiosity, you would want to find out how this is to be done. ‘This’ meaning writing a SOAP webservice which processes XML files for input and output without using JAXB. In any case, whether you use it or not, it’s good to know how to do it.


Knowing the sequence of the server-side operations of a SOAP call helps understand the steps better. At the end of this post are two extracts from the SOA-Book [1]. The key phase is “Dispatching” where the SOAP request is handed over to the endpoint class. As part of this process, the server calls Handlers before invoking the endpoints. And handlers is where you can try out your XML processing magic.

Since we don’t want to use JAXB, we write our own handler class that extracts the XML from the SOAP request, parses the XML, populates a business object and puts it in the SOAP context. Within the endpoint class, you execute your business logic and use the data you want to send back as StreamSource object.

Let’s get into the details.

The building blocks to begin with. All the required supporting libraries can be taken from Apache CXF, which “is an open source services framework. CXF helps you build and develop services using frontend programming APIs, like JAX-WS and JAX-RS. These services can speak a variety of protocols such as SOAP, XML/HTTP, RESTful HTTP, or CORBA and work over a variety of transports such as HTTP, JMS or JBI.”[2]

First step is to create the WSDL file that specifies the request XML and the response XML. The customer (your actual client or your business analyst or dev lead or whoever) has given you the XML files. You take those XML files and make the schema definition (.xsd) files as I have described in a previous blog post, one each for the request and response.

Then you complete the wsdl file as follows:
  • Import the two namespaces, one for the request and second for the response from the respective xsd files, and specifying their location.
  • Define the Request message with parameters element as the root element of your request xsd file.
  • Define the Response message with parameters as the root element of your response xsd file.
  • Define a portType for operation invoke with input message as tns:Request and output message as tns:Response.
  • For the above portType, define a binding.
  • Define you service that combines the portType and binding.

Here is my sample wsdl:
Next step is to specify your handler chain. This is an xml file, let’s say mySoap_handler.xml, in which you configure your handler name and handler class.

Here is my sample mySoap_handler.xml
We now look at how to write the Handler.
1. Write the handler class, say mySoapHandler, that implements SOAPHandler.
2. Implement the method handleMessage(SOAPMessageContext). In this method --
  • Extract SOAP Body and convert to jdom Document.
  • Convert w3c document to jdom Document.
  • Parse the XML in the Document and build your business object. Use XPath with JDOM2 as I have outlined in my previous post.
  • Put the business object into SOAPMessageContext object, say context.
  • In context, set the scope of the business object to be MessageContext.Scope.APPLICATION.
  • Leave the methods handleFault, close, getHeaders as empty methods.

Here’s my sample handler class
Note that the location of mySoap_handler.xml file is in the same package structure as the mySoapHandler class but in the resources directory of your Maven webapp.

Finally, let’s see how to implement the endpoint class.
1. Your endpoint class, say CustomerEndpoint, has to implement Provider interface.
2. You have to annotate this class as
  • ServiceMode as PAYLOAD.
  • HandlerChain file as mySoap_handler.xml
  • WebServiceProvider with wsdl file location and target namespace, service name and port name same as those configured in the wsdl.
3. Inject WebServiceContext.
4. Implement the invoke(Source) method. In this method --
  • Get the WrappedMessageContext object from WebServiceContext.
  • From WrappedMessageContext object, get the WrappedMessage.
  • From WrappedMessage, get your business object.
  • Perform your business logic.
  • Create a response Document, using the DOM objects DocumentBuilderFactory and DocumentBuilder. You would use Element and Text objects to do this.
  • Using a Transformer object, transform the Document to ByteArrayOutputStream object, say baos.
  • Get string from baos, create a StringReader object from it.
  • Use StringReader object to create a StreamSource object.
  • Return StreamSource object.

Here’s my sample endpoint class
The example given so far shows the usage of Handler chain and processing XML from a SOAP request message. The handler class parses the XML, populates a business object and sends the business object to the service endpoint class.

Though this approach looks good because it separates the concerns into two classes, we may consider this is an overhead. In fact, we can do all the XML handling and business logic execution in the endpoint class itself. Which means we don't need mySoap_handler.xml and mySoapHandler.java at all. The steps would be:
  • Get XML from request as a String, using Transformer and StringWriter classes.
  • Convert XML string to w3c Document using DocumentBuilder’s parse method with an InputSource object.
  • Convert w3c Document to jdom2 Document using DOMBuilder’s build method.
  • Parse Document and execute business logic.
  • Return response as shown in the previous code example.

With the combined code, this is how the endpoint class looks like:
You may question why we need the overhead of Document, XPath and JDOM2, when we can directly process the XML string. Yes, we can. You may try it in your own project. For me, XPath with JDOM2 gives an elegant and concise way of XML processing, so I went with it.

References
[1]. SOA Using Java Web Services by Mark D. Hansen. ISBN 978-81-317-2296-1, Pearson Education, Inc., published by Dorling Kindersley (India) Pvt. Ltd.
[2] http://cxf.apache.org/


Extracts from [1]
A. JAX-WS Server-Side Architecture
1. The client starts by getting the WSDL for the Web service that has been deployed. WSEE requires that a JAX-WS provider support URL publication. In the examples supplied in this chapter, I use the WSDL that GlassFish publishes to the URL of the form http://?wsdl. Publishing the WSDL at a URL of that form is a common convention across Web Services providers, but is not mandated by any standard.
2. Based on the WSDL, the client composes a SOAP request and does an HTTP POST to the URL specified by the soap:address’s location attribute.
3. The HTTP request containing the SOAP message is received by the Endpoint Listener. This listener is a servlet. The process by which the listener servlet is deployed or registered varies by Java EE container and even by type of deployment. The listener servlet passes the HTTP request along to the Dispatcher. The Dispatcher may be implemented by a separate class from the Endpoint Listener, or the two may be combined, but the functionality is logically distinct. The Dispatcher’s job is to look up the correct Web service endpoint implementation and dispatch the HTTP request to that endpoint.
4. At this stage, the request processing transitions to the JAX-WS run-time system. Along with the request, the JAX-WS has received from the Dispatcher a description of the correct endpoint. A javax.xml.ws.handler.MessageContext is built from the contents of the HTTP request. In this case (since we are talking about SOAP), the message context is an instance of javax.xml.ws.handler.SOAPMessageContext and contains the SOAP request as a SAAJ SOAPMessage. This SOAPMessageContext is processed by the SOAP protocol binding before the actual Web service endpoint is invoked. The SOAP protocol binding is an example of JAX-WS protocol binding. The primary responsibilities of such a JAX-WS protocol binding are to extract the message context from the transport protocol (e.g., SOAP/HTTP or XML/HTTP); process the message context through the handlers that have been configured for the Web service endpoint; and configure the result (either a response or an exception) to be sent back to the client using the appropriate transport. In this case, a SOAP protocol binding has the additional task of doing the mustUnderstand processing required by SOAP. If there are any “must understand” headers that are not understood, either a SOAP fault is dispatched or (if the endpoint is deployed as a one-way service) processing stops.
5. Next, the SOAP protocol binding invokes each handler in its associated handler chain. The handlers associated with the endpoint are defined by a deployment descriptor file that is specified by the @HandlerChain annotation on the service implementation bean. Handlers provide developers with the capability of preprocessing a message context before the endpoint gets invoked. Examples of the the types of processing typically done by server-side handlers include persisting a message to provide recovery in the event of a server crash; encryption/decryption; sequencing (i.e., examining message header sequence IDs to ensure that messages are delivered in order); and so on. SOAP header processing is usually done by handlers, but the JAX-WS framework provides handlers with access to the SOAP body as well.
6. After the inbound handlers are finished, the SOAP message is unmarshalled into instances of the Java objects that are used to invoke the endpoint method. This unmarshalling process is governed by the JAX-WS WSDL to Java mapping and the JAXB 2.0 XML to Java mapping. The WSDL to Java mapping determines (from the wsdl:operation) which endpoint method to invoke based on the structure of the SOAP message. And the JAXB runtime serializes the SOAP message into the parameters required to invoke that method. If the deployed service implementation bean is an implementation bean of javax.xml.ws.Dispatch, this process is much simpler. In that case, the message payload is simply passed to the Dispatch.invoke() method and the implementation processes the XML directly.
7. The last step of the inbound request processing is the invocation of the appropriate method on the deployed service implementation bean. After invocation, the process is reversed. The return value from the invocation (along with any parameters that have been declared OUT or IN/OUT) is marshaled to a SOAP response message of the appropriate form based on the JAX-WS WSDL to Java mapping and the JAXB 2.0 XML to Java mapping.
8. The outbound response processing invokes the handlers (in reverse order) again. If at any point, during inbound handler processing, endpoint invocation, or outbound handler processing, an unhandled exception is thrown, the SOAP Fault Processing component maps the exception to a SOAP fault message. In either case, SOAP fault or SOAP response message, the SOAP protocol binding formats the result for the appropriate transport (e.g., SOAP/HTTP).
9. Lastly, the Endpoint listener servlet completes its processing and sends back the result received from the Dispatcher as an HTTP response to the client.

B. Dispatching
Dispatching is the mechanism by which a SOAP request message is dispatched to the appropriate Java implementation for execution. It is the heart of the invocation subsystem for a Web Services Platform Architecture.

Summary of the Dispatching Process
The dispatching process involves the following steps.

1. Identify the SOAP message’s target soap:address
  The endpoint listener gets the target URL of the SOAP message from the underlying HTTP transmission. From a Web services point of view, this is “cheating.” But, there is really no alternative unless you use WS-Addressing. This URL is matched against the available WSDLs that have been deployed by the deployment subsystem. Matching against the soap:address enables the wsdl:port to be identified.
2. From the soap:address, get the wsdl:port and wsdl:portType.
  The relationships in the WSDL are traced to get from the wsdl:port, through the wsdl:binding, to the wsdl:portType.
3. Identify its target wsdl:operation.
  The invocation subsystem looks at the wrapper element from the SOAP message and thereby identifies the wsdl:operation of the same name within the wsdl:portType identified in step 2.
4. Look up the associated Java class and method.
  The deployment subsystem, which maintains a mapping table (of some kind) that correlates WSDL with Java, looks up the Java class and method from the wsdl:operation -- based on this correlation.

2 comments: