Assignment 5
Supplemental pages
- FAQ and addenda.
- RMI notes.
- RMI sample program (zip version).
- Sun's Java RMI tutorials
- places.zip: a zip file containing places2k.txt (a list of places in the U.S.) and airport-locations.txt (1,090,3776 bytes)
Your assignment
Your assignment is to use Java RMI to write a program to find the five nearest airports to a given town. You will have two server programs that are contacted through remote methods: one is responsible for looking up places, returning the longitude and latitude for a place; the other is responsible for taking a latitude and longitude as input and returning the five nearest airports.
The following figure illustrates the process:
Details
You will be writing two servers and one client for this assignment:
Client (Client.java)
The client is designed to test your servers. It accepts a simple command-line interface:
java Client [ -h rmiregistryserver ] [ -p port ] city state
where:
- rmiregistryserver is the name of the server where you are running the rmiregistry with which the servers will register themselves (typically the same system as the servers are running on). The -h rmiregistryserver is optional. If it is omitted, localhost is used as a default.
- port is the number of the port on which the rmiregistry is listening. If -p port is omitted, the default rmiregistry port, 1099, is used.
- city state arguments identify the city and state for the query (for example, Princeton NJ, "New Brunswick" NJ; note the use of quotes to ensure that the shell treats New Brunswick as one argument).
The client looks up the two RMI services: the first for looking up places and the second for looking airports. It then contacts the first service to look up the place (city, state) to find its latitude and longitude and then contacts the second service to get a list of nearest airports. In summary:
- look up the places server
- look up the airports server
- (place name, state, latitude, longitude) = find_place(city, state)
- print full place name, state, latitude, longitude
- list of five nearest: (airport code, state, distance) = find_airports(latitude, longitude)
- print each airport code, airport name, distance
For example:
Place server
The place server comprises three programs:
- server (PlaceServer.java, startup and registration)
- interface definition
- service class (Places.java, the stuff that does the work)
You may also need a Java file to define a data structure to store state, city, latitude, and longitude information.
Your place server [1] accepts an optional argument identifying the port number on which the rmiregistry is listening (see the rmi-example sample program). The rmiregistry process runs on the same server as this server. The server's only function is to parse arguments, install a security manager, and register the places service with the RMI registry. You can pretty much copy SampleServer.java and change the names.
The interface definition [2] defines the interface for the lookup function, which accepts a location and state (two strings) and returns a data structure containing place information.
You can use the code in sample-rmi as a pattern:
SampleInterface.java
defines the method exposed via the remote interface whose implementation
is in the server code – Sample.java.
For this assignment, you will define two interfaces:
- an interface that defines the places service: a method that takes a city and state and returns information about the place.
- an interface that defines the airport info service: a method that takes a latitude and longitude and returns an array of nearest airports.
The two server functions implement these interfaces (one each). For
example, your PlaceServer.java program might instantiate a class
called Places that implements the remote interface (
Naming.rebind(url, new Places())).
Places.java implements PlacesInterface, which defines
a find_place method that returns a class (data structure) that's
populated with information about the place.
The places service itself [3] accepts two arguments: the name of the place and a two-letter abbreviation for the state. It returns a structure containing:
- full place name (useful if you're doing a match on the leading substring)
- state
- latitude
- longitude
A null is returned if the name is not found.
Upon startup, the service reads in the contents of the file places2k.txt (extract it
from places.zip),
a list of place names compiled by the U.S. Census.
The places file contains data for all Incorporated and Census Designated places
in the 50 states, the District of Columbia and Puerto Rico as of the January 1, 2000.
The file is plain ASCII text, one line per record:
| columns | meaning |
|---|---|
| 1-2 | United States Postal Service State Abbreviation |
| 3-4 | State Federal Information Processing Standard (FIPS) code |
| 5-9 | Place FIPS Code |
| 10-73 | Name |
| 74-82 | Total Population (2000) |
| 83-91 | Total Housing Units (2000) |
| 92-105 | Land Area (square meters) - Created for statistical purposes only. |
| 106-119 | Water Area(square meters) - Created for statistical purposes only. |
| 120-131 | Land Area (square miles) - Created for statistical purposes only. |
| 132-143 | Water Area (square miles) - Created for statistical purposes only. |
| 144-153 | Latitude (decimal degrees) First character is blank or "-" denoting North or South latitude respectively |
| 154-164 | Longitude (decimal degrees) First character is blank or "-" denoting East or West longitude respectively |
Airport server
The airport server comprises three programs:
- server (AirportServer.java, startup and registration)
- interface definition
- service class (Airports.java, the stuff that does the work)
Your airport server [1] accepts an optional argument identifying the port number on which the rmiregistry is listening (see the rmi-example sample program). The rmiregistry process runs on the same server as this server. The server's only function is to parse arguments, install a security manager, and register the places service with the RMI registry. You can pretty much copy SampleServer.java and change the names.
The interface definition [2] defines the interface for the lookup function, which accepts a latitude and longitude (two floats or doubles) and returns an array of the five closest airports.
The airport service itself [3] accepts two arguments: the latitude and longitude that was found from the places service. It returns an array of the closest airports, with each structure containing:
- Airport code
- Airport name
- Airport's latitude (float or double)
- Airport's longitude (float or double)
- Distance from search city (miles)
Upon startup, the service reads in the contents of the file
airport-locations.txt (extract it from places.zip), which contains a
list of 1,065 airport locations in the U.S.
The file is plain ASCII text, one line per record but includes blank lines and a header
on the first line. Its format is:
[airport_code] latitude longitude city
Implementation Advice
- Before starting this assignment, make sure that you can
understand, compile, and run programs that use Java RMI.
Read the recitation notes for programming Java RMI (see the
RPC lecture). Then download
the sample program rmi-example.tar
(or rmi-example.zip for the zip version).
Extract it with:
tar xvf rmi-example.tar
(or unzip rmi-example.zip). This will create a directory named rmi-example with a bunch of files in it, including a README file. - I recommend writing a standalone version first just to ensure that you're doing all the parsing correctly and getting the correct results.
- Reading and parsing places2k.txt:
Open the file using FileReader and BufferedReader and read the lines one at
a time using the readLine method. Since the data occupy fixed fields, each datum
can be extracted via the substring method. Use trim to remove extra
whitespace that you might have grabbed. For example:
String name = line.substring(9,73).trim();
You can parse the latitude and longitude with the parseDouble method of the Double class. - Storing place information: It's up to you to pick a decent data structure to make searching easy. Don't get carried away. Go with something simple that works (at least as a first pass). Using a Vector, for example, is perfectly fine. The only thing you shouldn't use is a fixed-length array. If you're serious about this, realize that there's only one key: all your searches are by city.
- Searching places: I just search for a place name where the string given by the user matches the leading string of the name (e.g., using regionMatches). This way, the user doesn't have to worry about the peculiarities of typing things like "princeton borough." There might be cases where this could give you a false match but I didn't worry about that. Neither should you. Doing a case-insensitive match up to the length of the string given by the user for the place name is fine for this assignment. (You don't need to use regionMatches to match strings. I used that instead of startsWith so I could specify a case-insensitive match without having to convert both strings to a known case. There are other ways of doing this.)
- Reading and parsing airport-locations.txt: To weed out lines that don't contain airport info, you can just skip any lines that don't have a comma; it will save checking if a line is blank or is a header. The airport code, latitude, and longitude occupy fixed columns: you can extract that data with substring. The airport name is everything between a tab and a comma. The state is everything after the comma. You can use substring again but you'll need to set markers with indexOf.
- Calculating distance:
You can approximate the distance between two points by using spherical trigonometry and
calculating the great circle distance. The distance in nautical miles between two
points is:
d = 60 cos-1( sin(lat1) sin(lat2) + cos(lat1) cos(lat2) cos(lon2-lon1))
Where lat is latitude and lon is longitude. Both are expressed in degrees. In Java, you'll have to convert the values to radians (Math.toRadians) before calling Math.sin or Math.cos and convert the result from Math.acos back to degrees with Math.toDegrees. Finally, you'll need to convert the result from nautical miles to statute miles by multiplying by 1.1507794 (1 nautical mile = 1.1507794 statute miles). - Your assignment does not have to be multi-threaded.
- rmiregistry has to run on the same machine as your server. You should use localhost as the hostname on your server. You will lose many points for using hard-coded names such as remus or eden or romulus.
Submission
Before submitting, create a file named id that contains your full name on the first line and your id number on the second line. Make sure you have a newline at the end of the second line.
To submit the assignment, you will create a tar archive of all the files that are needed to compile the no class files.
So that I can remain sane while testing your program, be sure that you name your test client Client.java, your airport info server AirportServer.java, and your place server PlaceServer.java. Name the class that implements place lookup Places.java and the class that implements airport lookup Airports.java.
Before sending the file to me, make sure that all the components are there. If I can't compile it, you will lose virtually all credit:
mkdir test cd test tar xvf a5.tar javac *.java rmic Places rmic AirportsAlso, make sure that no stray files are present (e.g. .class files or emacs temporary files). You will lose points for sloppy submissions. In particular, be sure that you do not submit the places2k.txt and airport-locations.txt files. I already have those and you did not modify them.
Hand the assignment in using the web-based handin procedure. Go to https://handin.rutgers.edu/. If you never used web-based handin, check out the instructions at https://handin.rutgers.edu/handin/manual/stud/stud.html . The project name for this assignment is Assignment 5.