Mike Knepper

Sever/Application Relay

June 5, 2014

My Java server has been dramatically simplified this week. As I’ve alluded to in previous posts, the server is now simply responsible for accepting requests and sending responses. Everything regarding parsing the details of the request and generating the appropriate data for a response has been stripped from the server and moved into a completely separate external application. Here’s how it all works.

Specify application at runtime

When firing up my server, I can pass in optional flags to specify which port to run on and which directory to serve. For example: java -jar dahomey.jar will by default run on port 1961 and serve the “public/” directory, but if I wanted to run on port 6000 and serve my Desktop directory, I could run java -jar dahomey.jar -p 6000 -d /Users/mrk/Desktop/. I decided that I similarly wanted to be able to specify at runtime which routing application I wanted the server to communicate with with an “-a” flag. (Side note, unlike port and directory, the application flag is required; the server will not fall back on any default routing logic.)

This decision had some profound effects on my design. Communicating with code from some other application (or “library”) would have been significantly different if I had chosen to jar the two applications together. In that kind of setup, I would’ve had direct access to the routing application’s code and could call any of its methods I wanted in the server, but the relationship between the two applications would be fixed and static. I wanted to be able to have my server ready to run at any moment and swap in and out different routing applications for any reason.


The key to executing the external jar dynamically is to use Java’s Process class, which I’m generating using a helpful ProcessBuilder object. These objects are created like this:

String application = "someapp.jar"
ProcessBuilder builder = new ProcessBuilder("java", "-jar", application);
Process process = builder.start();

The ProcessBuilder is instantiated with each component of a command that would be executed on the system. The Process object is then generated by calling the builder’s start() method. By default, a process will have two InputStreams associated with it: the standard input stream and a separate stream dedicated to errors. Calling builder.redirectErrorStream(true); merges those two streams together, which is easier to work with (I find Java’s input and output streams quite clunky and confusing, so the fewer the better!).

Important note before I discuss collecting data from the process: once you’re finished “using” the external process, you need to explicitly terminate the process by calling process.destroy();.

Data exchange

The code above would be great if all we needed was to tell some external application to execute and be happy that it did something, but in this case, we need to receive data back from the application. I recently spent some time looking into Rack, the interface between web servers and ruby applications, and, admiring its simplicity, wanted to emulate it in my Java project. Unfortunately, there is a fundamental difference between Java and Ruby that made that goal more complicated.

In Ruby, every method has a return value. In Java, some methods have return values, but others are void and do not return any value (they just “do” stuff). When running a jar file with java -jar someapp.jar, the system looks for and executes the jar file’s main method, which must be declared like this: public static void main(String[] args) {..}. Because the main method is void, I cannot simply have my process call a jar and return the necessary data from that call like one would with Rack/Ruby, because by rule and definition there is no data returned.

The solution is, rather unfortunately, to print data out from the router in an output stream and have the server listen for that data and collect it from the process’s input stream. This procedure is a little tricky because ideally we’d like to send Java objects instead of “translating” them as simple strings and re-translating them back into objects on the server’s side. Fortunately, Java has a class named ObjectOutputStream to handle this sort of thing. I found this class surprisingly difficult to test, but the production code is relatively simple.

The routing application has a Relayer class that prints out the data structures the server needs for a response:

public class Relayer {
  public static void sendData(String status, HashMap<String, String> headers, byte[] body) throws Exception {
    ObjectOutputStream oos = new ObjectOutputStream(System.out);

On the other side, the server reads the objects from the running process:

// set up a Process object via ProcessBuilder

String status;
HashMap<String, String> headers;
byte[] body;

ObjectInputStream ois = new ObjectInputStream(process.getInputStream());
status = (String) ois.readObject();
headers = (HashMap<String, String>) ois.readObject();
body = (byte[]) ois.readObject();

The objects must be read back in the same order they are written. Also, the specific class types are cast to the ois.readObject() call because the Object I/O streams only recognize the objects as class Object.

With the streams closed and the process destroyed, the data exchange is complete. The server now has all the data it needs to format a response and send it back to the client.