Java EE 6 and modular JAX-RS services

Problem Statement

I often create custom web applications to support mobile products that have a few functions in common. For e.g., many web applications need to let users authenticate themselves from a mobile application. These functions are exposed to the mobile applications using a RESTful API. I’d like to create re-usable bundles of these services and add as many or as few of them as needed to my custom web application and write as little integration logic as possible, focusing all my time on the custom business logic of the product being developed.

Here is a “note-to-self” primer on how to go about bundling JAX-RS services in a jar and then using them from a war.

I assume that the projects are built using Maven and that they are being deployed to a Java EE 6 app container.

The REST API and the jar

Let’s create a jar project called resource (I assume you know how to create a jar using Maven).

The JAX-RS API dependency

First let’s get our dependencies lined up. Our code will refer to the JAX-RS APIs at least. The APIs are provided by the app container, so our dependency will be in the provided scope. The only question is, what GAV (groupId-artifactId-version triplet that uniquely identifies a dependency in Maven) vector to use?

Typical web applications have this in their pom.xml:

        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>

but I prefer dependencies from apache geronimo project instead (because the above dependency cannot be used in unit tests and does not allow me to download javadocs):

        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-jaxrs_1.1_spec</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>

For illustration, we shall create a JAX-RS based REST API to manage a simple entity. For simplicity we shall not bother about where this entity is stored, retrieved from etc.

Here is my entity class:

// file src/main/java/net/nihilanth/demo/resource/GenericEntity.java
package net.nihilanth.demo.resource;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class GenericEntity {

    private long id;
    private String name;

    // getter, setters and constructors omitted
}

Note the @XmlRootElement annotation. This is needed so that I can marshal my entity to/from XML. It also automatically allows me to marshal it to/from JSON. If I were not using XML but only JSON, I’d still keep this annotation just because it easily lets me marshal my entity to/from JSON.

And I expose this as a REST resource:

// file src/main/java/net/nihilanth/demo/resources/GenericResource.java
package net.nihilanth.demo.resource;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.ws.rs.*;

@Path("/generic")
public class GenericResource {
    private long nextId = 1;

    // in lieu of a database, we shall retrieve entities from a list
    List<GenericEntity> list = new ArrayList<GenericEntity>();

    // we create a few instances so that we can see something in our tests
    // in real life you'd have code to retrieve entities from a database etc.
    public GenericResource() {
        createEntity(new GenericEntity("alpha"));
        createEntity(new GenericEntity("beta"));
        createEntity(new GenericEntity("gamma"));
    }

    @POST
    @Consumes("application/json")
    public String createEntity(GenericEntity entity) {
        entity.setId(nextId);
        nextId++;
        list.add(entity);
        return "" + entity.getId();
    }

    @GET
    @Produces("application/json")
    public List<GenericEntity> list() {
        return Collections.unmodifiableList(list);
    }
}

And that’s all that is needed. I now compile and “install” the jar in my local repository (or eventually, publish to our corporate maven repository). I can’t see my REST API in action yet: I have to create a web application that uses my JAR first.

The web application

Let’s create a war project called, simply, webapp.

The JAX-RS API dependency and the JAR dependency

I replace the default javaee-web-api dependency put there by Maven with our geronimo based dependency, and also add a dependency to the jar we just created:

<!-- this the pom.xml depedencies section for my war -->
    <dependencies>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-jaxrs_1.1_spec</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>net.nihilanth.demo</groupId>
            <artifactId>resource</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

Using our REST API

The JAR we created above is included in our WAR as a dependency. We have to figure out a way to tell the app container to locate our GenericResource from the JAR and publish it. We also have to decide the URL under which the resource will be available. We decide that all our REST resources will be made available under the URL /resources under our context path. To coax the app container to search our JARs and instantiate any REST resources found in them, we create this class:

// file src/main/java/net/nihilanth/demo/webapp/JaxRsApplication.java
package net.nihilanth.demo.webapp;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/resources")
public class JaxRsApplication extends Application {
}

The exact name and package of the class is not important. It just has to exist in our WAR, and the important thing is that:

  • It extends javax.ws.rs.core.Application, which tell the app container that our WAR contains one or more JAX-RS resources
  • It is annotated with @ApplicationPath annotation, which tells the app container the relative path under which our JAX-RS resources will be available (“resources”, in this case).

And that’s it. We don’t even need a web.xml in this example. Just build and deploy the war to an app container (e.g., glassfish v3). Note:

  • My instance of glassfish is listening on port 8080 (the default). I can thus access my web applications from the URL http://localhost:8080/
  • I have specified nothing special regarding the context path of my web application. By default, therefore, the web application’s context name is the same as my war file name, that is, webapp. Thus I can access my web app at the URL http://localhost:8080/webapp/
  • In the @ApplicationPath annotation I specified that the JAX-RS resources be made available under “resources” URI; thus all my JAX-RS resources are available at the URL http://localhost:8080/webapp/resources/
  • Finally, in my GenericResource class, the annotation @Path specified that this resource in named generic. So the individual methods of the GenericResource will be available under the URL http://localhost:8080/webapp/resources/generic

So let’s give it a spin. Here is here how I list the existing GenericEntity instances from the command line:

$ curl -H 'Accept: application/json' 'http://localhost:8080/webapp/resources/generic'
{"genericEntity":[{"id":"1","name":"alpha"},{"id":"2","name":"beta"},{"id":"3","name":"gamma"}]}

We got the three entities we had pre-prepared. Let’s add a new one:

$ curl -X POST -A 'Accept: application/json' -H 'Content-type: application/json' -d '{"name": "foo"}' 'http://localhost:8080/webapp/resources/generic'
4

There we go! The new entity was added and its new ID, 4, was returned.

Issues

You might have noticed that the return type of the GenericResource.createEntity method is String, even though the underlying data type (the ID of the created entity) is long. Why? Because out of the box, JAX-RS implementations cannot convert long or java.lang.Long to an external media type. If we want to keep the method return type as a primitive type, we shall have to provide our own entity mapper. But that is fodder for another post.

Another issue you might notice is that after creating a new entity, if we try to list all the entities, we get the same three entities back. Our fourth entity is not listed. This is because a new instance of GenericResource is created for every new request. We will fix this issue in a subsequent article.

About these ads
Post a comment or leave a trackback: Trackback URL.

Comments

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 80 other followers

%d bloggers like this: