Project 4

CMSC 433
Programming Language Technologies and Paradigms
Fall 2002

Due Wednesday, November 27, 2002 at 6pm


The goals of this project are to develop more experience with concurrency, to learn about asynchronous event handling, and to explore reconfiguring applications at runtime.

The project will involve extending our previous server applications with a monitoring subsystem. The server will log events of interest. These events will be monitored by the monitoring subsystem and, under certain conditions, the monitor will direct the server to reconfiguring itself in order to improve the server's performance.

1  ReconfigPageServer

ReconfigPageServer is an application composed of a (slightly modified) BoundedThreadServer, some handlers from project 3, and a new handler, called ReconfigHandler. This handler will process requests that reconfigure the server in some way. In this projects, reconfigurations can have two flavors: changing the handler list (adding or removing a handler), or changing the number of processing threads.

1.1  Modified BoundedThreadServer

To support adding and removing handlers, we must change the signature of the BoundedThreadServer to be the following:
public class BoundedThreadedServer {
  public BoundedThreadedServer(int port, int maxThreads);
  public void attach (HttpHandler handler, String handlerName) 
                       throws HandlerNameInUseException;
  public void detach (HttpHandler handler);
  public HttpHandler getHandlerByName (String handlerName);
  public void adjustMaxThreads(int k);
  public void run ();
Here is a summary of the changes:

1.2  ReconfigHandler

Here is the specification for this handler: Be aware that you will need to deal with shared data when implementing this handler, so take care to use proper synchronization.

1.3  Output of EndTimerHandler and EndSessionHandler

To facilitate better testing, please ensure that your EndTimerHandler outputs its results to the log in the following format:
Request requestName processed in time ms
and EndSessionHandler should output records of the form:
Session key ended; elapsed time: time ms

1.4  The SimpleReconfigPageServer Application

To build the application, in the main method of the class SimpleReconfigPageServer you will create the handlers as follows; unless otherwise noted, they should have order constraint NO_PREFERENCE:
  1. Create the same handlers (i.e. RemoteGetFileHandler, StartTimerHandler, SummaryHandler, etc.) that you did in your SeqPageProxy in Project 3. For this application, StartTimerHandler and EndTimerHandler will write their LogRecords to a LocalLog; this part will change in Section 3.
  2. In addition, create a ReconfigHandler, passing it an instance of a BoundedThreadServer, call it server.
  3. Attach all created handlers to server, and start it running.
All applications should accept the -Dport=XXX flag to set the port to use to listen for connections. In addition, all of the names of the handlers that you attach should be exactly the name of their class; for example:
BoundedThreadedServer bth = new BoundedThreadedServer(port,num);
HttpHandler eth = new EndTimerHandler(t,p,l,c);
Finally, be sure to accept the -Dnumthreads=XXX flag for setting the number of threads.

2  RemoteLogMonitor

The second part is to generalize RemoteLogServer from project 1: rather than only processing messages it receives on a single LocalLog, a RemoteLogMonitor will now, in addition, also forward the same message to each of several event queues. These queues will be used by other classes discussed later.

RemoteLogMonitor will have the following signature:
public class RemoteLogMonitor 
  Log myLog;
  public RemoteLogMonitor(int port, Log myLog);
  public void attach(EventQ q);
  public void detach(EventQ q);
  public void process() throws IOException;
Briefly, the methods work as follows: Each time the RemoteLogMonitor receives a message, it will invoke the corresponding method on myLog. It will also create a LogEvent (See below) corresponding to the message and enqueue it in each of its attached EventQs.

Once you have implemented this part of the RemoteLogMonitor, you could implement what amounts to a regular RemoteLogServer by adding a main method to RemoteLogMonitor as follows (you should do this for testing):

2.1  Event Queues

One disadvantage of the original RemoteLogServer is that each request must be serviced immediately. If this is fast, then this isn't a problem. However, if servicing is slow, it will hold up the processing of other incoming log messages, just as our sequential BasicServer did. For example, if myLog is a RemoteLogClient whose server was located somewhere on Pluto, then everything would be slowed significantly.

To fix this problem, we can introduce asynchrony via events. In particular, we will translate messages into LogEvents which we store in a queue. Another thread can then drain the queue to process the events. This other thread can run in parallel with the main server thread, and so incoming messages are not delayed in their processing.

We can do this in several stages. First, you should create a generic class for implementing events of interest to Logs. Second, you should create a a queue in which to store those events. Finally, you will need to create another class that dequeues the events and processes them. The first class will be called LogEvent, the second will be called EventQ, and the third is called EventThread.

2.2  LogEvent

To record events, we'll create an abstract LogEvent class and subclass it for every event that an EventThread may need to process.
public abstract class LogEvent {
  public LogEvent();
public class AddEvent extends LogEvent {
  public AddEvent(LogRecord r);
  public LogRecord getLogRecord();
public class GetAllEvent extends LogEvent {
  public GetAllEvent(long ms);
  public long getWindow();
public class SetFilterEvent extends LogEvent {
  public SetFilterEvent(String filter);
  public String getFilter();

2.3  EventQ

Now that we have a way to create events, we can create a queue to hold them.
public class EventQ {
  public EventQ();
  public void enqueue(LogEvent event);
  public LogEvent dequeue(); // blocks when empty
The constructor creates the queue (having essentially unbounded size). The enqueue method stores the given LogEvent in the queue, while dequeue will return the first element of the queue (and block if the queue is empty).

You must take care to either synchronize your EventQ, or else synchronize all threads that use it.

2.4  EventThread

To process requests queued on an EventQ, we can use the class EventThread:
public class EventThread extends Thread {
  public EventThread(EventQ eventQ, LogEventHandler handler);
  public void run();
interface LogEventHandler {
  public void process (LogEvent event);
The thread should dequeue events from the EventQ, and then process them by calling the process() method of the provided LogEventHandler. We will implement two LogEventHandlers, one for printing out events to the console, and another for reconfiguring the remote server.

2.4.1  Printer

To better use the new functionality afforded by RemoteLogMonitor, we create an EventThread with an implementation of LogEventHandler called Printer that simply prints to the console (using System.out) what log messages have been sent to the server. Its function will be to simply print out the fact that a certain method was called. For example, if an add method was called with LogRecord l, then Printer will print to the console the word ``add'' followed by the string representation of the record l.

You can now create a ``printing'' version of a RemoteLogServer by creating a RemoteLogMonitor as described above, but additionally start an instance of and EventThread with a Printer and an EventQ that has been attached to the RemoteLogServer.

Figure 1: Monitoring and Reconfiguration Architecture

3  AdvancedReconfigPageServer and ReconfigMonitor

Now that you have a reconfigurable server and a generalized remote logging monitor, we can make the two interact with each other. In particular, we can have the monitor watch relevant events being logged by the server, and then reconfigure the server.

The AdvancedReconfigPageServer application will be a variant of the BasicReconfigPageServer that talks to a RemoteLogMonitor. In Section 1.4, we created the BasicReconfigPageServer using the same handlers as SeqPageProxy. Now however, when we create a Log for use by StartTimerHandler and EndTimerHandler, and to store in the Map for SummaryHandler, we will create a RemoteLogClient rather than a LocalLog. The port to use for this client should be retrieved from the command line, using -Dlogport=XXX.

In addition, you will create a class ReconfigMonitor as follows. The main method of this class will create an instance of RemoteLogMonitor, and then attach to it two eventQs. One EventQ will be read by a Printer EventThread and the other by a new handler, a PerformanceMonitor EventThread. This is depicted in Figure 1. The PerformanceMonitor will monitor the ``health'' of the AdvancedReconfigPageServer, and potentially reconfigure it; we describe it more below. Once you have attached the eventQ's, you can start running the ReconfigMonitor by invoking the RemoteLogMonitor's process() method. Finally, use the -Dwebport=XXX flag for setting the port that your AdvancedReconfigPageServer is listening on.

3.1  PerformanceMonitor

Ideally, the basic idea is that the PerformanceMonitor will analyze the LogEvents it sees, decide whether the server is performing well, and, if it is not, send reconfiguration requests to the server in order to improve its performance. The processing functionality should be placed in a class called PerformanceMonitor that implements LogEventHandler.

For this project you will examine the result of each AddLogEvent generated by EndTimerHandler to decide whether to detach or reattach handlers, or to change the thread threshhold. (Recall that the AddLogEvent will be created and stored in the queue by RemoteLogMonitor whenever an EndTimerHandler adds a record to the remote log. You should only process AddLogEvents resulting from an EndTimerHandler, and not other LogEvents.) You will need to continuously calculate a rolling average of the last three EndTimerHandler timing results. Then apply the following rules:

4  Testing

Write a JUnit TestCase class for each of the following classes, with each TestCase class implementing at least two tests: Each TestCase class must be self-contained, and runnable from the command line. If you must fork other processes to run your test, do it from with the test class itself (and be sure to kill the processes when you're done).

5  Amendments

This document was translated from LATEX by HEVEA.