Creating a CXF service that responds with JSON or XML

In the first post of the three, we generated the source code from an XSD, now we’re going to create a CXF service that will be able to handle responses in either JSON or XML.

Note: Unlike a previous post on CXF services, this is not a SOAP webservice but is a REST webservice which can respond in the two formats (JSON or XML)

Show me the POM

Here’s the POM (save it as pom.xml in your project’s folder if you’re following along with this post).

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.putridparrot.server</groupId>
    <artifactId>ppserver</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>ppserver</name>
    <url>http://maven.apache.org</url>

    <properties>
        <cxf.version>2.7.18</cxf.version>
        <httpclient.version>4.3.6</httpclient.version>
        <surefire.version>2.19.1</surefire.version>
        <jackson.version>1.9.13</jackson.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>${httpclient.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-logging</artifactId>
                    <groupId>commons-logging</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-jaxrs</artifactId>
            <version>${jackson.version}</version>
        </dependency>
    </dependencies>
</project>

Setting up the project

If using IntelliJ, select File | Open, navigate to the pom and open it, you’ll be prompted to open in different ways – Open as Project and then IntelliJ will generate a new project around it.

Use the IntelliJ Terminal window to run

mvn install

We now need to create the standard folder structure, so off of the top level tree node in the project explorer create the following folders

src/main/java

In the project explorer select the java folder and right mouse click on it, select Mark Directory As and then Sources Root.

Now copy the folders generated by XJC (in the previous post) into the java folder.

Implement the server code

It’s preferable to separate the interface (which we’ll need for our client) from the implementation of our service, so create a SampleService interface in the java folder that looks like this

import com.putridparrot.sample.PurchaseOrderType;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("service")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public interface SampleService {
    @GET
    @Path("purchaseOrder")
    public PurchaseOrderType getPurchaseOrder();
}

Notice we’ve marked the interface as producing either JSON or XML.

We’ve set the path to our services as /service and the getPurchaseOrder method as purchaseOrder (we’ve also marked this method as a GET method) so to access we’ll simply append /service/purchaseOrder to our server’s URL.

Now let’s create the implementation, so add a file to the java folder named SampleServiceImpl and it should look like this

import com.putridparrot.sample.PurchaseOrderType;
import com.putridparrot.sample.USAddress;

public class SampleServiceImpl implements SampleService{
    public PurchaseOrderType getPurchaseOrder() {
        PurchaseOrderType po = new PurchaseOrderType();

        USAddress address = new USAddress();
        address.setCity("New York");
        address.setCountry("USA");
        address.setName("SpongeBob");

        po.setBillTo(address);

        return po;
    }
}

Obviously this isn’t useful, but it’s good enough to demonstrate how things fit together.

Finally we’re going to create a server to run this service, so create a new file in the java folder named SampleServer.java and put the following code into it.

import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;

import javax.ws.rs.core.MediaType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SampleServer {
    public static void main(String args[]) throws Exception {
        JAXRSServerFactoryBean factoryBean = new JAXRSServerFactoryBean();
        factoryBean.setResourceClasses(SampleServiceImpl.class);
        factoryBean.setResourceProvider(new SingletonResourceProvider(new SampleServiceImpl()));

        Map<Object, Object> extensionMappings = new HashMap<Object, Object>();
        extensionMappings.put("xml", MediaType.APPLICATION_XML);
        extensionMappings.put("json", MediaType.APPLICATION_JSON);
        factoryBean.setExtensionMappings(extensionMappings);

        List<Object> providers = new ArrayList<Object>();
        providers.add(new JAXBElementProvider());
        providers.add(new JacksonJsonProvider());
        factoryBean.setProviders(providers);

        factoryBean.setAddress("http://localhost:9000/");
        Server server = factoryBean.create();

        System.out.println("Server ready...");

        System.in.read();

        server.destroy();
        System.exit(0);
    }
}

Now if you run this server up and using your preferred browser access http://localhost:9000/service/purchaseOrder you’ll find an error

JAXBException occurred : unable to marshal type “com.putridparrot.sample.PurchaseOrderType” as an element because it is missing an @XmlRootElement annotation. unable to marshal type “com.putridparrot.sample.PurchaseOrderType” as an element because it is missing an @XmlRootElement annotation.

As can be seen, JAXB expects an XmlRootElement and our generated code does not have one. This is why XJC generated more files than types in the XSD. You may have noticed ObjectFactory. Well without an XmlRoot we need to handle things slightly differently and this is where the ObjectFactory comes in – but for this example (and frankly because it’s ultimately simpler) we’ll add XmlRoot to the PurchaseOrderType ourselves, so it should now look like this

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "PurchaseOrderType", propOrder = {
    "shipTo",
    "billTo"
})
public class PurchaseOrderType {
// rest of the code
}

Now if we run the server and again use our browser to access http://localhost:9000/service/purchaseOrder you should see the following

<PurchaseOrder xmlns="http://tempuri.org/PurchaseOrderSchema.xsd">
   <BillTo country="USA">
      <name>SpongeBob</name>
      <city>New York</city>
   </BillTo>
</PurchaseOrder>

Now I mentioned this server should be able to return both JSON or XML. In this case our browser accepts application/xml and hence that’s what’s returned. We can use a chrome application such as ARC and add accept: application/json to return the JSON for the same request, her’s what’s returned

{
"shipTo": [],
"billTo": {
"name": "SpongeBob",
"street": null,
"city": "New York",
"state": null,
"zip": null,
"country": "USA"
},
"orderDate": null
}

Wait, what just happened?

We’ve jumped ahead a little and not described the server. So let’s backtrack a little. In the server we’re creating a JAXRSServerFactoryBean and creating a singleton of the SampleServiceImpl which will be used to handle service requests, but then we do the following

Map<Object, Object> extensionMappings = new HashMap<Object, Object>();
extensionMappings.put("xml", MediaType.APPLICATION_XML);
extensionMappings.put("json", MediaType.APPLICATION_JSON);
factoryBean.setExtensionMappings(extensionMappings);

Here we’re telling the factoryBean that we can handle these specific extension types and mapping them, but we need to have providers which actually do the work for us, so then we add the following

List<Object> providers = new ArrayList<Object>();
providers.add(new JAXBElementProvider());
providers.add(new JacksonJsonProvider());
factoryBean.setProviders(providers);

The JAXBElementProvider handles the XML whilst the JacksonJsonProvider (as I’m sure you guessed) handles the JSON. Basically these will generate the message body for our responses etc.

Finally in the main method we create a server from our bean for the given address.