This article shows how to start a Grizzly HTTP server to run a JAX-RS or Eclipse Jersey application.
Tested with
- Jersey 3.0.2
- Grizzly 3 HTTP server
- Java 8
- Maven
- JUnit 5
Jersey basics
- Many Java-based HTTP servers deploy Jersey applications, for example, JDK HTTP Server, Grizzly, Simple, Jetty and Netty. Read this Jersey and HTTP servers.
- Jersey uses HK2 dependency injection framework.
- Jersey uses Java APIs logging, or JUL, or
java.util.logging.*.
Maven CLI
mvn archetype:generate "-DgroupId=com.favtuts" "-DartifactId=jersey-hello-world-example" "-Dpackage=com.favtuts" "-Dversion=1.0-SNAPSHOT" "-DarchetypeArtifactId=maven-archetype-webapp" "-DinteractiveMode=false"
In windows, you should add quotes for every parameter, if not you will encounter the error: “The goal you specified requires a project to execute but there is no POM in this directory“
* https://stackoverflow.com/questions/16348459/error-the-goal-you-specified-requires-a-project-to-execute-but-there-is-no-pom
* https://stackoverflow.com/questions/6704813/maven-generating-pom-file/11199865#11199865
1. Project Directory
A standard Maven project directory.
tree /F
C:.
│ pom.xml
│
├───src
│ ├───main
│ │ ├───java
│ │ │ └───com
│ │ │ └───favtuts
│ │ │ │ MainApp.java
│ │ │ │
│ │ │ ├───config
│ │ │ │ AutoScanFeature.java
│ │ │ │
│ │ │ ├───resource
│ │ │ │ MyResource.java
│ │ │ │
│ │ │ └───service
│ │ │ MessageService.java
│ │ │ MessageServiceImpl.java
│ │ │
│ │ └───resources
│ └───test
│ └───java
│ └───com
│ └───favtuts
│ MyResourceTest.java
│
└───target
2. Jersey dependencies
The main Jersey dependencies are the jersey-bom and jersey-hk2, others are optional, read the comment for self-explanatory.
pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.favtuts</groupId>
<artifactId>jersey-hello-world-example</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>jersey-hello-world-example</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<java.version>1.8</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>
<!-- Grizzly2 HTTP Server -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<!-- Jersey related and HK2 dependency injection -->
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<!-- add jackson for json conversion
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
-->
<!-- generates the META-INF/hk2-locator/default files for auto scan and discovery -->
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-metadata-generator</artifactId>
<version>3.0.2</version>
</dependency>
<!-- Need this to hide warning for jakarta.activation.DataSource -->
<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>
</dependencies>
<build>
<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>
<!-- create a thin-jar -->
<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.favtuts.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>
<!-- we need runtime dependency only -->
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/lib/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Review the complete Jersey dependencies for a simple Jersey application.
Terminal
$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------< com.favtuts:jersey-hello-world-example >---------------
[INFO] Building jersey-hello-world-example 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ jersey-hello-world-example ---
[INFO] com.favtuts:jersey-hello-world-example:jar:1.0
[INFO] +- org.glassfish.jersey.containers:jersey-container-grizzly2-http:jar:3.0.2:compile
[INFO] | +- jakarta.inject:jakarta.inject-api:jar:2.0.0:compile
[INFO] | +- org.glassfish.grizzly:grizzly-http-server:jar:3.0.0:compile
[INFO] | | \- org.glassfish.grizzly:grizzly-http:jar:3.0.0:compile
[INFO] | | \- org.glassfish.grizzly:grizzly-framework:jar:3.0.0:compile
[INFO] | +- org.glassfish.jersey.core:jersey-common:jar:3.0.2: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.javassist:javassist:jar:3.25.0-GA:compile
[INFO] +- org.glassfish.hk2:hk2-metadata-generator:jar:3.0.2:compile
[INFO] | +- org.glassfish.hk2:hk2-api:jar:3.0.2:compile
[INFO] | +- org.glassfish.hk2:hk2-utils:jar:3.0.2:compile
[INFO] | \- jakarta.annotation:jakarta.annotation-api:jar:2.0.0:compile
[INFO] +- jakarta.activation:jakarta.activation-api:jar:2.0.1: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] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.423 s
[INFO] Finished at: 2022-07-24T18:25:41+07:00
[INFO] ------------------------------------------------------------------------
3. Jersey and HK2 dependency injection
3.1 Jersey uses HK2 as the dependency injection framework. The below are @Contract and @Service components, and later we will inject them into the Jersey application.
MessageService.java
package com.favtuts.service;
import org.jvnet.hk2.annotations.Contract;
@Contract
public interface MessageService {
String getHello();
}
MessageServiceImpl.java
package com.favtuts.service;
import org.jvnet.hk2.annotations.Service;
@Service
public class MessageServiceImpl implements MessageService {
@Override
public String getHello() {
return "Hello World Jersey from HK2";
}
}
3.2 This Feature enables the auto-scanning components.
AutoScanFeature.java
package com.favtuts.config;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Feature;
import jakarta.ws.rs.core.FeatureContext;
import org.glassfish.hk2.api.DynamicConfigurationService;
import org.glassfish.hk2.api.MultiException;
import org.glassfish.hk2.api.Populator;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ClasspathDescriptorFileFinder;
import org.glassfish.hk2.utilities.DuplicatePostProcessor;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/* Auto scan the JAX-RS @Contract and @Service */
public class AutoScanFeature implements Feature {
@Inject
ServiceLocator serviceLocator;
@Override
public boolean configure(FeatureContext context) {
DynamicConfigurationService dcs =
serviceLocator.getService(DynamicConfigurationService.class);
Populator populator = dcs.getPopulator();
try {
// Populator - populate HK2 service locators from inhabitants files
// ClasspathDescriptorFileFinder - find files from META-INF/hk2-locator/default
populator.populate(
new ClasspathDescriptorFileFinder(this.getClass().getClassLoader()),
new DuplicatePostProcessor());
} catch (IOException | MultiException ex) {
Logger.getLogger(AutoScanFeature.class.getName()).log(Level.SEVERE, null, ex);
}
return true;
}
}
Note
More Jersey and HK2 examples.
4. Jersey endpoints
The below is a Jersey application that exposed a few endpoints to produce different output in text format.
MyResource.java
package com.favtuts.resource;
import com.favtuts.service.MessageService;
import jakarta.inject.Inject;
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;
@Path("/hello")
public class MyResource {
// DI via HK2
@Inject
private MessageService messageService;
// output text
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Jersey hello world example.";
}
// output text with argument
@Path("/{name}")
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello(@PathParam("name") String name) {
return "Jersey: hello " + name;
}
// for dependency injection
@Path("/hk2")
@GET
@Produces(MediaType.TEXT_PLAIN)
public String helloHK2() {
return messageService.getHello();
}
}
5. Start Jersey application
There is no magic here; we need to manually configure the HTTP server and start and end the HTTP Server and the Jersey application. Read the code comments for self-explanatory.
MainApp.java
package com.favtuts;
import com.favtuts.config.AutoScanFeature;
import com.favtuts.resource.MyResource;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MainApp {
private static final Logger LOGGER = Logger.getLogger(MainApp.class.getName());
// we start at port 8080
public static final String BASE_URI = "http://localhost:8080/";
// Starts Grizzly HTTP server
public static HttpServer startServer() {
// scan packages
final ResourceConfig config = new ResourceConfig();
// config.packages(true, "com.favtuts");
config.register(MyResource.class);
// enable auto scan @Contract and @Service
config.register(AutoScanFeature.class);
LOGGER.info("Starting Server........");
final HttpServer httpServer =
GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), config);
return httpServer;
}
public static void main(String[] args) {
try {
final HttpServer httpServer = startServer();
// add jvm shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
System.out.println("Shutting down the application...");
httpServer.shutdownNow();
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();
} catch (InterruptedException ex) {
Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
6. Demo
6.1 Start the MainApp directly, or pack it into a single jar and run it.
Terminal
$ cd {project}
$ mvn package
$ java -jar target/jersey-hello-world-1.0.jar
Jun 17, 2021 3:34:56 PM com.mkyong.MainApp startServer
INFO: Starting Server........
Jun 17, 2021 3:34:57 PM org.glassfish.jersey.server.wadl.WadlFeature configure
WARNING: JAX-B API not found . WADL feature is disabled.
Jun 17, 2021 3:34:57 PM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [localhost:8080]
Jun 17, 2021 3:34:57 PM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Application started.
Stop the application using CTRL+C
6.2 We can use a simple cURL to test the Jersey endpoints.
Terminal
$ curl http://localhost:8080/hello
Jersey hello world example.
$ curl http://localhost:8080/hello/favtuts
Jersey: hello favtuts
$ curl http://localhost:8080/hello/hk2
Hello World Jersey from HK2
$ curl -v http://localhost:8080/hello/hk2
* Trying ::1...
* TCP_NODELAY set
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /hello/hk2 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Content-Length: 27
<
Hello World Jersey from HK2
7. Jersey and unit test
A JAX-RS standard Client and WebTarget class to test the Jersey endpoints.
MyResourceTest.java
package com.favtuts;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import org.glassfish.grizzly.http.server.HttpServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MyResourceTest {
private static HttpServer httpServer;
private static WebTarget target;
@BeforeAll
public static void beforeAllTests() {
httpServer = MainApp.startServer();
Client c = ClientBuilder.newClient();
target = c.target(MainApp.BASE_URI);
}
@AfterAll
public static void afterAllTests() {
httpServer.stop();
}
@Test
public void testHello() {
String response = target.path("hello").request().get(String.class);
assertEquals("Jersey hello world example.", response);
}
@Test
public void testHelloName() {
String response = target.path("hello/mkyong").request().get(String.class);
assertEquals("Jersey: hello mkyong", response);
}
@Test
public void testHelloHK2() {
String response = target.path("hello/hk2").request().get(String.class);
assertEquals("Hello World Jersey from HK2", response);
}
}
8. Download Source Code
$ git clone https://github.com/favtuts/java-jax-rs-tutorials.git
$ cd jersey-hello-world-example
$ mvn clean package
$ java -jar target/jersey-hello-world-example-1.0.jar