Wednesday, May 28, 2014

Handling External Processes in Java made Easy

Handling external processes in Java has never been easy.  The JDK provides a way to start and manage external processes and they do work; but they are awkward.  If the process hangs for some reason, getting at the input and output streams to figure out what’s going wrong is a pain.

I ran into this issue recently with my work on the Transform4J project, am open source Java ETL Transformation API,  where my test cases need to start and stop databases such as Cassandra and MongoDB.  Getting these test cases to work as far as starting up and shutting down these databases was very irritating and took more time than I would like to admit.  This prompted me to look for an open source product that makes process management easier.  I found one.

The Apache Commons Exec product makes external process management from Java very, very easy.  To illustrate with a simple example, let’s start up a Cassandra database for a series of unit tests.  

Executor executor = new DefaultExecutor();
executor.execute(new CommandLine(cassandraStartupCommand));

In my case, I was having trouble getting Cassandra to start up properly at one point, so I added a StreamHandler to capture any output so I could more easily debug the issue.  By default, this output goes to standard out and standard error (this is configurable).  I just added one line before execution:

executor.setStreamHandler(new PumpStreamHandler());

As it happens, I need to shut the database down after unit tests have completed.  It turns out that this is easy to do.  Cassandra initiates a graceful shutdown when the process is terminated.  To accomplish this with Commons Exec, you need an ExecuteWatchdog.  Adding one to the execution  is relatively simple:

ExecuteWatchdog  watchdog = new ExecuteWatchdog(5000);
executor.setWatchdog(watchdog);

When the unit tests are complete, terminating the process is simple:
watchdog.destroyProcess();

Note that there are additional features in this API that look useful (but that I didn’t happen to need for my unit tests).  With an ExecuteResultHandler, you can optionally throw an exception should the sub-process fail; I may incorporate this into my testing process.  You can very easily associate a ProcessDestroyer with the execution to terminate the executing process via shutdown hook when your JVM terminates.

I wish I had found this product sooner; it’s been around for five years or so.  Those looking for a more in depth introduction should check out the tutorial here.

No comments:

Post a Comment