Main Tutorials

Jersey and Jetty HTTP Server examples

jersey and jetty

This article shows how to start a Jetty HTTP Sever to run a JAX-RS or Eclipse Jersey application.

Tested with

  • Jersey 3.0.2
  • Jetty 11
  • Jackson 2.12.2
  • JUnit 5.4.0 (unit test)
  • JSONassert 1.5.0 (unit test)
  • Maven 3.8.3
  • Java 11

Tables of contents

At the end of the article, we will show a complete Jersey + Jetty + Jackson example to create a few endpoints to return a JSON formatted response.

1. Using Jersey with Jetty HTTP Server

1.1 Add the jersey-container-jetty-http dependency for Jetty.

pom.xml

  <!-- Jetty -->
  <dependency>
      <groupId>org.glassfish.jersey.containers</groupId>
      <artifactId>jersey-container-jetty-http</artifactId>
  </dependency>

  <!-- Jersey DI and core-->
  <dependency>
      <groupId>org.glassfish.jersey.inject</groupId>
      <artifactId>jersey-hk2</artifactId>
  </dependency>

And we use the JettyHttpContainerFactory to create and start the Jetty HTTP Server.


  import org.eclipse.jetty.server.Server;
  import org.glassfish.jersey.jetty.JettyHttpContainerFactory;

  // scan packages
  final ResourceConfig config = new ResourceConfig().packages("com.mkyong");

  // Start Jetty Server
  final Server server =
          JettyHttpContainerFactory.createServer(
                  URI.create("http://localhost:8080/"), config);

2. Project Directory

Create the Maven standard directory manually.

Terminal

  mkdir jersey-jetty\src\main\java
  mkdir jersey-jetty\src\main\resources
  mkdir jersey-jetty\src\test\resources
  mkdir jersey-jetty\src\test\java

jersey and jetty project directory

3. Project dependencies

3.1 Create a jersey-jetty\pom.xml file, put the following content, and import or open it with the IDEs.

Notes

  • jersey-hk2 for Jersey core and dependency injection.
  • jersey-container-jetty-http for Jetty HTTP server.
  • jakarta.servlet-api, Jetty 11 needs Servlet 5 APIs, package jakarta.*
  • jersey-media-json-jackson for Jackson to support JSON response.
  • junit-jupiter-params for JUnit 5, for testing purpose.
  • jsonassert for testing purposes.
  • The plugins are for the package of the executable jar file and project dependencies.
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mkyong</groupId>
    <artifactId>jersey-jetty-example</artifactId>
    <packaging>jar</packaging>
    <version>1.0</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <java.version>11</java.version>
        <junit.version>5.4.0</junit.version>
        <jsonassert.version>1.5.0</jsonassert.version>
        <jersey.version>3.0.2</jersey.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.glassfish.jersey</groupId>
                <artifactId>jersey-bom</artifactId>
                <version>${jersey.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <!-- Jetty -->
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-jetty-http</artifactId>
        </dependency>

        <!-- Jersey DI and core-->
        <dependency>
            <groupId>org.glassfish.jersey.inject</groupId>
            <artifactId>jersey-hk2</artifactId>
        </dependency>

        <!-- Jetty 11 needs Servlet 5 -->
        <!-- java.lang.NoClassDefFoundError: jakarta/servlet/ServletInputStream -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</version>
        </dependency>

        <!-- add jackson to support json -->
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
        </dependency>

        <!-- Need this to hide warning
        <dependency>
            <groupId>jakarta.activation</groupId>
            <artifactId>jakarta.activation-api</artifactId>
            <version>2.0.1</version>
        </dependency>
        -->

        <!-- JUnit 5 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- test json data -->
        <dependency>
            <groupId>org.skyscreamer</groupId>
            <artifactId>jsonassert</artifactId>
            <version>${jsonassert.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>jersey-jetty</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>

            <!-- JUnit 5 need at least 2.22.0 to support -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>com.mkyong.MainApp</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <!-- copy project dependencies -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.1.2</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!-- exclude junit, we need runtime dependency only -->
                            <includeScope>runtime</includeScope>
                            <outputDirectory>${project.build.directory}/lib/</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

</project>

4. mvn dependency:tree

List out all the project dependencies. The Jersey 3.0.2 uses Jetty 11 and Jackson 2.12.2.

Terminal

C:\Users\mkyong\projects\jax-rs\jersey\jersey-jetty>mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< com.mkyong:jersey-jetty-example >-------------------
[INFO] Building jersey-jetty-example 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ jersey-jetty-example ---
[INFO] com.mkyong:jersey-jetty-example:jar:1.0
[INFO] +- org.glassfish.jersey.containers:jersey-container-jetty-http:jar:3.0.2:compile
[INFO] |  +- jakarta.inject:jakarta.inject-api:jar:2.0.0:compile
[INFO] |  +- org.eclipse.jetty:jetty-server:jar:11.0.0:compile
[INFO] |  |  +- org.eclipse.jetty:jetty-http:jar:11.0.0:compile
[INFO] |  |  +- org.eclipse.jetty:jetty-io:jar:11.0.0:compile
[INFO] |  |  \- org.slf4j:slf4j-api:jar:2.0.0-alpha1:compile
[INFO] |  +- org.eclipse.jetty:jetty-util:jar:11.0.0:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-common:jar:3.0.2:compile
[INFO] |  |  +- jakarta.annotation:jakarta.annotation-api:jar:2.0.0:compile
[INFO] |  |  \- org.glassfish.hk2:osgi-resource-locator:jar:1.0.3:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-server:jar:3.0.2:compile
[INFO] |  |  +- org.glassfish.jersey.core:jersey-client:jar:3.0.2:compile
[INFO] |  |  \- jakarta.validation:jakarta.validation-api:jar:3.0.0:compile
[INFO] |  \- jakarta.ws.rs:jakarta.ws.rs-api:jar:3.0.0:compile
[INFO] +- org.glassfish.jersey.inject:jersey-hk2:jar:3.0.2:compile
[INFO] |  +- org.glassfish.hk2:hk2-locator:jar:3.0.1:compile
[INFO] |  |  +- org.glassfish.hk2.external:aopalliance-repackaged:jar:3.0.1:compile
[INFO] |  |  +- org.glassfish.hk2:hk2-api:jar:3.0.1:compile
[INFO] |  |  \- org.glassfish.hk2:hk2-utils:jar:3.0.1:compile
[INFO] |  \- org.javassist:javassist:jar:3.25.0-GA:compile
[INFO] +- jakarta.servlet:jakarta.servlet-api:jar:5.0.0:compile
[INFO] +- org.glassfish.jersey.media:jersey-media-json-jackson:jar:3.0.2:compile
[INFO] |  +- org.glassfish.jersey.ext:jersey-entity-filtering:jar:3.0.2:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.12.2:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-databind:jar:2.12.2:compile
[INFO] |  |  \- com.fasterxml.jackson.core:jackson-core:jar:2.12.2:compile
[INFO] |  +- com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar:2.12.2:compile
[INFO] |  \- javax.xml.bind:jaxb-api:jar:2.3.1:compile
[INFO] |     \- javax.activation:javax.activation-api:jar:1.2.0:compile
[INFO] +- org.junit.jupiter:junit-jupiter-params:jar:5.4.0:test
[INFO] |  +- org.apiguardian:apiguardian-api:jar:1.0.0:test
[INFO] |  \- org.junit.jupiter:junit-jupiter-api:jar:5.4.0:test
[INFO] |     +- org.opentest4j:opentest4j:jar:1.1.1:test
[INFO] |     \- org.junit.platform:junit-platform-commons:jar:1.4.0:test
[INFO] \- org.skyscreamer:jsonassert:jar:1.5.0:test
[INFO]    \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.557 s
[INFO] Finished at: 2021-05-26T12:33:16+08:00
[INFO] ------------------------------------------------------------------------

5. JAX-RS (Jersey) endpoints

5.1 A dummy bean, and later, we will return this object in JSON formatted string.

User

package com.mkyong;

public class User {

  private int id;
  String name;

  //getter and setters
}

5.2 JAX-RS APIs (Jersey is a JAX-RS implementation) to create a few endpoints to return the JSON and TEXT response.

  • /hello returns a string.
  • /hello/{name} returns a JSON formatted string.
  • /hello/all returns a JDON formatted array.

P.S The Jackson provider will convert the object to JSON automatically.

MyResource.java

package com.mkyong;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

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

@Path("/hello")
public class MyResource {

  @GET
  @Produces(MediaType.TEXT_PLAIN)
  public String hello() {
      return "Jersey Jetty example.";
  }

  @Path("/{username}")
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public User hello(@PathParam("username") String name) {

      User obj = new User();
      obj.setId(0);
      obj.setName(name);

      return obj;

  }

  @Path("/all")
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public List<User> helloList() {

      List<User> list = new ArrayList<>();

      User obj1 = new User();
      obj1.setId(1);
      obj1.setName("mkyong");
      list.add(obj1);

      User obj2 = new User();
      obj2.setId(2);
      obj2.setName("zilap");
      list.add(obj2);

      return list;

  }

}

6. Start Jersey + Jetty application

We need to control the starting and stopping of the Jetty HTTP server.

Notes

  • We use Runtime.getRuntime().addShutdownHook to add a JVM shutdown hook; If this started application received a shutdown signal like CTRL+C, the shutdown hook would fire and stop the Jetty HTTP server.
  • The Thread.currentThread().join() will block the main thread to exit, acts like Thread.sleep(Long.MAX_VALUE); This asks the started application to wait for the shutdown signal. (Any alternative?)
MainApp.java

package com.mkyong;

import org.eclipse.jetty.server.Server;
import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;

import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MainApp {

  public static final String BASE_URI = "http://localhost:8080/";

  public static Server startServer() {

      // scan packages
      // final ResourceConfig config = new ResourceConfig().packages("com.mkyong");

      final ResourceConfig config = new ResourceConfig(MyResource.class);
      final Server server =
              JettyHttpContainerFactory.createServer(URI.create(BASE_URI), config);

      return server;

  }

  public static void main(String[] args) {

      try {

          final Server server = startServer();

          Runtime.getRuntime().addShutdownHook(new Thread(() -> {
              try {
                  System.out.println("Shutting down the application...");
                  server.stop();
                  System.out.println("Done, exit.");
              } catch (Exception e) {
                  Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, e);
              }
          }));

          System.out.println(
              String.format("Application started.%nStop the application using CTRL+C"));

          // block and wait shut down signal, like CTRL+C
          Thread.currentThread().join();

          // alternative
          // Thread.sleep(Long.MAX_VALUE);       // sleep forever...
          // Thread.sleep(Integer.MAX_VALUE);    // sleep around 60+ years

      } catch (InterruptedException ex) {
          Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
      }

  }

}

7. Demo

The below command package the application as an executable jar and copying the project dependencies to target\lib.

Terminal

> mvn clean package   

> java -jar target\jersey-jetty.jar

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#noProviders for further details.
May 26, 2021 1:14:07 PM org.glassfish.jersey.message.internal.MessagingBinders$EnabledProvidersBinder bindToBinder
May 26, 2021 1:14:07 PM org.glassfish.jersey.server.wadl.WadlFeature configure
WARNING: JAX-B API not found . WADL feature is disabled.
Application started.
Stop the application using CTRL+C

Note
If you are using IDEs like IntelliJ or Eclipse, start the MainApp.java will start the entire Jersey + Jetty application.

7.1 Test the JAX-RS endpoints with the cURL command.

Terminal

> curl http://localhost:8080/hello
Jersey Jetty example.

> curl http://localhost:8080/hello/mkyong
{"id":0,"name":"mkyong"}  

> curl http://localhost:8080/hello/all
[{"id":1,"name":"mkyong"},{"id":2,"name":"zilap"}]

7.2 Display the request and response headers.

Terminal

> curl -v http://localhost:8080/hello/mkyong
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /hello/mkyong HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 26 May 2021 05:19:45 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Server: Jetty(11.0.0)
<
{"id":0,"name":"mkyong"}

8. Unit Test, Jersey + JUnit 5

We use JUnit 5 and JSONassert to test the Jersey endpoints and JSON response.

MyResourceTest.java

package com.mkyong;

import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.jetty.server.Server;
import jakarta.ws.rs.client.WebTarget;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class MyResourceTest {

    private static Server server;
    private static WebTarget target;

    @BeforeAll
    public static void beforeAllTests() {
        server = MainApp.startServer();
        Client c = ClientBuilder.newClient();
        target = c.target(MainApp.BASE_URI);
    }

    @AfterAll
    public static void afterAllTests() throws Exception {
        server.stop();
    }

    @Test
    public void testHello() {
        String response = target.path("hello").request().get(String.class);
        assertEquals("Jersey Jetty example.", response);
    }

    @Test
    public void testHelloName() throws JSONException {

        String response = target.path("hello/mkyong")
                .request(MediaType.APPLICATION_JSON)
                .get(String.class);

        // convert json string to JSONObject
        JSONObject actual = new JSONObject(response);

        String expected = "{\"id\":0,\"name\":\"mkyong\"}";
        JSONAssert.assertEquals(expected, actual, false);

    }

    @Test
    public void testHelloAll() throws JSONException {

        String response = target.path("hello/all")
                .request(MediaType.APPLICATION_JSON)
                .get(String.class);

        // convert json string to JSONArray
        JSONArray actual = new JSONArray(response);

        String expected = "[{\"id\":1,\"name\":\"mkyong\"},{\"id\":2,\"name\":\"zilap\"}]";
        JSONAssert.assertEquals(expected, actual, false);

    }

}

9. Download Source Code

$ git clone https://github.com/mkyong/jax-rs

$ cd jax-rs/jersey/jersey-jetty/

$ mvn clean package

$ java -jar target\jersey-jetty.jar

10. References

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
3 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Walter
1 month ago

Thank you! that works perfectly.

Donald
6 months ago

Good day Mkyong,

Thank you for the article on jersey-jetty with jakarta.rs. Runs very well!

You are a pro.

Br,
Donald

jay
11 months ago

Hi, Have you tried to bind this API to an IPaddress instead of local host?