Project 3

CMSC 433
Programming Language Technologies and Paradigms
Fall 2003

Due Wed., Nov. 5, 2003

Introduction

Increasing numbers of applications---include cell phones, routers, and industrial control applications---are choosing to use HTTP, HTML, and other WWW protocols to perform operations like configuration, control, query, and more. In this assignment, we will build a server that uses the web protocols to communicate. Rather than hardcoding the server to serve files, as happens for most webservers, we will abstract away from what is being served, and focus on the server infrastructure itself. This way, the same code can be used to write web-based servers for files, router configuration, industrial machine control, and so on.

This project has several goals:

All provided Java files for this assignment can be found at (TBD). You can also get this assignment in PDF.

1  Basic Webserver Model

The general structure of the webserver will be just as with any server: there will be an infinite loop that listens with a ServerSocket on a given port, accepts TCP connections, and then processes requests on those connections. Each time you get a connection, you will do the following: Note: You are responsible for studying and understanding how the support classes we give you work. Read the documentation thoroughly. Pay attention to the consequences of writing responses to the client. The kind of response you create and the time at which you create it affect how other handlers must behave.

A key part of the implementation is the calling of HttpRequestProcessors (we will refer them as handlers from now on) to process the request. We will use the Observer pattern to forward requests to handlers. That is, a number of handlers, which are the observers, attach to the server, which is the subject, indicating that they are interested in processing web events. When web events come in, the server will forward them to interested handler so they can process them and create a response. In our case, the update is a call to the handler's process method, described below. The responses of each of the invoked handlers are combined and a single HTTP response is sent to the client.

This project has several kinds of Handlers. They are HttpRequestProcessors, HttpCommandProcessors, and HttpRequestDispatchers. In a nutshell, HttpRequestProcessors only handle simple Http requests. HttpCommandProcessors handle http requests and simple commands. HttpRequestDispatchers handle simple http requests and perform simple commands,  but also forward HttpRequestEvents to other HttpRequestProcessors, and support commands for manipulating attached HttpRequestProcessors..

1.1  The HttpRequestProcessor interface

Each handler will implement the HttpRequestProcessor interface, which is described more completely in the class documentation:

public interface HttpRequestProcessor{
    public void process(HttpRequestEvent event);
    public String getStatus();
    public String getName();
    public String getType();
}  

Subclasses must implement the following methods:

1.1.1  Handling Errors

Errors should generally be handled directly by the processor using the HttpRequestEvent's writeError method.   

1.2 The HttpCommand interface

Besides serving web pages handlers also perform computations and dynamically update the behavior of the server. To make this process more extensible we will implement some handler services in a subclasses of the HttpCommand interface. These objects will be registered with handlers and later invoked by them in response to specific http requests. This is an example of the Command pattern.

public interface HttpCommand{
public void execute(String[] args, HttpRequestEvent e);
}

The HttpCommand execute methods assumes that args[0] is the command name. All other arguments are to be treated as command parameters. HttpCommand implementations are responsible for parsing/casting command parameters.

1.3 The HttpCommandProcessor class

HttpCommandProcessors execute HttpCommands to perform services requested by clients. Client requests services by sending http requests with encoded commands. Commands are encoded in a browser as follows:

http://serverName:portNum/?recipient_name/command,arg1,arg2,...

Assume that the http request is a command whenever the first two characters of the request target are "/?".

Recipient_name represents one or more HttpCommandProcessors to whom the command is sent. HttpCommandProcessors whose "name" exactly matches the recipient_name will then invoke the indicated command. Otherwise they will ignore the command. The special recipient_name "global" matches all HttpCommandProcessors.  For instance, the address request:

http://serverName:portNum/?global/name

Will cause all handlers supporting the 'name' command to write their names to the response.

The HttpCommandProcessor is an class that implements HttpRequestProcessor and adds some new methods to handle commands.  Command processors contain HttpCommand instances that implement specific commands. This class' process method uses the Template pattern. An outline of the new methods for this class is given below:

public class HttpCommandProcessor implements HttpRequestProcessor{

public HttpCommand registerCommand(String name, HttpCommand h);
public void removeCommand(String name);
public HttpCommand getCommand(String name);
public Map getCommands() //returns all commands in an unmodifiable form.
public void preProcess(HttpRequestEvent event);
public void postProcess(HttpRequestEvent event);

public final void process(HttpRequestEvent event) {
    preProcess(event);
    String args[] = parseArgs(event); // looks for /?name/a,b,c and returns "a","b","c"
    if(args!=null){
        Command c = getCommand(args[0]);
        if(c!=null) {
            try{ c.execute(args, event); }
            catch(Exception e){ writeError(...); }
        }
    }
    postProcess(event);
}

}

The process method is a Template method. It calls preProcess, parses the incoming command (parseArgs is provided for you), finds the requested command executes it, and calls postProcess. If the http request is not an encoded command or if the recipient_name does not match the name of the current object, then parseArgs returns null.

As an example of how HttpCommands work, consider a client who wants to know the status of a particular handler named "handler27." That client would issue the following request.

http://serverAddr/?handler27/status

This command will get passed to every handler. If there is a HttpCommandProcessor with the name "handler27" (let's call it hcp), it will look for a registered HttpCommand object with the name "status". Assume that hcp has been configured to support the status command, it will find an instance of the HttpCommandStatus class (class is defined later, let's call the instance s). Next, hcp will call s.execute, which will get cp's HttpRequestEvent, get the status of cp, and write that status to the HttpRequestEvent.

Note: if your HttpCommandProcessor must also handle traditional http requests, that code must be placed in the preProcess (before command execution) or postProcess (after execution) methods (which is usually given in the class specification).

1.4 The HttpRequestDispatcher class

Sometimes we want to implement complex server behaviors by composing the behaviors of several components. This allows us both to build up higher level services from lower level ones, and to change behaviors at runtime. To do this we use the HttpRequestDispatcher class. This class forwards incoming HttpRequests to any registered HttpRequestProcessors. You will need to implement methods to attach and remove HttpRequestProcessors. HttpRequestDispatchers can perform local processing before or after forwarding the HttpRequestEvent (depends on the application), but the process method should forward the HttpRequestEvent in the order in which HttpRequestProcessors are attached.

An outline of this new methods for this class is as follows:

public class HttpRequestDispatcher extends HttpCommandProcessor{
public void attach(HttpRequestProcessor h);
public void attach(int position, HttpRequestProcessor h);
public HttpRequestProcessor removeAt(int pos);
public HttpRequestProcessor remove(String pos);
}

Note: HttpRequestDispatcher should handle commands and traditional http requests before forwarding the HttpRequestEvent to any attached HttpRequestProcessors. Therefore, place the forwarding code in the postProcess method.

2.  Implementing the BasicServer class

The BasicServer class must be implemented as follows:

public class BasicServer {

  HttpRequestProcessor proc;
  public BasicServer(int port, HttpRequestProcessor proc);
  public void go ();
}

The constructor takes as an argument the TCP port that the server will listen on for connections
and an HttpRequestProcessor which will process the incoming requests.  The go method is used to actually start the server; it will contain the infinite server loop that accepts connections on the given port, parses the HTTP request, passes the request to command processor, and returns the response to the client, etc.

3  Implementing HttpCommands

Below we describe some HttpCommand subclasses you will implement.

We give a description of what the command does, and the specification for the  class' constructor.

 

We've already given you the HttpHandlerFactory interface as well as a simple implementation- BasicHandlerFactory, which knows how to create some of the Handlers you are required to support.  When testing we will use factories that typically only know how to create minimal sets of Handlers, so that if there is one handler you can't get to work it won't fail all of your tests.  This approach uses the Factory pattern.

public interface HttpHandlerFactory {
    public HttpRequestProcessor create(String type, String name)
}

4. Implementing handlers

Handlers define the part of each server that differs. In our project, we will always use HTTP as the means of communication, but rather than just retrieve HTML files, we also do more sophisticated processing.  This way, we can build HTTP-based servers that do different things by reusing the BasicServer class, but attaching different HttpRequestProcessors.

           HttpRemoteManager (String name);

           HttpRemoteFileHandler (String name);

            HttpProxyHandler (String name, HttpRemoteFileHandler h);

            HttpFileLogger (String name, String logFileName);
       

5  Test Applications

Using these handlers, we will write several server applications. These servers will be accessed by HTTP clients, like Netscape. For each application we will do the following:
  1. Create a BasicServer instance.
  2. Create all necessary handlers.
  3. Attach the handlers to the server.
  4. Invoke the server's go() method.
  5. Send requests to BasicServer
Each application will be encapsulated within a class. Each application class and the necessary handlers are described below. Note that in all cases, the public static main() method for your server should get the port number to pass to BasicServer's constructor from the command line (i.e., start the server with java -Dport=portnum ServerClassName). The port number can be extracted in your server with the Integer.getInteger() method. For testing purposes, so everyone uses different ports, test your server using port x33yy, where x is your section number (either 1 or 2) and yy is the last two digits of your account number.

Web Accessibility