Building RESTful webservices with JAX-RS

27 March 2014

Prerequisites

To start deploying your JAX-RS application you need to install:

  • Java JDK (1.6 or later)
  • Glassfish (4.0 or later) or any other JavaEE server which implements Java EE 6 or 7.

Download java latest version and follow installation instructions. Download Glassfish and unzip on preferred location, test installation using commands:

cd $GLASSFISH_FOLDER$/glassfish/bin/
asadmin start-domain domain1 

If you got the domain up and running successfully you're ready to step to the next action



Create REST webservices in your Webapp

Configure your server side Java webapp to be able to respond to incoming REST HTTP requests from client side UI via JAX-RS.

Since our application server is Glassfish, the default JAX-RS implementation of glassfish is Jersey. You can either configure Jersey Servlet in your web.xml as:

   <servlet>
        <servlet-name>JerseyRESTServlet</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
        <servlet-name>JerseyRESTServlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
   </servlet-mapping>

With this Jersey will scan your packages to check if there are any annotated resources Or else use the implementation agnostic way, which is the prefferred way:

package com.guilhebl.test.web.common.application;

import java.util.HashSet; import java.util.Set;

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

import com.guilhebl.test.web.service.facade.UserService;

@ApplicationPath("/rest") public class SampleAppApplication extends Application { @Override public Set<Class<?>> getClasses() { final Set<Class<?>> classes = new HashSet<Class<?>>(); // register root resource classes.add(UserService.class); return classes; } }

in the sample above we are registering one REST resource class UserService which we will implement like this:

package com.guilhebl.test.web.service.facade;

import java.util.ArrayList; import java.util.List;

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

import com.guilhebl.test.web.service.domain.UserVO;

@Path("/user") public class UserService {

<span class="kd">public</span> <span class="nf">UserService</span><span class="o">()</span> <span class="o">{}</span>

<span class="nd">@GET</span>
<span class="nd">@Produces</span><span class="o">({</span><span class="n">MediaType</span><span class="o">.</span><span class="na">TEXT_PLAIN</span><span class="o">})</span>
<span class="nd">@Path</span><span class="o">(</span><span class="s">"/text-sample"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">textSample</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="s">"Hi! This is a test!"</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@GET</span>
<span class="nd">@Produces</span><span class="o">({</span><span class="n">MediaType</span><span class="o">.</span><span class="na">TEXT_HTML</span><span class="o">})</span>
<span class="nd">@Path</span><span class="o">(</span><span class="s">"/html-sample"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">htmlSample</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="s">"&lt;ul&gt;&lt;li&gt;Element 1&lt;/li&gt;&lt;li&gt;Element 2&lt;/li&gt;&lt;li&gt;Element 3&lt;/li&gt;&lt;/ul&gt;"</span><span class="o">;</span>
<span class="o">}</span>

<span class="nd">@GET</span>
<span class="nd">@Produces</span><span class="o">({</span><span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_XML</span><span class="o">,</span> <span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">})</span>
<span class="nd">@Path</span><span class="o">(</span><span class="s">"/json-sample"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">UserVO</span> <span class="nf">jsonSample</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">UserVO</span> <span class="n">vo</span> <span class="o">=</span> <span class="n">buildSampleUserVO</span><span class="o">();</span>     
    <span class="k">return</span> <span class="n">vo</span><span class="o">;</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="n">UserVO</span> <span class="nf">buildSampleUserVO</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">UserVO</span> <span class="n">userVO</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UserVO</span><span class="o">();</span>
    <span class="n">userVO</span><span class="o">.</span><span class="na">setId</span><span class="o">(</span><span class="s">"test@testdomain.com"</span><span class="o">);</span>
    <span class="n">userVO</span><span class="o">.</span><span class="na">setActive</span><span class="o">(</span><span class="s">"T"</span><span class="o">);</span>
    <span class="n">userVO</span><span class="o">.</span><span class="na">setPassword</span><span class="o">(</span><span class="s">"*************"</span><span class="o">);</span>     
    <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">userGroupsStr</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;();</span>      
    <span class="n">userGroupsStr</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="s">"abc"</span><span class="o">);</span>
    <span class="n">userGroupsStr</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="s">"admin"</span><span class="o">);</span>
    <span class="n">userGroupsStr</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="s">"users"</span><span class="o">);</span>
    <span class="n">userVO</span><span class="o">.</span><span class="na">setUserGroups</span><span class="o">(</span><span class="n">userGroupsStr</span><span class="o">);</span>                         
    <span class="k">return</span> <span class="n">userVO</span><span class="o">;</span>
<span class="o">}</span>

}



Configure your domain model to support JAXB

Notice how UserService is returning a UserVO object in the last 2 methods, JAXB: Java API for XML Binding is the default api for transforming Java objects into XML or JSON, in order to make JAXB understand our domain model we must annotate our domain classes with @XmlRootElement:

package com.guilhebl.test.web.service.domain;

import java.util.List;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement public class UserVO {

<span class="kd">private</span> <span class="n">String</span> <span class="n">id</span><span class="o">;</span> 
<span class="kd">private</span> <span class="n">String</span> <span class="n">password</span><span class="o">;</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">active</span><span class="o">;</span>
<span class="kd">private</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">userGroups</span><span class="o">;</span>

<span class="kd">public</span> <span class="n">String</span> <span class="nf">Id</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setId</span><span class="o">(</span><span class="n">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">id</span> <span class="o">=</span> <span class="n">id</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getPassword</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">password</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setPassword</span><span class="o">(</span><span class="n">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">password</span> <span class="o">=</span> <span class="n">password</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getActive</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">active</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setActive</span><span class="o">(</span><span class="n">String</span> <span class="n">active</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">active</span> <span class="o">=</span> <span class="n">active</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="nf">getUserGroups</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">userGroups</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setUserGroups</span><span class="o">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">userGroups</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">userGroups</span> <span class="o">=</span> <span class="n">userGroups</span><span class="o">;</span>
<span class="o">}</span>

}



Build and test

Your REST resource will answer HTTP requests and send back responses with MIME type according to each request URI(path) and to the Accepted-Type headers in the HTTP request.

After that we're ready to build and deploy our webapp in Glassfish. You can either drop your .war or .ear file inside Glassfish autodeploy folder, or either deploy via admin console, maven or some other automated procedure, I'd rather use the maven glassfish plugin since I can integrate my project build and deploy steps easily.

In case you get a successful deploy it's time to test your deployed REST webservices, open your browser and navigate to "localhost:8080/myApp/rest/user/" myApp should be replaced with your webapp name.


. TEXT Response - localhost:8080/myApp/rest/user/text-sample

TEXT screenshot


. HTML Response - "localhost:8080/myApp/rest/user/html-sample"

HTML screenshot


. JSON Response - "localhost:8080/myApp/rest/user/json-sample"

If you just place the location above in your browser and hit enter you will get a HTTP 404 - Not found page This is because your browser expects presentable MIME types like text/html or text/plain.

In order to test your XML and JSON responses you need to access your URLs using a HTTP client tool, in this example I use Chrome's DEV HTTP Client, but there are many other tools and you can even create your own Java client app to test. In order to accept JSON data responses you must have in your HTTP request headers the Accepted types: application/json or application/xml for XML.

Response using client tool:

JSON screenshot



Additional Resources

A REST endpoint can deliver many other HTTP MIME type responses, it's up to you the way you want to assemble your collection of webservices focusing to match according to the necessities of your project. With this approach you can achieve a cleaner separation between the UI Layer (Javascript + HTML) and Service Layer (Business and Domain model components), giving greater independence and flexibility to your UI and service layer teams, since their integration will be based on the REST interface contract.

Additional links:

JavaEE 6 Oracle JAX-RS and JAXB tutorial

comments powered by Disqus