Chapter 6. Marshalling and Object-XML Mapping (OXM)

In this chapter, we will cover the following topics:

  • Marshalling with JAXB2

  • Marshalling with XMLBeans

  • Marshalling with JiBX

  • Marshalling with XStream

  • Marshalling with MooseXML

  • Creating a custom marshaller using XPath for conditional XML parsing

Introduction

In Object/XML Mapping (OXM) terminology, marshalling (serializing) converts the object representation of data into the XML format and unmarshalling converts XML into the corresponding object.

Spring's OXM simplifies OXM operations by using rich aspects of the Spring framework. For example, the dependency injection feature can be used to instantiate different OXM technologies into objects to use them, and Spring can use annotations to map a class or a class's field to XML.

Spring-WS benefits from Spring's OXM for converting a Payload message into objects or vice versa. For example, set JAXB as the OXM framework using the following configuration in the application context:

<bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
<constructor-arg ref="marshaller" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.packtpub.liverestaurant.domain" />
</bean>

In addition, marshalling frameworks could be changed by changing the marshaller bean in the configuration file, while keeping the implementation of Web-Services unchanged.

There are many implementations of marshalling frameworks available. JAXB (Java Architecture for XML Binding), JiBX, XMLBeans, Castor, and so on are examples. For some of the OXM frameworks, tools are provided to convert schema into POJO classes and generate mapping data within these classes, or in a separate external configuration file.

This chapter provides recipes to illustrate the usage of different frameworks for Object/XML mapping.

For simplification, most of the recipes in this chapter use projects used in the Integration testing using Spring-JUnit support recipe, discussed in Chapter 3, Testing and Monitoring Web-Services, to set up a server and send and receive messages by client. However, in the recipe Marshalling with XStream, projects from the Creating Web-Service client for WS-Addressing endpoint recipe, discussed in Chapter 2, Building Clients for SOAP Web-Services, are used for the server and client sides.

Marshalling with JAXB2

Java Architecture for XML Binding (http://jaxb.java.net/tutorial/) is an API that allows developers to bind Java objects to XML representations. JAXB implementation is a part of the project Metro (http://metro.java.net/), which is a high-performance, extensible, and easy-to-use Web-Service stack. The main functionality of JAXB is to marshall Java objects into XML equivalents and unmarshall them back to the Java object (which can be called Object/XML binding or marshalling) as needed. JAXB is particularly useful when the specification is complex and changing.

JAXB provides many extensions and tools that make the Object/XML binding an easy job. Its annotation support allows developers to mark the O/X binding within the existing classes in order to generate the XML at runtime. Its Maven tool plugin (maven-jaxb2-plugin) enables the generation of Java classes from a given XML Schema file.

This recipe illustrates how to set up a marshalling end point and build a client program using JAXB2 as the marshalling library.

Getting ready

This recipe contains a server (LiveRestaurant_R-6.1) and a client (LiveRestaurant_R-6.1-Client) project.

LiveRestaurant_R-6.1 has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

LiveRestaurant_R-6.1-Client has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

  • spring-test-3.0.5.RELEASE.jar

  • junit-4.7.jar

This recipe uses maven-jaxb2-plugin to generate classes from a schema.

How to do it...

  1. Register the JAXB marshaller inside the server/client-side configuration file.

  2. Configure maven-jaxb2-plugin inside server/client-side POM files.

  3. Set up the server and run the client (it also generates classes from a schema):

    • Client project-root: mvn clean package

    • Server project-root: mvn clean package tomcat:run

      The following is the client-side output:

    - Received response ....
    <ns2:cancelOrderResponse...>
    <ns2:cancelled>true</ns2:cancelled>
    </ns2:cancelOrderResponse>
    ...
    for request ...
    <ns2:cancelOrderRequest ...>
    <ns2:refNumber>Ref-2010..</ns2:refNumber>
    </ns2:cancelOrderRequest>
    .....
    ....
    - Received response ....
    <ns2:placeOrderResponse ...>
    <ns2:refNumber>Ref-2011-1..</ns2:refNumber>
    </ns2:placeOrderResponse>
    ...
    for request ...
    <ns2:placeOrderRequest ...>
    <ns2:order>.....
    </ns2:order></ns2:placeOrderRequest>
    Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.293 sec
    
    

How it works...

The main player in this marshalling business is GenericMarshallingMethodEndpointAdapter, which utilizes a marshaller to perform the Object/XML marshalling process. The marshaller used here is org.springframework.oxm.jaxb.Jaxb2Marshaller, which performs O/X marshalling, utilizing the JAXB2 framework. If you examine the Java classes generated by the Maven plugin tool, you can see the JAXB annotations such as @XmlType, @XmlRootElement, @XmlElement, and so on. These annotations are the instructions to the JAXB engine that determines the structure of the XML to be generated at runtime.

The following section in the POM files generates JAXB classes from the schema (OrderService.xsd) in the folder srcmainwebappWEB-INF (set by schemaDirectory).

GeneratePackage set the package includes the generated classes and generateDirectory set the folder host generatedPackage:

<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.1</version>
</plugin>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<configuration>
<schemaDirectory>srcmainwebappWEB-INF</schemaDirectory>

<schemaIncludes>
<include>orderService.xsd</include>

</schemaIncludes>
<generatePackage>com.packtpub.liverestaurant.domain</generatePackage>

</configuration>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>

The OrderServiceEndPoint, which is annotated as an @Endpoint, maps the Web-Service request, with a payload-root, placeOrderRequest, to the method getOrder, recognizing the annotation @PayloadRoot. While the marshaller marshalls the incoming XML into an instance of PlaceOrderRequest, the method getOrder returns PlaceOrderResponse. The same thing happens to the method cancelOrder:

@PayloadRoot(localPart = "placeOrderRequest", namespace = SERVICE_NS)
public PlaceOrderResponse getOrder(
PlaceOrderRequest placeOrderRequest) {
PlaceOrderResponse response = JAXB_OBJECT_FACTORY
.createPlaceOrderResponse();
response.setRefNumber(orderService.placeOrder(placeOrderRequest
.getOrder()));
return response;
}
@PayloadRoot(localPart = "cancelOrderRequest", namespace = SERVICE_NS)
public CancelOrderResponse cancelOrder(
CancelOrderRequest cancelOrderRequest) {
CancelOrderResponse response = JAXB_OBJECT_FACTORY
.createCancelOrderResponse();
response.setCancelled(orderService.cancelOrder(cancelOrderRequest
.getRefNumber()));
return response;
}

The following section in spring-ws-servlet.xml in the server sets the marshaller in the endpoint (OrderServiceEndpoint) to Jaxb2Marshaller. The setting contextPath in the marshaller bean registers all beans included in the package com.packtpub.liverestaurant.domain to be marshalled/unmarshalled by Jaxb2Marshaller:

<bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
<constructor-arg ref="marshaller" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.packtpub.liverestaurant.domain" />
</bean>

The same things happen in the client. The only difference is that the marshaller is set for WebServiceTemplate:

<bean id="orderServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory" />
<property name="marshaller" ref="orderServiceMarshaller"></property>
<property name="unmarshaller" ref="orderServiceMarshaller"></property>
.........</bean>
<bean id="orderServiceMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.packtpub.liverestaurant.domain" />
</bean>

The MessageDispatcherServlet, with the help of the Jaxb2Marshaller, detects the O/X mapping annotations as well as the reflection and delegates the final marshalling process to the JAXB framework.

Marshalling with XMLBeans

XMLBeans (http://xmlbeans.apache.org/) is a technology for accessing XML by binding it to Java types. The library comes from the Apache Foundation and is a part of the Apache XML project. Known for its Java-friendliness, XMLBeans allows the developers to take advantage of the richness and features of XML and XML Schema and have these features mapped as naturally as possible to the equivalent Java language and typing constructs.

Two major features that make XMLBeans unique from other XML-Java binding options are:

  • Full XML Schema support: XMLBeans fully supports (built-in) XML Schema and the corresponding Java classes provide constructs for all of the major functionality of XML Schema.

  • Full XML infoset fidelity: While unmarshalling XML data, the full XML infoset is available to the developer. The XMLBeans provides many extensions and tools that make the Object/XML binding an easy job.

Getting ready

This recipe contains a server (LiveRestaurant_R-6.2) and a client (LiveRestaurant_R-6.2-Client) project.

LiveRestaurant_R-6.2 has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

  • xmlbeans-2.4.0.jar

LiveRestaurant_R-6.2-Client has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

  • xmlbeans-2.4.0.jar

  • spring-test-3.0.5.RELEASE.jar

  • junit-4.7.jar

This recipe uses xmlbeans-maven-plugin to generate classes and bind files from a schema.

How to do it...

  1. Register the XMLBean marshaller inside the server/client-side configuration file.

  2. Configure xmlbeans-maven-plugin inside the server/client-side POM files.

  3. Set up the server and run the client (it also generates classes from a schema):

  4. Run the following commands:

    • Server project-root: mvn clean package tomcat:run

    • Client project-root: mvn clean package

      The following is the client-side output:

    [INFO]
    [INFO] --......
    [INFO]
    [INFO] --- xmlbeans-maven-plugin:2.3.2:xmlbeans ....
    [INFO]
    [INFO] .....
    Received response ...
    <sch:cancelOrderResponse ...>
    <sch:cancelled>true</sch:cancelled>
    </sch:cancelOr
    derResponse>...
    for request.....
    ......
    - Received response ...
    <sch:placeOrderResponse ...>
    <sch:refNumber>Ref-2011-10-..</sch:refNumber>
    </sch:placeOrderResponse>
    ...
    for request ....
    ...
    Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.845 sec
    
    

How it works...

This recipe works exactly the same way as the first one, Marshalling with JAXB2, except that it is using a different marshaller, XMLBeansMarshaller. The scomp (Schema Compiler) tool used here generates the Java XMLBeans classes from the XML schema (OrderService.xsd). Besides the domain classes, it generates the classes representing the document root element, for example, CancelOrderRequestDocument. All the generated classes contain the Factory methods to instantiate them.

As can be noticed easily, the two main differences in the code are in OrderServiceEndPoint and spring-ws-servlet.xml. Unlike that of the previous recipe, the method getOrder returns an instance of OrderResponseDocument and it accepts OrderRequestDocument as an input argument. The same description is true about the method cancelOrderDoc:

@PayloadRoot(localPart = "placeOrderRequest", namespace = SERVICE_NS)
public PlaceOrderResponseDocument getOrder(PlaceOrderRequestDocument orderRequestDoc) {
PlaceOrderResponseDocument orderResponseDocument =PlaceOrderResponseDocument.Factory.newInstance();
orderResponseDocument.addNewPlaceOrderResponse();
orderResponseDocument.getPlaceOrderResponse().setRefNumber(orderService.placeOrder(orderRequestDoc));
return orderResponseDocument;
}
@PayloadRoot(localPart = "cancelOrderRequest", namespace = SERVICE_NS)
public CancelOrderResponseDocument placeCancelOrderDoc(
CancelOrderRequestDocument cancelOrderRequestDoc) {
CancelOrderResponseDocument cancelOrderResponseDocument= CancelOrderResponseDocument.Factory.newInstance();
cancelOrderResponseDocument.addNewCancelOrderResponse();
cancelOrderResponseDocument.getCancelOrderResponse().setCancelled(orderService.cancelOrder(cancelOrderRequestDoc.getCancelOrderRequest().getRefNumber()));
return cancelOrderResponseDocument;
}

The marshaller used in spring-ws-servlet.xml is the XMLBeansMarshaller, which marshalls and unmarshalls between XML and Java using the XMLBeans library.

<bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
<constructor-arg ref="marshaller" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller"/>

The contract between the @Endpoint class and XMLBeansMarshaller is that the @PayloadRoot methods should accept and return instances of org.apache.xmlbeans.XmlObject. Then it dynamically finds out the corresponding classes, and using their Factory methods, it creates instances and binds to the XML at runtime.

Same as the previous recipe, a plugin in the POM files generates XMLBean classes from the schema (OrderService.xsd) in the folder srcmainwebappWEB-INF (set by schemaDirectory):

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xmlbeans-maven-plugin</artifactId>
<version>2.3.2</version>
<executions>
<execution>
<goals>
<goal>xmlbeans</goal>
</goals>
</execution>
</executions>
<inherited>true</inherited>
<configuration>
<schemaDirectory>src/main/webapp/WEB-INF/</schemaDirectory>

</configuration>
</plugin>

The MessageDispatcherServlet, with the help of XMLBeansMarshaller, detects the O/X mapping annotations and the marshaller configuration, and delegates the final marshalling process to the XMLBeans framework.

There's more...

XMLBeans come with a set of built-in powerful tools to add much more functionality than merely marshalling between XML and Java. The recipe utilized just one such tool, scomp, the Schema Compiler that generates Java classes/compressed JAR files out of an XML Schema (.xsd) file. A few other tools that may be helpful are:

  • inst2xsd (Instance to Schema Tool): Generates XML schema from XML instance files.

  • scopy (Schema Copier): Copies the XML schema at the specified URL to the specified file

  • validate (Instance Validator): Validates an instance against a schema

  • xpretty (XML Pretty Printer): Pretty prints the specified XML to the console

  • xsd2inst (Schema to Instance Tool): Prints an XML instance from the specified global element using the specified schema

  • xsdtree (Schema Type Hierarchy Printer): Prints an inheritance hierarchy of the types defined in a schema

  • xmlbean Ant task: Compiles a set of XSD and/or WSDL files into XMLBeans types

    The xmlbean Ant task is a nice way to automate the generation of Java classes in integration with your build scripts.

Marshalling with JiBX

JiBX (http://jibx.sourceforge.net/) is another tool and library for binding XML data to Java objects. JiBX is known to be the best for speed performance as well as flexibility. However, it has also been known for its complexity of binding, especially for a complex data model.

From version 1.2 onwards, JiBX has addressed these bottlenecks and now it has easy-to-use marshalling tools and framework. Using the JiBX tool, a user can generate a schema from existing Java code or generate Java code and binding files from an existing schema. JiBX library at runtime binds Java classes to XML data and vice versa.

In this recipe, the JiBX tool (jibx-maven-plugin) is used to generate POJO classes and bind a definition file from an existing schema, and then a Web-Service client and server will be built upon the JiBX libraries.

Getting ready

This recipe contains a server (LiveRestaurant_R-6.3) and a client (LiveRestaurant_R-6.3-Client) project.

LiveRestaurant_R-6.3 has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

  • spring-expression-3.0.5.RELEASE.jar

  • jibx-run-1.2.3.jar

  • jibx-extras-1.2.3.jar

  • jibx-ws-0.9.1.jar

LiveRestaurant_R-6.3-Client has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

  • spring-expression-3.0.5.RELEASE.jar

  • jibx-run-1.2.3.jar

  • jibx-extras-1.2.3.jar

  • jibx-ws-0.9.1.jar

  • spring-test-3.0.5.RELEASE.jar

  • junit-4.7.jar

How to do it...

  1. Register the JiBX marshaller inside the server/client-side configuration file.

  2. Configure xmlbeans-maven-plugin inside the server/client-side POM files.

  3. Set up the server and run the client (it also generates classes from a schema):

    • Server project root: mvn clean package (it also generates classes from schema). Copy the WAR file into the Tomcat webapp folder and run Tomcat (apache-tomcat-6.0.18)

    • Client project-root: mvn clean package (it also generate classes from schema)

      The following is the client-side output:

    .......
    .........
    [INFO] --- jibx-maven-plugin:1.2.3:bind (compile-binding) @ LiveRestaurant_Client ---
    [INFO] Running JiBX binding compiler (single-module mode) on 1 binding file(s)
    [INFO]
    [INFO] ....
    Received response ...
    <tns:cancelOrderResponse ...>
    <tns:cancelled>true</tns:cancelled></tns:cancelOrderResponse>
    ...
    for request ...
    <tns:cancelOrderRequest ...><tns:refNumber>12345</tns:refNumber>
    </tns:cancelOrderRequest>
    .....
    Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
    
    

How it works...

As explained in the previous recipe, the application context for the server/client uses a customized marshaller (org.springframework.oxm.jibx.JibxMarshaller) to perform the Object/XML marshalling process. This Spring marshaller uses JiBX libraries for binding and marshalling processes. The following POM plugin setting (goal: schema-codegen) generates POJO classes from a schema (OrderService.xsd) into a package (com.packtpub.liverestaurant.domain) and it also generates a binding file (goal: bind):

<plugin>
<groupId>org.jibx</groupId>
<artifactId>jibx-maven-plugin</artifactId>
<version>1.2.3</version>
<executions>
<execution>
<id>generate-java-code-from-schema</id>
<goals>
<goal>schema-codegen</goal>

</goals>
</execution>
<execution>
<id>compile-binding</id>
<goals>
<goal>bind</goal>

</goals>
</execution>
</executions>
<configuration>
<schemaLocation>src/main/webapp/WEB-INF</schemaLocation>

<includeSchemas>
<includeSchema>orderService.xsd</includeSchema>
</includeSchemas>
<options>
<package>com.packtpub.liverestaurant.domain</package>

</options>
</configuration>
</plugin>

As described in the earlier recipes, this setting in the server and client Spring context file causes the client and server to use a customized marshaller (JibxMarshaller) for marshalling/unmarshalling POJO classes to/from XML data:

<bean id="marshaller"
class="org.springframework.oxm.jibx.JibxMarshaller">
<property name="targetClass" value="com.packtpub.liverestaurant.domain.CancelOrderRequest" />
</bean>

JibxMarshaller uses mapping the binding.xml file for the marshalling task. As it is shown in the mapping file, JiBX supports for simple data binding (<value style="element" name="fName"...) as well as complex data binding known as structure (<structure map-as="tns:Address"...). This feature makes JiBX the most flexibility-binding framework among the others.

......
<mapping abstract="true" type-name="tns:Customer" class="com.packtpub.liverestaurant.domain.Customer">
<structure map-as="tns:Address" get-method="getAddressPrimary" set-method="setAddressPrimary" name="addressPrimary"/>
<structure map-as="tns:Address" get-method="getAddressSecondary" set-method="setAddressSecondary" name="addressSecondary"/>
<structure map-as="tns:Name" get-method="getName" set-method="setName" name="name"/>
</mapping>
<mapping abstract="true" type-name="tns:Name" class="com.packtpub.liverestaurant.domain.Name">
<value style="element" name="fName" get-method="getFName" set-method="setFName"/>
<value style="element" name="mName" get-method="getMName" set-method="setMName"/>
<value style="element" name="lName" get-method="getLName" set-method="setLName"/>
</mapping>
.....

The OrderServiceEndPoint, which is annotated as an @Endpoint, is almost the same as earlier recipes (Marshalling with JAXB2); only the implementation is slightly different.

@PayloadRoot(localPart = "cancelOrderRequest", namespace = SERVICE_NS)
public
CancelOrderResponse handleCancelOrderRequest(CancelOrderRequest cancelOrderRequest) throws Exception {
CancelOrderResponse cancelOrderResponse=new CancelOrderResponse();
cancelOrderResponse.setCancelled(orderService.cancelOrder(cancelOrderRequest.getRefNumber()));
return cancelOrderResponse;
}
@PayloadRoot(localPart = "placeOrderRequest", namespace = SERVICE_NS)
public
PlaceOrderResponse handleCancelOrderRequest(PlaceOrderRequest placeOrderRequest) throws Exception {
PlaceOrderResponse orderResponse=new PlaceOrderResponse();
orderResponse.setRefNumber(orderService.placeOrder(placeOrderRequest.getOrder()));
return orderResponse;
}
......

There's more...

JiBX provides more flexibility by letting users create their own customized marshaller. It means instead of using a generated binding file, a custom binding file and custom marshaller classes to marshal any kind of data structure inside an XML document.

Marshalling with XStream

XStream (http://xstream.codehaus.org/) is a simple library for marshalling/unmarshalling objects to/from XML data. The following major features make this library different from others:

  • Doesn't need a mapping file

  • Doesn't need to change POJO (no need for a setter/getter and default constructor)

  • Alternative output format (JSON support and morphing)

  • XStream does not have a tool to generate a schema from existing Java code or to generate Java code from an existing schema

  • XStream does not support namespaces

In this recipe, a Web-Service client and server are created that use XStream libraries as a marshaller. Since XStream is not using any namespace in XML data (payload), a web address style of Web-Service is set up.

Getting ready

This recipe contains a server (LiveRestaurant_R-6.4) and a client (LiveRestaurant_R-6.4-Client) project.

LiveRestaurant_R-6.4 has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

  • spring-expression-3.0.5.RELEASE.jar

  • jxstream-1.3.1.jar

LiveRestaurant_R-6.4-Client has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

  • jxstream-1.3.1.jar

  • spring-test-3.0.5.RELEASE.jar

  • junit-4.7.jar

How to do it...

  1. Register XStream marshaller inside the server/client-side configuration file.

  2. Annotate domain classes with the Xstream annotation.

  3. Set up the server and run the client:

    • Server project-root: mvn clean package tomcat:run

    • Client project-root: mvn clean package

      The following is the client-side output:

    Received response
    ..
    ...
    <wsa:Action>http://www.packtpub.com/OrderService/CancelOrdReqResponse</wsa:Action>
    <wsa:MessageID>urn:uuid:a4b681ff-00f5-429e-9ab9-f9054e796a89</wsa:MessageID>
    ....
    <cancelOrderResponse><cancelled>true</cancelled>
    </cancelOrderResponse></SOAP-ENV:Body>
    ....
    ...
    <wsa:Action>http://www.packtpub.com/OrderService/CancelOrdReq</wsa:Action>
    ...<cancelOrderRequest><refNumber>12345</refNumber></cancelOrderRequest>
    
    

How it works...

As explained in the previous recipe, the application context for server/client uses a customized marshaller (org.springframework.oxm.xstream.XStreamMarshaller) to perform the Object/XML marshalling process. This spring marshaller uses XStream libraries for the marshalling process. The beans that are input and output parameters of the method in the endpoint (OrderServiceEndPoint.java) have to be registered in XstreamMarshaller. autodetectAnnotations is set to detect annotating within POJO classes:

<bean id="marshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="autodetectAnnotations" value="true"/>
<property name="aliases">
<map>
<entry key="placeOrderResponse" value="com.packtpub. liverestaurant.domain.PlaceOrderResponse" />
<entry key="placeOrderRequest" value="com.packtpub. liverestaurant.domain.PlaceOrderRequest" />
<entry key="cancelOrderRequest" value="com.packtpub. liverestaurant.domain.CancelOrderRequest" />
<entry key="cancelOrderResponse" value="com.packtpub. liverestaurant.domain.CancelOrderResponse" />
</map>
</property></bean>

XStreamMarshaller uses annotation in POJO classes (instead of the binding file) for the marshalling task. @XstreamAlias tells the marshaller that this class will be serialized/deserialized as 'name'. There is other annotation that is optional, but it tells marshaller how to serialize/deserialize the field of a class (@XStreamAsAttribute, @XStreamImplicit, and so on).

import com.thoughtworks.xstream.annotations.XStreamAlias;
@XStreamAlias("name")
public class Name
{
private String FName;
private String MName;
private String LName;

The OrderServiceEndPoint, which is annotated as an @Endpoint is the same as JiBX recipes that " the endpoint method's input and and return parameters are POJO (PlaceOrderResponse, PlaceOrderRequest, and so on) that is mapped to the schema. The only difference is that the endpoint uses web addressing for method mapping:

@Action("http://www.packtpub.com/OrderService/CancelOrdReq")
public
CancelOrderResponse handleCancelOrderRequest(CancelOrderRequest cancelOrderRequest) throws Exception {
CancelOrderResponse cancelOrderResponse=new CancelOrderResponse();
cancelOrderResponse.setCancelled(orderService.cancelOrder(cancelOrderRequest.getRefNumber()));
return cancelOrderResponse;
}
@Action("http://www.packtpub.com/OrderService/OrdReq")
public
PlaceOrderResponse handlePancelOrderRequest(PlaceOrderRequest placeOrderRequest) throws Exception {
PlaceOrderResponse orderResponse=new PlaceOrderResponse();
orderResponse.setRefNumber(orderService.placeOrder(placeOrderRequest.getOrder()));
return orderResponse;
}

Marshalling with MooseXML

Moose (http://quigley.com/moose/) is a lightweight framework for marshalling/unmarshalling objects to/from XML data. The schema generator of Moose is what makes this framework different from others. Moose is able to generate schema directly from annotated POJO classes. This is what is required to develop contract-last Web-Service development.

In this recipe, Moose is used to marshall/unmarshall objects to/from XML data in the Web-Service client and server communications.

Getting ready

This recipe contains a server (LiveRestaurant_R-6.5) and a client (LiveRestaurant_R-6.5-Client) project.

LiveRestaurant_R-6.5 has the following Maven dependencies:

  • log4j-1.2.9.jar

  • moose-0.4.6.jar

LiveRestaurant_R-6.5-Client has the following Maven dependencies:

  • log4j-1.2.9.jar

  • moose-0.4.6.jar

  • spring-test-3.0.5.RELEASE.jar

  • junit-4.7.jar

How to do it...

  1. Register Moose marshaller inside the server/client-side configuration file.

  2. Annotate domain classes with the @XML annotation.

  3. Set up the server and run the client:

    • Server project-root: mvn clean package tomcat:run

    • Client project-root: mvn clean package

      The following is the client-side output:

    Received response ...
    <ns:cancelOrderResponse...>
    <ns:cancelled>true</ns:cancelled>
    </ns:cancelOrderResponse>
    ...
    for request ...
    <ns:cancelOrderRequest...>
    <ns:refNumber>12345</ns:refNumber>
    </ns:cancelOrderRequest>
    .......
    
    

How it works...

As explained in the previous recipe, the application context for the server/client uses a customized marshaller (com.quigley.moose.spring.MooseMarshaller) to perform the Object/XML marshalling process. A mapping provider is injected into this custom marshaller. The mapping provider is to set the namespace and xmlPrefix when the object is being marshalled into XML and when the XML data is being converted into an object. The mapping provider gets the list of registered POJO classes from com.quigley.moose.mapping.provider.annotation.StaticClassesProvider:

<bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
<constructor-arg ref="mooseMarshaller"/>
</bean>
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>
<bean id="mooseMarshaller" class="com.quigley.moose.spring.MooseMarshaller">
<property name="mappingProvider"><ref bean="mooseMappingProvider"/></property>
</bean>
<bean id="mooseMappingProvider"
class="com.quigley.moose.mapping.provider.annotation.AnnotationMappingProvider">
<property name="xmlNamespace">
<value>http://www.liverestaurant.com/OrderService/schema</value></property>
<property name="xmlPrefix"><value>ns</value></property>
<property name="annotatedClassesProvider"><ref bean="mooseClassesProvider"/></property>
</bean>
<bean id="mooseClassesProvider"
class="com.quigley.moose.mapping.provider.annotation. StaticClassesProvider">
<property name="classes">
<list>
<value>com.packtpub.liverestaurant.domain. CancelOrderRequest</value>
<value>com.packtpub.liverestaurant.domain. CancelOrderResponse</value>
<value>com.packtpub.liverestaurant.domain.Order </value>
<value>com.packtpub.liverestaurant.domain.Address </value>
<value>com.packtpub.liverestaurant.domain.Customer </value>
<value>com.packtpub.liverestaurant.domain.FoodItem </value>
<value>com.packtpub.liverestaurant.domain.Name </value>
<value>com.packtpub.liverestaurant.domain. PlaceOrderResponse</value>
<value>com.packtpub.liverestaurant.domain. PlaceOrderRequest</value>
</list>
</property>
</bean>

MooseMarshaller, just like XStreamMarshaller, uses annotation in POJO classes for marshalling tasks. @XML tells the marshaller that this class will be serialized/deserialized as 'name'. @XMLField is the tag that should be placed for each class field.

@XML(name="cancelOrderRequest")
public class CancelOrderRequest
{
@XMLField(name="refNumber")
private String refNumber;
/** * Get the 'refNumber' element value.
*
* @return value
*/
public String getRefNumber() {
return refNumber;
}
/**
* Set the 'refNumber' element value.
*
* @param refNumber
*/
public void setRefNumber(String refNumber) {
this.refNumber = refNumber;
}
}

The OrderServiceEndPoint, which is annotated as an @Endpoint, is the same as the JiBX recipes that passing and return parameter is mapped POJO (PlaceOrderResponse, PlaceOrderRequest, and so on) that is mapped to the schema.

@PayloadRoot(localPart = "cancelOrderRequest", namespace = SERVICE_NS)
public
CancelOrderResponse handleCancelOrderRequest(CancelOrderRequest cancelOrderRequest) throws Exception {
CancelOrderResponse cancelOrderResponse=new CancelOrderResponse();
cancelOrderResponse.setCancelled(orderService.cancelOrder(cancelOrderRequest.getRefNumber()));
return cancelOrderResponse;
}
@PayloadRoot(localPart = "placeOrderRequest", namespace = SERVICE_NS)
public
PlaceOrderResponse handleCancelOrderRequest(PlaceOrderRequest placeOrderRequest) throws Exception {
PlaceOrderResponse orderResponse=new PlaceOrderResponse();
orderResponse.setRefNumber(orderService.placeOrder(placeOrderRequest.getOrder()));
return orderResponse;
}

Creating a custom marshaller using XPath for conditional XML parsing

Using the existing marshaller frameworks (JAXB, JiBX, and so on) is always the easiest way to handle a marshalling task. However, eventually you may need to write a customized marshaller. For example, you may get an XML input data, which is in a different format from the one that is generally is being used by the recognized marshaller.

Spring lets you define a customized marshaller and inject it into your endpoint marshaller as existing marshaller frameworks. In this recipe, the client sends/receives this data to/from the server in the following format:

<ns:placeOrderRequest xmlns:ns="http://www.packtpub.com/LiveRestaurant/OrderService/schema">
<ns:order refNumber="12345" customerfName="fName" customerlName="lName" customerTel="12345" dateSubmitted="2008-09-29 05:49:45" orderDate="2008-09-29 05:40:45">
<ns:item type="SNACKS" name="Snacks" quantity="1.0"/>
<ns:item type="DESSERTS" name="Desserts" quantity="1.0"/>
</ns:order>
</ns:placeOrderRequest>
<ns:placeOrderResponse xmlns:ns="http://www.packtpub.com/LiveRestaurant/OrderService/schema" refNumber="1234"/>

However, the XML input that can be mapped to/from the server's POJO is as follows:

<ns:placeOrderRequest xmlns:ns="http://www.packtpub.com/LiveRestaurant/OrderService/schema">
<ns:order>
<ns:refNumber>12345</ns:refNumber>
<ns:customerfName>fName</ns:customerfName>
<ns:customerlName>lName</ns:customerlName>
<ns:customerTel>12345</ns:customerTel>
<ns:dateSubmitted>2008-09-29 05:49:45</ns:dateSubmitted>
<ns:orderDate>2008-09-29 05:40:45</ns:orderDate>
<ns:items>
<FoodItem>
<ns:type>SNACKS</ns:type>
<ns:name>Snack</ns:name>
<ns:quantity>1.0</ns:quantity>
</FoodItem>
<FoodItem>
<ns:type>COFEE</ns:type>
<ns:name>Cofee</ns:name>
<ns:quantity>1.0</ns:quantity>
</FoodItem>
</ns:items>
</ns:order>
</ns:placeOrderRequest>
<ns:placeOrderResponse xmlns:ns="http://www.packtpub.com/LiveRestaurant/OrderService/schema" />
<ns:refNumber>1234</ns:refNumber>
</ns:placeOrderResponse>

In this recipe, a customized marshaller is used to map the incoming XML data to the server's POJO and the unmarshalling server response to the client format.

Getting ready

This recipe contains a server (LiveRestaurant_R-6.6) and a client (LiveRestaurant_R-6.6-Client) project.

LiveRestaurant_R-6.6 has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

  • dom4j-1.6.1.jar

LiveRestaurant_R-6.6-Client has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

  • spring-test-3.0.5.RELEASE.jar

  • junit-4.7.jar

  • dom4j-1.6.1.jar

How to do it...

  1. Create a customized marshaller class.

  2. Register the new marshaller inside the server-side configuration file.

  3. Set up the server and run the client:

    • Server project-root: mvn clean package tomcat:run

    • Client project-root: mvn clean package

      The following is the server-side output:

    Received request ..
    ...
    <ns:placeOrderRequest ...>
    <ns:order customerTel="12345" customerfName="fName" customerlName="lName" dateSubmitted="2008-09-29 05:49:45" orderDate="2008-09-29 05:40:45" refNumber="12345">
    <ns:item name="Snacks" quantity="1.0" type="SNACKS"/>
    <ns:item name="Desserts" quantity="1.0" type="DESSERTS"/>
    </ns:order>
    </ns:placeOrderRequest>
    ....
    Sent response...
    <ns:placeOrderResponse xmlns:ns="http://www.packtpub.com/LiveRestaurant/OrderService/schema" refNumber="12345"/>
    
    

How it works...

To be able to work as an endpoint marshaller, a customized marshaller (ServerCustomMarshaller) should implement Marshaller and Unmarshaller interfaces. The method supports is for verifying if the POJO class is registered with this marshaller. The value of the registered POJO comes from the Spring context file.

The method unmarshal will be called by the endpoint when the Web-Service calls the endpoint method (handleOrderRequest) to build the passing parameter (PlaceOrderRequest). In the unmarshal method, DOM4j and XPath are used to fetch a value from the incoming XML data. These values will populate the POJO class and return it back to the endpoint. The method marshal will be called by the endpoint when the endpoint method (handleOrderRequest) returns the response (PlaceOrderResponse). Inside the marshal method, XMLStreamWriter is used to return the desired format XML data to the client:

public boolean supports(Class<?> arg0) {
return registeredClassNames.contains(arg0.getSimpleName()) ; }
@Override
public Object unmarshal(Source source) throws IOException,
XmlMappingException {
PlaceOrderRequest placeOrderRequest=new PlaceOrderRequest();
Order order=new Order();
try {
DOMSource in = (DOMSource)source;
org.dom4j.Document document = org.dom4j.DocumentHelper.parseText( xmlToString(source) );
org.dom4j.Element orderRequestElem=document.getRootElement();
org.dom4j.Node orderNode=orderRequestElem.selectSingleNode("//ns:order");
order.setRefNumber(orderNode.valueOf("@refNumber"));
....
placeOrderRequest.setOrder(order);
List orderItems=orderNode.selectNodes("//ns:order/ns:item");
.....
}
@Override
public void marshal(Object bean, Result result) throws IOException,
XmlMappingException
{
XMLStreamWriter writer=null;
PlaceOrderResponse placeOrderResponse=(PlaceOrderResponse) bean;
try {
DOMResult out = (DOMResult)result;
writer = XMLOutputFactory.newInstance().createXMLStreamWriter(out);
writer.writeStartElement("ns", "placeOrderResponse", "http://www.packtpub.com/LiveRestaurant/OrderService/schema");
writer.writeAttribute( "refNumber", placeOrderResponse.getRefNumber());
writer.writeEndElement();
writer.flush();
} catch (Exception e) {
e.printStackTrace();
} finally{
try{writer.close();}catch (Exception e) {}
}
} .......

As explained in the previous recipe, the application context for the server/client uses this customized marshaller (ServerCustomMarshaller) to perform the Object/XML marshalling process. RegisteredClassNames is for registering the POJO classes eligible for marshalling/unmarshalling via the customized marshaller (ServerCustomMarshaller).

<bean id="customMarshaller"
class="com.packtpub.liverestaurant.marshaller.ServerCustomMarshaller">
<property name="registeredClassNames">
<list>
<value>PlaceOrderRequest</value>
<value>PlaceOrderResponse</value>
</list>
</property>
</bean>

The OrderEndPoint, which is annotated as an @Endpoint, is the same as the JiBX recipes that the endpoint method's input and and return parameters are POJO (PlaceOrderResponse, PlaceOrderRequest, and so on) that is mapped to the schema.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset