Sunday, April 6, 2008

Web Services

It’s all about web services nowadays. It was very simple to create one in .NET – just run dev studio wizard and off you go. Not so fast in Java. First you should pick a library that takes care of SOAP layer. There are a couple big ones, namely Axis2 and XFire/CXF, both look robust enough.

Axis2 is a complete re-write and in some cases is not compatible with original Axis clients. I still picked Axis2 as it has simple way of wrapping objects into web service calls and doesn’t require tagging them with attributes like CXF. Also, Axis2 allows your object be exposed via REST. Basically, you create a POJO (fancy word for a simple object with setters and getters), update service.xml file to reference this object and you’re done…
Well, there are some caveats:

  • You must have an empty constructor.
  • Do not overload constructors – it usually works but I’ve ran into scenario where objects were not de-serialized properly and wasted a day chasing down the problem: removing overloaded constructors fixed the issue.
  • No helper functions other than setters/getters in your object – or they’ll show up in wsdl and mess things up on the client side.

e.g. my entire web service layer now looks like this:
public class MyWS
{
public MyWSResponse SomeRequest(String myarg)
{
MyWSResponse = MyLogicLayer.getInstance().processSomeRequest(myarg);
}
}

MyWSResponse has only an empty constructor and few setters/getters. Add the following to service.xml and you’re done.

<service name="MyWS" scope="application">
<description>
My SOAP Service
</description>
<messageReceivers>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</messageReceivers>
<parameter name="ServiceClass">com.mycompany.MyWS</parameter>
</service>

Entire logic is encapsulated in the MyLogicLayer – makes a very convenient setup for unittests.

You can run axis2 web service standalone or package it in a war file with other web services or servlets.

Axis2 comes with many jars – I recommend keeping them in a separate folder and package with your code in build time - this way upgrade to newer Axis2 release will be easy.

Finally, you can extend Axis’ AbstractHandler to create a module logging every incoming and outgoing message (which was rather painful to do in .NET). The skeleton code is below:

public InvocationResponse invoke(MessageContext msgContext) throws AxisFault
{
HttpServletRequest request = (HttpServletRequest)msgContext.getProperty("transport.http.servletRequest");
//call request.getHeaderNames() and log headers

//then log message body: msgContext.getEnvelope().toString()

return InvocationResponse.CONTINUE;
}

I also removed axis admin servlet from final deployment – I am a bit paranoid of having a GUI-based access to production environment (or I’ll be tempted to make ad-hoc changes to running system at 3am :-).

No comments: