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