Mimic CGI Behavior in RESTful Java Web Services

java-server-225x225In my article RESTful Java Web Services with NetBeans, Jersey and Tomcat I showed you how to create a simple Java web service to handle HTTP GET and POST requests. The service handlers in this example didn’t do much. The GET handler just returned a single string and the POST handler echoed back any text sent to it.

Now that we have a web service framework, let’s add some useful functionality to it. I’m going to show you how to add the capability to run external commands or scripts from your RESTful services in a manner similar to CGI (Common Gateway Interface).


Common Gateway Interface

CGI defines a mechanism that web servers can use to execute programs and scripts to handle HTTP requests, usually GET or POST. Web servers that support CGI minimally provide configuration parameters to specify the directory where CGI programs are located and that define the URLs used by web clients to invoke CGI functionality.

When a CGI program is invoked the web server connects to the stdin and stdout of the program through IPC, such as pipes or socketpairs, over which it exchanges an HTTP request and response with the program. The CGI program reads the request from stdin, processes the request and writes its response to stdout.

RESTful CGI Web Service

Create a Web Service Project

Open your browser to my RESTful Java Web Services with NetBeans, Jersey and Tomcat blog and keep it handy. I’m going to reference it throughout this article.

Create a RESTful web service project according to the directions I mention in my blog but call it cgi instead. Let’s simplify the service URL by calling the web service project cgi then eliminating the webresources/ part of the URL by changing the <url-pattern> line in web.xml to this:


With these changes the new service URL becomes http://<server>:8080/cgi/service.

ServiceResource Class

Assuming you call the service class ServiceResource, NetBeans will generate a service framework for you that looks something like the code below.

package com.vichargrave;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.Map;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Produces;

public class ServiceResource {

    private final int bsize = 1024;

    private HttpServletRequest request;

    public ServiceResource() {

    // GET and POST Service Handlers

Note I’ve changed the java.ws.rs.PUT import in line 12 that you would normally get to java.ws.rs.POST and deleted the PUT handlers.  I’ll add GET and POST handlers in the following sections.

Add the private bsize parameter as shown line 18. That value will be used to size a buffer in the POST handler coming up.

GET Service Handler

Next add the GET handler method shown below to your service class. In this example it will simply return a help message to the client, kind of like the usage message you get with most command line applications.

public String getHandler() {
    string server = request.getServerName();
    return "POST request command - curl http://"+server+":8080/cgi/service --data-binary @file";

POST Service Handler

The POST handler method contains the CGI handling code. The key to this handler is creating a process in which the external command will be run and getting I/O streams set up to send a request and receive a response from it. All of this is conveniently handled using the Java ProcessBuilder class.

public String postHandler(String content) {
    StringBuilder response = new StringBuilder();
    try {
        // Create a process build for the external command
        ProcessBuilder builder = new ProcessBuilder();

        // Specify the command followed by 0 or more arguments

        // Start the command then send it the content from the client
        final Process process = builder.start();
        OutputStreamWriter stdout = new OutputStreamWriter(process.getOutputStream());

        // Collect the command output to send back to the client
        int len;
        byte[] line = new byte[bsize];
        InputStream stdin = process.getInputStream();
        while ((len = stdin.read(line, 0, bsize)) > -1) {
            response.append(new String(line, 0, len));
    catch(IOException ex) {     
        response = new StringBuilder();
    finally {
        return response.toString();

[Lines 2-3] The @Consumes annotation specifies that the POST handler accepts requests containing URL encoded data. The Content-Type of the POST request much match the @Consumes type or else the POST handler will not get called. The @Produces annotation specifies that the POST handler will return ordinary text.

[Line 8] First we create a ProcessBuilder and get the default process environment, although the environment will not be used in this example. Next assign the external command that will be executed, in this case “grep”, although it can be any command that accepts input from stdin. The command argument is followed by 0 or more command arguments. The grep command will scan the content sent and display lines containing semi-colons so the sole command line argument is “;”.

[Lines 14-17] The external command is run by calling builder.start(). Create an OutputStreamWriter from the OutputStream provided by the ProcessBuilder which connects with the POST handler service to the stdin of the external command process. The handler writes out the request content to the command’s stdin then close the OutputStreamWriter to flush all the data.

[Lines 20-24] The output from the external command can be read from the InputStream provided by the ProcessBuilder which connects to the stdout of the external command process. The handler reads data from the input stream bsize bytes at a time into the a response StringBuffer until the end of file is detected.

[Lines 27-32] If an exception is thrown during the service I/O, it is caught and a message explaining the exception is assigned to the response string. Otherwise, the lines from the request input that contained semicolons are returned to the client.

Test the CGI Web Service

You can get the source code for this project at GitHub – https://github.com/vichargrave/restfulcgi. Test the RESTful CGI POST handler by sending a C source file, like sniffer.c from my Develop a Packet Sniffer with Libpcap, with this curl command:

curl http://localhost:8080/cgi/service --data-binary @sniffer.c

The grep command will output all the lines in sniffer.c that contain a semicolon, which is most of them since this is a C file.


Article by Vic Hargrave

Software developer, blogger and family man enjoying life one cup of coffee at a time. I like programming and writing articles on tech topics. And yeah, I like coffee.

Leave a Reply

Your email address will not be published. Required fields are marked *