REST with Embedded Jetty and Jersey in a Single Jar – Step by Step

Introduction

Setting up a simple RESTful development environment to play with can be surprisingly difficult. Furthermore, if you’re using Java you may have to use different IDE plugins (e.g. for Eclipse or NetBeans), and to rely on the IDE’s integration with a web server.

In this tutorial I will show how to set up a RESTful server (Jetty+Jersey) to start from a standard Java main method. Furthermore, using Maven we’ll package the entire application in a single executable jar file, which can be started from any machine with a Java environment. Finally, we’ll see how to import it into Eclipse, so we can develop and debug there.

Prerequisites

Before following this tutorial you should have JDK 7 and Maven 3.2 or later installed on your system. Installation instructions can be found here:

If you plan to work with Eclipse, you will need Eclipse IDE Java EE Developers.

Maven Project

First, we need to create a simple maven project called restprj. We do so by using the simplest quickstart maven archetype. It creates a simple App java class and an AppTest unit test for us. We’ll delete the test, as we won’t need it for now:

mvn archetype:create -DgroupId=com.rest.test \
         -DartifactId=restprj \
         -DarchetypeArtifactId=maven-archetype-quickstart
cd restprj
rm src/test/java/com/rest/test/AppTest.java

Now we need to edit the pom.xml file to include the following dependencies:

<dependencies>
  <dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>9.2.3.v20140905</version>
  </dependency>
  <dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-servlet</artifactId>
    <version>9.2.3.v20140905</version>
  </dependency>
  <dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-server</artifactId>
    <version>2.7</version>
  </dependency>
  <dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet-core</artifactId>
    <version>2.7</version>
  </dependency>
  <dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-jetty-http</artifactId>
    <version>2.7</version>
  </dependency>
  <dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-moxy</artifactId>
    <version>2.7</version>
  </dependency>
</dependencies>

Note that there may be newer versions of these dependencies. Visit http://mvnrepository.com/ to check for new versions.

Server and REST service

Now we need to create a simple test REST service class EntryPoint:

package com.rest.test;

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

@Path("/entry-point")
public class EntryPoint {

    @GET
    @Path("test")
    @Produces(MediaType.TEXT_PLAIN)
    public String test() {
        return "Test";
    }
}

Afterwards we need to update the main class (i.e. App.java), which will start the server:

package com.rest.test;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

public class App {
    public static void main(String[] args) throws Exception {
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");

        Server jettyServer = new Server(8080);
        jettyServer.setHandler(context);

        ServletHolder jerseyServlet = context.addServlet(
             org.glassfish.jersey.servlet.ServletContainer.class, "/*");
        jerseyServlet.setInitOrder(0);

        // Tells the Jersey Servlet which REST service/class to load.
        jerseyServlet.setInitParameter(
           "jersey.config.server.provider.classnames",
           EntryPoint.class.getCanonicalName());

        try {
            jettyServer.start();
            jettyServer.join();
        } finally {
            jettyServer.destroy();
        }
    }
}

Building a Single Uber Jar

This step is in fact optional. It allows you to create an executable single/uber jar file with all dependencies. You can then execute it from any java-endabled machine. To do so, update the pom.xml with the following:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-shade-plugin</artifactId>
      <version>1.6</version>
      <configuration>
        <createDependencyReducedPom>true</createDependencyReducedPom>
        <filters>
          <filter>
            <artifact>*:*</artifact>
            <excludes>
              <exclude>META-INF/*.SF</exclude>
              <exclude>META-INF/*.DSA</exclude>
              <exclude>META-INF/*.RSA</exclude>
            </excludes>
          </filter>
        </filters>
      </configuration>

      <executions>
        <execution>
          <phase>package</phase>
          <goals>
            <goal>shade</goal>
          </goals>
          <configuration>
            <transformers>
              <transformer
                implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
              <transformer
                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                <manifestEntries>
                  <Main-Class>com.rest.test.App</Main-Class>
                </manifestEntries>
              </transformer>
            </transformers>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Now you can build and the run the jar file:

cd restprj
mvn clean install
cd target
java -jar restprj-1.0-SNAPSHOT.jar

At this point you can open http://localhost:8080/entry-point/test in your browser to test the web service.

Eclipse

This section is also optional. It describes how to import the Maven project in Eclipse, so you can run and debug your app from there.

First you need to set up the Eclipse maven environment:

Then you need to import the project:

 

References

This post is based on the following StackOverflow discussions:

 

34 thoughts on “REST with Embedded Jetty and Jersey in a Single Jar – Step by Step

  1. Maarten Boekhold

    I’m trying to do something similar. I’ve got an application that’s currently using Grizzly as the embedded web server, and for various reasons I’d like to swap that out for Jetty, completely getting rid of the Grizzly-related jars from the dependencies.
    In my case, I already have an instance of the “service bean”, and I can’t get Jersey manage the lifecycle of that bean. Do you know of a way to enable this? Is there some way I can do:
    “jerseyServlet.setProvider(Object useThisBean)”?

    Reply
    1. nikolaygrozev Post author

      Hi there,

      I am not sure what is this service bean you mentioned? The example above dynamically creates an instance (a service bean?) which implements the web service. Isn’t that what you want?

      If a service bean can be specified in Jersey, then this should be done in the init parameters.
      Perhaps you could google what’s the name of the init param you want and use jerseyServlet.setInitParameter(“param-name”, useThisBean);

      Reply
      1. Maarten Boekhold

        Figured it out. Here’s what I wanted to do. Note that I’m explicitly creating an instance of “TheResource” and pass it to the SevletContainer. This allows me to fully control the instantiation and setup of my bean purely from code.

        ServletContextHandler sch = new ServletContextHandler();
        sch.setContextPath(“/xxx”);

        resource = new TheResource();
        ResourceConfig rc = new ResourceConfig();
        rc.register(resource);

        ServletContainer sc = new ServletContainer(rc);
        ServletHolder holder = new ServletHolder(sc);
        sch.addServlet(holder, “/*”);

        Server server = new Server(port);
        server.setHandler(sch);
        server.start();
        server.join();

        Reply
  2. Maarten Boekhold

    This example works with Jersey 2.7, but not with 2.9 or 2.14. With 2.9/2.14 I’m getting:
    java.lang.NoSuchMethodError: org.glassfish.jersey.internal.util.PropertiesHelper.getValue(Ljava/util/Map;Ljavax/ws/rs/RuntimeType;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
    at org.glassfish.jersey.moxy.json.MoxyJsonFeature.configure(MoxyJsonFeature.java:67)
    at org.glassfish.jersey.model.internal.CommonConfig.configureFeatures(CommonConfig.java:709)
    at org.glassfish.jersey.model.internal.CommonConfig.configureMetaProviders(CommonConfig.java:639)
    at org.glassfish.jersey.server.ResourceConfig.configureMetaProviders(ResourceConfig.java:809)
    at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:388)
    at org.glassfish.jersey.server.ApplicationHandler.access$500(ApplicationHandler.java:163)
    at org.glassfish.jersey.server.ApplicationHandler$3.run(ApplicationHandler.java:323)
    at org.glassfish.jersey.internal.Errors$2.call(Errors.java:289)

    Any idea of the cause for this (and a fix!)

    Maarten

    Reply
    1. nikolaygrozev Post author

      Perhaps there is a mismatch in the used versions of Jersey and some of the libraries (glassfish.jersey?). Try updating the pom dependencies to the latest versions which are compatible with Jersey 2.9 or 2.14.

      Reply
      1. Maarten Boekhold

        Ah, I had made a mistake in my maven dependencies. I used a property to configure the version of the Jersey dependencies, but for the jersey-media-moxy dependency I had the version hardcoded to “2.7”…

        Reply
  3. David Williams

    Hi there, great article. Question: how do you add Jackson for Json serialization and deserialization. Best I can think of is this but I dont know what to do next:

    jerseyServlet.setInitParameter(
    ServerProperties.PROVIDER_CLASSNAMES,
    StringUtils.join(
    Arrays.asList(
    HealthCheck.class.getCanonicalName(),
    Rest.class.getCanonicalName()),
    “;”));

    // Create JAX-RS application.
    final Application application = new ResourceConfig()
    .packages(“com.apple.ast.management.console”)
    .register(JacksonFeature.class);

    Reply
  4. Art Lasky

    Nikolay, this is a great article that provides a newbie like me with a starting foundation in developing REST services. I got it to work perfectly but in my company we are using version 8.1.2 of jetty. So I changed the dependencies as given in your article to:

    org.eclipse.jetty
    jetty-server
    8.1.2.v20120308
    <!– 9.2.3.v20140905 –>

    org.eclipse.jetty
    jetty-servlet
    8.1.2.v20120308
    <!– 9.2.3.v20140905 –>

    But I get the following when trying to do ‘mvn install’:

    [INFO] ————————————————————-
    [ERROR] COMPILATION ERROR :
    [INFO] ————————————————————-
    [ERROR] ~/rest/src/main/java/com/rest/App.java:[16,15] error: cannot access AggregateLifeCycle
    [ERROR] class file for org.eclipse.jetty.util.component.AggregateLifeCycle not found
    ~/rest/src/main/java/com/rest/App.java:[31,23] error: cannot find symbol

    Can you suggest how to modify the Java code so it compiles & runs? Or is there another example like this one that uses jetty version 8.1.2? (I did some searching for one but didn’t find anything)

    Reply
  5. Clara

    This tutorial helped me a lot, thanks!

    Now I’m trying to load more than one Rest classes. Do I really need to create a new ServletContextHandler for each one?

    Thank you in advance.

    Reply
  6. Sergio Levin

    Hi, i’m trying to add multipart support to your example (file upload), but i get an error:
    No injection source found for a parameter of type public void com.dmway.scoring.rest.resources.ScoringModelResource.uploadFile(java.io.InputStream,org.glassfish.jersey.media.multipart.FormDataContentDisposition) …

    Any advice?

    Reply
  7. Shirl Hart

    This is exactly what I was looking for and it worked the first time! One question, I imported it into eclipse and then changed the test() message text and clicked ‘build project’ but nothing happened. How do I issued the maven build inside eclipse?

    Reply
    1. nikolaygrozev Post author

      Hi Filippo,
      You should be able to use JPA… but it should be application managed JPA. I’m not sure how and if you can do container managed JPA.

      Nik

      Reply
  8. Carlo S.

    Your example works well, but I need to access in my rest service class (e.g. EntryPoint class) to an object created in my main application (e.g. App class).

    How can I do this?
    Thanks!

    Reply
    1. nikolaygrozev Post author

      Hi Carlo,

      You’ll have to build your other application with maven, or install its jar file in your local maven repository.

      Afterwards you can simply put the proper dependency in the pom.xml of the web service project.

      Cheers

      Reply
  9. Niceguy

    In new versions of maven you need to use “archetype:generate” instead of “archetype:create”

    Reply
  10. Rajesh

    I am following this article. Followed everything except “Building a single Uber jar”. I am trying to run it from eclipse App as Java Application but I am getting the following error :-

    Exception in thread “main” java.lang.UnsupportedClassVersionError: org/eclipse/jetty/server/Handler : Unsupported major.minor version 52.0

    Java version on my system is

    java -version
    java version “1.7.0_80”
    Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
    Java HotSpot(TM) Client VM (build 24.80-b11, mixed mode, sharing)

    javac -version
    javac 1.7.0_80

    9.3.4.v20151007
    2.7

    org.eclipse.jetty
    jetty-server
    ${jettyVersion}

    org.eclipse.jetty
    jetty-servlet
    ${jettyVersion}

    org.glassfish.jersey.core
    jersey-server
    ${jerseyVersion}

    org.glassfish.jersey.containers
    jersey-container-servlet-core
    ${jerseyVersion}

    org.glassfish.jersey.containers
    jersey-container-jetty-http
    ${jerseyVersion}

    org.glassfish.jersey.media
    jersey-media-moxy
    ${jerseyVersion}

    Please let me know what might be the reason

    Reply
  11. ocantov

    Great tutorial!! thank you for sharing. I have a question related to

    // Tells the Jersey Servlet which REST service/class to load.
    jerseyServlet.setInitParameter(
    “jersey.config.server.provider.classnames”,
    EntryPoint.class.getCanonicalName());

    what should I do in order to load more than one REST service/class??

    thanks

    Reply
    1. Greg Patnude

      In your Servlet-holder configuration — you can do this two ways: “jersey.config.server.provider.classnames” also accepts a String array

      servlet.setInitParameter(“jersey.config.server.provider.classnames”, {“path.to.service.Service1”, “path.to.service.Service2”});

      Or you can use need to point to the package(s) that contain your service endpoints – using the packages initaliazation key instead: “jersey.config.server.provider.packages”

      I like to do it this way: assuming ALL of your services are suspended in directories and packages below com.package.services.*; Jersey will scan the class path to locate services @nnotated with @Path …

      ServletHolder servlet = new ServletHolder(org.glassfish.jersey.servlet.ServletContainer.class);
      servlet.setInitParameter(“jersey.config.server.provider.packages”, “com.package.services”);

      Reply
  12. KJSudarshan

    the only place where i could find the detailed step by step accurate information required! This helped me a lot. Thank You!

    Reply

Leave a comment