REST API Service Made Easy with Jersey Framework in Java

Because REST APIs are easy to get started with, I see more products are adopting it for remote management APIs. To implement the REST on the server side, you can use different programming lanaguages and frameworks. In Java, you can use the Jersey framework which is a reference implementation of the JAX-RS (JSR 311 & JSR 339).

As I tried several tutorials, I found them mostly no longer working with latest versions of Jersey and Tomcat 7 which implements Java Servlet 3.0. After getting a sample working finally, I think it’s beneficial to share it so that you can avoid the issues I had gone through.

Lost VMs or Containers? Too Many Consoles? Too Slow GUI? Time to learn how to "Google" and manage your VMware and clouds in a fast and secure HTML5 App.

Working Sample First

If you use Eclipse, you can create a Dynamic Web project first. Then add the following Java class into the src folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.doublecloud.rest.demo;
 
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
 
@Path("/hi")
public class HelloWorldService
{
  @GET
  @Path("/{name}")
  public Response getMessage(@PathParam("name") String name)
  {
    String outMsg = "Hello " + name + "!";
    return Response.status(200).entity(outMsg).build();
  }
}

To help the Tomcat to locate your service and hook it up with incoming request from clients, you need to either use web.xml or another class with annotation.

Let’s first look at the annotation approach. You need to create another class as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.doublecloud.rest.demo;
 
import java.util.HashSet;
import java.util.Set;
 
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
 
@ApplicationPath("/rest")
public class HelloApp extends Application
{
  @Override
  public Set<Class<?>> getClasses()
  {
    Set<Class<?>> s = new HashSet<Class<?>>();
    s.add(HelloWorldService.class);
    return s;
  }
}

For the second alternative approach, you create a file called web.xml in the WebContent\WEB-INF folder as the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<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_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>REST Web Application Demo</display-name>
	<servlet>
		<servlet-name>jersey-serlvet</servlet-name>
		<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
		<init-param>
			<param-name>jersey.config.server.provider.packages</param-name>
			<param-value>org.doublecloud.rest.demo</param-value>
		</init-param>
	</servlet>
 
	<servlet-mapping>
		<servlet-name>jersey-serlvet</servlet-name>
		<url-pattern>/rest/*</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
</web-app>

Note that the servlet class from previous versions no longer work in Jersey 2.

1
2
3
4
5
6
7
8
    <servlet>
		<servlet-name>jersey-serlvet</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>org.doublecloud.rest.demo</param-value>
		</init-param>
	</servlet>

Once you are done with either one of the above two approaches, you can simply type in the following URL in your browser to get a string “hello steve!” back. In real project, you will either use XML or JSON as response body.

http://localhost:8080/RestDemo/rest/hi/steve

Update: Maven Dependencies

I used Maven in NetBeans for a REST project in Feb 2014. Here are the dependencies required by Jersey and JSON using Gson. Depending on your project, you may need more dependencies.

    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>2.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <version>2.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>2.4.1</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.2.4</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

Trouble Shooting Tips

While working on the sample, I got into several issues which turned out to be dependency issue mostly. As the Jersey project relies on the Maven to manage the dependent jars, it may be easier to go that way. But to simplify the creation process, I just decided to explicitly add required jar files.

Initially, I just downloaded and added the binary jaxrs-ri-2.1.jar file from repository and it did not work. After trying various combination leads to several exceptions as listed below, I finally decided to add all the libraries from the jaxrs-ri\ext\ folder plus javax.ws.rs-api-2.0.jar in the jarxrs-ri\api folder from jaxrs-ri-2.1.zip. With the 16 or so jar files in the WebContent\WEB-INFO\lib folder, things start to work. It’s possible that not all the 16 jar files are necessary, but as it worked I didn’t bother to try them out one by one. The tricky part of trying that out is that checking compilation error is not enough and you have to call the REST service due to the dynamic class loading in the framework.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Caused by: java.lang.ClassNotFoundException: com.google.common.base.Function
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
	... 12 more
 
<== the class com.google.common.base.Function is included in the guava-14.0.1.jar file.
 
SEVERE: Allocate exception for servlet jersey-serlvet
java.lang.ClassNotFoundException: com.sun.jersey.spi.container.servlet.ServletContainer
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
	at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:532)
	at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:514)
	at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:133)
	at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1137)
	at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:858)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:136)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:662)
 
 
	SEVERE: Allocate exception for servlet jersey-serlvet
java.lang.ClassNotFoundException: org.glassfish.hk2.utilities.binding.AbstractBinder
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)

More Tutorial and Samples

The above sample just touched the tip of the iceberg. There are many other features you want to explore. For that, you may find the Jersey 2.1 User Guide and the samples included in the Jersey project very helpful.
More samples at https://github.com/jersey/jersey/tree/master/examples, or http://jersey.java.net/documentation/latest/user-guide.html

This entry was posted in Software Development and tagged , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

16 Comments

  1. Yannick
    Posted September 9, 2013 at 8:56 am | Permalink

    Hi, thanks for this tutorial ! I’ve been searching for hours and I finally found a simple yet effective example !

    One thing, though : if you’re going to use “http://localhost:8080/RestDemo/rest/hi/steve” as the demo URL, then you should annotate your HelloApp class with @ApplicationPath(“/rest”).

    That way, both configurations are the same. Otherwise, you have to use “http://localhost:8080/RestDemo/hi/steve”.

    Cheers, and thanks again !

  2. Posted September 10, 2013 at 2:19 pm | Permalink

    Hi Yannick,

    I am glad you like this example.

    You are right. The HelloApp should be annotated as you pointed out. I am updating the sample now.

    Thanks a lot for the help!

    Steve

  3. Maxrunner
    Posted October 24, 2013 at 11:24 am | Permalink

    Hi! This article helped me. so thanks for that. Just want to add a tip too. If you want to use jersey with jackson json, just change this:

    org.doublecloud.rest.demo

    to:

    org.doublecloud.rest.demo;com.fasterxml.jackson.jaxrs.json

    This worked for me and i would at same time find the the resource classes and my JacksonObjectMapper class that implements the ContextResolver.

  4. Gavin
    Posted November 15, 2013 at 2:11 pm | Permalink

    Steve,

    You’re awesome. I appreciate you taking the time to post your example. It really helped me out in trying to figure out RESTful services and the configuration under Jersey 2.0 with NetBeans 7.4.

    Gavin

  5. Posted November 15, 2013 at 2:25 pm | Permalink

    You are very welcome Gavin, I am glad it helped.

    Steve

  6. Kevin
    Posted November 19, 2013 at 6:41 pm | Permalink

    Coming to this post after two days of trying out various tutorials, none working. Followed yours exactly but it does not work either (tried both URLs). Could you please double check what you posted? Thanks.

    HTTP Status 404 – /RestDemo/rest/hi/steve
    type Status report
    message /RestDemo/rest/hi/steve
    description The requested resource (/RestDemo/rest/hi/steve) is not available.

    I’m using Eclipse Kepler, Apache Tomcat/7.0.12, Jersey 2.4.1 (latest)

  7. Posted November 20, 2013 at 1:32 am | Permalink

    Sorry to hear that. It seems the service is not found. Did you use web.xml or annotation?

    Steve

  8. Maxrunner
    Posted November 26, 2013 at 7:56 am | Permalink

    Hi Steve! Currently i’m trying to use multi-part resource to receive files with other json data, from Android. I’m yet to understand how to do this on Jersey, but its possible, according to the documentation:
    https://jersey.java.net/documentation/latest/user-guide.html#multipart

    Still how do i activate the Multipart.class thing on the web.xml?I assume i can also activate this on the web.xml file instead of the other alternative.

  9. Maxrunner
    Posted November 26, 2013 at 1:15 pm | Permalink

    Here’s how you set the web.xml with using the application class:

    Jersey REST Service
    org.glassfish.jersey.servlet.ServletContainer

    javax.ws.rs.Application
    com.mobile.rest.MyApplication

    1

    Jersey REST Service
    /rest/*

  10. Rick
    Posted November 26, 2013 at 4:35 pm | Permalink

    Thanks man! All the other tutorials out there were outdated. Replacing with jersey.config.server.provider.packages in the web.xml was critical since all the other examples, as you mention, use com.sun.jersey.config.property.packages

    For those declaring their maven dependencies (which is also confusing when you look at various tutorials)..For Tomcat 6 I used (server not client): (Removed
    groupId>org.glassfish.jersey.containers /groupId>
    artifactId>jersey-container-servlet-core /artifactId>
    version>2.4.1
    /dependency>

  11. Maxrunner
    Posted January 3, 2014 at 9:02 am | Permalink

    @Rick
    using maven do you only need to add serveletc-core? what about the extensions?

  12. RAdil
    Posted December 12, 2014 at 7:10 am | Permalink

    Do you have a maven project that you can share? I am using JDK 1.7

    For some reason Maven does not find PathParam and also the status method used in the here.

    cannot find symbol
    [ERROR] symbol: class PathParam

    cannot find symbol
    [ERROR] symbol: method status(int)
    [ERROR] location: interface javax.xml.ws.Response

    @GET
    @Path(“/{name}”)
    public Response getMessage(@PathParam(“name”) String name)
    {
    String outMsg = “First Jersey Service ” + name + “!”;
    return Response.status(200).entity(outMsg).build();
    }

    My pom file looks like this

    javax.xml.ws
    jaxws-api
    2.2.8

    com.sun.jersey
    jersey-json
    1.1.1-ea

    com.sun.jersey
    jersey-core
    1.1.1-ea

    com.sun.jersey
    jersey-client
    1.1.1-ea

    javax.ws.rs
    jsr311-api
    1.1

    com.google.code.gson
    gson
    2.2.4

    javax
    javaee-web-api
    6.0
    provided

    org.glassfish.jersey.bundles.repackaged
    jersey-guava
    2.6

    maven-compiler-plugin
    3.0

    1.7
    1.7

    maven-war-plugin
    2.2

    ${project.artifactId}

  13. Sachin G N
    Posted July 28, 2015 at 9:24 pm | Permalink

    Working on REST webservice using Jersey implementation. Please help me to resolve this issue.
    Java -1.7
    Maven
    Dynamic Web Module Project Facet – 2.4

    pom.xml:

    maven2-repository.java.net
    Java.net Repository for Maven
    http://download.java.net/maven/2/
    default

    junit
    junit
    4.8.2
    test

    com.sun.jersey
    jersey-server
    1.8

    Stacktrace:
    2015-07-28 19:19:16.809:INFO:oejs.Server:jetty-8.1.16.v20140903
    2015-07-28 19:19:16.984:WARN:oejw.WebAppContext:Failed startup of context o.e.j.w.WebAppContext{null,file:/Users/ctsuser/Documents/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/RESTfulExample/},/Users/ctsuser/Documents/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/RESTfulExample
    java.lang.IllegalStateException: Null contextPath
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:693)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:494)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:64)
    at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:229)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:64)
    at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:95)
    at org.eclipse.jetty.server.Server.doStart(Server.java:282)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:64)
    at org.eclipse.wst.server.preview.internal.PreviewStarter.run(PreviewStarter.java:72)
    at org.eclipse.wst.server.preview.internal.PreviewStarter.main(PreviewStarter.java:29)
    2015-07-28 19:19:17.407:WARN:oejs.Holder:
    java.lang.ClassNotFoundException: com.sun.jersey.spi.container.servlet.ServletContainer

  14. Sachin G N
    Posted August 7, 2015 at 5:46 pm | Permalink

    Hi All,

    We get this error because of build path issue. You should add “Server Runtime” libraries in Build Path.

    “java.lang.ClassNotFoundException: com.sun.jersey.spi.container.servlet.ServletContainer”

    Please follow below steps to resolve class not found exception.

    Right click on project –> Build Path –> Java Build Path –> Add Library –> Server Runtime –> Apache Tomcat v7.0

    Thanks,
    Sachin G N

  15. Sanchita
    Posted May 3, 2016 at 3:44 am | Permalink

    Thanks a lot! my jars were in different folders and Iwas getting class not found exception. After I copied all the jars directly under WEB_INF/lib directly the error was solved

  16. Posted May 14, 2016 at 1:55 am | Permalink

    Thanks for sharing your thoughts on framework.
    Regards

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

  • NEED HELP?


    My company has created products like vSearch ("Super vCenter"), vijavaNG APIs, EAM APIs, ICE tool. We also help clients with virtualization and cloud computing on customized development, training. Should you, or someone you know, need these products and services, please feel free to contact me: steve __AT__ doublecloud.org.

    Me: Steve Jin, VMware vExpert who authored the VMware VI and vSphere SDK by Prentice Hall, and created the de factor open source vSphere Java API while working at VMware engineering. Companies like Cisco, EMC, NetApp, HP, Dell, VMware, are among the users of the API and other tools I developed for their products, internal IT orchestration, and test automation.