It is easy to be seduced by some of the attractive qualities of Node (aka Node.js) – the JavaScript technology that makes server side development fun again. Developing light weight applications that handle HTTP requests in a rapid, straightforward way with little overhead and no bloated infrastructure is easy as pie – and feels a long way from the traditional Java development. I like Node. I feel the attraction. I have used Node for simple and more complex applications. It’s cool.
I have realized that what is so nice about Node, is also largely available with Java. Of course, there are many ways of doing Java development that is not lightweight and rapid and low overhead at all. As I am sure we can find ways to spoil Node development. More importantly, there are ways to make Java development comparably breezy as Node development. In this article I take a brief look at the development of a REST API using nothing but the [Oracle] Java Runtime and Maven as the package manager (Java’s equivalent to Node’s npm). Using the Java 8 JDK and Maven I am able to program and run a REST API from my command line, running locally on my laptop, using under two dozen lines of code. In a way to that is very similar to what I would do with Node and the Express library. The steps described below can be executed in less than 15 minutes – similar to what Node based development of this type of REST API foundation would require.
The source code accompanying this article is in GitHub: https://github.com/lucasjellema/java-express-style-rest-api – but it is not a lot of code at all.
The final result of this article is simple: a REST API running locally that handles simple GET and POST requests. The logic of the API has to be implemented (and some JSON processing may have to be added, which granted is in Java more complex than in Node) – but that is fairly evident to do.
Here is a screenshot of Postman where the REST API is invoked:
and here is the command line for the running REST API:
The application is started with a single command line (compare to npm start) and listens on port 8765 on localhost to process incoming requests.
The steps for implementing this REST API and running it locally are described below.
Implementation of REST API
Again, the only two prerequisites for these steps are: a locally installed Oracle JDK 8 (http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) and Maven 3 environment (https://maven.apache.org/download.cgi)
1. Create scaffold for new application using Maven (compare npm init)
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=nl.amis.rest -DartifactId=my-rest
2. Edit Maven’s pom.xml to add dependencies for Jersey and Jersey Container (compare package.json and npm install –save)
Note: also add build section in pom.xml with explicit indication of Java 1.8 as source and target version (to ensure Lambda expressions are supported)
3. Retrieve required libraries (jar files) using Maven (compare npm install)
mvn install dependency:copy-dependencies
This will install all required JARs into directory target\dependency – compare to node-modules in a Node application.
4. Edit Java class App to create the most simple and straightforward http request serving application conceivable – use imports for required dependencies (compare require instructions in node application)
package nl.amis.rest; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpServer; public class App { private final static int port = 8765; public static void main(String[] args) throws IOException { HttpServer server = HttpServer.create(new InetSocketAddress(port), 0); server.createContext("/app", (HttpExchange t) -> { byte[] response = "Hello World from HttpServer".getBytes(); t.sendResponseHeaders(200, response.length); OutputStream os = t.getResponseBody(); os.write(response); os.close(); }); server.setExecutor(null); // creates a default executor server.start(); System.out.println("HTTP Server is running and listening at " + server.getAddress() + "/app"); } }
and invoke it:
5. Class App2.java builds on App2 to add the REST API capabilities – using class Api as the REST Resource (@Path Api) with the Resource Methods to handle GET and POST requests)
package nl.amis.rest; import java.io.IOException; 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 App2 { private final static int port = 8765; private final static String host = "http://localhost/app"; public static void main(String[] args) throws IOException { URI baseUri = UriBuilder.fromUri(host).port(port).build(); ResourceConfig config = new ResourceConfig(Api.class); HttpServer server = JdkHttpServerFactory.createHttpServer(baseUri, config); System.out.println("HTTP Server is running and listening at "+baseUri+"/api" ); } }
and
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("api") public class Api { @POST @Consumes("application/json") @Produces("text/plain") public String postApiMessage(@Context Request request, String json) { System.out.println("received event:" + json); return "post message received " + json; } @GET @Produces("text/plain") public String getApiMessage(@Context Request request) { return "nothing to report from getApiMessage."; } }
6. Build application using Maven (this step does not really exist for node applications; programming errors come out at run time )
mvn package
This creates a JAR file – my-rest-1.0-SNAPSHOT.jar, 6 KB – that can be shipped, cloud deployed or simply executed (as in the next section)
7. Run application which starts the REST API at http://localhost:8765
java -cp target/my-rest-1.0-SNAPSHOT.jar;target/dependency/* nl.amis.rest.App
or
java -cp target/my-rest-1.0-SNAPSHOT.jar;target/dependency/* nl.amis.rest.App2
Resources
Get URL parameters using JDK HTTP server http://www.rgagnon.com/javadetails/java-get-url-parameters-using-jdk-http-server.html
Example of reading headers and of downloading (PDF) file through HTTP Server: http://www.rgagnon.com/javadetails/java-have-a-simple-http-server.html