Jax-RS

Solution: JAX-RS Servlet properties for QueryParam Form

Recently, I have done a good amount of JAX-RS development, and I needed to kept hitting an issue with forms parameter validation errors on my query params.  The errors kept driving me nuts, until I read the documentation and found – jersey.config.servlet.form.queryParams.disabled .   I also learned the java property is available for those with code based configuration.  ServletProperties.QUERY_PARAMS_AS_FORM_PARAMS_DISABLED

If true then query parameters will not be treated as form parameters (e.g. injectable using @FormParam) in case a Form request is processed by server.

https://jersey.github.io/documentation/latest/user-guide.html#appendix-properties

JAX-RS Mapping All ClientExceptions with ExceptionMappers

For those familiar with JAX-RS, you know it generates some funky errors. I wanted to control the data in the response. I started looking at Servlet Filters, however, by the time you get the response it’s fully committed. I started looking at JAX-RS filters and exception handlers. I found ExceptionMapper and ClientErrorException. The WebSphere Liberty documentation includes a good overview.

I implemented the ExceptionMapper and set a custom response. You could add any of these exceptions – BadRequestException, ForbiddenException, NotAcceptableException, NotAllowedException, NotAuthorizedException, NotFoundException, NotSupportedException. I also added the @Provider annotation to the top of the class, and added a reference in the Application declaration.

AsyncResponse with JAX-RS

Many of the projects and APIs I build now are asynchronous – request-wait-download. This common pattern is baked into JAX-RS, as described in Link: Asynchronous JAX-RS: Basics and Gotchas. The following is based on my experience and reading the article.

<dependencies>
<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>8.0</version>
    <scope>provided</scope>     
</dependency>
<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.1</version>
</dependency>
</dependencies>
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
<dependency>
	<groupId>javax.enterprise.concurrent</groupId>
	<artifactId>javax.enterprise.concurrent-api</artifactId>
	<version>1.1</version>
	<scope>provided</scope>
</dependency>

A great resource is an IBM Conference Presentation which describes the Managed Executor Service.

JaxbAnnotationIntrospector.findAnnotation:909′ java.lang.NullPointerException

We built a quick demo/entity class (Still using the Default package. ) The Entity Class is in default package, and just holds a string.

<init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <param-value>Demo</param-value>
</init-param>

When we navigate to the test project / REST interface – http://localhost:9080/JaxRsExample/rest/demo/demo

Launching defaultServer (WebSphere Application Server 8.5.5.2/wlp-1.0.5.cl50220140403-1858) on IBM J9 VM, version pwa6470sr5-20130619_01 (SR5) (en_US)
[AUDIT   ] CWWKE0001I: The server defaultServer has been launched.
[AUDIT   ] CWWKZ0058I: Monitoring dropins for applications.
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://localhost:9080/JaxRsExample/
[AUDIT   ] CWWKZ0003I: The application JaxRsExample updated in 0.037 seconds.
[ERROR   ] An unhandled exception occurred which will be propagated to the container.
[ERROR   ] SRVE0777E: Exception thrown by application class ‘org.codehaus.jackson.xc.JaxbAnnotationIntrospector.findAnnotation:909‘
java.lang.NullPointerException
        at org.codehaus.jackson.xc.JaxbAnnotationIntrospector.findAnnotation(JaxbAnnotationIntrospector.java:909)
        at org.codehaus.jackson.xc.JaxbAnnotationIntrospector.findAccessType(JaxbAnnotationIntrospector.java:321)
        at org.codehaus.jackson.xc.JaxbAnnotationIntrospector.findAutoDetectVisibility(JaxbAnnotationIntrospector.java:256)
        at org.codehaus.jackson.map.AnnotationIntrospector$Pair.findAutoDetectVisibility(AnnotationIntrospector.java:808)
        at org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:274)
        at org.codehaus.jackson.map.ser.BeanSerializerFactory.constructBeanSerializer(BeanSerializerFactory.java:216)
        at org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanSerializer(BeanSerializerFactory.java:139)
        at org.codehaus.jackson.map.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:108)
        at org.codehaus.jackson.map.ser.StdSerializerProvider._createUntypedSerializer(StdSerializerProvider.java:709)
        at org.codehaus.jackson.map.ser.StdSerializerProvider._createAndCacheUntypedSerializer(StdSerializerProvider.java:652)
        at org.codehaus.jackson.map.ser.StdSerializerProvider.findValueSerializer(StdSerializerProvider.java:426)
        at org.codehaus.jackson.map.ser.StdSerializerProvider.findTypedValueSerializer(StdSerializerProvider.java:500)
        at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:312)
        at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:242)
        at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1030)
        at org.codehaus.jackson.jaxrs.JacksonJsonProvider.writeTo(JacksonJsonProvider.java:509)
        at org.apache.wink.providers.jackson.WinkJacksonJaxbJsonProvider.writeTo(WinkJacksonJaxbJsonProvider.java:84)
        at org.apache.wink.server.internal.handlers.FlushResultHandler.handleResponse(FlushResultHandler.java:199)
        at org.apache.wink.server.handlers.AbstractHandler.handleResponse(AbstractHandler.java:38)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:26)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:22)
        at org.apache.wink.server.handlers.AbstractHandlersChain.doChain(AbstractHandlersChain.java:75)
        at org.apache.wink.server.handlers.AbstractHandler.handleResponse(AbstractHandler.java:39)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:26)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:22)
        at org.apache.wink.server.handlers.AbstractHandlersChain.doChain(AbstractHandlersChain.java:75)
        at org.apache.wink.server.handlers.AbstractHandler.handleResponse(AbstractHandler.java:39)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:26)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:22)
        at org.apache.wink.server.handlers.AbstractHandlersChain.doChain(AbstractHandlersChain.java:75)
        at org.apache.wink.server.internal.log.Responses.handleResponse(Responses.java:90)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:26)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:22)
        at org.apache.wink.server.handlers.AbstractHandlersChain.doChain(AbstractHandlersChain.java:75)
        at org.apache.wink.server.handlers.AbstractHandlersChain.run(AbstractHandlersChain.java:60)
        at org.apache.wink.server.internal.RequestProcessor.handleRequestWithoutFaultBarrier(RequestProcessor.java:212)
        at org.apache.wink.server.internal.RequestProcessor.handleRequest(RequestProcessor.java:154)
        at com.ibm.websphere.jaxrs.server.IBMRestServlet.service(IBMRestServlet.java:106)
        at [internal classes]

[ERROR   ] SRVE0315E: An execption occurred: com.ibm.ws.webcontainer.webapp.WebAppErrorReport: java.lang.NullPointerException
        at org.codehaus.jackson.xc.JaxbAnnotationIntrospector.findAnnotation(JaxbAnnotationIntrospector.java:909)
        at org.codehaus.jackson.xc.JaxbAnnotationIntrospector.findAccessType(JaxbAnnotationIntrospector.java:321)
        at org.codehaus.jackson.xc.JaxbAnnotationIntrospector.findAutoDetectVisibility(JaxbAnnotationIntrospector.java:256)
        at org.codehaus.jackson.map.AnnotationIntrospector$Pair.findAutoDetectVisibility(AnnotationIntrospector.java:808)
        at org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:274)
        at org.codehaus.jackson.map.ser.BeanSerializerFactory.constructBeanSerializer(BeanSerializerFactory.java:216)
        at org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanSerializer(BeanSerializerFactory.java:139)
        at org.codehaus.jackson.map.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:108)
        at org.codehaus.jackson.map.ser.StdSerializerProvider._createUntypedSerializer(StdSerializerProvider.java:709)
        at org.codehaus.jackson.map.ser.StdSerializerProvider._createAndCacheUntypedSerializer(StdSerializerProvider.java:652)
        at org.codehaus.jackson.map.ser.StdSerializerProvider.findValueSerializer(StdSerializerProvider.java:426)
        at org.codehaus.jackson.map.ser.StdSerializerProvider.findTypedValueSerializer(StdSerializerProvider.java:500)
        at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:312)
        at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:242)
        at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1030)
        at org.codehaus.jackson.jaxrs.JacksonJsonProvider.writeTo(JacksonJsonProvider.java:509)
        at org.apache.wink.providers.jackson.WinkJacksonJaxbJsonProvider.writeTo(WinkJacksonJaxbJsonProvider.java:84)
        at org.apache.wink.server.internal.handlers.FlushResultHandler.handleResponse(FlushResultHandler.java:199)
        at org.apache.wink.server.handlers.AbstractHandler.handleResponse(AbstractHandler.java:38)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:26)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:22)
        at org.apache.wink.server.handlers.AbstractHandlersChain.doChain(AbstractHandlersChain.java:75)
        at org.apache.wink.server.handlers.AbstractHandler.handleResponse(AbstractHandler.java:39)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:26)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:22)
        at org.apache.wink.server.handlers.AbstractHandlersChain.doChain(AbstractHandlersChain.java:75)
        at org.apache.wink.server.handlers.AbstractHandler.handleResponse(AbstractHandler.java:39)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:26)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:22)
        at org.apache.wink.server.handlers.AbstractHandlersChain.doChain(AbstractHandlersChain.java:75)
        at org.apache.wink.server.internal.log.Responses.handleResponse(Responses.java:90)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:26)
        at org.apache.wink.server.handlers.ResponseHandlersChain.handle(ResponseHandlersChain.java:22)
        at org.apache.wink.server.handlers.AbstractHandlersChain.doChain(AbstractHandlersChain.java:75)
        at org.apache.wink.server.handlers.AbstractHandlersChain.run(AbstractHandlersChain.java:60)
        at org.apache.wink.server.internal.RequestProcessor.handleRequestWithoutFaultBarrier(RequestProcessor.java:212)
        at org.apache.wink.server.internal.RequestProcessor.handleRequest(RequestProcessor.java:154)
        at com.ibm.websphere.jaxrs.server.IBMRestServlet.service(IBMRestServlet.java:106)
        at [internal classes]
Caused by: java.lang.NullPointerException
        … 40 more

DON’T EVER USE THE DEFAULT PACKAGE

Exception thrown by application class ‘org.codehaus.jackson.xc.JaxbAnnotationIntrospector.findAnnotation:909‘ java.lang.NullPointerException

We changed to package com.test and it all worked.

JAX-RS Async

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)

[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

Custom Serialization with Jackson

Date serialization can be a pain.

@JsonSerialize(using = JsonDateSerializer.class)

Date Serializer