Technical Documentation for the API of Java Services on the SCRIBE Server




Editors:

Louis W. G. Barton

Debra Lacoste

Contributors:

Louis W. G. Barton

Hans Bickel

Koon Shan Barry Ng

Revision Date: 28 November 2007

Filename: Tech-Doc_Services-API.odt

Copyright 2007, The University of Oxford





Table of Contents

Background 2

I. The Authenticate Service API 4

I.1. The register() method 6

I.2. The close() method 7

I.3. The authenticateUser() method 7

I.4. The logoutUser() method 9

I.5. The validateUserRequest() method 9

I.6. The getUserInformation() method 10

I.7. The updateUserAccount() method 10

I.8. Guidelines for the Application's handing of client requests 10

I.9. The Application's user-database 10

II. The DBAccess Service API 11

III. The PushHTMLTemplate Service API 11

IV. The NotifyByEmail Service API 12

Appendix A. Guidelines for Implementation of class Authenticate 13



Typographical conventions:

Words in the body of this text that are displayed in magenta are technical terms that have a reserved meaning in computer science and/or in the Java programming language.

Words in the body of this text that are displayed in green are particular Java class names, class method names, or names of files or of data fields.

Words in the body of this text that are displayed in blue are particular names that are specific to this software that shall be written for the SCRIBE server.

Method names are always written with trailing parentheses, e.g., “doGet()”.

Background

This document discusses the design of service classes in Java for shared use by various client/server apps. An 'app' (or, applications program) typically shall be either a Java Applet or a Java Servlet-based system where HTML pages act as the UI (user interface). The basic idea is illustrated in Figure 1: four services are listed at left, which are shared in common by several apps, at right.



Figure 1. Service classes on the SCRIBE server that are shared by many Java apps.


The general architecture that is the context of this discussion is as follows.

  1. We discuss the division of responsibilities between a client/server, Java applications program on the SCRIBE server (the “Application”), and various services (including: login and session-tracking; HTML template document handling; database connection and query execution; and so on) provided to all such Applications by Java utility classes in the /utils sub-directory of the SCRIBE server (the “Service”). The division of responsibilities shall be implemented via an API (applications programming interface) of the Service. By its API, the Service shall provide public methods for invoking the services. With the exception of publicly-declared constants in the Service, an 'abstraction barrier' shall prohibit the Application from direct manipulation of any data that are within the Service except by invoking of one of the Service's public methods.

  2. Servlet requests from the client-side shall come into the Application only; no Servlet requests directly from the client shall be accepted by the Service. The Application shall pass to the Service an object reference to the in-coming HTTPRequest object for purposes of user-validation or for parsing of parameters in the Servlet request. Conversely, the Application shall pass to the Service a reference to the out-going HTTPResponse object for pushing HTML content to the client.

  3. All program logic that is specific to the Application shall be confined to the program space of the Application. The Service shall be deemed to have no knowledge of:

A typical user session would include a log-in request, followed by one or more data requests. Ordinarily, a data request would involve pushing an HTML page that contains the requested data, or an HTML page with a Flash presentation of data.

There shall be six permission levels defined, as follows.

Level 1: System Manager

Level 2: Privileged (or Editor)

Level 3: First-class (or Subscription user)

Level 4: Second-class (or Trial-Period user)

Level 5: Guest (or anonymous; anyone)

Level 6: Invalid (a catch-all category to cover redundant or otherwise invalid requests)

The meaning and name given to each of these permission levels shall be particular to the Application.

Many of the services described herein shall return to the caller a reference to a disposable object of type RequestStatus. Class RequestStatus shall be a data structure to include the following information:

The basic dialog between the Application and various Services is illustrated in Figure 2, below.



Figure 2. Walk-through of message-passing for user log-in and user data-request.


I. The Authenticate Service API

The purpose of the Authenticate class shall be to provide a simple API by which the Application can determine whether the end-user who sends a Servlet request to the Application has a valid userID and, if what this user's permission level is. Such requests shall include:

  1. data requests for public data by 'guest' or anonymous users;

  2. log-in requests by authorized users;

  3. data requests for public or protected data by logged-in users; and

  4. log-out requests by logged-in users.

The Application shall pass to the Authenticate service's Authenticate() method a reference to the in-coming HTTPRequest object of a Servlet request of the client. The Service shall parse the request to extract the user's log-in or session-tracking information. The Service shall return to the Application a notification that:

  1. the client is a 'guest' or anonymous user;

  2. the client's log-in request succeeded or failed;

  3. the data request is from a logged-in user having the specified permission level; or

  4. the client's log-out request succeeded or failed.

From the viewpoint of the Application, calls to the Authenticate() method shall be 'stupid', in that the Application shall not need to pre-parse the request.

The Service shall accomplish the user-validation of requests in two alternative ways:

  1. consultation of the Application's database of authorized users; or

  2. consultation of the Service's session-tracking data of logged-in users of the Application.

The Service shall deem each Application to be separable, such that the database of authorized users and the Service's session-tracking data shall be distinct for each Application.

The principal services provided by the Authenticate class are summarized in Figure 3.



Figure 3. Services provided to Java apps by the API of class /utils/Authenticate.


The Service shall lock-out a user account for an interval of time (that shall be specified in the Application's properties file) if a valid userID is given, but an incorrect userPassword is given. The number of unsuccessful log-in attempts that shall be permitted shall be particular to each Application (specified in the Application's properties file).

Each Application shall have a time-out constant (specified in the Application's properties file) that the Service shall consult to automatically log-out any userID that has not invoked any data-request activity of the Service within the time interval of the Application's time-out constant.

The Service shall record the I.P address (Internet Protocol address of origin) of each userID's last valid log-in, and the Service shall notify the Application if the userID attempts a log-in from an I.P. address that is different from the last valid log-in. The Application's behavior in this situation shall be decided by the Application. (The Application may allow end-users to specify their preferred behavior in this situation, in which case the Application may consult the userID's record in the user-database to make this decision).

The Service shall notify the Application if a logged-in userID's data request originates at an I.P. address that is different from the I.P. address from which the userID logged-in. The Application shall decide how to handle such a situation. (The Application may allow users to specify their preferred behaviour in these situations, and this information may be stored in the user-database.)

Implementation Note: The Authenticate class shall be static, so that an Application does not need to instantiate an object of this class.


0.1. The register() method

In order to use services of the Authenticate class, the Application shall register itself with the Service; the Application shall do this once when it first starts.

Implementation Note: Typically the Application shall call the register() method in a static initialization block, so that it is guaranteed to be called exactly once, when the Application loads.

The Application registers by providing: an application name that shall be unique to the Application; the harddrive path and name of its properties file; and the harddrive path and filename of its database of authorized users (the Application's “user-database”). The application name parameter shall be the same as the ApplicationName field of the Application's properties file; this redundancy will help to ensure against errors by applications programmers. The properties file of each Application shall be in uniform, standardized format; it shall contain the ApplicationName and some general information about the Application. [.. to-do: detail ...]

The Authenticate service shall generate a unique appID (application ID) for each Application at the time the Application registers itself, and return this value to the Application. If a currently-registered Application attempts to register itself again, the Service shall just return the existing appID.

The Exception-handling behavior of the register() method is summarized in Figure 4.



Figure 4. Exception protocol of method register() of class /utils/Authenticate.


In all subsequent requests to the Service, the Application shall pass its appID as a parameter of service-method calls in order to identify itself. In case there are multiple instances of the same Application running concurrently, they shall share the same appID. In case the Application ends abruptly and gets restarted, the Application shall recover its appID by a new call to register().

In generating an appID, the Service shall embed in it some information that is sure to be unique to the Application, such that no other Application can possibly be issued the same appID.

Implementation Note: Each Application shall have just one user-database, even if multiple instances of the Application can run concurrently; if distinct user-databases are required for each instance of the Application, then each instance must have a distinct name and a separate properties file. If the DBMS (database management system) involves more than one physical disk file for each Application's user-database, then it shall be understood that the harddrive path and name of such other files shall be derivable by-rule from the main user-database harddrive path and name. (If this policy turns out to be infeasible for a different DBMS from HSQLDB that may be adopted in the future, then the Application's properties file shall contain whatever additional information is required.)


0.2. The close() method

An orderly process for shut-down of the Application shall be provided by the Service, such that the user accounts data of the Application shall be finalized, and the user-account database and properties file shall be closed. The Application shall pass its appID as a parameter to the Authenticate.close() method; this Service method shall return a status indicator when the Application's properties file and user-database have been released by the Service. In case multiple instances of the Application are running concurrently, the Service shall re-open the Application's properties file and user-database at the next occasion when these data are needed.


0.3. The authenticateUser() method

When a user attempts to log-in to the Application via a Servlet request, the Application shall pass the HTTPRequest object to the authenticateUser() method of class Authenticate. The Service shall validate the userID and userPassword by querying the Application's user-database. If the log-in data are valid, then the Service shall assign a unique sessionID to this user. Depending on the returned status code from this method, the Application shall decide for itself how to proceed.

Implementation Note: Within any particular Application, each userID shall be unique. Typically, the Application shall enforce this constraint by requiring that the userID given by the end-user when the user-database record is established shall be a valid e-mail address; such validity may be determined by-rule from parsing the e-mail address given by the end-user when s/he 'signs up' to use the Application, it may be determined de facto by sending an initial, temporary password to the e-mail address given by the end-user. Since each userID shall be unique within an Application, therefore the userPassword is not needed for identifying the end-user; nevertheless, a userPassword shall be required in all client-server interactions in order to deter attempts to 'hack into' a user account.

The roster of the various status codes that shall be returned by method authenticateUser() are detailed in Table 1, below. The behavior of the Application for each status code shall be determined solely by each individual Application (not by the Service, whose role is simply to tell the Application what the user's status is).

Account Lock-Out: The time-out constant shall be 'global' in the Authenticate service: that is to say, this constant shall be declared as a public static final int, usable directly in the Application by the constant's public symbolic name. Likewise, the lock-out time (i.e., the length of time that a userID gets locked-out) shall be a public constant of the Authenticate service. The main purpose of these constants is to protect the server from malicious attacks or password 'hacking' by robots.

Account Inactivity: The Service shall record a date/timestamp for each currently-active sessionID, indicating when the most recent data request or log-in occurred. The data-request method of the Service shall use this timestamp in determining whether the sessionID has timed-out, per the time-out constant specified in the Application's properties file.


Table 1. Status Codes returned from Log-in Requests.

Code

Short Name

Detail

1

Log-in successful

This is a valid user for this Application, and s/he has the permission level specified in this object.

Alternatively, a redundant log-in request has been made by a currently logged-in user with the same userID/userPassword combination coming from the same I.P. address (viz., an idempotent login request).

2

Log-in successful, changed I.P. address

This is a valid user for this Application, and s/he has the permission level specified in this object, but the source I.P. address of this request has changed from this userID's last successful log-in.

Implementation note: The Application may choose to allow end-users to specify in their individual accounts:

(a) to deny log-in requests from different I.P. addresses;

(b) to notify the end-user via e-mail if a log-in to her/his account has been made from a different I.P. address; or

(c) to ignore changes of I.P. address.

The Application may initiate a separate method call to the Authenticate service in order to access the user's default I.P. address, the user's preferences in this regard, and the user's notification e-mail address. (E-mail notifications shall also be via a service.)

3

Log-in failed, invalid userID

Invalid userID (e-mail address) given.

4

Log-in failed, invalid userPassword

Invalid userPassword given for a valid userID.

5

Log-in failed, multiple logged-in from different I.P. addresses

A different client is currently logged-in with same userID/userPassword combination from different I.P. address.

6

Log-in failed, userID lock-out

Too many failed login attempts in nn minutes on this userID. The Authenticate service shall lock-out this userID for the next mm minutes. (The constants nn and mm shall be global in the Authenticate service in order to protect the server. These values shall be 'public static final' constants in the Service and accessible via their public symbolic names.)


0.4. The logoutUser() method

When a user sends a Servlet request to log-out from the Application, then the Application shall send the HTTPRequest object to the logoutUser() method of class Authenticate. The Service shall check for the sessionID that is embedded in the HTTPRequest object, in order to determine whether the request is coming from a currently logged-in user. If this is a currently logged-in user, then the Service shall invalidate the corresponding sessionID. The Application shall decide for itself how to proceed.

The status code that shall be returned by method logoutUser() is detailed in Table 2.


Table 2. Status Codes returned from Log-out Requests.

Code

Short Name

Detail

7

Log-out successful

User has logged-out. This may be:

(a) log-out of a valid sessionID;

(b) redundant log-out of a previously logged-out sessionID; or

(c) log-out attempt of an invalid sessionID.

If the log-out request is coming from a valid sessionID, then the Authenticate service shall close the sessionID.

Implementation Note: The Application may use the permission level of this object to form its feedback to the user.


0.5. The validateUserRequest() method

When a user requests data or any other action by the Application via Servlet request (not including log-in or log-out), the Application shall first send the HTTPRequest object to the validateUserRequest() method of class Authenticate. The Service shall check for the sessionID that is embedded in the HTTPRequest object, in order to determine whether the request is coming from a currently logged-in user. If no sessionID is present in the HTTPRequest, then the Service shall treat the request as if it had an invalid sessionID. Depending on the returned status code from this method, the Application shall decide for itself how to proceed. The roster of status codes returned by method validateUserRequest() are detailed in Table 3, below

NOTE: The purpose of method validateUserRequest() is simply to determine whether the request is coming from a logged-in sessionID; the Service does not process the request in any other way in this method.

Account Inactivity: The Service shall record a date/timestamp for each currently-active sessionID, indicating when the most recent data request or log-in occurred. When a new data request comes into method validateUserRequest(), the Service shall compute the interval of time between the aforementioned timestamp and the current time. If the interval is greater than the time-out constant for the Application (as specified in the Application's properties file), then the Service shall automatically log-out the userID and return a status code of 'invalid sessionID' due to the userID time-out.


Table 3. Status Codes returned from Data-View or Data-Entry Requests.

Code

Short Name

Detail

8

Valid sessionID for data request

The user of this sessionID has permission level specified in the RequestStatus object.

Implementation Note: If the sessionID is of a permanently open 'demo' or 'guest' account, then the Application should not allow access to any 'hidden' content.

9

Invalid sessionID for data request

The sessionID given is not currently valid; or it is valid but has timed-out.

Implementation Note: The Application should instruct the user to log-in. The Application may inform the user that her/his log-in session might have timed out, and it may inform the user of what the time-out interval is for this Application.


0.6. The getUserInformation() method

The Service shall allow the Application to read information from the user-account database for any user, or from the current session of any logged-in user. The Application shall do this by calling the getUserInformation() method of class Authenticate. In such a call, the Application shall pass (a) the userID, which shall be unique within this Application, and (b) a symbolic name indicating the item of information that is requested. The information shall be returned from this method as a String. Only one item of information can be requested in each call. The Service shall define a public static String constant for each item of information that can be so-requested. The Service shall throw an Exception if the userID is not valid. (The Java compiler guarantees that that only defined names of information may be used.)


0.7. The updateUserAccount() method


0.8. Guidelines for the Application's handing of client requests

These guidelines are suggestions for how the Application shall handle client requests; a particular Application may handle requests in a different way, if warranted.

'Guest' or 'visitor' requests shall be handled by the Servlet's doGet() method in response to an HTML FORM GET request. For security reasons, all requests from registered users shall be handled by the Servlet's doPost() method in response to an HTML FORM POST request. At least three separate Servlets shall be provided by the Application:

  1. user log-in;

  2. user data request; and

  3. user log-out.

The Application shall safeguard its appID (Application ID) that it receives from the register() method of class Authenticate. Typically, the Application shall store its appID in a private String of its static State class, and the Application shall provide an accessor method by which other classes of the Application can read the appID.

The Application normally shall have separate Servlet classes for log-in, log-out, and data requests in order to avoid having to parse requests to predetermine their type.


0.9. The Application's user-database

Common fields of all user-account databases shall include the following fields.

  1. SeqNr, a unique record number that shall never be re-used or re-assigned.

  2. UserID, which shall be a valid e-mail address (this might or might not be the same as the e-mail address where the user wishes to receive notifications).

  3. Password (ideally, this should prohibit short words from the dictionary that can easily be hacked, these should not be longer than the length of passwords that major browsers can store for auto-completion of HTML Forms, and the passwords should be encrypted fields in the database).

  4. User's permission level for this Application.

  5. Current log-in status (logged-in, logged-out, session timed-out, or hacking lockout);

  6. Date and time of last log-in.

  7. Source I.P. address of last successful log-in.

  8. Date and time of last log-out.

  9. User's preferred action when a log-in is made from a different I.P. address.

  10. User's preferred e-mail contact address for notifications.

The Application's user-account database may also include additional information, such as the user's name, the user's postal address, and other account information as deemed necessary by the Application.


II. The DBAccess Service API

The purpose of the DBAccess class shall be to provide a simple API by which the Application can use the SQL database query language for reading from, and writing to, the Application's database (if any) of Application content, without having to handle opening and closing of the database engine. Note that this service does not handle the Application's user database, which is handled separately by class Authenticate.

This class shall be static, so that the Application does not need to instantiate it.

Implementation Note: currently we are using the HSQLDB relational database engine, which does not accommodate multiple simultaneous access to a database; therefore, this Service shall open, query, and re-close the database as quickly and efficiently as possible.


III. The PushHTMLTemplate Service API

The purpose of the PushHTMLTemplate class shall be to provide a simple API by which the Application can push an HTML 'template' file to the client, such that the Application's values for each TagItem automatically are inserted into the template before push.

This class shall be static, so that the Application does not need to instantiate it.

The auxiliary class utils/TagItem shall be instantiated by the Application for each individual TagItem needed for the particular HTML template. The class TagItem is a data structure consisting of two data items, as follows:

private String tagname; // an HTML 'custom tag', e.g., "[-SourcesCount-]"

private String content; // the replacement text for HTML output.

The protocol of message-passing for use of this class is illustrated in Figure 5.



Figure 5. Service class /utils/PushHTMLTemplate, message-passing protocol.


IV. The NotifyByEmail Service API


Appendix A. Guidelines for Implementation of class Authenticate

Note: this section is not part of the API; instead, it has to do with one possible design of the internal implementation of class Authenticate. Whatever internal design is used, however, this should have no effect on the API by which Applications interact with the Service.

To check whether a sessionID is currently valid for the Application, the Service may maintain a HashTable (of some type) for each Application that is currently registered, where each item in the HashTable is an object containing: the sessionID; the I.P. address from which the user logged-in for the current session; the I.P. address of the user's most-recent data request in the current session; the date/timestamp when the user logged-in for the current session; the date/timestamp of the user's most-recent data request in the current session; and possibly some additional information (see, Figure 6). The Service may use the sessionID as the HashTable key for finding the object.



Figure 6. Proposed internals of class /utils/Authenticate.