Skip to main content

Rapid Web Services Development with Moose XML

April 28, 2010



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 5
enum named LicenseType.

Each operation needs to include a "request" and "response"
message. We'll start with the
CreatePurchaseOrderRequest and
CreatePurchaseOrderResponse 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 Mapping
instance is typically obtained by instantiating and configuring a
MappingProvider 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 a
Mapping to marshall a
CreatePurchaseOrderRequest 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 a
CreatePurchaseOrderRequest 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 nice
AbstractMarshaller class, which facilitates the
integration of various marshalling frameworks into Spring. Moose
XML extends AbstractMarshaller to provide a
MooseMarshaller 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 of
AbstractMarshaller 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 our
ServiceEndpoint 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

Conclusion

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.

Resources


width="1" height="1" border="0" alt=" " />
Michael Quigley is a software architect, a musician, a sailor, and a lover of animals. He enjoys spending his time somewhere in the confluence of technology and the arts and wants your development projects to be awesome.
Related Topics >> Java Tech   |   Tools   |   Web Services and XML   |   Featured Article   |