Engineering Computing

PI: Laboratory 3: Systems of Objects; Nodes and Channels

Overview

This lab focuses on debugging and testing as well as their role in incremental development. You will spend much of your time in this lab exploring programs that do not always work, predicting where they will fail, and learning to design fixes and tests for these fixes. This lab also reinforces the idea of coding to an interface and extends the set of basic java constructs that you are using to include exceptions.

Before you start this lab, you should have read the book chapter on exceptions (chapter 10). You will probably also want to review the parts of chapter 2 that deal with incremental development, debugging and testing. (Begin with the section on "Building the Program") You should of course also be familiar with the material that we have covered in preceding labs (chapters 1-8), especially with the sections on reading and implementing interfaces.

Although you will not be writing a great deal of code in this lab, you will be writing and testing the same code over and over. You will need to come to lab with a good development plan, including the various stages of development that you will build, the behavior that you expect to see at each stage of the plan, the tests that will verify that behavior, and the tests that you would expect that code to fail. This staged development and testing plan is a required part of your lab check-in.

As usual, you should read the general information handout for the collaboration policy. For this assignment, you may discuss the code for the project in as much detail as you like with your classmates, but you should do the testing of networks in the prelab as well as the writeup on your own. You may also get comments from other students on all portions of your writeup before turning it in, if you wish. Please include the names of anyone with whom you collaborate, in any way, on this assignment, and indicate the nature of the collaboration. If you do not collaborate at all, please indicate this as well. [Failure to include this information is a violation of the collaboration policy.]

You will be turning in the fully documented final code that you write for this lab. This code must be your own. We do encourage you, however, to ask your classmates for help debugging during lab. Helping someone else to debug their code is a great way to improve your own programming skills.

This assignment emphasizes the following topics

  • Designing good tests
  • Incremental development
  • Reading javadoc
  • Working with exceptions

You should read through this entire assignment and complete the Lab preparation section before you come to lab.

This assignment is due at 5pm on Wednesday, September 24, 2003. You should email it to the pi-homework list.


Contents

You should read through this entire assignment before you begin.


Preparing for the Lab

1. Experiment with the application.

This week's laboratory project involves a network routing simulation. In the simulation, routers (nodes) send and receive packets (pieces of information) to each other along various routes (channels). The nodes could be computers, for instance, sending and receiving information from each other across the internet. This problem set could be a scaled down (very scaled down) version of a commercially available product.

To run the code, you should be able to simply download the nodenet.jar file and run it under DrJava. You will need to add the nodenet.jar to the classpath via DrJava's Edit-->Preferences-->Resource Locations-->Extra Classpath. The command line to run the basic application within DrJava is

java nodenet.Main
Try this now.

If you just want to use the command line, open a command prompt to the directory containing the nodenet.jar and issue the command:

java -jar nodenet.jar

The idea of the simulation is to experiment with various network configurations and empirically determine their behaviors. In other words, play with the application and see what happens. When the GUI pops up, you will see two panels, side by side. The panel on the right, which is currently empty, is where all of the simulations will take place. The panel on the left contains the available GUI elements -- all nodes, in this case. Above both panels are three menus: File, Simulation, and Selection. To use the simulator in the most basic way, you first construct a network and then set it in motion. The necessary steps are described below.

One of the main things you need in a simulator are nodes. In order to add a node to your simulator, select one of the nodes from the panel on the left, then click on the drawing panel to place it somewhere. There are three types of nodes which appear when you first run the program:

  • Generator Node - a source. It generates objects (packets).
  • Default Connecting Node - a connecting node. It receives packets and passes them along.
  • Terminator Node - a sink. It eats packets.
You can create a node by selecting its node type and clicking in the right-hand panel. To remove an element, click on it and then select Remove from the Selection menu. You can clear the whole right-hand screen by using the Clear option under the Simulation menu.

The other crucial element in a simulator is channels. Channels run from one node to another, and are represented by a black line with an arrow, connecting the two nodes. In order to add a channel to your simulator, double click on the first node, then click on the second node. Channels have directions: they go from the node you start with to the node you end with. It is possible to have more than one channel between two nodes (e.g., one in each direction), but this isn't drawn particularly well. To remove a channel, outline the channel's arrow by clicking on it, then select Remove from the Selection menu.

To start objects moving through the demo, select Start from the Simulation menu. You can pause the simulation using Pause. You can dynamically add elements while the simulation is running. You can also temporally disable a node or channel by selecting it and then choosing the Disable command from the Selection menu. To re-enable an element, use Enable. Using the Remove option to physically remove an element from the network while the simulation is running is discouraged; deletion is provided only for creating a new network, not for dynamically changing network behavior.

Experiment with the network for a few minutes. (Not too long, at least not now!) Can you get the channels to fill up? Can you create any other interesting behaviors? Remember that you can disable channels (using the Selection menu) while the simulation is running. You may want to keep a record of your experiments to compare your later results with. (Such a record should include both network topology (how things are connected) and dynamic behavior (what you did, what it did).) NB: Although you can theoretically save any particularly interesting network configurations using the Save As option under the File menu, this feature doesn't always work when your code is recompiled.

The DefaultConnectingNode is a pretty well behaved intermediate node type. We have also provided two less functional types of connecting nodes, TypeAConnectingNode and TypeCConnectingNode. You can load these into the simulation by restarting it with an argument:

java nodenet.Main nodenet.TypeAConnectingNode      (in DrJava)
or
java -jar nodenet.jar nodenet.TypeAConnectingNode  (at command prompt)
Try this now. You should see the new node type in the left-hand panel. Although DefaultConnectingNode is still there, you should probably ignore it for the remainder of your experimentation.

At this point, your job is to characterize TypeAConnectingNode (and, similarly but separately, TypeCConnectingNode). First, you should see how the node type works under simple conditions. Use a sketchpad (or electronic equivalent) to record how each node type behaves. Try different network conditions. Remember that you can add, disable, and remove Nodes and Channels (though you should only do so while the simulation is stopped). NB: You should be able to reset a particular configuration by saving it and then reloading it, but we do not promise that this feature will work under all conditions.

Your immediate goal is to find at least one configuration under which TypeAConnectingNode does something that seems inherently unreasonable to you. You should also find at least one such configuration for TypeCConnectingNode. Ideally, though, you should go beyond just finding a single configuration that "breaks" the node type. You should spend some time characterizing (and recording) how the node type responds to a variety of conditions to see whether you can figure out what types of mistaken assumptions or other errors it contains. Also try to predict how the node type will behave under certain conditions, then experiment to see whether you are right. (Again, record these predictions as well as their outcomes. Do not spend more than about 30 minutes on the characterization task (total).

2. Familiarizing yourself with the code.

In the coding portion of this lab, you're going to create your own behavior for a Node. You will do this by creating a class that implements NodeBehavior. You will probably want to look at the code for the NodeBehavior interface now.

Your NodeBehavior class will not be a Runnable; instead it will be called by a Node, which is Runnable and has its own Thread. The Node's run method also contains the while(true) loop that we discussed in class. (Node also implements the graphical user interface (GUI) for a node.) The heart of Node's run method looks something like this:

InputChannelVector inputChannels;
OutputChannelVector outputChannels;
public void run( ) {
    while( true ) {
        this.nodeBehavior.transmitPacket( this.inputChannels,
                               this.outputChannels );
    }
}
Since transmitPacket( ) will be called from inside a while( true ) loop, you only have to handle one object at a time: You should read (at most) one object from one channel and write it out.

The transmitPacket( ) takes two arguments. The first argument is an InputChannelVector; the second argument is an OutputChannelVector. Both InputChannelVector and OutputChannelVector are essentially Vectors (in the java.util sense), but with some typing changes that make them nicer to work with in this problem set. That is, the two ChannelVectors are collections of Channels. For example, the particular InputChannelVector that gets handed to a particular node's transmitPacket method is the Vector (collection) containing all of the InputChannels that lead to that node. Note that you cannot get a packet out of an InputChannelVector; you can only get an InputChannel out of an InputChannelVector (and a packet from that!) You should look at the interface documentation (linked above) to see what features these Vectors offer.

Inside your transmitPacket method, then, you'll need to select a particular InputChannel from the InputChannelVector. Each InputChannel in the InputChannelVector has the following method:

public Object readObject() throws ChannelEmptyException, 
                                    ChannelDisabledException;

You only need to read one packet from one input channel on each time around the interactive control loop. (The packets are of type java.lang.Object.) Be careful, though: a given input channel may be empty or disabled, in which case it will throw an appropriate exception.

The second argument to transmitPacket( ) is an OutputChannelVector. An active non-full output channel would be a good place to put any packet you may have picked up. As a corollary to InputChannel, OutputChannel has

public void writeObject( Object ) throws ChannelFullException, 
                                           ChannelDisabledException;
But be careful: when an output channel is full, it will throw an exception if you try to write to it. Eventually, you'll have to make sure that you don't drop any packets if this happens, or write packets that don't exist.

4. Design your code.

The main piece of your coding will be to implement the transmitPacket( )method of NodeBehavior. This method should get an object from a single InputChannel from inputChannels (using readObject()) and feed it to an OutputChannel in outputChannels (using writeObject()). Remember that you may have multiple InputChannels and OutputChannels, and that some of them may be disabled..

To begin with, you should write a piece of code that compiles. This will involve writing a class that implements the NodeBehavior interface and starting up your application with that class loaded. It is perfectly fine if you cannot move any packets on that initial load; compiling and running the application is always a good place to start.

You should develop a plan for how you will slowly build up good functionality. For each step, you should indicate what code you will write, what tests you will use to determine whether your code works, and at least one test on which you expect your code to fail. Then go on to the next step; what will you build next, how will you know whether it works, and what will it not be able to do? This sequence of construction and testing is your development plan. Your development plan should have at least four steps with associated tests (and may have many more) before you come to lab.

Ultimately, you will need to think about strategies for how you want to read packets from channels. There are obviuosly several ways that you can choose the input and output channels from their respective Vectors. Think of at least two, and record these strategies. The more creative you can be, the more interesting your lab will be. You should design code for at least one of these strategies, and ideally for more than one. (It's OK, and even sometimes preferred, that you include at least one very simple strategy.)

One of the main points of this lab is to observe the effects of different strategies on the behavior of your network as a whole. Is your strategy fair? Does it drop packets? Create them? Do packets cluster in certain parts of your network? Can you come up with a strategy that behaves differently?

5. Helpful references.

The interfaces used in this lab are
  • NodeBehavior
  • InputChannelVector
  • OutputChannelVector
  • InputChannel
  • OutputChannel
    • You should be aware that both InputChannelVector and OutputChannelVector contain a method, size(), which returns the size of the Vector; additionally, both contain a method, isEmpty, which returns true if the Vector is empty.

      Additionally, InputChannelVector contains three more methods:

      • elementAt( int index ) returns the InputChannel which is at index. Remember that numbering begins with 0.
      • firstElement( ) returns the InputChannel which is the first in the InputChannelVector.
      • lastElement( ) returns the InputChannel which is the last in the InputChannelVector.

      OutputChannelVector contains the same three methods listed above, except that each method returns an OutputChannel instead of an InputChannel.

      Channels occasionally throw exceptions. You need to be concerned about three of these:

      • ChannelFullException, thrown by a OutputChannel on writeObject().
      • ChannelEmptyException, thrown by a InputChannel on readObject().
      • ChannelDisabledException, thrown by any Channel when its corresponding Channel has been disabled.
      Make sure that your code is robust to these exceptions, and does not lose objects.

      Some utilities that you might want to use:

      public static int cs101.util.MoreMath.randomInt( int range );
      returns an int between 0 and range, inclusive. You can refer to it as MoreMath.randomInt if you include the line import cs101.util.*; at the top of your file.

      public static void Thread.sleep( int millis )
          throws InterruptedException;
      causes the current Thread to sleep for millis milliseconds. This allows you to put delays in your nodes. Warning: You will have to catch the exception, though you don't necessarily have to do anything with it.

      If you have other ideas, design them and then ask us about them.

      What to bring to lab

      You will need a record of your experimentation with TypeAConnectingNode and TypeCConnectingNode, a development plan containing at least four steps with testing strategies, and two different channel selection strategies before you come to lab. You should also be able to sit down and write the simplest piece of code that you will test (i.e., what you need to get it to compile) when you first come to lab. This means that you will need to be able to answer the question marked with a Q, below.

      Building your code.

      Your job in lab is to implement one or more node behaviors, to test these behaviors on your own examples, and to exchange example networks with other students in the class. During lab, we will use the whiteboards to create a shared repository of test networks. Test data development should be pooled, and sharing of test data is encouraged. Discussion of why networks behave as they do is strongly encouraged. Talk to the people next to you to get good ideas for test networks. If you need help loading another student's test network, please ask us to show you how.

      Q:Begin the lab by implementing a node behavior. You will need to define a new class, say MyClass. What should you call the file it's defined in? What does it need to extend or implement? What methods does it have? (Remember that the interactive control loop is already defined for you, but it expects to be able to call your transmitPacket() method with two arrays.

      The first lines of your file, before your class definition, will need to say:

      package nodenet;
      Also, you should be sure that your class is public.

      Once your code compiles, you should try running it. To run your code, you will need to supply the name of your node behavior class to nodenet. You can do this by supplying the fully-qualified name of your class just as you did with nodenet.TypeAConnectingNode, etc. Remember that the fully-qualified class name includes the package the class is in: nodenet.MyClass. (assuming that MyClass is the name of your class, and also that you've already compiled your class.)

      You can now test your code using a the network(s) that you have designed for this purpose. Does it work as you expected? Does it break as you expected? Record your observations. When you have verified that your network works as you expected (or not, and you have understood why not), go on to the next step in your development plan.

      Don't forget to tell the course staff about your networks, so that we can use them, too :)

      Writing a class that compiles, runs, and attempts to forward packets is the target exercise for this lab. It need not forward packets perfectly, just make some progress on sane networks.

      Post-Lab, AKA What To Turn In

      Your completed assignment should include:
      • on the front page, how much out-of-class time (approximately) you've spent on reading, on preparation of the homework, in lab, and on other non-class-time course-related activities (and what). These times should include work from last Wednesday 5pm to this Wednesday at 5pm
      • your pre-lab work.
      • a discussion of how you spent your time in lab, including observations on how well your actual activities matched your development plan.
      • a record of your observations, particularly those that surprised you
      • your check-out question and its answer
      • ONE well-documented Java file. If you write more than one java file, pick one to document and turn in.
      • How closely does the following statement describe you:
        I can write a .java file containing a Java class that implements an interface.
        1. I am the master of java class file writing.
        2. I can work it out given a while.
        3. I find the syntax of the class file confusing.
        4. Impl-huh the inter-what?
        5. Java Rules!
Questions, comments, gripes and other communication to pi-staff@lists.cognition.olin.edu
This course is a part of Lynn Andrea Stein's Rethinking CS101 project at the Computers and Cognition Laboratory and the Electrical and Computer Engineering area at Franklin W. Olin College of Engineering. Olin College Logo