Written By:
David Ludwig <davidl@wpi.edu>
James Abbatiello <abbeyj@wpi.edu>
This document provides information on how GradeView's client and server talk to each other. (NOTE: It does not talk about how the server and the database comminicate.) For a general overview of GradeView's networking architecture (i.e., what obstacles had to be overcome, what technologies were used, what had to be written from scratch, etc), continue to section two. If you are already familiar with these technologies, and/or you simply want to look at the actual implementation details, skip to section three.
The GradeView system is designed such that users can access their grades from any Java2-capable web browser. As such, all of the grades are kept in a single master database (or, optionally, a cluster of databases) to which clients (users) connect to. More specifically, the Java applet connects to a web server which, in turn, connects to the database. Out of the two connection types (client-to-server and server-to-database), this document describes the client-to-server connection type.
When the client wants information from the server (or if the client wants to tell the server something), it makes a connection to a designated web server. What's important to note is that each and every time the client wants to send data to the server, it has to establish a brand new connection to the server. This presents a problem; the server needs to be able to determine which "session" (a "session" being a set of connections starting when a particular user logs in and later logs out (or times out)) a particular connection belongs to.
To correct this, each session is given a unique id. This id is generated by the server upon a new login, which is sent to, and stored by the client. Each time the client makes a connection to the server [within that session], it packs the session id along with the rest of the data.
(NOTE: Even though GradeView's current specification does not call for the implementation of security, this is still something which needs to be thought about. I.e., future GradeView developers should be able to implement [more] comprehensive security without having to change the networking architecture too much.)
Such a scheme also brings up a security question; what prevents a person from sniffing the packets off someone else's GradeView session, recording the session id (which, over the span of a session, will be sent over the network several times) and using it as their own (aka. "session hijacking")?
GradeView takes a number of steps to prevent session hijacking. First, upon each connection, starting with a client logging in, the server will generate a unique, one-time ticket (consisting of a string of characters and numbers) which is both passed to the client and stored on the server. Upon the next connection, the client will sent this ticket to the server, along with the rest of the connection's data. The server will then try to match the client-sent ticket against the last-generated ticket (for that particular session). If they are equal, the connection may continue. Otherwise, the connection will be terminated (and various security alerts may be risen.)
While the above scheme makes network hijacking more difficult, it's still fairly easy to accomplish. To fix this, each individual connection may be encrypted using any one of a number of technologies. The easiest, in this case, would likely be SSL. SSL is handled outside of the GradeView code, requiring only minimal modifications to the system as a whole.
Within each connection (of a particular session), a variety of different kinds of data must be stored. This includes network/session specific information, such as session ids and tickets, as well as presented data (data displayed to the user via the GradeView client). In order to allow all of this data to be sent over any one particular connection, and to allow all of it to be separated, GradeView will encode all of one connection's data into an XML document.
XML (eXtensible Markup Language), is a specification for encoding data into a structured format. More specifically, it places data in between sets of "tags", each of which gives information on what type of data it encapsulates. This method of encoding works in the same way HTML does.
The GradeView client applet consists of many different Java classes, many of which need to be able to talk to the server. In order to simplify network accesses to each of these classes, and to better encapsulate the various network implementation details, all networking functionality was implemented into one class called "GradeViewSession".
Each running copy of the GradeView applet will maintain one instance of the GradeViewSession class. This instance is accessible via GradeViewSession's class method "getCurrentSession()". After that, the object may then proceed to call one of its instance methods, each of which are listed below:
(NOTE: More detailed API documentation is available at http://gradeview.sourceforge.net/doc/gradeview/net/GradeViewSession.html )
boolean establish(String username, String password, String server) - Connects to 'server' using 'username' and 'password'. Returns true if a session was established, false otherwise.
void disconnect() - Disconnects from the current server.
String getServerAddress() - Gets the address of the current server.
boolean isEstablished() - Returns true if a session has already been established, false otherwise.
String getConnectionType() - Returns a string specifying what sort of user is connected to the system (a student, a ta, or a professor.)
Node sendData(String data) - Sends the XML encoded string 'data' to the server. The data received from the server is then returned as an XML node (represented by the 'Node' type, which is defined in Sun's XML libraries.)
Node sendData(Node data) - Works identically to sendData(String), except that the string has already been parsed into a 'Node' structure.
GradeViewSession also has another useful static function (in addition to "getCurrentSession()":
Node parseToNode(String parseMe) - Given a string, it will parse it into an XML 'Node' structure.
By the manner in which the above APIs are listed, it makes it relatively easy for various UI compents (within the GradeView applet) to be hooked up to the server. Two examples will be shown here. The first will have an instance of the LoginPanel class validate the user's inputted username and password against the username and password listings on the server. The second will have an instance of the OrganizationTable class ask the server for data to fill in the fields of its table. (NOTE: These two examples will also be used in section 3.2, which will show the actual data being transmitted between the client and the server.)
The code below is a copy of the code in LoginPanel.java. The instance method shown, "String check()", is meant to check the username and password (which are stored in a JTextField and a JPasswordField, respectively.) If the login was successfull, the method will return the type of user which connected to the system (a "student", a "ta", or a "prof" (professor.)) If the login failed, the method will return null.
(Example 3.1.1.1-1)
public String check() { // Store the inputted username and password in the strings // 'user' and 'pass', respectively. String user = username.getText(); String pass = new String( password.getPassword() ); // Get this applet's instance of the GradeViewSession class. GradeViewSession gvsession = GradeViewSession.getCurrentSession(); // Try to log in. If it fails, gvsession.establish() will return // null. if ( !gvsession.establish(null, user, pass) ) { return null; } // If execution reaches this point, the login was successfull. return gvsession.getConnectionType(); }
The code below is a copy of the code in OrganizationTableSubPanel.java. The instance method shown, "boolean getOrganization()", is meant to retrieve the current organization table data from the server. If successfull, the method will return true. Otherwise, it will return false.
(Example 3.1.1.2-1)
public boolean getOrganization() { GradeViewSession gvsession = GradeViewSession.getCurrentSession(); if (gvsession == null) return false; // Send the server a request for the current organization table. Store // the result in 'orgNode'. ElementNode orgNode = (ElementNode)gvsession.sendData("<getorganization/>"); if (orgNode == null) return false; // Store all of the "element" nodes in the list 'elemList'. NodeList elemList = orgNode.getElementsByTagName("element"); if (elemList == null) return true; // having no elements is not an error // Pull the results out of 'elemList' and add them to the GUI. for (int i = 0; i < elemList.getLength(); i++) { ElementNode elemNode = (ElementNode)elemList.item(i); NodeList nameList = elemNode.getElementsByTagName("name"); int e = orgTableModel.addElement(nameList.item(0).getFirstChild().getNodeValue()); NodeList subelemList = elemNode.getElementsByTagName("subelement"); if (subelemList == null) continue; for (int j = 0; j < subelemList.getLength(); j++) { ElementNode subelemNode = (ElementNode)subelemList.item(j); orgTableModel.addSubElement(e, Misc.buildHashMap( "name=" + subelemNode.getElementsByTagName("name").item(0).getFirstChild().getNodeValue() + "," + "totalpct=" + subelemNode.getElementsByTagName("totalpct").item(0).getFirstChild().getNodeValue() + "," + "maxscore=" + subelemNode.getElementsByTagName("maxscore").item(0).getFirstChild().getNodeValue())); } } return true; }
The next few subsections show some examples of the data sent to/from the server under various situations.
In all of the examples below, the XML header has been removed (it is the same for all examples. However it is listed below:
<?xml version="1.0"?>
Also note that each example will likely contain many comments which are not normally included in the actual connections. However if they were there, it wouldn't make a difference, as the XML parsers on both the client and the server ignore all comments. These comments are specified by their special start and end tags, which are as shown:
<!-- This is a comment -->
This particular XML document shows what the client sends to the server when it wants to log in. Specifically, it will be sending the server a login request, accompanied by a username and password. Also note that it will not be sending any session ids or tickets, as the client has not yet received any yet (as this is the very first connection in a session.)
(Example 3.2.1-1)
<!-- All data sent in any GradeView connection is included inside a "connection" node. --> <connection> <!-- All authorization related data is included inside an "auth" node. --> <auth> <login> <username>prof</username> <password>frop</password> </login> </auth> <!-- All GradeView-specific data is included inside a "data" node. However, in a login, only authorization data is being sent. Therefore, nothing is included underneath the "data" node. --> <data /> </connection>
Once this document has been sent, the server will examine the document's contents, process the login request, and then send back a reply. This reply, which is also encoded as an XML document, will contain the result of the login. If successful, it will contain a session id and a ticket (to be used in the next connection). If unsuccessful, it will contain an error message. Shown below are examples of both:
(Example 3.2.1-2) Successful login:
<connection> <auth> <!-- Here the server will specify the authorization/connection type (which will be made available to the client via GradeViewSession's instance method "getConnectionType()".) --> <auth_type>prof</auth_type> <!-- Both the ticket and session ids are stored by the client. However, the ticket id will only be used in the next connection (at which time, the server will create a new ticket), while the session id is sent in all remaining connections. --> <ticket>127_0_0_1_428631909376</ticket> <session>127_0_0_1_477889921536</session> </auth> <data> </data> </connection>
(Example 3.2.1-3) Failed login:
<connection> <auth error="badlogin"/> <data/> </connection>
Assuming the client is able to successfully log in, it can then perform various requests for data from the server. For instance, suppose the client wishes to display the Organization Table. In this case it would send an XML document containing the request "<getorganization/>" (this is demonstrated below.) The actual sent data would also contain the session and ticket identifiers as maintained by the GradeViewSession class. The client would then get back an XML document from the server like the one below. This contains all the data the client needs to render the Organization Table. It would be parsed into a tree by the GradeViewSession class and this would then be returned to the caller.
(Example 3.2.2-1) Data sent from client to server:
<connection> <auth> <!-- Notice how the ticket and session ids are the same ones sent back from the server (in Example 3.2.1-2). In the next connection (from the client to the server), the ticket id will change but the session id will not. The next ticket to use will be generated by the server and sent back in the next connection (Example 3.2.2-2). --> <ticket>127_0_0_1_428631909376</ticket> <session>127_0_0_1_477889921536</session> </auth> <data> <!-- Here is the actual request for the Organization Table data. --> <getorganization/> </data> </connection>
(Example 3.2.2-2) Data returned from server to client:
<connection> <auth> <ticket>127_0_0_1_501079610368</ticket> <session>127_0_0_1_477889921536</session> </auth> <data> <!-- Everything underneath the "organization" node includes data to be stored in an organization table. --> <organization> <element> <name>Homeworks</name> <subelement> <name>HW1</name> <totalpct>50</totalpct> <maxscore>100</maxscore> </subelement> <subelement> <name>HW2</name> <totalpct>50</totalpct> <maxscore>100</maxscore> </subelement> </element> <element> <name>Exams</name> <subelement> <name>Exam 1</name> <totalpct>50</totalpct> <maxscore>100</maxscore> </subelement> <subelement> <name>Exam 2</name> <totalpct>50</totalpct> <maxscore>100</maxscore> </subelement> </element> </organization> </data> </connection>
All of the clients requests are answered by the web server (for us, this was the Apache web server (http://www.apache.org)) and then passed on to the GradeView CGI script. This script was written in the Perl programming language (http://www.perl.com), as Perl provides much more powerful string handling capabilities than Java does. It also uses the XML::DOM Perl module (http://users.erols.com/enno/dom/) to parse and search through all XML data.
All of the data sent from the client to the server is stored in a string labelled "$buffer". One of the first actions performed by the server is to parse this string into an XML document. This document is placed into the global "$doc" variable.
The server will then check to see what sort of connection it is. I.e, is it a user trying to log in (and establish a new session) or is it a user trying to send data as part of a previously established session? It does this by looking inside the "auth" node. If it contains a "login" node, it will treat the connection's data as a login request. If it contains a "ticket" node, it will treat it as a reconnect, but not before checking to see if the ticket and session ids properly match.
Assuming, at this point, that a session has already been established and that we have a user making a request for data, control will be passed off to the "handleData(node)" function. This function is passed the contents of everything inside the "data" node, which will contain all requests for actual GradeView data, along with any needed parameters for that request.
Shown below, is the code to the "handleData(node)" function. (NOTE: Only the code relevant to the organization table example is shown. Various other data handlers have been removed (to keep this example short.))
(Example 3.3-1)
sub handleData { my ($data_node) = @_; for my $node ($data_node->getChildNodes) { if ( $node->getNodeName eq "getorganization" ) { print "<organization>\n"; print " <element>\n"; print " <name>Homeworks</name>\n"; print " <subelement>\n"; print " <name>HW1</name>\n"; print " <totalpct>50</totalpct>\n"; print " <maxscore>100</maxscore>\n"; print " </subelement>\n"; print " <subelement>\n"; print " <name>HW2</name>\n"; print " <totalpct>50</totalpct>\n"; print " <maxscore>100</maxscore>\n"; print " </subelement>\n"; print " </element>\n"; print " <element>\n"; print " <name>Exams</name>\n"; print " <subelement>\n"; print " <name>Exam 1</name>\n"; print " <totalpct>50</totalpct>\n"; print " <maxscore>100</maxscore>\n"; print " </subelement>\n"; print " <subelement>\n"; print " <name>Exam 2</name>\n"; print " <totalpct>50</totalpct>\n"; print " <maxscore>100</maxscore>\n"; print " </subelement>\n"; print " </element>\n"; print "</organization>\n"; } # At this point, various other data handlers (for other # GradeView data types) would exist. } }
As the full source code to GradeView is quite lengthy (it is a few thousand lines in length. The networking code alone is approximately a thousand lines long), it has not been placed in this document. Instead, it is available on the web at the following URLs:
Applet source code: http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/gvapplet/?cvsroot=gradeview
GradeViewSession source code (select the highlighted revision number (of the most recent version, which will be listed at the top) to view the code): http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/gvapplet/src/GradeViewSession.java?cvsroot=gradeview
Server source code: http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/gvserver/?cvsroot=gradeview