THE UNIVERSITY OF BIRMINGHAM
School of Computer Science
Software Workshop Java
Route Finder

Dimitar Guelev and Mark Ryan

This exercise involves querying a database by means of SQL. It is meant to show you how to establish a connection with a database from a Java program and how to retrieve from it. You are given a database which contains a list of train services. For every service, its origin, destination, departure hour and, arrival hour. The target of your query is to find a route using the available services in the database with some given origin and destination and certain limitations such as a limited number of changes of trains, and bounds on the times of departure from the point of origin and arrival at the point of destination. 

This data has been sampled from the actual schedule of train services between cities in Britain. For the sake of simplicity, each city is assumed to have a single railway station. All times are given in the format hour.minute . The database lists services between Birmingham, London, Edinburgh, Manchester, York, Oxford, Newcastle, Leeds, Liverpool, Glasgow, Warwick, Colchester, Leicester and Sheffield. Many services have been deliberately omitted, to make changes in itineraries necessary.

The database is stored on the School's PostgreSQL server. Java programs can query it using a JDBC connection, and sending queries using psql commands. Some details of how the PostgreSQL server is organised are here, though you don't need to read that in order to complete these exercises. You might also find Alan Sexton's tutorial on JDBC and servlets useful.

The database consists of a single table called "trains" which looks like this:

   origin   | departure | destination |  arrival  
------------+-----------+-------------+-----------
London | 5.440000 | Birmingham | 8.150000
London | 8.100000 | Birmingham | 9.520000
London | 8.400000 | Birmingham | 10.220000
Manchester | 10.130000 | York | 11.420000
Manchester | 10.460000 | York | 12.100000
Manchester | 11.130000 | York | 12.420000
The origin and destination fields are strings; the departure and arrival fields are numeric. Note that, although they are stored as decimal floating-point numbers, they are actually times. The 5.440000 above actually means the time 05:44. This is safe, provided we don't try to add times together and expect sensible results. We cannot easily do any arithmetic computations on times stored in this way. However, we can safely compare these numbers with <, =, >, and this will correspond to testing whether one is time earlier, equal to, or later than another one.

This database is stored on the School's PostgrSQL server, called dbhost.cs.bham.ac.uk. You can view that database without using a Java program, as follows: type

psql -h dbhost -U yr2 -d trainsdb
at a unix prompt. This is a request to access the database known as trainsdb, logging in as user yr2. The password you have to give is 2ndyr. When you are logged into the database system with prompt trainsdb=>, type these three commands, observing the output each time:
\d
\d trains
select * from trains where origin='London';
Don't forget the semi-colon at the end of the last one. Use \q or Ctrl-D to exit.

In this exercise, we want to access the database from a Java program. Here is a very simple program fragment which queries the TRAINS relation from the trainsdb database, accessing it as user yr2 and citing the password 2ndyr. The code then prints out three places you can go to from London after 10:30:

    try { 
// Load and register the driver for Postgresql
Class.forName ("org.postgresql.Driver") ;
// Establish a connection with the database
Connection con = DriverManager.getConnection
("jdbc:postgresql://dbhost/trainsdb","yr2","2ndyr") ;
Statement stmt = con.createStatement ();
ResultSet rs = stmt.executeQuery (
"SELECT * FROM TRAINS WHERE (ORIGIN = 'London') AND "+
"(DEPARTURE >= '10.30'::NUMERIC)" );
int i=3;
while (rs.next() && i>0) {
i--;
float depTime = rs.getFloat ("DEPARTURE");
float arrTime = rs.getFloat ("ARRIVAL");
String destination = rs.getString ("DESTINATION");
System.out.println(
"London ("+depTime+") to "+destination+" ("+arrTime+")");
}
rs.close();
stmt.close();
con.close();
}

catch (ClassNotFoundException e) {
System.out.print ("org.postgresql.Driver not found");
System.exit(1);
}
catch (SQLException e) {
System.out.println ("SQL Exception: "+e);
System.exit(1);
}
}
Note that classes such as Connection and Statement live in the package java.sql, so this should be imported in your program.

Your answers

Please produce four program files as your answer, one for each part of the exercise. Please call your programs RouteFinder1.java, RouteFinder2.java, RouteFinder3.java, and RouteFinder4.java.

You should submit your program using the procedures described here.

Setting things up on the computer

You need to be able to access the database from your Java program. For this, you need to have the org.postgresql.Driver class on your class path. In the School, this is achieved by the command setup Postgresql on our Unix system, which you can put in your .login file for convenience. On our Windows system, you need to run j:\setup\java\postgresql.

If you want to work from home, see the end of this handout for some hints about the SoCS firewall.

If you are using NetBeans, you must explicitly mount postgresql.jar in Netbeans.  In Windows it is:
J:\Java\postgresql.jar
and Linux:  
/bham/common/java/lib/postgresql.jar
(Netbeans does not use your CLASSPATH environment variable, that's whydoing setup Postgresql doesn't help.)

1. Accessing the database

Your first task is to write a program which accesses the database and provides a link from station A to station B, if one exists. For example:

java RouteFinder London Manchester 
produces the result:
London (6.00) to Manchester (9.02).
while the query
java RouteFinder1 Glasgow Sheffield
produces the result:
No direct route.

Hints

2. Finding Routes to answer command-line queries

Your next task is to answer more sophisticated queries, which involve computing itineraries which consist of several legs. Your program will be called like this:

java RouteFinder2 <origin> <destination> <departure time> <arrival time> <maximum legs>

Your program should consult the database and respond by producing all the itineraries which satisfy the given restrictions. For example,

java RouteFinder2 London Sheffield 9.00 15.00 4

would produce:

London (9.55) to Manchester (12.36), Manchester (12.43) to Sheffield (13.35).
London (10.55) to Manchester (13.36), Manchester (13.43) to Sheffield (14.37).

Please use this format for your output.

You will have to think very carefully about the route finding algorithm you will use. If you program this naively, you'll find that your program produces incorrect results, and/or quickly runs out of memory or takes inordinately long. The lectures will discuss route finding in some detail. 

You can get part marks for this exercise if you only manage to do some of what is required, for example

You can get full marks if your program can print out all itineraries satisfying the requirement. For extra credit, your program will not print out itineraries which are worse than other ones, for example:


3. Using Dijkstra's algorithm

For this part, you are asked to program your route finder using Dijkstra's algorithm, which is one of the most efficient ways to find an optimal route in a graph, in terms of space and time and the number of database results you need to extract.

One measure of the efficiency of your program is how many database results it extracts in order to answer your query. Get your program for the previous part to print out this figure, by adding up the size of all the ResultSets that were returned. (For these figures to be meaningful, you should not cache legs you extract from the database.) The program we wrote RouteFinder2 implements a fairly naive algorithm which accumulates a set of ever longer paths until there are no more satisfying the constraints, as explained in the lectures. Our RouteFinder3, which implements Dijkstra's algorithm, is much more efficient than RouteFinder2, as demonstrated by the table below showing the number of database results returned:

Query RouteFinder2 RouteFinder3
London Sheffield 9.00 15.00 1379 242
Sheffield Glasgow 9.00 16.00 1183 660
Colchester Edinburgh 12.00 20.00 4314 370

(Of course, the RouteFinder2 returns more results than that for RouteFinder3. RouteFinder2 can return all the results satisfying the user's constraints, while RouteFinder3 only produces the shortest one.)

In this part of the exercise, we are interested in efficiency. Implement Dijkstra's algorithm. Can you get lower numbers than those for RouteFinder3 in the table above?

4. Answering queries on the web

Having completed the above, make a servlet version of your program. The servlet should provide the user with a form to fill in with the parameters of the query using a browser, then check the filled-in form for consistency and process it to obtain a list of answers. You can use your RouteFinder2 or RouteFinder3 program to compute the answers. The answers should be sent to the user in html format again, with the form fields occurring in the bottom of the page again to enable further queries.

In order to run servlets on the School's machines, you will need to read "Developing and Running Servlets with Tomcat in SoCS" from the Support Team's documentation on Java servlets. (Indicentally, if you try to access this link from home, you will get the message "Access to this page is only available within the School of Computer Science". See "Working from home" for a workaround.) You can also look at Alan Sexton's tutorial on JDBC and servlets. In order to run servlets on your machine, you will need to install and run the servlet server Tomcat. You can find information on how to do this here.

The tutorials don't cover the issue of html forms very well. Here is an example html form using the GET method.  Here is a "Hello world, enter a parameter and I will display it" complete servlet written by one of the demonstrators. It avoids having to have an initial form; this servlet checks to see if a request was made, and if no request was made it just displays the form. If a request was made, it answers the request, and still displays the form afterwards.

Hints for working from home

You may wish to work from home. However, please remember that you have to demonstrate your work in the labs. Therefore, you must either have it working on one of the machines in the labs, or you can bring in your own laptop.

One option is to install PostgreSQL and a Java servlet server (for part 4) on your machine. I haven't done this myself, so I can't help you.

Another option is to get your Java program to connect to the School's database server and servlet server. This probably only makes sense if you have a permanent internet connection from home, or a fixed-price dial-up connection. To make this work, you will need some technical information to overcome some restrictions imposed by the School's firewall. We advise you not to attempt this unless you are interested in understanding the technicalities, because it is quite complicated.

Marking scheme

For students who have attempted Part 1

Part 1: Accessing the database

Does the program work correctly when it is given two arguments which are station names?
10
Does the program respond sensibly if it is given the wrong number of arguments and/or arguments which are not valid station names? 10
Subtotal
20

For students who have attempted Part 2 and possibly more

Design

Clarity and neatness of the diagram showing the classes and their relationships
5
Content, clarity and brevity of narrative explaining design, including explanation of route finder algo
5
Choice of classes: do they correspond to natural concepts or actors, are they named as nouns
5
Choice of classes: low coupling between classes and high cohesion of each class
5
Subtotal 20
Coding

The code implements the design
7
Methods do one job. Variable names are sensible. Classes begin Uppercase, variables and methods begin lowercase
3
Subtotal
10
Javadoc
Code has Javadoc in it
5
Student has compiled Javadoc into a meaningful set of web pages
5
Subotal 10
Functionality

Part 2 (constructing routes)
The program can at least produce a route satisfying the requirements, even if only in some cases.
The program reliably produces all routes (or all optimal routes) satisfying the requirements.
10
(Extra credit!) The program produces only optimal routes satisfying the requirements 5
Part 3 (Dijkstra's algorithm)
The program codes DA. It produces the best route. The code is clear and it is possible to see that it encapsulates DA.
15
Part 4 (Web access)
Student can explain how servlets work.  Servlet works correctly.  The response page includes a new form, so that user can enter a new query.
15
Subtotal
45
(Extra credit!)  5 points if your viva is on Tuesday. 2 points if it is on Thursday. None for Friday.
5
TOTAL
110

Page written by Dimitar Guelev and Mark Ryan
Page maintained by Mark Ryan
Last modified: 5 November 2004