Publish REST service from Java SE - outside Java EE Container image3

Publish REST service from Java SE – outside Java EE Container

The topic of this article: how to publish a REST service from a stand alone JVM with only Java SE (standard edition). So without involving any kind of Java EE container – Tomcat, WebLogic, WildFly, Jetty – I want to run a Java program and be able to invoke a REST service that is handled by that Java program. My initial goal is for test purposes: easily spin up mock-implementations of REST services that later on will be implemented externally.

Since Java SE 6, Sun’s implementation of Java SE 6 JRE (HotSpot JVM) included a built-in Java HTTP Server. We can leverage this HTTP Server in conjunction with special elements of Jersey – the library that provides the JAX-RS implementation.

In this article I have used Java SE 7 (JVM 7U55) and Jersey 2.17. I have not used Maven – which would have been the smart option for implicit dependency management and JAR fetching.

First I have downloaded the Jersey 2.17 bundle – a zip file with a substantial number of jar-files: image

I have extracted these JAR files to a project directory. In the project, I have created dependencies on these JAR files (I may not need all of them, but for simplicity’s sake I did use them all).

The REST service I initially need has to handle a simple GET and POST request. I need access to the body of whatever is POST-ed and I will return a simple response.

The REST resource is implemented using a simple POJO with @Path (JAX-RS) annotation. The resource is called movieevent. The URL used in the REST calls will be something like http://host:port/servicecontextpath/movieevent. The method getMovieEvent has the @GET annotation. It handles get-requests received at /movieevent. All it does it return a simple text/plain response. The postMovieEvent method has the @POST annotation. It handles post requests with content type set to application/json (because of the @Consumes annotation). The body of such requests (the JSON payload) will be handed to the method through the input parameter json. The request parameter will contain the ContainerRequest with some information about the request such as the sender.

image

package nl.amis.rest;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;

@Path(“movieevent”)
public class CinemaEventHandler {
public CinemaEventHandler() {
super();
}

@POST
@Consumes(“application/json”)
@Produces(“text/plain”)
public String postMovieEvent(@Context Request request, String json) {
System.out.println(“received event:” + json);
CinemaEventRouter.sendPost(json);
return “event received ” + json;
}

@GET
@Produces(“text/plain”)
public String getMovieEvent(@Context Request request) {
return “nothing to report from getMovieEvent”;
}

}

The big question in this article is: how to actually bring this REST service live? Who interprets these annotations and starts listening at some port to HTTP requests that end up in this POJO?

The answer is deceptively simple – or at least the required code is:

image

package nl.amis.rest;

import com.sun.net.httpserver.HttpServer;

import java.net.URI;

import javax.ws.rs.core.UriBuilder;

import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;

public class CinemaEventHandlerRestStartup {

private final static int port = 9998;
private final static String host=”http://localhost/”;

public static void main(String[] args) {
URI baseUri = UriBuilder.fromUri(host).port(port).build();
ResourceConfig config = new ResourceConfig(CinemaEventHandler.class);
HttpServer server = JdkHttpServerFactory.createHttpServer(baseUri, config);
}

This class CinemaEventHandlerRestStartup leverages both the Http Server built into the JVM as well as Jersey’s capabilities to work with that server. As you can see, the code crease a URI (the address on which to have the HTTP Server listen), creates a (JAX-RS) ResourceConfig for the CinemaEventHandler class with the @Path annotation and uses Jersey’s class JdkHttpServerFactory to bring the two – JAX-RS and the HTTP Server – together.

When we run CinemaEventHandlerRestStartup from the command line or from inside an IDE – on a plain JVM – it starts the HttpServer and registers itself for http://localhost:9998. GET and POST requests (the latter with application/json set as content type) are handed over to CinemaEventHandler for processing.

Here the result for a simple GET request directly from a browser:

image

And here a POST request sent from SoapUI:

image

The output from the second request on the JVM side:

image

Clearly it is this simple: exposing a REST service from Java code running in a plain JVM, outside any container. Configure the dependencies on Jersey, write a few lines of code and you’re in business.

 

 

 

Resources

HttpServer in Sun’s Java 6 JVM: http://marxsoftware.blogspot.nl/2008/03/suns-java-http-server.html

Undocumented Oracle JVM HttpServer Properties

Jersey Documentation on Java SE deployment

Download page for Jersey.