CMSC 433, Fall 2004

Programming Language Technologies and Paradigms

Project 5


Due Saturday, December 11, 2004

Updates:


Note:

Please get started on this right away. If you have any questions, ask them sooner rather than later.


Introduction

This project is divided into two parts. In the first you will develop another application using the pipe and filter architecture of Project 4. Specifically, you will develop an online chicken sorting simulation. You are required to use our FilterImpl and FilterBufferImpl classes. They are provided below.

In the second part you will adapt your implementation to modify the application to use RMI so that filters can run on distributed nodes.

Part I: Local Online Chicken Sorting

This application consists of three kinds of filters: Router, Station, and Bin. These filters must be attached in a specific hierarchy as detailed below. The data exchanged by these filters via buffers will be encoded chicken parts of the following form:

<begin chicken>type weight id<end chicken>

For example:

<begin chicken>thigh 86 T342<end chicken>
<begin chicken>wing 24 W91<end chicken>

Note that although whitespace is required between the <begin chicken> and <end chicken> tags to separate the fields, no whitespace is required between the <end chicken> of one chicken part and the <begin chicken> of the next chicken part.

The table below lists the four types of chicken parts, along with a hypothetical approximate weight. These weights won't be used when implementing this project -- they're just there to give you a conceptual idea of the average size of each type relative to the other types.

TypeApprox.
Weight
breast 150g
thigh 85g
drum 50g
wing 25g

Chicken parts are represented by a ChickenPart class. Most of this class has been implemented. You must implement the fromString() method. The fromString() method takes as input a string formatted as a tagged chicken part (see above) and returns a ChickenPart object representing the tagged chicken part string. If fromString() is called with an improperly formatted string, then a MalformedChickenPartException is thrown.

Chicken part types are represented by a ChickenPartType class. Again, a lot of this class has already been implemented. You must fill in the missing pieces. Your completed implementation should strictly follow the TypeSafe Enum pattern.

Here is a description of the three Compute (as defined in Project 4) classes you will write for this application:

  1. Router reads chicken parts from its input buffer. The chicken parts are then written out its output buffers in round robin fashion.

    By round robin fashion we mean that the first buffer (at index 0 in the array returned by getOutputBuffers()) gets the first chicken part. The second buffer (at index 1 in the array returned by getOutputBuffers()) gets the second chicken part and so on. If, for example, getOutputBuffers() returned only two buffers, then the scheme would wrap around and the third chicken part would go to the first output buffer.

    Routers' output buffers are the input buffers of Stations.

  2. Station reads chicken parts from its input buffer and distributes them to its output buffers based on a number of rules.

    The first output buffer (at index 0 in the array returned by getOutputBuffer()) is for wings only. The second buffer is for drumsticks only. The third and fourth buffers are mixed (they take all types of chicken parts.) A working Station always has exactly four output buffers. The goal of a Station is to distribute chicken parts among the four output buffers in the following way:

    To simplify our grading your implementation should follow a specific order. There is an embedded round-robin rotation. For example, the first wing should go to the wings only (the first) output buffer. The second wing will go to the third output buffer. The third wing should go to the first output buffer and the fourth to the fourth buffer. Then the rotation should repeat. Drumstick distribution follows the same scheme as wings. When distributing thighs, the first thigh should go to the third output buffer. The second thigh should go to the fourth output buffer and then the rotation should repeat. Breast distribution follows the same scheme as thighs.

    Stations' output buffers are the input buffers of Bins.

  3. A Bin reads chicken parts from its input buffer and assembles the parts into (virtual) packages based on a couple of heuristics.

    Packages have a preferred minimum weight and preferred maximum weight. Upon receiving a chicken part, the Bin adds the chicken part to the current package. (You should probably update other state based on the type and weight of the chicken part). If the total weight of all chicken parts in the current package is greater than the preferred minimum, ship the package (print the package contents to System.out). If the total weight of all chicken parts in the current package, plus the expected weight of the next chicken part, is greater than the preferred maximum, ship the package. Otherwise, don't ship the package yet. If the input buffer is closed and there are no remaining chicken parts to be processed, ship the package.

    The expected weight of the next chicken part is the average weight of all chicken parts processed by the Bin so far.

    Shipping a package involves printing the contents of the package to System.out in the following format:

    [Bin ID] [Total weight of package] [IDs of all chicken parts in package]
    
    For example, the following output shows a shipped package from a bin with ID (Station 2) Mixed1, a total package weight of 335, and containing four chicken parts -- with IDs T48, D18, W35, and B111.
    (Station 2) Mixed1 335 T48 D18 W35 B111
    

    After a package is shipped, the Bin is now empty and processing continues (except if the input buffer was closed, in which case the Bin filter stops running.)

    The constructor for Bin's Compute takes three arguments. The first argument is a String identifying the Bin, the second is an int representing the preferred minimum weight of the Bin's packages, and the third is the preferred maximum weight of the Bin's packages.

A sample driver program that instantiates Router, Station, and Bin Filters and connects them in a proper configuration is given in Chicken.java. This driver also requires the file ChickenData.java.

Part II - Distributed Online Chicken Sorting with RMI

Part I of the project will only work if all objects are running on a single machine. In this part of the project, you will use RMI to remove this limitation.

To do this you will need to make several changes to your solution to Part I. These include:

FilterFactory.main()'s function is to initialize Filters and allow them to be connected to other Filters running on (other) machines. To do this it will generally perform the following steps (you should be able to tell what is already completed for you and what you must fill in yourself):

  1. Start a tiny webserver to serve class files (e.g., stubs) to clients and set the codebase appropriately.
  2. Install a security manager.
  3. Initialize a "local" RMI registry, if one is needed and doesn't already exist.
  4. Parse records (its command-line arguments) describing what Filter to run.

For each requested Filter:

  1. Lookup the input RemoteFilterBuffer in a "remote" RMI registry.
  2. Create output RemoteFilterBuffers as requested in the record passed in via the command line.
  3. Register the newly constructed output RemoteFilterBuffers in the "local" registry.
  4. Call the appropriate constructors to build Compute and Filter instances.
  5. Start the Filter.
You will need to handle the following special cases:

Note: Depending on where and how many times FilterFactory is run, it is possible that the "remote" and "local" registries may be the same.

FilterFactory.main()'s command line arguments are given in the following form:

java -Dregport=portNum1 -Dfileport=portNum2 FilterFactory record1 record2 ... recordn

Record formats are described by the following grammar:

<record>  := <router> | <station> | <bin>
<router>  := Router InBufferName InBufferSize <output>
<station> := Station InBufferLocatorString <output>
<bin>     := Bin BinName PrefMin PrefMax InBufferLocatorString <output>
<output>  := OutputBufferName OutputBufferSize <output> |
             OutputBufferName OutputBufferSize

Where,

We are providing an initial driver called RemoteChicken. RemoteChicken will look up the input buffer for the initial Router and then will write chicken parts to that buffer. RemoteChicken might be started up in the following way:

java RemoteChicken rmi://nauseous.csic.umd.edu:330XX/rtrInput

Here is a set of sample scripts for starting up the same example you saw in Part I. Please start the scripts in the order listed.

  1. ./startRouter.sh filePort regPort
  2. ./startStation.sh hostnamestartRouter:regPortstartRouter filePort regPort
  3. ./startBin.sh hostnamestartStation:regPortstartStation filePort regPort
  4. ./startRemoteChicken.sh hostnamestartRouter:regPortstartRouter
For example:
(on nauseous.csic.umd.edu)  ./startRouter.sh 331XX 330XX
(on bilious.csic.umd.edu)   ./startStation.sh nauseous.csic.umd.edu:330XX 331XX 330XX
(on churning.csic.umd.edu)  ./startBin.sh bilious.csic.umd.edu:330XX 331XX 330XX
(on squeamish.csic.umd.edu) ./startRemoteChicken.sh nauseous.csic.umd.edu:330XX

Please make sure to download our java.policy file. You must put it in the code directory. Filter and FilterImpl have also changed as a result of introducing the new RemoteFilterBuffer interface.

Hints

Fill in the missing parts of the ChickenPartType class before implementing the fromString() method in the ChickenPart class.

Bins never write to their output buffers.

In Part II, the old FilterBuffer interface has also changed. This was done so you can use Chicken.java to test your implementation after modifying your Router, Station, and Bin classes to work with RemoteFilterBuffer, but before proceeding to work on RemoteFilterBufferImpl and FilterFactory.

Use rmic to generate stubs.

After you have completed Part II, to test you implemented everything correctly, start the Chicken Sorting chain distributed across many nodes. Then, open another terminal, create an empty directory and copy the following classes and policy file to that directory:

BufTooSmallException
ChickenData
ChickenPart
ChickenPartType
java.policy
MalformedChickenPartException
RemoteChicken
RemoteChickenInputThread
RemoteFilterBuffer
You should be able to succesfully run RemoteChicken with just these files. If you look at the terminal where you started Router (via FilterFactory), you should see that RemoteChicken downloaded stub files from Router's mini webserver.

What to Submit for Part I

You must submit the following classes: Remember to submit Part I as project 5a.

What to Submit for Part II

You must submit the following classes: Remember to submit Part II as project 5b.

Testing

See the Resources section below for sample output from running the driver. Please note that many interleavings of output are possible. You may find the UNIX sort utility useful when comparing your output with the sample output.

Resources

Part I

Part II

Valid HTML 4.01!

Web Accessibility