Jersey and Jetty HTTP Server examples
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
- 1. Using Jersey with Jetty HTTP Server
- 2. Project Directory
- 3. Project dependencies
- 4. mvn dependency:tree
- 5. JAX-RS (Jersey) endpoints
- 6. Start Jersey + Jetty application
- 7. Demo
- 8. Unit Test, Jersey + JUnit 5
- 9. Download Source Code
- 10. References
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.
<!-- 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.
mkdir jersey-jetty\src\main\java
mkdir jersey-jetty\src\main\resources
mkdir jersey-jetty\src\test\resources
mkdir jersey-jetty\src\test\java
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, packagejakarta.*
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 executablejar
file and project dependencies.
<?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
.
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.
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.
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 likeCTRL+C
, the shutdown hook would fire and stop the Jetty HTTP server. - The
Thread.currentThread().join()
will block the main thread to exit, acts likeThread.sleep(Long.MAX_VALUE);
This asks the started application to wait for the shutdown signal. (Any alternative?)
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
.
> 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.
> 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.
> 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.
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
Thank you! that works perfectly.
Good day Mkyong,
Thank you for the article on jersey-jetty with jakarta.rs. Runs very well!
You are a pro.
Br,
Donald
Hi, Have you tried to bind this API to an IPaddress instead of local host?