13th July 2010 under Java

The Magic of Embedded Webservers

Sometimes non web-based applications you build may be installed on servers which are not physically accessible to you. In such cases you might want to rely on configuration files and log files to find out the status of your application. This may be cool in cases where you have very few or simple applications to manage. However, when the complexity rises, log files may no longer be easy to read and you may find yourself asking questions about variables in your own configuration files. Trust me these scenarios can get really frustrating and that is why you might want to consider embedded web servers.

I don't really know how recent the technology I'm going to describe in this post is but it saved my life and I think its worth blogging about. (Afterall I haven't written on this blog in a while). Embedded servers are light servers which sit right in the core of your application. The cool thing about these servers is the fact that they are part of your application. This means they have the opportunity (if given) to access all the variables in the very guts of your application. They are able to provide universal graphical user interfaces (web pages) which could easily be viewed accross several devices and platforms without much hassle. Through these interfaces, you can do anything from displaying status information, altering configurations or even issuing real-time commands to the application. The embedded web servers could also be used to add remote procedure call capabilities or other forms of API features to your application. This would make it easy for other developers to use services of your application without having to worry about low level networking protocols.

A Basic 404 Server

Lets take a quick look at one embedded web server class library for the Java platform. This class library comes with the com.sun.net.httpserver package. It's currently my favourite embedded web server for Java software development. Its powerful enough to allow you to build any kind of web based control mechanism for your application and it is also very easy to develop with. Without talking much lets start coding.

import com.sun.net.httpserver.*;
import java.net.InetSocketAddress;
import java.io.IOException;

public class WebServer
{
    public static void main(String[] args)
    {
        HttpServer server;
        InetSocketAddress address = new InetSocketAddress(4500);
        try {
            server = HttpServer.create(address, 0);
            server.start();
        } catch(IOException ex) {
            System.err.println("Error");
        }
    }
}

The code above is a basic embedded webserver. After compiling it on your machine, you can access it through http://localhost:4500. It only displays a 404 Error Message because it has nothing to display. The code basically creates an instance of the HttpServer class by passing an object of the InetSocketAddress class to it. The InetSocketAddress object contains information about which hosts are allowed to access the web server. In this particular instance the object makes it possible for any host to access the web server on the 4500 port.

Serving Hello World

To serve content through your embedded web server, you need to create handlers. These handlers are implementations of the HttpHandler interface from the class library. The interface expects your handler classes which implement it to have a handle method which takes up the job of serving your webpages and interpreting requests. The handle method is passed an object of the HttpExchange class and this object contains information about the request made to the server. It contains the request headers and it gives you the opportunity to set the response headers. It also gives you access to output and input streams for passing the information around. To add a handler to a server you need to add contexts to the server. These contexts represent the paths which need to be invoked in the URL to access the particular context handler. So to access the root or default page of your server you might want to define a context for the "/" path. The handler for this context would then handle the output. Lets take a look at an implementation of a handler.

import com.sun.net.httpserver.*;
import java.io.IOException;

class HomePageHandler implements HttpHandler {
    public void handle (HttpExchange exchange)
    {
        String response;

        // Set the response headers
        Headers responseHeaders = exchange.getResponseHeaders();
        responseHeaders.set("Content-Type", "text/html");

        // Set the text for the response
        response = "<html><head><title>Hello World</title>" +
                   "</head><body><h1>Hello World</h1></body></html>";

        // Send the response
        try {
            exchange.sendResponseHeaders(200, response.length());
            exchange.getResponseBody().write(response.getBytes());
        } catch (IOException ex) {

        }
    }
}

Now we need to define the context in our server.

import com.sun.net.httpserver.*;
import java.net.InetSocketAddress;
import java.io.IOException;

public class WebServer
{
    public static void main(String[] args)
    {
        HttpServer server;
        InetSocketAddress address = new InetSocketAddress(4500);
        try {
            server = HttpServer.create(address, 0);
            // Define the context
            server.createContext("/", new HomePageHandler());
            server.start();
        } catch(IOException ex) {
            System.err.println("Error");
        }
    }
}

After compiling this code and executing it, you would see the text Hello World written boldly in your browser when you access your local server.

Handling Form Data

When user input is required forms are used. There are mainly two ways of handling form data in web applications. With forms which use the GET method, the data is directly sent through the URL. With POST method forms the data is however sent through the body of the request. The code below shows how to access form data through both the POST and GET methods. Once compiled and executed, the code below would provide three contexts. The default context would present two forms, one using the GET method and the other for the POST method. The other contexts handle either the GET or POST forms.

import com.sun.net.httpserver.*;
import java.net.InetSocketAddress;
import java.io.IOException;

public class WebServer
{
    public static void main(String[] args)
    {
        HttpServer server;
        InetSocketAddress address = new InetSocketAddress(4500);
        try {
            server = HttpServer.create(address, 0);
            server.createContext("/", new HomePageHandler());
            server.createContext("/get", new GetRequestHandler());
            server.createContext("/post", new PostRequestHandler());
            server.start();
        } catch(IOException ex) {
        System.err.println("Error");
    }
    }
}
import com.sun.net.httpserver.*;
import java.io.IOException;

class HomePageHandler implements HttpHandler {
    public void handle (HttpExchange exchange)
    {
        String response;
        Headers responseHeaders = exchange.getResponseHeaders();
        responseHeaders.set("Content-Type", "text/html");
        response = "<html><head><title>Hello World</title>" +
                   "</head><body>"+
                   "<h1>Get Form</h1>"+
                   "<form action='/get' method='GET'>"+
                   "Firstname "+
                   "<input type='text' name='firstname'/><br/>"+
                   "Lastname "+
                   "<input type='text' name='lastname'/><br/>"+
                   "<input type='submit' value='Submit'/><br/>"+
                   "</form>"+
                   "<h1>Post Form</h1>"+
                   "<form action='/post' method='POST'>"+
                   "Firstname "+
                   "<input type='text' name='firstname'/><br/>"+
                   "Lastname "+
                   "<input type='text' name='lastname'/><br/>"+
                   "<input type='submit' value='Submit'/><br/>"+
                   "</form>"+
                   "</body></html>";
        try {
            exchange.sendResponseHeaders(200, response.length());
            exchange.getResponseBody().write(response.getBytes());
        } catch (IOException ex) {

        }
    }
}
import com.sun.net.httpserver.*;
import java.io.IOException;
import java.net.URLDecoder;

class GetRequestHandler implements HttpHandler {
    public void handle (HttpExchange exchange)
    {
            String response = "";
            Headers responseHeaders = exchange.getResponseHeaders();
            responseHeaders.set("Content-Type", "text/plain");
            String[] variables = exchange.getRequestURI().getRawQuery().split("&");
             try {
                for(int i = 0; i < variables.length; i++)
            {
                String[] parts = variables[i].split("=");
                response += (parts[0] + " = " + URLDecoder.decode(parts[1], "utf8") + "\n");
            }
            exchange.sendResponseHeaders(200, response.length());
            exchange.getResponseBody().write(response.getBytes());
        } catch (IOException ex) {
            System.out.println(ex);
        }
    }
}

Conclusion?

I am very sure we have covered enough ground concerning this webserver. There is a lot more that could be done through it. I would advice going through the documentation for this class which could be found here http://java.sun.com/javase/6/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/package-summary.html.

Happy Programming!