Search This Blog

Tuesday, 6 August 2013

DataBinding Options with CXF

When using CXF, the marshaling and unmarshaling of data happens under the covers. So when we make the call through java, the request is marshaled to XML by CXF client. It sends it over the wire to the server.
At the server, CXF will unmarshal the XML back to java objects and execute the webservice method at the server endpoint. A reverse process happens for the response. The response generated is marshaled by the server into XML and sent to the client over the wire. The client will unmarshal the XML to java returning the result object to the client code.
Can we customize the marshaling /unmarshaling process?
From the website :
CXF uses JAXB 2.x as its default databinding.CXF also includes other data bindings. 
XMLBeans is available, and there is the Aegis data binding which will turn nearly 
any Java object into something that can be represented using schema, including Maps,
Lists, and unannotated java types. Also, there is the SourceDataBinding for Source 
objects. CXF 2.3.0 adds an SDO data binding.
I decided to look at the webservice created in the previous example. On the server side we have defined the endpoint:
<jaxws:endpoint id="randomWs"
   implementor="com.ws.service.samplews_ns.SampleServiceOperationsPortTypeImpl"
   address="randomService">
</jaxws:endpoint>
This is the default setting for data binding. CXF will use JAXB for the marshaling and the unmarshaling. This is equivalent to:
<jaxws:endpoint id="randomWs"
   implementor="com.ws.service.samplews_ns.SampleServiceOperationsPortTypeImpl"
   address="randomService">
   <jaxws:dataBinding>
      <bean class="org.apache.cxf.jaxb.JAXBDataBinding" />
   </jaxws:dataBinding>
</jaxws:endpoint>
Similarly on the client too we can specify the dataBinding element. In fact I decided to continue to use jaxb binding on my server, while using aegis data binding on the client. In the client's XML configuration:
<jaxws:client id="client"
   serviceClass="com.ws.service.samplews_ns.SampleServiceOperationsPortType"
   address="http://localhost:8080/WsdlFirst/services/randomService">
   <jaxws:dataBinding>
      <bean class="org.apache.cxf.aegis.databinding.AegisDatabinding" />
   </jaxws:dataBinding>
</jaxws:client>
I have specified the data binding. Now to add the .aegis.xml files for the elements: First is for the interface: (SampleServiceOperationsPortType.aegis.xml):
<mappings>
   <mapping uri="http://ws.com/Service/samplews-ns" name="SampleServiceOperationsPortType">
      <method name="methodName">
         <return-type mappedName="GetRandomResponse"
            componentType="GetRandomResponse" />
         <parameter index="0" mappedName="GetRandomRequest" />
      </method>
   </mapping>
</mappings>
Next is for the request and response classes:
GetRandomRequest.aegis.xml
<mappings>
   <mapping name="GetRandomRequest" uri="http://ws.com/Service/xsd/random-schema">
      <property name="name" mappedName="name" nillable='false' />
   </mapping>
</mappings>
GetRandomResponse.aegis.xml
<mappings>
   <mapping name="GetRandomResponse" uri="http://ws.com/Service/xsd/random-schema">
      <property name="value" mappedName="value" nillable='false' />
   </mapping>
</mappings>
The XML files were placed in the same package as the java classes.

On testing the code:
public static void main(final String[] args) {
 final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("client.xml");
 final SampleServiceOperationsPortType client = (SampleServiceOperationsPortType) context.getBean("client");
 final GetRandomRequest getRandomRequest = new GetRandomRequest();
 getRandomRequest.setName("Tom");
 final GetRandomResponse getRandomResponse = client.random(getRandomRequest);
 System.out.println("Response: " + getRandomResponse.getValue());
}
The logs at the client are as below:
6500 [main] TRACE org.apache.cxf.aegis.type.XMLTypeCreator  - Found mapping file
 : /com/ws/service/samplews_ns/SampleServiceOperationsPortType.aegis.xml
6579 [main] TRACE org.apache.cxf.aegis.type.XMLTypeCreator  - Found mapping file
 : /com/ws/service/xsd/random_schema/GetRandomRequest.aegis.xml
...
6688 [main] TRACE org.apache.cxf.aegis.type.XMLTypeCreator  - Mapping file : /ja
va/lang/String.aegis.xml not found.
6704 [main] TRACE org.apache.cxf.aegis.type.XMLTypeCreator  - Found mapping file
 : /com/ws/service/xsd/random_schema/GetRandomResponse.aegis.xml
...
6750 [main] TRACE org.apache.cxf.aegis.type.XMLTypeCreator  - Mapping file : /in
t.aegis.xml not found.
...
Response: 21
If we were to add Logging interceptors than we can see the XML data that was transported along the wire: Request XML :
Address: http://localhost:8080/WsdlFirst/services/randomService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=["/Service/random"]}
Payload: 
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:GetRandomRequest xmlns:ns1="http://ws.com/Service/xsd/random-schema">
      <ns1:name>Tom</ns1:name>
    </ns1:GetRandomRequest>
  </soap:Body>
</soap:Envelope>
Response XML :
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml;charset=UTF-8
Headers: {Content-Length=[213], content-type=[text/xml;charset=UTF-8], Date=[Sat
, 04 May 2013 10:45:03 GMT], Server=[Apache-Coyote/1.1]}
Payload: 
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetRandomResponse xmlns="http://ws.com/Service/xsd/random-schema">
      <value>56</value>
    </GetRandomResponse>
  </soap:Body>
</soap:Envelope>

1 comment: