RESTful web-services in Java using JAX-RS - Part 1: Getting up and running

Posted on September 14th, 2009

After having spent some time working with Suns JAX-RS implementation Jersey and integrating RESTful-services in a few products i work on, i thought it might be a good idea to share my experiences.

So i decided to write a few blog-posts. I am not sure yet how many parts there will be, my current plan are 3. In this first part i want to talk about the "trivial" things. It took me some time to gather all the information i needed, especially the Maven artifacts to include were not obvious to me.

Maven: Getting Jersey into your project

In this post i will assume, that you are working on a web-project and use Maven as build-tool. I run most of my apps with an embedded Tomcat, but you can just as well deploy the resulting war to any other container. Further i will explain how to use Suns Jersey, so if you have reasons to use a different JAX-RS-implementation, this part will not apply to you. (If you think these reasons might apply to others as well, please let me know, i am always happy to learn about alternatives.)

Last time i checked, the current versions of Jersey were not available on Mavens central repository. To get them add the following repository to your pom.xml (unless you already have for other dependencies):

    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net Repository for Maven</name>
            <url>http://download.java.net/maven/2/</url>
        </repository>
    </repositories>

Now add the following artifacts:

    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-server</artifactId>
        <version>1.0.3.1</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.1.12</version>
        <scope>compile</scope>
    </dependency>

This should pull in everything else to get a basic web-service up and running.

If you want to put some classes for your web-service into an external pojo, just add the following artifacts to its pom.xml:

    <dependency>
        <groupId>javax.ws.rs</groupId>
        <artifactId>jsr311-api</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.1</version>
        <scope>provided</scope>
    </dependency>

Integration work: How to get Jersey running

First we need to map some requests to Jersey, this works by registering a servlet in your web.xml. Just add this:

    <servlet>
        <servlet-name>Jersey Servlet</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>my.package.api</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Servlet</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>

Replace my.package.api with the real package you intend to use for your resource-classes.
We're mostly done with the implementation specific parts. The next steps involve only JAX-RS things, which lets you take your webservice-classes to any JAX-RS-implementation you like.

Hello World: Your first REST resource

Just really quick: in RESTful web-services everything is modeled as a resource, so basically you create operations for CRUD (create, read, update, delete). For different operations, different HTTP-methods are used, mainly GET, POST and DELETE. You might also use PUT sometimes. Important: it should never matter wether the same GET-request is sent multiple times.
Lets create a first, really basic resource-class:

    @Path("/notes")
    @Produces({"application/xml"})
    public class NotesHandler {
 
        @GET
        public String list() {
            return "Hello World, I still need some work to be useful!";
        }
 
    }

After deploying this to the container of your choice, you can open /api/notes in your web-browser. This should produce an xml-syntax-error, since only "Hello World, I still need some work to be useful!" is returned, which is obviously not valid xml.
So lets move on to create a class to wrap our result. We'll use JAXB-annotations to have this easily marshalled to xml:

    @XmlRootElement(name="notes")
    public class NotesList {
 
        private String result;
 
        public NotesList() {
        }
 
        public NotesList(String result) {
            this.result = result;
        }
 
        public String getResult() {
            return result;
        }
 
        public void setResult(String result) {
            this.result = result;
        }
 
    }

Now, also modify your handler-class like this:

        @GET
        public NotesList list() {
            return new NotesList("Hello World, I still need some work to be useful!");
        }

We now get valid xml, but this is still really useless. Lets add some logic to it:

    @Path("/notes")
    @Produces({"application/xml"})
    public class NotesHandler {
 
        /**
         * This is really, really wrong. Just so that we can do some playing around
         * without having to setup a database.
         */
        private static SortedMap<Long, Note> notes = new TreeMap<Long, Note>();
        static {
            add("Hello World, I still need some work to be useful!");
        }
 
        @GET
        public NotesList list() {
            NotesList result = new NotesList();
            result.getNotes().addAll(notes.values());
            return result;
        }
 
        private static Note add(String text) {
            Note note = new Note(notes.isEmpty() ? 1 : notes.lastKey(), text);
            notes.put(note.getId(), note);
            return note;
        }
 
    }
 
    @XmlRootElement(name="notes")
    public class NotesList {
 
        @XmlElement(name="note")
        private List<Note> notes = new ArrayList<Note>();
 
        public NotesList() {
        }
 
        @XmlTransient
        public List<Note> getNotes() {
            return notes;
        }
 
        public void setNotes(List<Note> notes) {
            this.notes = notes;
        }
 
    }
 
    @XmlRootElement(name="note")
    @XmlType
    public class Note {
 
        private Long id;
        private String text;
 
        public Note() {
        }
 
        public Note(Long id, String text) {
            this.id = id;
            this.text = text;
        }
 
        public Long getId() {
            return id;
        }
 
        public void setId(Long id) {
            this.id = id;
        }
 
        public String getText() {
            return text;
        }
 
        public void setText(String text) {
            this.text = text;
        }
 
    }

Enough with that GET, lets go POST something

Wouldnt it be great, if we could actually create notes, too? In RESTful web-services this is done with POST instead of GET, so we'll change the add method to handle POSTs:

        @POST
        @Path("/new")
        @Consumes("text/plain")
        public Note add(String text) {
            Note note = new Note(notes.isEmpty() ? 1 : notes.lastKey(), text);
            notes.put(note.getId(), note);
            return note;
        }

Also, remember to remove the static-block, that we added for testing.
You can now send a POST-request to /api/notes/new with the note as body to add a new note. I use the Firefox-extension Poster for this.

Okay... I think i'll just end this right here! I was going to write a little more, but this is getting really long as it is right now, so i'll just move the next steps to the next part!

I hope this is helpful for somebody to get started with JAX-RS.

AddThis button



About Me

Photo of myself Aike J Sommer
web [at] aikesommer [dot] name
Feed: RSS Syndicate content
Company: AS Media