Project 1: Project Source Setup
Overview
All of the project code you write this quarter must operate inside the
infrastructure we're distributing as the project 1 base code. This page
gives an overview of that infrastructure. Armed with that overview,
the code you see, which inevitably contains a lot of details, should be
manageable.
The project infrastructure uses a dynamic loading scheme to assemble
the set of classes that will loaded together into a JVM for execution.
The figure immediately below gives a static view of this process.
(This picture shows the console (desktop) execution. On Android,
it's identical, except that ConsoleStart is replaced by
AndroidStart .)
Execution starts in the main() of ConsoleStart .
ConsoleStart statically references NetBase ,
so initially these two classes are loaded in the JVM.
ConsoleStart provides the mechanism required for the user
to pick a configuration file. Configuration files contain arguments
to programs that run in the JVM. For instance, the configuration file
may list the port that the EchoService should use.
Among other things,
by having distinct configuration files, listing distinct ports, you
can run more than one JVM on a single machine (e.g., one playing the role
of the client and one the server).
ConsoleStart hands the chosen configuration file to NetBase .
Among the information in the configuration file are lists of names of Java classes for
which we want an object created in the JVM.
NetBase reads these lists of classes and creates a single instance of each.
It then transfers control to a specially designated "shell application."
Here's the picture of the JVM at this point.
The shell application prints
a prompt listing all loaded applications and asking the user which one to run.
When the user chooses, the shell transfers control to the run() method
of the chosen application.
In this infrastructure,
the ping and dataxfer clients you write are applications.
(Their code should go in the ConsoleApps Eclipse project,
in package edu.uw.cs.cse461.ConsoleApps .)
To be loaded as applications, they must implement the NetLoadableConsoleAppInterface interface. That requires implementing a constructor that takes no arguments,
a run() method that is invoked when the user requests execution of your application,
and a shutdown() method called when the entire JVM is coming
down.
In this infrastructure, the dataxfer service you write is a service.
(It's code should go in the Eclipse Net project, in package
edu.uw.cs.cse461.Net.Base .)
It must implement the NetLoadableServiceInterface .
During construction it creates UDP and TCP sockets. For each socket, it creates
a thread. The sits in a loop waiting for a packet to arrive (UDP) or a connection to
be made (TCP). When that happens, the thread sends the required amount of data back
to the client, then waits for another incoming packet or connection.
A timeout must be set so that the thread will occasionally wake up even if there
is no incoming packet connection. That allows the thread to check whether the
entire application is trying to terminate.
(If so, your service's shutdown()
method will have been called, and you can have set a flag in the object that the threads
can check.)
During execution, any component can access the configuration file selected
when the infrastructure was brought up, and extract from it parameter values
that affect that components behavior.
The configuration file is represented in memory by a ConfigManager
object (found in Eclipse project util ).
A handle to that object is available as NetBase.theNetbase().config() .
Coding: Interfaces
In many cases, the infrastructure uses Java interfaces as a form
of documentation. The idea is to have a short file that shows the constraints
imposed by the infrastructure - something much shorter than the full source file,
which is cluttered with implementation and comments.
An interface file for a component you implement shows what promises your component
must fulfill -- what the rest of the infrastructure might be relying on.
An interface file for a component of the infrastructure you might need to use shows
the methods we think you should be interested in, and doesn't show ones we think
you shouldn't be interested in. (Method attributes related to Java's protection
scheme (public , private , and the like) are unreliable
guides to what we expect you to be able to use. A method may be public
to ease implementation of the infrastructure itself without that being a sign that
we are inviting you to invoke it.)
We followed a convention where interface files have the string Interface
in their file name.
Coding: Taking Measurements
The infrastructure implements a class intended to make it easy to run repeated
experiements and aggregate sampling information from them. The implementation is in
file SampledStatistic.java in Eclipse package util .
Because the classes it uses are static, there is no interface file for it, but here
is a brief description of how to use it.
The main classes you deal with are ElapsedTime and TransferRate . Each manages a dynamically created set of counters. Counters are identified
by string names you make up. A new counter is created when a new name is seen.
ElapsedTime counters measure time intervals. TransferRate
counters measure transfer rates, which also involves measuring time intervals.
Counters have start and stop methods that demarcate the
time interval.
Here's an overview of how to use ElapsedTime . TransferRate
is nearly identical, except that you have to provide the amount of data transferred
on the stop event.
ElapsedTime.clear(); // destroy all existing ElapsedTime counters
for (...) {
ElapsedTime.start("foo"); // create a foo counter, if necessary
...
ElapsedTime.start("bar"); // create the bar counter, if necessary
...
ElapsedTime.stop("bar"); // take a new sample - the time since start("bar")
..
ElapsedTime.stop("foo"); // new foo sample
}
System.out.println(ElapsedTime.get("bar").mean()); // print mean of bar counter
Each start of a counter must be terminated with either a stop
or an abort call before another start can be performed.
(abort records that a failed experiment occurred, but doesn't use
the sample in estimating, for instance, the mean.)
Other information is available besides just the mean;
look at the SampleSet class. There is no requirement
that start and stop of distinct counters must nest;
the two stop calls above could be reversed, for instance.
Running: The Config File
You must run a client and a server. To contact the server, the client must know
what ports it is using. The easiest way to communicate that
is to use the configuration files: the server reads the information in it to
determine where it should be listening, and the client reads the same information
to determine to where it should send.
For the dataxfer
application, the distributed config file does this:
Property | Value |
dataxfer.server.ip
| The "IP address" of the machine on which your server instance is running.
Because this value is passed through some generous Java interfaces,
you can specify either a hostname (e.g., attu1.cs.washington.edu )
or an IP address (e.g., 127,208.1.136 ).
Note: The name attu.cs.washington.edu means any one of
four machines, named attu1 through attu4 . You
should use only the four specific names, to make sure your client is contacting
the right machine when trying to locate your server.
Note: You don't have to debug using attu . You can run
both the client and server on your development machine while debugging.
|
dataxferraw.server.baseport
| The lowest of the four consecutive port numbers used by the server.
|
net.socket.timeout
| The number of milliseconds for a thread to wait for data to arrive on a socket
before timing out.
|
net.serversocket.timeout
| The number of milliseconds for a thread to wait for a connection on a server socket
before timing out.
|
The configuration file also contains properties that apply to the echo
server.
Note: The configuration file also contains values intended for later projects
(e.g., the echoRPC.xxx properties are for the RPC project).
Note: A full list of configuration file fields is here.
Running: Debug Printing
Eclipse provides an interactive debugger that works well for both console and Android
execution (including setting breakpoints and the like in code running on a physical
phone). Despite that, you might find it useful to print some debugging/logging
information.
The infrastructure supports this via the Log class, which provides
methods with names like e() , w() , and d()
to print messages at error, warning, and debug levels, respectively. (For instance,
Log.e(TAG, "some error occurred"); prints a message at the error level.)
This facility works both for console and Android compiles, so you can safely use it
in any code you write. It is modeled on the native Android implementation;
look at it's documentation for more details.
The config file contains two properties that control
Log printing When running in console mode.
Property debug.enable is set to 0 to turn
off printing entirely, and to 1 to allow printing.
Property debug.level is set to a value between 2 (meaning verbose
level) and 6 (meaning error level).
Printing at levels lower than debug.level is suppressed.
Running: Moving Code to a Remote Machine
When you want to run over the network, you'll need to move your code to some remote
machine. The easiest way to do this is to export the code as a jar .
The easiest way to do that is to right-click on the ConsoleApps
project, then Export... , expand the Java entry and
select Runnable JAR file , then Next. For Launch Configuration,
choose ConsoleStart. Choose some directory into which the jar should
be written for the Export destination, make the name of the file ConsoleApps.jar , and hit Finish.
Now copy ConsoleApps.jar to the remote machine. Put it in a directory
that also contains a single configuration file
(whose name ends with config.ini ), and invoke it with:
java -jar ConsoleApps.jar -d .
(The -H switch will cause a help message to be printed, listing
other command line options.)
|