Rapid Web Services Development with Moose XML Blog

Version 2

    Moose XML is a framework that provides a set of components for getting XML data into and out of Java applications. Moose XML was created after I had experienced some of the rough edges in existing XML marshalling solutions when used in web services environments. Wherever possible, design choices have been made to simplify Moose XML's support for "contract last" web services development.

    Moose XML's schema generator is what distinguishes it from the other frameworks. The schema generator can generate an XML schema directly from your annotated Java beans. These generated schemas are useful for driving "contract last" web services development approaches. When combined with the WSDL plumbing in your favorite web services toolkit, the schema generator can make the rapid development of web services applications a reality. Because the stack is automatically managing your schemas, your mapping code is easily refactored, expanded, and maintained alongside your other Java code—changes flow directly from your annotated Java beans into your generated WSDL.

    This article will give you a quick tour of Moose XML. I'll start by showing you the basics of creating a mapping. I'll finish by showing you how this can be plugged into Spring Web Services, creating a rapid web services development stack. We'll create a simple "purchase order" web service, allowing for storage and retrieval of simplified purchase order data.

    More information is available at the Moose XML website. The full source code of this example is available in the Moose XML source distribution. Links are provided in the Resources section at the bottom of the article.

    Creating the Mapping

    Our service stores and retrieves "purchase orders". Imagine that these purchase orders are for software licenses. As such, each purchase order contains a "licensee" and "license type" and a currency "amount". Purchase orders are identified by a numeric "identifier". The license type is represented using a Java 5enum named LicenseType.

    Each operation needs to include a "request" and "response" message. We'll start with theCreatePurchaseOrderRequest andCreatePurchaseOrderResponse types:

    package com.quigley.moose.example.service; public class CreatePurchaseOrderRequest { // getters and setters removed for brevity private String licensee; private LicenseType licenseType; private Float amount; } 
    package com.quigley.moose.example.service; public class CreatePurchaseOrderResponse { // getters and setters removed for brevity private Long identifier; } 

    Simple POJO classes like these make up our mapping layer. We'll need to add a few annotations so that Moose XML understands how to marshall and unmarshall instances of these classes:

    package com.quigley.moose.example.service; import com.quigley.moose.mapping.provider.annotation.*; @XML(name="createPurchaseOrderRequest") public class CreatePurchaseOrderRequest { // getters and setters removed for brevity @XMLField(name="licensee") private String licensee; @XMLField(name="licenseType") private LicenseType licenseType; @XMLField(name="amount") private Float amount; } 
    package com.quigley.moose.example.service; import com.quigley.moose.mapping.provider.annotation.*; @XML(name="createPurchaseOrderResponse") public class CreatePurchaseOrderResponse { // getters and setters removed for brevity @XMLField(name="identifier") private Long identifier; } 

    The @XML annotation defines the class-level mapping details. Each of the @XMLField annotations define the field-level mapping details. The Moose XML developer guide contains information on many other annotations, which are useful for more complicated mapping scenarios.

    Marshalling and Unmarshalling

    In order to initialize Moose XML, the application will need to acquire a Mapping instance. A Mappinginstance is typically obtained by instantiating and configuring aMappingProvider instance. Here's an example:

    StaticClassesProvider classesProvider = new StaticClassesProvider(); classesProvider.addClass(CreatePurchaseOrderRequest.class); AnnotationMappingProvider mappingProvider = new AnnotationMappingProvider(classesProvider); Mapping mapping = mappingProvider.getMapping(); 

    The Mapping class is central to Moose XML. Once your application has access to a Mapping, performing operations with the framework is straightforward.

    The following code snippet illustrates how to use aMapping to marshall aCreatePurchaseOrderRequest object out to XML:

    CreatePurchaseOrderRequest request = new CreatePurchaseOrderRequest(); request.setAmount(64.95F); request.setLicensee("Benjamin Franklin"); request.setLicenseType(LicenseType.Renewal); MarshallingContext ctx = new MarshallingContext(); mapping.marshall(request, ctx); String xml = ctx.getOutput().toString(); 

    Here's how to unmarshall aCreatePurchaseOrderRequest from XML:

    CreatePurchaseOrderRequest request = (CreatePurchaseOrderRequest) mapping.unmarshall(xml); 

    Generating a XML schema from your Mapping instance is no more difficult:

    String xsd = SchemaGenerator.generate(mapping); 

    Spring Web Services Integration

    For the remainder of this example, I'm going to assume that you're familiar with the Spring Web Services framework and SOAP web services. Instead of describing every part of the configuration, we'll just look at the parts where Moose XML facilitates rapid web services development.

    Our example contains a single "endpoint" class, which is used to expose the web service operations. This example uses the Spring@Endpoint and @PayloadRoot annotations to establish a service endpoint. Here is a snippet from the example service endpoint to illustrate the pattern:

    @Endpoint public class ServiceEndpoint { @PayloadRoot(localPart="createPurchaseOrderRequest", namespace="http://quigley.com/moose/example/service/") public CreatePurchaseOrderResponse createPurchaseOrder(CreatePurchaseOrderRequest req) { // } } 

    When configured, Spring will route incoming messages to the method which matches the @PayloadRoot annotations defined in your @Endpoint classes. Spring Web Services also needs to know how to convert the incoming and outgoing XML to and from the parameter types and return values of your endpoint methods. Spring Web Services provides a niceAbstractMarshaller class, which facilitates the integration of various marshalling frameworks into Spring. Moose XML extends AbstractMarshaller to provide aMooseMarshaller type.

    The bulk of the magic happens in our Spring context configuration:

    <beans> <!-- xmlns and schema location declarations removed for brevity --> <context:annotation-config/> <bean id="serviceEndpoint" class="com.quigley.moose.example.service.ServiceEndpoint"/> <bean id="service" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition"> <property name="schema"><ref bean="mooseSchema"/></property> <property name="portTypeName"><value>ExampleService</value></property> <property name="locationUri"><value>http://localhost:8080/example/</value></property> </bean> <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://quigley.com/moose/example/service/</value></property> <property name="xmlPrefix"><value>es</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.quigley.moose.example.service.CreatePurchaseOrderRequest</value> <value>com.quigley.moose.example.service.CreatePurchaseOrderResponse</value> <value>com.quigley.moose.example.service.RetrievePurchaseOrderRequest</value> <value>com.quigley.moose.example.service.RetrievePurchaseOrderResponse</value> </list> </property> </bean> <bean id="mooseSchema" class="com.quigley.moose.spring.MooseXsdSchema"> <property name="mappingProvider"><ref bean="mooseMappingProvider"/></property> </bean> </beans> 

    Notice the GenericMarshallingMethodEndpointAdapter. It's intended to take an instance ofAbstractMarshaller as a constructor argument. We're configuring it to use our MooseMarshaller. This bean configuration is what allows our service endpoint to magically marshall and unmarshall XML to and from the methods of ourServiceEndpoint class.

    The most interesting bits are the mooseSchema bean and the service bean. This combination of beans connects Moose XML's schema generator to the WSDL generation capabilities in Spring Web Services. With that linkage in place, any changes you make to your mapping beans will be automatically reflected in your WSDL.

    Building the Example from the Source Distribution

    Download the Moose XML source distribution from the link below. Extract the distribution into a directory. From the directory where you extracted the source distribution, execute:

    $ ant -f build-example.xml example-service 

    When that completes successfully, you can start up a fully-configured container, pre-loaded with this example, by executing:

    $ build/example-service/deploy/server/bin/run.sh 


    There are many tools and frameworks for getting XML data into and out of Java applications. Moose XML tries to smooth some of the rough edges that are typically encountered when prototyping and rapidly developing XML web services. In these scenarios, generating the schema and contract information from code ("contract last") seems to be the least cumbersome methodology.

    Development is currently underway extending Moose XML to support "contract first" development approaches. The centerpiece is a new tool which can ingest an existing XML schema, and generate an annotated mapping layer. This functionality will be available before Moose XML hits version 1.0.