Java Quick Start – JAX-RS RESTful services with CXF and TomCat

JAX-RS makes it pretty straightforward to build RESTful services in Java. In this quick start we create a simple-as-possible example with a little help from CXF and TomCat.

In the world of serious, heavyweight business-to-business web services, SOAP will probably remain the undisputed king for the foreseeable future. But many web services aren’t serious heavyweights and in these cases simple XML or JSON over HTTP is perfectly sufficient.

Moreover with WADL (Web Application Definition Language) files to describe our RESTful (Representation State Transfer) service endpoints and the payloads they accept and return our services can be just as self-descriptive and easy to work with in general purpose clients such as SoapUI.

In the Java world the JAX-RS specification provides a standard for implementing RESTful services in an implementation-independent manner for those of us who care about that sort of thing. For those of us who don’t care about that sort of thing, Apache CXF provides everything you need to build production ready REST services effectively the JAX-RS way.

The code for this quick start can be found on GitHub at https://github.com/devguerrilla/java-quick-starts/tree/master/christmas-turkey-service.

Step One: Assemble your ingredients

For this quick start we’re going to create a simple-as-possible, retrieval only Java based JSON REST service which we’ll deploy to TomCat. Since we’re using Maven as a build tool, Java, Maven and TomCat are the only components you’ll need to worry about:

  • Java 8
  • Maven 3.2
  • TomCat 8

There’s no rocket science in this example (skip ahead if you don’t believe me) and recent, older version of these tools will work just fine.

To help us along the way we’re going to use CXF 3 as a JAX-RS framework, Jettison 1.3 for our JSON handling and Spring 3 for runtime assembly. All of these are brought in via our Maven POM file:-

<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/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.devguerrilla.quickstart.java</groupId>
    <version>0.0.1-SNAPSHOT</version>
    <artifactId>christmas-turkey-service</artifactId>
    <packaging>war</packaging>
    <name>christmas-turkey-service</name>

    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-rs-extension-providers</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jettison</groupId>
            <artifactId>jettison</artifactId>
            <version>1.3.5</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Here we’re pulling in CXF’s JAX-RS front-end and extension providers along with Spring web and Jettison. There are other transitive dependencies of course but Maven will take care of them for us.

Step Two: Define the interface

It’s Christmas as I write this and, while devguerrilla’s culinary skills don’t stretch much further than re-heating frozen meals or ordering takeaways, the holiday season affords me the opportunity to check the oven still works and try actually cooking something for a change, safe in the knowledge that I’ve got a few days to recover from the inevitable food poisoning before I’m back in the office.

The instructions on the back of my pre-packaged, pre-prepared, pretty late and pretty small budget supermarket festive turkey for lonely sad-acts also inspires today’s quick start:-

public interface CookingTimeService {
    public CookingInstruction getCookingTime(
            String fleshType,
            Float fleshWeight) 
        throws AnimalNotFoundException;
}

Our CookingTime service will accept a type of meat and a weight and return a CookingInstruction which tells us what temperature to set the oven at and how long to cook it (trust a techie to make Christmas dinner a programming exercise!).

To kick off with, let’s define our CookingInstruction model object:-

@XmlRootElement
public class CookingInstruction {

    private Integer timeToCook = null;
    private Integer temperatureToSet = null;

    public CookingInstruction() {
    }

    public CookingInstruction(Integer timeToCook, 
            Integer temperatureToSet) {
        this.timeToCook = timeToCook;
        this.temperatureToSet = temperatureToSet;
    }

    // Getters and Setters

}

This is a pretty straightforward Java bean containing our two required properties. Even though we’re going to be using JSON for this service we still need to annotate our class with @XmlRootElement since CXF’s JSON marshalling works by post-processing a JAXB built response – if you don’t annotate your model objects in this way you’ll see errors like this when you make requests:-

No message body writer has been found for class 
com.devguerrilla.quickstarts.java.turkeyservice.CookingInstruction,
ContentType: application/json

Next we need to add some JAX-RS annotations to our interface so that the runtime can map request URLs to our methods and parameters.

@Path("/cookingtime")
public interface CookingTimeService {

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/{fleshType}/{fleshWeight}")
    public CookingInstruction getCookingTime(
            @PathParam("fleshType") String fleshType,
            @PathParam("fleshWeight") Float fleshWeight)
        throws AnimalNotFoundException;
}

The @Path annotation on the interface tells JAX-RS the root path all our methods will sit under. As our service is query-only associating our method with an HTTP GET with the @GET annotation is a bit of a no-brainer; if we were supporting updates we could use @PUT, @POST or @DELETE here instead. The @Produces annotation tells JAX-RS that we return JSON and makes sure it sets the appropriate Content-Type header for us.

Our other annotations map our method signature to a sub-path relative to the root. @Path(“/{fleshWeight}/{fleshType}”) tells JAX-RS that any URL with two components under our root context maps to this method and that the two components map to parameters in our method signature, which we’ve marked with @PathParam annotations.

It seems obvious to me that our fleshType should be part of the request URI (e.g. http://myserver/cookingtime/turkey). Arguably the weight would make more sense as a query parameter (e.g. ../turkey?fleshWeight=1.2) rather than part of the path (../turkey/1.2). We could easily do it that way instead by dropping the {fleshType} bit off the @Path and changing the parameter’s annotation to @QueryParam instead.

Step Three: Implement our interface

I’m going to be lazy here (hey, it’s Christmas) and implement the “business logic” of my service in an enumeration which defines each type of flesh we know how to cook and the details on how to cook it:-

public enum CookingTime {

    TURKEY(30, 50, 180);

    private Integer baseTime = null;
    private Integer perKiloTime = null;
    private Integer temperatureInCelsius = null;

    private CookingTime(Integer baseTime,
                        Integer perKiloTime,
                        Integer temperatureInCelsius) {
        this.baseTime = baseTime;
        this.perKiloTime = perKiloTime;
        this.temperatureInCelsius = temperatureInCelsius;
    }

    public Integer getTotalTime(Float weightInKilos) {
        return Integer.valueOf((int)((weightInKilos * perKiloTime) + 
            baseTime));
    }

    public Integer getTemperatureInCelsius() {
        return temperatureInCelsius;
    }
}

For each type of meat we know how to cook our enumeration configures a base cooking time, a per-kilo cooking time and a temperature (in Celsius) and we provide a couple of getters, one for the oven temperature and one that calculates the overall time based on weight. It’s not the most configurable approach but I can live with recompiling this next year if I fancy beef for a change.

My service implementation class therefore just needs to lookup the appropriate enum based on the flesh type and return a CookingInstruction based on the values returned from its two getters:-

public class CookingTimeServiceImpl 
        implements CookingTimeService {

    @Override
    public CookingInstruction getCookingTime(String fleshType, 
            Float fleshWeight) throws AnimalNotFoundException {
        try {
            CookingTime cookingTime =
                CookingTime.valueOf(fleshType.toUpperCase());
            return new CookingInstruction(
                cookingTime.getTotalTime(fleshWeight),
                cookingTime.getTemperatureInCelsius());
        } catch(IllegalArgumentException eUnknownAnimal) {
            throw new AnimalNotFoundException(
                "Sorry, don't know how to cook " + fleshType);
        }
    }
}

Step Four: Map errors to HTTP codes

Okay, I lied a little when I said this was going to be the simplest possible example. We could just let the IllegalArgumentException throw up to the runtime and return an HTTP 500 with a stack-trace to the caller, but that’s pretty poor user experience. In the world of REST we should, wherever possible, map stuff to HTTP and that includes errors.  404 (not found) would seem a good fit here.

So instead, if we get a request for a type of meat we don’t know how to cook we’ll throw a reasonably helpful error message back in an AnimalNotFoundException:-

public class AnimalNotFoundException extends Exception {
    public AnimalNotFoundException(String message) {
        super(message);
    }
}

And we’ll map this to an HTTP 404 error code with our error message encoded in a JSON payload. To do this we need to implement an ExceptionMapper which can convert our exception to a JSON response:-

public class AnimalNotFoundExceptionMapper
        implements ExceptionMapper<AnimalNotFoundException> {

    @Override
    public Response toResponse(AnimalNotFoundException exception) {
        String errorMessage = null;
        try {
            errorMessage = new JSONObject().
                 put("message", exception.getMessage()).
                 toString();
        } catch(JSONException eJson) {
            errorMessage = "";
        }
        return Response.status(Response.Status.NOT_FOUND).
                        entity(errorMessage).
                        build();
    }
}

Since I’ve got Jettison as part of my build I’ll use its JSONObject class to render a {“message”, “Sorry, don’t know how to cook poodle”} style response and use JAX-RS’s Response builder to set the appropriate status and include the error message when an AnimalNotFoundException is caught.

Step Five: Assemble our web application.

Almost there – that’s all the code we need written so we just need to configure our service in Spring and publish it via our web application’s web.xml file.

First of all, in src/main/resources we’ll create our spring beans.xml file:

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

    <bean id="jsonProvider"
          class="org.apache.cxf.jaxrs.provider.json.JSONProvider"/>

    <bean id="animalNotFoundExceptionProvider"
          class="com.devguerrilla.quickstarts.java.turkeyservice.AnimalNotFoundExceptionMapper"/>

    <bean id="cookingTimeService"
          class="com.devguerrilla.quickstarts.java.turkeyservice.CookingTimeServiceImpl"/>

    <jaxrs:server id="cookingTimeServer" address="/">
        <jaxrs:serviceBeans>
            <ref bean="cookingTimeService"/>
        </jaxrs:serviceBeans>
        <jaxrs:extensionMappings>
            <entry key="json" value="application/json" />
        </jaxrs:extensionMappings>
        <jaxrs:providers>
            <ref bean="jsonProvider"/>
            <ref bean="animalNotFoundExceptionProvider"/>
        </jaxrs:providers>
    </jaxrs:server>
</beans>

We start off by defining a couple of provider beans for JAX-RS. The first is a JSONProvider which will handle mapping to and from JSON for us and the second is an instance of our exception mapper.

Secondly we define an instance of our service as a bean.

Thirdly we create a JAX-RS server instance which includes our two providers and our service instance along with an extensionMapping entry to tie in the JSON handler. The address property controls the base address for our service between the context our application is deployed on and the root @Path of our service. Collectively that’s quite a lot of URL so here we’ll just leave it as the shortest possible /.

Finally, in src/main/webapp/WEB-INF we can define our web.xml file which just needs to initialise our Spring context when the application starts and tie the CXFServlet to our URLs:-

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3.0.xsd"
         xsi:schemaLocation="
             http://java.sun.com/xml/ns/javaee
             http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <display-name>
        Java Quick Starts - Christmas Turkey Service
    </display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:beans.xml</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

Step Six: Cook our Christmas dinner

Running an mvn clean install gives us a christmas-turkey-service.war file we can drop into TomCat’s webapps folder and we’re good to go.

Since our service is query-only we can easily use a web-browser or wget to test it, but curl is probably the best command line tool to have in your armoury here as it’ll handle create, update and delete calls as well and also show you the payload from the server even when error codes are returned. It’s also pretty widely available.

Let’s check that error handling first:-

$ curl -w "\nHTTP: %{http_code}\n"
http://localhost:8080/christmas-turkey-service/cookingtime/pussycat/1.23
{"message":"Sorry, don't know how to cook pussycat"}
HTTP: 404

Perfect We’ve got a reassuringly useful and meaningful error message there.

Finally, time to find our how long to cook my pre-packaged, supermarket Christmas turkey for the mateless and dateless:-

$ curl -w "\nHTTP: %{http_code}\n"
http://localhost:8080/christmas-turkey-service/cookingtime/turkey/1.1
{"cookingInstruction":{"temperatureToSet":180,"timeToCook":85}}
HTTP: 200

Great! Dinner is only 85 minutes away. Time to open the wine I think.

Hmmm. Serves four eah? Should see me through the weekend too!

The full code for this quick start is on GitHub at https://github.com/devguerrilla/java-quick-starts/tree/master/christmas-turkey-service and, if I survive my own cooking, next I’ll show you a neat way of testing CXF JAX-RS APIs in a Spock unit test.

 

2 responses to “Java Quick Start – JAX-RS RESTful services with CXF and TomCat

  1. Excellent guide – thank you. Both the JAX-WS and JAX-RS articles were very useful.

    I think you have a small mistake there though – you refer to enums as annotations in a couple of places. Otherwise – pure gold

    • Cheers mate. Yep, I’d definitely got my annotations and enumerations mixed up in the middle of this – now corrected. I should never blog when I’ve been drinking!

Leave a Reply

Your email address will not be published. Required fields are marked *