Rmiregistry is a program that is provided by JDK 1.1 or higher. It functions similar to a web server, providing the services to bind names to remote objects, receive incoming requests for objects, marshal arguments and unmarshal arguments. The registry listens by default to port number 1099 and must be running for the fish tank to run. For more information about rmiregistry, see Sun's documentation on remote method invocation (RMI).
TankLocationbag is the class that keeps track of a collection of strings that represent tank locations. The bag is implemented as a circular list, a pointer to the current position is kept and all new locations are added there. (Unlike a conventional bag no repeated locations are allowed.) Locations that are removed are overwritten with the empty string. When a location is needed, a position is picked at random. If the location is empty, it will scan up the list until a location is found.
Fish and fish tanks have their own bags. At times, the bag of a fish is added to a bag of the fish tank. This is done by going through the list of one bag and adding all of the new locations of the other.
All of the remote objects (tanks and servers) are identified by URL names. The names follow the form: rmi://<host name or IP address>/<Object name>. This name will always uniquely identify an object on the network. The registry knows the object names for all of the registered objects on its host. These names are all stored as strings.
The fish shipper module is divided into 3 parts: the interface, the implementation, and the stub and skeleton classes used for the remote method calls. The purpose of the module is to receive a fish or to receive a message from the server to send a fish to a new tank.
The FishShipperServer requires 2 pieces of information that are passed in on the command line. The first is the host it is on. This is required so that it can tell the tank server where it is. The second is the host that the tank server is running on. The location of the server is needed only once, however the host that the fish shipper is on is used by both fish shipper and the fish tank.
The FishShipperServer starts a security manager and creates an instance of the fish shipper implementation. This implementation is bound to a randomly chosen name. The name is "Tank" followed by a random 2 digit number. This allows multiple tanks to run on the same host. Once the fish shipper is bound to a name it starts running.
Once the implementation part starts executing, it creates an instance of a fish tank (which handles the user interface) and then registers itself with the tank server. At this point, fish will start to arrive from other tanks.
The client part of the fish shipper is either handled by the fish or by the tank server. A fish who wishes to ship itself to the tank must first ask the tank if it has room. If so it then calls the ship method with itself as the argument. The current limit of the number of fish in the tank is 3; however, this may be changed so the user can not create a fish if there are more than 2 fish in the tank, but if a fish wants to ship itself the limit is 5 fish in a tank. When the fish arrives it is passed to the fish tank.
The tank server calls a method to have fish sent to a new tank. The force send method accepts a tank location. This location is passed to the fish tank. The message passing to the various other hosts is what would violate the Java security restrictions for applets (as defined before the implementation of signed applets.)
The tank server is the object that helps new tanks get established in the network. It is a remote object that has one method that is called remotely. When a fish shipper has set itself up and is ready for fish to be sent to it, the fish shipper calls a method on the tank server that passes the fish shipper's name to the server. The server then contacts 3 other tanks and has them ship fish to the new tank. The server keeps a list of the newest tanks in a tank location bag. The 3 tanks are chosen at random from the bag.
When the tank server is started, an instance of the server is created, then bound to the name "TheTankServer," and then started. At this point it is ready and the first fish tanks can come on line. If the server crashes, currently it will result in the network being frozen and a new one being started. Future releases should have the tank location bag written to disk periodically to allow it to recover from a crash.
The fish tank is responsible for the user interface and the graphics. It handles creating new fish as well as getting the tank location information for incoming fish. The fish tank when it is created creates its data structures, a tank location bag and a list of fish. The images of the fish are also loaded into the fish tank. The tank will then give pointers to the images to fish that need them when they are created or received. It also creates a fish and puts it into the tank to satisfy the requirement that there always be a fish in the tank at all times. All other times the fish list monitors the number of fish in the list and if it drops to zero asks the tank to put a fish into the tank.
When a fish is received and passed to the tank, the tank adds all of the new locations from the fish's tank location bag to its own. It then adds a few locations from the tank to fish's bag (the locations are chosen before the fish's bag is added.) It then gives the fish the correct images for its type as well as the graphic context (the tank can find out the type of the fish by calling a method that returns a string corresponding to its type.) And finally the fish is given to the fish list to be put in the tank.
If the user want to see the message a fish is carrying, the user clicks on the fish. The location of the click is recorded and then the fish list as asked what fish was at the location. If a fish is at that location the message is returned and it is displayed at the bottom of the tank. Otherwise the empty string is returned and message saying that the user missed the fish is displayed.
The pulldown menus are implemented using callbacks. The user selects the fish he/she wants to add to bring up the dialog box. In the box the fish type can be chosen and the message to be displayed can be typed in. When the user clicks "OK" the fish is actually created and put in the tank. If the user selects the type of fish to add out of the pull down menu it will just add that fish (the same mechanism is used, just that the message is set to a blank space.
When fish shippers are added to the network force send messages are sent out from the server. These message are forwarded from the fish shipper to the fish list. The fish shipper can also ask the tank how many fish are it to determine if a fish can be received. This is done by asking the list for the number of fish in it.
Fish List is responsible for maintaining a list of fish. It is a double linked list with 2 sentinel classes, First Fish and Null Fish. The fish list creates an instance of first fish and that in turn creates a null fish. The two sentinel classes are not displayed on the screen and do not contribute to the number of fish in the tank. The null fish returns messages that would be expected from reaching the end of the list, such as the clicked on method. If it reached the null fish it will return the empty string which signals that a fish was not clicked on.
The first fish is what actually add the fish to the list. This guarantees that the first fish will always be the first fish in the list. Both sentinel classes have selectively overridden methods to make their behavior what is should be as a sentinel class.
The fish list accepts fish, clicked on, and length messages from the fish tank. The fish to be added are passed on to the first fish. The first fish adds them as the next fish in the list. Clicked on messages are passed down the list of fish and a message is returned. In the length method the list is asked how long is it and the result is returned.
There are several methods that are to be called by the fish to signal that they have left the list. One method tells the list to find out the length of the list and can be called when a fish enters or leaves the list. This length is then passed down the list. When this message is received by the fish it indicates that there may be a new fish in the tank. The fish can then ask the tank if there is a specific fish in the tank. This method is then passed down the list. If a fish is of the type requested it will return true, other wise false. This is the mechanism that allows the fish to react to changes in the fish tank.
Fish and their behavior is divided up to into 2 categories and objects. Some of the behavior is the same for all fish. This behavior is in the Fish class. Anything that is specific to a certain fish are in the child classes. All of the fish ship themselves and this code is the Fish class. However drawing themselves and reacting to changes in the tank are handled in the child classes.
A fish keeps track of the time when it was put into the tank. After a minute it will ship itself to an other tank. The fish will pick a location, contact it, ask if it can be shipped there, and then ship itself there. If the tank can not be contacted it will remove the location out of its bag. If the fish can not ship itself to that tank, the next time through its move loop it will try again. When the fish is successful it will remove itself out of the doubly linked list and tell the list it has left. Finally it will erase itself from the screen and cease to exist on the old tank.
Each fish draws itself to the screen. The child classes (i.e. specific fish) know how to draw themselves to the canvas. The fish tank has its canvas as well as a class that has a pointer to the fish list. This class iterates through the list having each fish move and draw itself to an off screen buffer. Then at the end of the list it draws the buffer to the screen. This results in clean animation instead of the heavy flickering that is characteristic of non-double buffered systems. The animation is run in its own thread with the moving going on separately. This use of multithreading allows for more control over how fast the fish swims across the screen (the slower it swims the less load there will be on the system.)
The calculation that the fish can do is a simple calculation where the only problem is the size of the search space. Brute force code breaking by an exhaustive search of all possible or finding the factor of a number by trying all of the numbers from one to the square root of the number are two relevant applications that come directly from cryptography.
The calculation part is divided up into two parts, the server that runs on one host and the client that is a member variable of Fish. The calculation sets up a connection to the server when it is created and successively asks for data (in this case a number to factor) and attempts the calculation. The result of the calculation is then passed back to the server along with a success or failure and another number is asked for. This will continue until the search is finished or a success is returned. In either case when the server is contacted the client will be told to shut down.
The server has an array of numbers corresponding to the numbers being give out. Each cell can have one of three states. 0 indicates the number has not been assigned to a client, 1 indicates it has been assigned but the result has not been received, and 2 indicates that a result has been received and it was a failure. The server will first give out all of the numbers and then give out numbers that have been assigned but the result has not been passed back.
When a fish ships itself, the calculation is not shipped. Whatever calculation that the client was working on will be lost. However because the server keeps track of what is has received results for this will not effect the performance and will help to speedup the shipping of a fish (which is already very slow.)