Data Binding Options - 6.3

Talend ESB Service Developer Guide

EnrichVersion
6.3
EnrichProdName
Talend Data Fabric
Talend Data Services Platform
Talend ESB
Talend MDM Platform
Talend Open Studio for ESB
Talend Real-Time Big Data Platform
task
Design and Development
Installation and Upgrade
EnrichPlatform
Talend ESB

CXF uses JAXB 2.2 as its default databinding. JAXB is most commonly used for web services and will be covered in this section along with alternative XMLBeans and SDO databinding. CXF also supports the Aegis data binding, which originated with CXF's precursor XFire and is covered on the CXF website.

JAXB

JAXB is the default data binding for CXF. If you don't specify one of the other data bindings in your Spring configuration or through the API, you will get JAXB. Releases of CXF since 2.3.x have used the JDK7 default of JAXB 2.2, however Maven users running on JDK 6 will need to use the Java endorsed override mechanism to use JAXB 2.2 instead of JAXB 2.1.

JAXB uses Java annotation combined with files found on the classpath to build the mapping between XML and Java. JAXB supports both code-first and schema-first programming. Schema-first supports the ability to create a client proxy, dynamically, at runtime. See the CXF DynamicClientFactory class.

CXF uses the JAXB reference implementation. To learn more about annotating your classes or how to generate beans from a schema, please read the JAXB user's guide.

JAXB versus JAX-WS (or other front-ends)

There are some pitfalls in the interaction between the front end and the data binding. If you need detailed control over the XML that travels on the wire, you may want to avoid the 'wrapped' alternative, and stick with 'bare'. When you use the wrapped parameter style or the RPC binding, the front ends construct more or less elaborate XML representations for your operations. You have less control over those constructs than you do over JAXB's mappings. In particular, developers with detailed requirements to control the XML Schema 'elementFormDefault' or the use or non-use of XML namespace prefixes often become frustrated because the JAXB annotations for these options don't effect mappings that are purely the work of the front-end. The safest course is to use Document/Literal/Bare.

Configuring JAXB

CXF allows you to configure JAXB in two ways.

JAXB Properties

JAXB allows the application to specify two sets of properties that modify its behavior: context properties and marshaller properties. CXF allows applications to add to these properties. Take care. In some cases, CXF sets these properties for its own use.

You can add items to both of these property sets via the JAXBDataBinding class. The 'contextProperties' and 'marshallerProperties' properties (in the Spring sense) of JAXBDataBinding each store a Map<String, Object>. Whatever you put in the map, CXF will pass along to JAXB. See the JAXB documentation for details.

Example 19. Example of Configuring a Context Property

<jaxws:server id="bookServer"
   serviceClass="org.apache.cxf.mytype.AnonymousComplexTypeImpl"
   address="http://localhost:8080/act" 
   bus="cxf">
   <jaxws:invoker>
      <bean class="org.apache.cxf.service.invoker.BeanInvoker">
         <constructor-arg>
            <bean class="org.apache.cxf.mytype.AnonymousComplexTypeImpl"/>
         </constructor-arg>
      </bean>
   </jaxws:invoker>
   <jaxws:dataBinding>
      <bean class="org.apache.cxf.jaxb.JAXBDataBinding">
         <property name="contextProperties">
            <map>
               <entry>
                  <key>
                     <value>com.sun.xml.bind.defaultNamespaceRemap</value>
                  </key>
                  <value>uri:ultima:thule</value>
               </entry>
            </map>
         </property>
      </bean>
   </jaxws:dataBinding>
</jaxws:server>

Activating JAXB Validation of SOAP requests and responses

For the client side

<jaxws:client name="{http://apache.org/hello_world_soap_http}SoapPort"
   createdFromAPI="true">
   <jaxws:properties>
      <entry key="schema-validation-enabled" value="true" />
   </jaxws:properties>
</jaxws:client>

You may also do this programmatically:

((BindingProvider)port).getRequestContext().put(
   "schema-validation-enabled", "true");

For the server side

<jaxws:endpoint name="{http://apache.org/hello_world_soap_http}SoapPort"
   wsdlLocation="wsdl/hello_world.wsdl" createdFromAPI="true">
   <jaxws:properties>
      <entry key="schema-validation-enabled" value="true" />
   </jaxws:properties>
</jaxws:endpoint>

You can also use the org.apache.cxf.annotations.SchemaValidation annotation.

Namespace Prefix Management

The JAXB reference implementation allows the application to provide an object that in turn maps namespace URI's to prefixes. You can create such an object and supply it via the marshaller properties. However, CXF provides an easier process. The namespaceMap property of the JAXBDataBinding accepts a Map<String, String>. Think of it as a map from namespace URI to namespace prefix. If you load up this map, CXF will set up the necessary marshaller property for you.

Example 20. Example of Configuring a Namespace Mapping

<jaxws:server id="bookServer"
   serviceClass="org.apache.cxf.mytype.AnonymousComplexTypeImpl"
   address="http://localhost:8080/act" 
   bus="cxf">
   <jaxws:invoker>
      <bean class="org.apache.cxf.service.invoker.BeanInvoker">
         <constructor-arg>
            <bean class="org.apache.cxf.mytype.AnonymousComplexTypeImpl"/>
         </constructor-arg>
      </bean>
   </jaxws:invoker>
   <jaxws:dataBinding>
      <bean class="org.apache.cxf.jaxb.JAXBDataBinding">
         <property name="namespaceMap">
            <map>
               <entry>
                  <key>
                     <value>
                        http://cxf.apache.org/anonymous_complex_type/
                     </value>
                  </key>
                  <value>BeepBeep</value>
               </entry>
            </map>
         </property>
      </bean>
   </jaxws:dataBinding>
</jaxws:server>

MTOM Attachments with JAXB

MTOM is a standard which allows your services to transfer binary data efficiently and conveniently. Many frameworks have support for MTOM - Axis2, JAX-WS RI, JBoss WS, XFire, Microsoft's WCF, and more.

If the binary is part of the XML document, it needs to be base64 encoded - taking CPU time and increasing the payload size. When MTOM is enabled on a service, it takes binary data which might normally be part of the XML document, and creates an attachment for it.

Enabling MTOM is a rather simple process. First, you must annotate your schema type or POJO to let JAXB know that a particular field could be a candidate for MTOM optimization. Second, you just tell CXF that you wish to enable MTOM.

This page tells you how to activate MTOM for JAXB. MTOM is also supported in Aegis.

1) Annotating the Message

1a) Modifying your schema for MTOM

Lets say we have a Picture schema type like this:

<schema targetNamespace="http://pictures.com"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <element name="Picture">
      <complexType>
         <sequence>
            <element name="Title" type="xsd:string"/>
            <element name="ImageData" type="xsd:base64Binary"/>
         </sequence>
      </complexType>
   </element>
</schema>

In this case the ImageData element is something we would like to have transferred as an attachment. To do this we just need to add an xmime:expectedContentTypes annotation:

<schema targetNamespace="http://pictures.com" 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:xmime="http://www.w3.org/2005/05/xmlmime">
   <element name="Picture">
      <complexType>
         <sequence>
            <element name="Title" type="xsd:string"/>
            <element name="ImageData" type="xsd:base64Binary"
               xmime:expectedContentTypes="application/octet-stream"/>
         </sequence>
      </complexType>
   </element>
</schema>

This tells JAXB (which WSDL2Java uses to generate POJOs for your service) that this field could be of any content type. Instead of creating a byte[] array for the base64Binary element, it will create a DataHandler instead which can be used to stream the data.

1b) Annotation your JAXB beans to enable MTOM

If you're doing code first, you need to add an annotation to your POJO to tell JAXB that the field is a candidate for MTOM optimization. Lets say we have a Picture class with has Title and ImageData fields, then it might look like this:

@XmlType
public class Picture {
   private String title;

   @XmlMimeType("application/octet-stream")
   private DataHandler imageData;

   public String getTitle() { return title; }
   public void setTitle(String title) { this.title = title; }

   public DataHandler getImageData() { return imageData; }
   public void setImageData(DataHandler imageData) 
      { this.imageData = imageData; }
}

Note the use of 'application/octet-stream'. According to the standard, you should be able to use any MIME type you like, in order to specify the actual content of the attachment. However, due to a defect in the JAX-B reference implementation, this won't work.

2) Enable MTOM on your service

If you've used JAX-WS to publish your endpoint you can enable MTOM like so:

import javax.xml.ws.Endpoint;
import javax.xml.ws.soap.SOAPBinding;

Endpoint ep = Endpoint.publish("http://localhost/myService", 
   new MyService());
SOAPBinding binding = (SOAPBinding) ep.getBinding();
binding.setMTOMEnabled(true);

Or, if you used XML to publish your endpoint:

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

   <jaxws:endpoint 
      id="helloWorld" 
      implementor="demo.spring.HelloWorldImpl" 
      address="http://localhost/HelloWorld">
      <jaxws:properties>
         <entry key="mtom-enabled" value="true"/>
      </jaxws:properties>
   </jaxws:endpoint>

</beans>

If you're using the simple frontend you can set the mtom-enabled property on your ServerFactoryBean or ClientProxyFactoryBean:

Map<String,Object> props = new HashMap<String, Object>();
// Boolean.TRUE or "true" will work as the property value here
props.put("mtom-enabled", Boolean.TRUE); 

ClientProxyFactoryBean pf = new ClientProxyFactoryBean();
pf.setPropertyies(props);
...
YourClient client = (YourClient) pf.create();

ServerFactoryBean sf = new ServerFactoryBean();
sf.setPropertyies(props);
...
sf.create();

Similarly, you can use the XML configuration:

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

   <simple:server
      id="helloWorld" 
      serviceClass="demo.spring.HelloWorldImpl" 
      address="http://localhost/HelloWorld">
      <simple:properties>
         <entry key="mtom-enabled" value="true"/>
      </simple:properties>
   </simple:server>

   <simple:client
      id="helloWorldClient" 
      serviceClass="demo.spring.HelloWorldImpl" 
      address="http://localhost/HelloWorld">
      <simple:properties>
         <entry key="mtom-enabled" value="true"/>
      </simple:properties>
   </simple:client>

</beans>

Using DataHandlers

Once you've got the above done, its time to start writing your logic. DataHandlers are easy to use and create. To consume a DataHandler:

Picture picture = ...;
DataHandler handler = picture.getImageData();
InputStream is = handler.getInputStream();

There are many ways to create DataHandlers. You can use a FileDataSource, ByteArrayDataSource, or write your own DataSource:

DataSource source = new ByteArrayDataSource(new byte[] {...}, 
   "content/type");
DataSource source = new FileDataSource(new File("my/file"));

Picture picture = new Picture();
picture.setImageData(new DataHandler(source));

SDO

Apache CXF 2.3 added support for the Tuscany implementation of Service Data Objects as alternative data binding.

Setup

By default, CXF does not ship with the Tuscany SDO jars. You will need to acquire them elsewhere and add them to the classpath for the SDO databinding to work. The list of required jars are:

backport-util-concurrent-3.0.jar
codegen-2.2.3.jar
codegen-ecore-2.2.3.jar
common-2.2.3.jar
ecore-2.2.3.jar
ecore-change-2.2.3.jar
ecore-xmi-2.2.3.jar
tuscany-sdo-api-r2.1-1.1.1.jar
tuscany-sdo-impl-1.1.1.jar
tuscany-sdo-lib-1.1.1.jar
tuscany-sdo-tools-1.1.1.jar
xsd-2.2.3.jar

Code Generation

If all the SDO required jars are available (by default, CXF does not ship them, see above), wsld2java tool can be run with the -db sdo flag to have the code generator emit SDO objects instead of the default JAXB objects. The generated SEI interface will have @DataBinding(org.apache.cxf.sdo.SDODataBinding.class) annotation on it which is enough to configure the runtime to know to use SDO.

XMLBeans

Apache XMLBeans is another technology for mapping XML Schema to java objects. CXF added support for XMLBeans in 2.1. There are a two parts to the support for XMLBeans:

Code Generation

The wsdl2java tool now allows a "-db xmlbeans" flag to be added that will generate XMLBeans types for all the schema beans instead of the default JAXB beans. With 2.1 and 2.2, the types are generated, but you still need to configure the XMLBeans databinding to be used at runtime. With 2.3, the generated code contains an @Databinding annotation marking it as XMLBeans and the configuration is unnecessary.

Runtime

You need to configure the runtime to tell it to use XMLBeans for the databinding instead of JAXB.

Spring config

For the server side, your spring configuration would contain something like:

<jaxws:server serviceClass="demo.hw.server.HelloWorld" 
   address="/hello_world">
   <jaxws:dataBinding>
      <bean class="org.apache.cxf.xmlbeans.XmlBeansDataBinding" />
   </jaxws:dataBinding>
</jaxws:server>

or

<jaxws:endpoint
   id="helloWorld"
   implementor="demo.spring.HelloWorldImpl"
   address="http://localhost/HelloWorld">
   <jaxws:dataBinding>
      <bean class="org.apache.cxf.xmlbeans.XmlBeansDataBinding" />
   </jaxws:dataBinding>
</jaxws:endpoint>

The client side is very similar:

<jaxws:client id="helloClient"
   serviceClass="demo.spring.HelloWorld"
   address="http://localhost:9002/HelloWorld">
   <jaxws:dataBinding>
      <bean class="org.apache.cxf.xmlbeans.XmlBeansDataBinding" />
   </jaxws:dataBinding>
<jaxws:client>
FactoryBeans

If using programmatic factory beans instead of spring configuration, the databinding can be set on the ClientProxyFactoryBean (and subclasses) and the ServerFactoryBean (and subclasses) via:

factory.getServiceFactory().setDataBinding(
   new org.apache.cxf.xmlbeans.XmlBeansDataBinding());