Using SOAP service in java play framework application

Using SOAP service in java play framework application

On my previous assignment I was tasked with building a payment system for a large enterprise. Since it was an enterprise there were a bunch of subsystems to be integrated. One of the subsystem was a SOAP server!!..aaargh!!.

First step

There is no straight forward support for SOAP in play. In a servlets based solution supporting SOAP would be much simpler.

Since we were using play framework 2.5.x as our application framework, we need to make all the SOAP calls to be reactive to get maximum performance out of play framework.

To test the SOAP performance we will use a simple hello world SOAP server. This server has a single method which responds to requests. To monitor the performance better responses are delayed by 5 seconds.

Once we run the above application, a simple SOAP server will be available at localhost:3000. We can get the WSDL by navigating to http://127.0.0.1:3000/hw?wsdl from a browser. Or you can use wget.

  wget http://127.0.0.1:3000/hw?wsdl -O simple_soap_server.wsdl 

First we need to generate stub classes from the WSDL files with async support. We used wsimport tool with an addition configuration for generating async methods. Following external binding was used.

<bindings
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
 xmlns="http://java.sun.com/xml/ns/jaxws">
   <bindings node="wsdl:definitions">
     <enableAsyncMapping>true</enableAsyncMapping>
   </bindings>
</bindings>

Save the above binding to async.xml and use below command to generate stub.

wsimport helloworld.wsdl -keep -wsdllocation wsdl -b async.xml

In the sample code you can see this has generated one asynchronous method along with other methods.

 public Future<?> sayHelloAsync()

After the code generation call the methods asynchronously by passing a callback or using lambda functions.

example code

        HelloWorldServerImplService service =  new HelloWorldServerImplService();
    HelloWorldServer server = service.getHelloWorldServerImplPort();
    CompletableFuture<String> future = new CompletableFuture<>();
    server.sayHelloAsync("John",res-> {
        try{
            future.complete(res.get());
        }catch(Exception ex) {
          LOG.error("Exception {}",ex);
        }

    });
    return future.thenApply(res-> {
        return ok(res);
    });
Second step

Now we can start using SOAP services in our application. But there is a problem. Are these SOAP calls truly asynchronous?. We can check that from any jvm monitoring tool. Lets use jvisualvm for now. In the sample code provided, we know we are making SOAP call for every HTTP request. So fire few http requests using ab (apache benchmark)

ab -c 50 -n 1000 localhost:9000/async

In jvisualvm we can see output similar to the following result.
jax-ws soap async thread report

Important points note here are

  1. Live thread count : 682
  2. Thread state : parked (Color orange indicates parked state)

This shows that we are creating one thread per request. And most of the threads are waiting for the SOAP request to complete. This is exactly opposite to what we are trying to achieve. When we invoke SOAP request java internally uses JAX-WS implementation available to JVM. Unfortunately Oracle JRE’s default implementation uses blocking HTTP.

Solution : Use any other JAX-WS implementation which is not blocking in nature. Here we can use Apache Cxf library which has non blocking methods. To use apache cxf in our play application we need to add two dependencies to our build.sbt.

  • cxf-rt-frontend-jaxws will make CXF the JAX-WS implementation in our application

  • cxf-rt-transports-http-hc will allow CXF to use the non-blocking HTTP client

Java will pick apache cxf as its SOAP transport mechanism once its added in the class path. We don't need to make any other code change. Now we can run our apache benchmark against this and monitor the thread result.

apache cxc soap async thread report

Woha!!. As you can see from the screen the number of live threads have come down to 50 and most of the threads are in running(green) state.

What's happening here?

Apache cxf uses http client from Apache HttpComponents which uses event driven I/O model based on Java NIO. That means apache cxc threads, which is making the actual SOAP request over HTTP will not be waiting for the response to come back from server. Once the response is ready from the server, java NIO will invoke the correct thread. This means we will be able to server much larger number of request with minimum threads.

Further improvements

While doing performance testing we faces few more issues with SOAP services

  1. Creating service port for every request
    Since WSDL files are loaded in service constructor, every SOAP request had to wait for the WSDL file to be loaded. We fixed this issue by making the service request global and making sure the WSDL files are loaded only once.

  2. Apache cxf thread pool size was fixed at 50.
    This was fixed by keeping a cxf configuration file (cxf.xml) in conf folder. And keep below parameters.

        <property name="name" value="default" />
        <property name="lowWaterMark" value="200"/>
        <property name="highWaterMark" value="200"/>
        <property name="queueSize" value="512" />          
    

To read cxf.xml configuration file, play framework needs two more dependencies.

    "org.springframework" % "spring-expression" % "4.3.0.RELEASE"
    "org.springframework" % "spring-context" % "4.3.0.RELEASE"

Finally our application is ready for SOAP services. All the SOAP requests are now event driven non-blocking.

Sample code used can be found here