Async JAX-RS: Quick Tips…

This article outlines the use of Jetty with JAX-RS Async.

Use the webapp archetype and create the webapp in the specific archetype version.

mvn archetype:generate -DgroupId=org.bastide -DartifactId=webapp -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.4

When prompted, confirm details:
[INFO] Using property: groupId = org.bastide
[INFO] Using property: artifactId = webapp
Define value for property ‘version’ 1.0-SNAPSHOT: : 1.0-SNAPSHOT
[INFO] Using property: package = org.bastide
Confirm properties configuration:
groupId: org.bastide
artifactId: webapp
version: 1.0-SNAPSHOT
package: org.bastide
Y: : Y

You’ll see
[INFO] —————————————————————————-
[INFO] Using following parameters for creating project from Archetype: maven-archetype-webapp:1.4
[INFO] —————————————————————————-
[INFO] Parameter: groupId, Value: org.bastide
[INFO] Parameter: artifactId, Value: webapp
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: org.bastide
[INFO] Parameter: packageInPathFormat, Value: org/bastide
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: org.bastide
[INFO] Parameter: groupId, Value: org.bastide
[INFO] Parameter: artifactId, Value: webapp
[INFO] Project created from Archetype in dir: /Users/user/Desktop/j/project/webapp
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 01:02 min
[INFO] Finished at: 2019-08-18T16:52:03-04:00
[INFO] ————————————————————————

The typical archetype catalog is found at repo1 – http://repo1.maven.org/maven2/ . To add an archetype catalog in Eclipse use https://howtodoinjava.com/eclipse/how-to-import-maven-remote-archetype-catalogs-in-eclipse/ . To read more on the Apache archetypes, reference github https://github.com/apache/maven-archetypes/tree/maven-archetype-bundles-1.4

Open the pom.xml and update to Java 1.8.
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>

Add the Jetty Version. Find the latest version on Maven Central – https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-maven-plugin/ (in this case ) * scan carefully * the jetty versions are not in the approved versioning, and 20 comes before 9.4.3.
<jetty.version>9.4.20.v20190813</jetty.version>
<junit.version>5.5.1</junit.version>

Add a dependencies block for jetty, and replace junit.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>

Type mvn dependency:tree to show the jar files that’ll be accessible in the classpath.

~:webapp$ mvn dependency:tree
[INFO] Scanning for projects…
[INFO]
[INFO] ————————-< org.bastide:webapp >————————-
[INFO] Building webapp Maven Webapp 1.0-SNAPSHOT
[INFO] ——————————–[ war ]———————————
[INFO]
[INFO] — maven-dependency-plugin:2.8:tree (default-cli) @ webapp —
[INFO] org.bastide:webapp:war:1.0-SNAPSHOT
[INFO] +- org.junit.jupiter:junit-jupiter-api:jar:5.5.1:test
[INFO] | +- org.apiguardian:apiguardian-api:jar:1.1.0:test
[INFO] | +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] | \- org.junit.platform:junit-platform-commons:jar:1.5.1:test
[INFO] \- org.eclipse.jetty:jetty-server:jar:9.4.9.v20180320:compile
[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:compile
[INFO] +- org.eclipse.jetty:jetty-http:jar:9.4.9.v20180320:compile
[INFO] | \- org.eclipse.jetty:jetty-util:jar:9.4.9.v20180320:compile
[INFO] \- org.eclipse.jetty:jetty-io:jar:9.4.9.v20180320:compile
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 2.199 s
[INFO] Finished at: 2019-08-18T17:05:38-04:00
[INFO] ————————————————————————

Check the build mvn clean package, and check to see BUILD SUCCESS.

Add the maven plugin for jetty under build > plugins
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty.version}</version>
<configuration>
<jettyXml></jettyXml>
</configuration>
</plugin>
</plugins>

Execute mvn jetty:run and the command pauses at the end (as the process is not forked)
[INFO] Started o.e.j.m.p.JettyWebAppContext@29ea78b1{/,file:///Users/paulbastide/Desktop/j/project/webapp/src/main/webapp/,AVAILABLE}{file:///Users/paulbastide/Desktop/j/project/webapp/src/main/webapp/}
[INFO] Started ServerConnector@74294c1a{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
[INFO] Started @6480ms
[INFO] Started Jetty Server

Open a new terminal
curl http://localhost:8080/

Once you see the response, you know the project is setup correctly. (note this is from ./src/main/webapp/index.jsp)
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>

You can stop the mvn command (Control + C)

^C[INFO] Stopped ServerConnector@74294c1a{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
[INFO] Stopped scavenging
[INFO] Stopped o.e.j.m.p.JettyWebAppContext@29ea78b1{/,file:///Users/paulbastide/Desktop/j/project/webapp/src/main/webapp/,UNAVAILABLE}{file:///Users/paulbastide/Desktop/j/project/webapp/src/main/webapp/}
[INFO] Jetty server exiting.
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 02:31 min
[INFO] Finished at: 2019-08-18T19:04:02-04:00
[INFO] ————————————————————————

Add the jersey version to the properties element in the pom.xml
        <jersey.version>2.28</jersey.version>

Add the Jersey dependencies to the pom.xml
<dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-jetty-http</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>${jersey.version}</version>
        </dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey.version}</version>
</dependency>

You can test the dependencies and properties using mvn clean package and check the contents with jar -tvf target/webapp.war
webapp$ jar -tvf target/webapp.war
101 Sun Aug 18 19:09:22 EDT 2019 META-INF/MANIFEST.MF
0 Sun Aug 18 19:09:22 EDT 2019 META-INF/
0 Sun Aug 18 19:09:22 EDT 2019 WEB-INF/
0 Sun Aug 18 19:09:22 EDT 2019 WEB-INF/classes/
0 Sun Aug 18 19:09:22 EDT 2019 WEB-INF/lib/
52 Sun Aug 18 16:52:02 EDT 2019 index.jsp
83813 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jersey-entity-filtering-2.28.jar
502985 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jetty-util-9.4.12.v20180830.jar
20235 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/osgi-resource-locator-1.0.1.jar
40254 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jersey-container-jetty-http-2.28.jar
93107 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/validation-api-2.0.1.Final.jar
66894 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jackson-annotations-2.9.8.jar
25150 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jakarta.annotation-api-1.3.4.jar
85920 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jersey-media-jaxb-2.28.jar
5408 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jakarta.inject-2.5.0.jar
1141593 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jersey-common-2.28.jar
1347236 Sun Aug 18 19:09:20 EDT 2019 WEB-INF/lib/jackson-databind-2.9.8.jar
325619 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jackson-core-2.9.8.jar
16680 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jetty-continuation-9.4.12.v20180830.jar
75210 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jersey-media-json-jackson-2.28.jar
584440 Sun Aug 18 17:03:02 EDT 2019 WEB-INF/lib/jetty-server-9.4.9.v20180320.jar
215 Sun Aug 18 16:52:02 EDT 2019 WEB-INF/web.xml
3425 Sun Aug 18 19:09:00 EDT 2019 META-INF/maven/org.bastide/webapp/pom.xml
90 Sun Aug 18 19:09:22 EDT 2019 META-INF/maven/org.bastide/webapp/pom.properties
196494 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jersey-client-2.28.jar
169671 Sun Aug 18 17:03:02 EDT 2019 WEB-INF/lib/jetty-http-9.4.9.v20180320.jar
95806 Sun Aug 18 17:03:02 EDT 2019 WEB-INF/lib/javax.servlet-api-3.1.0.jar
32627 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jackson-module-jaxb-annotations-2.9.8.jar
73338 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jersey-container-servlet-core-2.28.jar
140262 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jakarta.ws.rs-api-2.1.5.jar
134935 Sun Aug 18 17:03:02 EDT 2019 WEB-INF/lib/jetty-io-9.4.9.v20180320.jar
935993 Sun Aug 18 19:09:18 EDT 2019 WEB-INF/lib/jersey-server-2.28.jar

Update the web.xml with inner xml.
 
<servlet>
        <servlet-name>App</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>org.bastide.jaxrs.Application</param-value>
        </init-param>
<async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>App</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

Create the Jax-RS application java file
mkdir -p src/main/java/org/bastide/jaxrs/
touch src/main/java/org/bastide/jaxrs/Application.java

Update the Application.java with the contents to activate your JAX-RS code.
package org.bastide.jaxrs;

import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath(“rest”)
public class Application extends ResourceConfig {
 
public Application() {
System.out.println(“Setting up the application”);
packages(“org.bastide.jaxrs”);
}
}

Add a new sync api SyncApi.java in the package.
package org.bastide.jaxrs;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

@Path(“/sync”)
public class SyncApi {
 
@GET
    @Path(“/get”)
public String test() {
return “Example Content”;
}
}

Startup Jetty using (jetty:run) and execute a curl request, and you should see:
webapp$ curl http://localhost:8080/rest/sync/get
Example Content
webapp$

Now, create AsyncApi.java
package org.bastide.jaxrs;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.container.CompletionCallback;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.container.AsyncResponse;

@Path(“/async”)
public class AsyncApi {
 
@GET
    @Path(“/get”)
public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse) {
asyncResponse.register(new CompletionCallback() {
@Override
public void onComplete(Throwable throwable) {
System.out.println(“Complete”);
}
});
 
new Thread(new Runnable() {
@Override
public void run() {
String result = veryExpensiveOperation();
asyncResponse.resume(result);
}
 
private String veryExpensiveOperation() {
try{
Thread.sleep(10000L);
System.out.println(“TEST IS COMPLETE”);
} catch (java.lang.InterruptedException ie){
ie.printStackTrace();
}
return “DONE”;
}
}).start();
}
}

The code starts a thread for 10 seconds, and runs as async.

time curl http://localhost:8080/rest/async/get
DONE
real    0m10.066s
user    0m0.008s
sys 0m0.014s

This technique is a great way to hold a connection, or dispatch work on a specific JVM and coordinate long running actions.  Eventually, I’ll be using this with my Raspberry Pi.

Reference
https://www.eclipse.org/jetty/documentation/9.4.20.v20190813/maven-and-jetty.html#jetty-maven-helloworld
https://www.eclipse.org/jetty/documentation/9.4.20.v20190813/jetty-maven-plugin.html
https://jersey.github.io/documentation/latest/index.html
https://jersey.github.io/documentation/latest/deployment.html#d0e3342
https://github.com/AlanHohn/jaxrs/blob/master/src/main/java/org/anvard/jaxrs/server/EmbeddedServer.java
https://stackoverflow.com/a/44546979
https://www.eclipse.org/jetty/documentation/9.4.x/jetty-maven-plugin.html#jetty-run-goal
https://jersey.github.io/documentation/latest/async.html#d0e9895
https://jersey.github.io/documentation/latest/async.html#d0e9895
 

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.