Sunday, April 28, 2013

Using Apache CXF SOAP service with customized JiBX databinding

This post gives a brief overview on how to build an Apache CXF SOAP service with JiBX databinding.


Apache CXF is an open source services framework that helps building JAX-WS and JAX-RS web services. JiBX is a XML binding tool for Java. To understand the post it is necessary to be familiar with Apache CXF and JiBX.

Apache CXF uses JAXB as its default XML databinding. Thus this combination works out off the box and is well documented. Although JiBX is also supported it seems to be a bit neglected. The support is not even mentioned in the CXF databinding documentation (to get it there an improvement request was opened CXF-4801).

To demonstrate the usage of Apache CXF with JiBX  I built a sample application (simple calculator service). It is accessible on github (cxf-soap-with-jibx) and shows how to build a wsdl first webservice using maven, the cxf-codegen-plugin and the jibx-maven-plugin.

The sample application offers a simple SOAP calculator service with the following definitions

 calculator.wsdl    service definition
                    targetNamespace="http://calculator.sample.frvabe.de/ws"
 calculator.xsd     business objects
                    targetNamespace="http://calculator.sample.frvabe.de/types"

I will not paste the file content here as it will make the post unnecessarily long. Instead I will explain the most recent parts of the maven pom.xml that are necessary to build the project.

First of all we use the cxf-codegen-plugin to generate the SOAP service classes. I used it that way:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- generate service code from wsdl (skip code generation from schema) -->
<!-- 
  This process will generate no 'de.frvabe.sample.calculator.types'
  source classes but the full JiBX binding definition file here:
  ${project.build.directory}/generated-sources/jibx_bindings/calculator.xml
  This file will be ignored by the further build process!
-->
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
  <execution>
 <phase>generate-sources</phase>
 <configuration>
   <sourceRoot>${project.build.directory}/generated-sources</sourceRoot>
   <wsdlOptions>
  <wsdlOption>
    <wsdl>${project.basedir}/src/main/resources/wsdl/calculator.wsdl</wsdl>
    <dataBinding>jibx</dataBinding>
    <extraargs>
   <extraarg>-nexclude</extraarg>
   <extraarg>http://calculator.sample.frvabe.de/types</extraarg>
    </extraargs>
  </wsdlOption>
   </wsdlOptions>
 </configuration>
 <goals>
   <goal>wsdl2java</goal>
 </goals>
  </execution>
</executions>
</plugin>

This will only generate the source code for the service classes. The usage of JiBX is specified in the <dataBinding> element. The cxf-codegen-plugin normally would also generate the classes for the business objects (specified in the calculator.xsd) but it does not allow to pass any specific options to the JiBX code generation process. But that should be done because the business object classes should implement the java.io.Serializable interface. Therefore we need to extend the code generation by using a class-decorator. The customization file custom-jibx-codegen.xml which defines the usage of the class-decorator for all classes of the http://calculator.sample.frvabe.de/types namespace looks like this:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<schema-set xmlns:xs="http://calculator.sample.frvabe.de/types">
    <class-decorator class="org.jibx.schema.codegen.extend.SerializableDecorator"
                     serial-version="1" />
</schema-set>

Now the jibx-maven-plugin is used to generate the business object classes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!-- generate classes from schema and run the binding compiler -->
<plugin>
  <groupId>org.jibx</groupId>
  <artifactId>jibx-maven-plugin</artifactId>
  <version>${jibx.version}</version>
  <executions>
    <!-- generate code from schema using JiBX with customization -->
    <!-- 
        This process will generate the 'de.frvabe.sample.calculator.types'
        source classes and the JiBX binding definition file at
        the default location: ${project.build.directory}/generated-sources/binding.xml
    -->
    <execution>
      <id>generate-java-code-from-schema</id>
      <phase>generate-sources</phase>
      <goals>
        <goal>schema-codegen</goal>
      </goals>
      <configuration>
        <schemaLocation>${project.basedir}/src/main/resources/wsdl</schemaLocation>
        <customizations>
          <customization>${project.basedir}/src/main/config/custom-jibx-codegen.xml</customization>
        </customizations>
      </configuration>
    </execution>
    <!-- run the JiBX binding compiler -->
    <!-- The binding.xml file at the default location will be used here. -->
    <execution>
      <id>compile-binding</id>
      <goals>
        <goal>bind</goal>
      </goals>
      <configuration>
        <schemaBindingDirectory>${project.build.directory}/generated-sources</schemaBindingDirectory>
        <includeSchemaBindings>
          <includeSchemaBinding>binding.xml</includeSchemaBinding>
        </includeSchemaBindings>
      </configuration>
    </execution>
  </executions>
</plugin>

The schema-codegen goal of this plugin definition will generate the code with the defined customization. The compile-binding goal will compile the generated binding definition into the Java classes (what makes the JiBX XML mapping so fast).

And that's all what has to be done.

In the example Spring is used to start the service with an embedded jetty. The application context xml file is short. Notice that JiBX is also declared as databinding here:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://cxf.apache.org/jaxws 
        http://cxf.apache.org/schemas/jaxws.xsd">

  <import resource="classpath:META-INF/cxf/cxf.xml" />

  <bean id="calculator" class="de.frvabe.sample.calculator.ws.CalculatorImpl" />

  <jaxws:endpoint xmlns:tns="http://calculator.sample.frvabe.de/ws"
                    id="calculatorEndpoint" implementor="#calculator"
                    wsdlLocation="classpath:wsdl/calculator.wsdl" endpointName="tns:calculatorPort"
                    serviceName="tns:calculatorService"
                    address="http://0.0.0.0:8080/calculator">
    <jaxws:dataBinding>
      <bean class="org.apache.cxf.jibx.JibxDataBinding" />
    </jaxws:dataBinding>
  </jaxws:endpoint>

</beans>

After starting the service you can use e.g. SoapUI to test it with some simple requests, like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://calculator.sample.frvabe.de/types">
 <soapenv:Header/>
 <soapenv:Body>
  <typ:term>
   <part>
    <value>1</value>
   </part>
   <part>
    <operator>ADD</operator>
    <value>2</value>
   </part>
   <part>
    <operator>MULTIPLY</operator>
    <value>3</value>
   </part>
   <part>
    <operator>DIVIDE</operator>
    <value>4</value>
   </part>
   <part>
    <operator>SUBTRACT</operator>
    <value>5</value>
   </part>
  </typ:term>
 </soapenv:Body>
</soapenv:Envelope>

The result is something like this (please apologize that it ignores most mathematical rules):

1
2
3
4
5
6
7
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 <soap:Body>
  <types:result xmlns:types="http://calculator.sample.frvabe.de/types">
   <value>-2.75</value>
  </types:result>
 </soap:Body>
</soap:Envelope>

Conclusion:

Apache CXF and JiBX work well together. But if code generation customization is required there is a lack of support on the Apache CXF tool side for JiBX. Thus some extra work(-around) has to be done.

I opened an improvement request for a better JiBX support CXF-4980. Feel free to vote on this issue!

3 comments:

  1. Are you certain this is working as described? Seems as if cxf is generating all the files and jibx is generating none.
    What java files exist after you run
    mvn org.apache.cxf:cxf-codegen-plugin:wsdl2java
    ?

    ReplyDelete
    Replies
    1. Hi Chaim,

      I tested your command with the -X flag to get some maven debug information.

      Running just

      > mvn org.apache.cxf:cxf-codegen-plugin:wsdl2java

      executes the command line variant of the goal "org.apache.cxf:cxf-codegen-plugin:2.7.4:wsdl2java (default-cli)" that does not consider my configuration and leads to the target output you describe.

      If you run the complete

      > mvn clean install

      phase than the plugin is executed with the goal "org.apache.cxf:cxf-codegen-plugin:2.7.4:wsdl2java (default)" (only "default" - not "default-cli") and my configuration is considered. The target output is like described by me.

      So it works if you run the complete install phase. I did not expect that running the dedicated plugin goal makes a difference. That was new to me - it is described here (http://maven.apache.org/guides/mini/guide-default-execution-ids.html).

      Thank you for pointing this out. If you like to get some more details use the -X flag on the mvn executions and have a look at the debug output.

      Regards,
      Franz

      Delete
  2. I've successfully compiled and run the project.

    Also i've imported wsdl in SoapUI but i'm getting runtime binding error:
    16.03.2015 12:43:06 org.apache.cxf.phase.PhaseInterceptorChain doDefaultLogging
    WARNING: Interceptor for {http://calculator.sample.frvabe.de/ws}calculatorService#{http://calculator.sample.frvabe.de/ws}calculate has thrown exception, unwinding now
    java.lang.RuntimeException: org.jibx.runtime.JiBXException: Unable to access binding information for class de.frvabe.sample.calculator.types.Term
    Make sure the binding has been compiled
    at org.apache.cxf.jibx.JibxDataReader.read(JibxDataReader.java:60)
    at org.apache.cxf.jibx.JibxDataReader.read(JibxDataReader.java:37)
    at org.apache.cxf.interceptor.DocLiteralInInterceptor.handleMessage(DocLiteralInInterceptor.java:189)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
    at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
    at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.serviceRequest(JettyHTTPDestination.java:355)
    at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.doService(JettyHTTPDestination.java:319)
    at org.apache.cxf.transport.http_jetty.JettyHTTPHandler.handle(JettyHTTPHandler.java:72)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1074)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1010)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:255)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
    at org.eclipse.jetty.server.Server.handle(Server.java:365)
    at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485)
    at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:937)
    at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:998)
    at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:856)
    at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240)
    at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:627)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:51)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
    at java.lang.Thread.run(Thread.java:680)
    Caused by: org.jibx.runtime.JiBXException: Unable to access binding information for class de.frvabe.sample.calculator.types.Term
    Make sure the binding has been compiled
    at org.jibx.runtime.BindingDirectory.getBindingList(BindingDirectory.java:75)
    at org.jibx.runtime.BindingDirectory.getFactory(BindingDirectory.java:211)
    at org.apache.cxf.jibx.JibxDataReader.getUnmarshallingContext(JibxDataReader.java:89)
    at org.apache.cxf.jibx.JibxDataReader.read(JibxDataReader.java:46)
    ... 24 more
    Caused by: java.lang.NoSuchFieldException: JiBX_bindingList
    at java.lang.Class.getDeclaredField(Class.java:1909)
    at org.jibx.runtime.BindingDirectory.getBindingList(BindingDirectory.java:68)
    ... 27 more


    What's the reason? The only difference i'm using java 1.6 and i've replaced '1.7' with '1.6' in pom.xml

    ReplyDelete