The Util Module

From MircWiki
Jump to navigation Jump to search

The Util module (util.jar) includes the embedded server, common servlets, and a collection of utility classes used in CTP, MIRC2 (TFS), Geneva, FileSender and other MIRC tools. This article describes the theory of operation of the packages contained in the Util module. The intended audience for this article is software engineers extending or maintaining CTP and TFS or any of the other MIRC software that uses it.

See Setting Up a MIRC Development Environment for information on obtaining and building the Util module.

The Util module contains five packages:

  • org.rsna.server contains an embedded servlet container, with user authentication and servlet mapping from requested resource paths.
  • org.rsna.multipart provides built-in support for multipart forms.
  • org.rsna.servlets contains the Servlet base class and servlets that are used for authentication and system management.
  • org.rsna.service contains code that simplifies the implemention of HTTP services.
  • org.rsna.util contains utility classes.

Each of these packages will be described in the sections below, with example code where necessary to illustrate the theory of operation. It will be helpful to get the source code and build it so you can reference the Javadocs as you go along.

1 org.rsna.server

The embedded servlet container is implemented in the in the HttpServer class. The implementation is not W3C-compliant; it is designed to minimize the need for configuration outside the application program within which it is embedded.

The HttpServer constructor requires a boolean indicating whether the server is to operate in SSL, the port on which the server is to listen, and an instance of the ServetSelector class.

The ServletSelector provides a mapping between requested resource paths and servlets. It also defines the root of the server file tree for served files. Servlets are added into the ServetSelector programmatically. Adding servlets can be done while the server is running.

When the server receives a connection, it creates an instance of the HttpHandler class to service the request. The instance is managed by an instance of java.util.concurrent.ExecutorService set to handle a maximum of 20 concurrent threads. When the HttpHandler is instantiated, it creates instances of the HttpRequest and HttpResponse classes. It then calls the ServletSelector to obtain an instance of the servlet that matches the requested resource. If no servlet matches the request, the ServletSelector returns the Servlet base class, which acts as a web server. The HttpHandler then calls the servlet's method specified in the HttpRequest.

The HttpRequest parses the request and calls the singleton Authenticator class to authenticate the user.

The Authenticator supports three methods of authentication:

  • If the RSNASESSION cookie is contained in the request, it is used as an index into a session table. If an entry exists for the value of the RSNASESSION cookie, the IP ddress of the session is compared to that of the connection, and the time of the connection is compared to the last connection time for the session. The IP addresses must match, and the time interval must be less than the timeout; otherwise, the authentication fails.
  • If the user cannot be authenticated by the RSNASESSION cookie, the Authorization header is used to obtain credentials via basic authorization.
  • If the user cannot be authenticated by the Authorization header, the RSNA header is used to obtain credentials. This header is supported only for backward compatibility with obsolete MIRC software. No current MIRC software uses the RSNA header to pass credentials.

When authenticating credentials, the Authenticator calls the Users implementation to do the actual authentication. The Users class is the base class for classes that represent users. There are two Users class implementations:

  • org.rsna.server.UsersXmlFileImpl represents users in an XML file (users.xml).
  • org.rsna.server.UsersLdapFileImpl represents users in an XML file (users.xml), but uses an external LDAP server to authenticate the users' credentials.

In both Users implementations, the user's roles are represented in the XML file.

1.1 Example Server Startup

This is an example of how to instantiate an HttpServer. It is loosely based on the way CTP starts the server, and it uses a singleton Configuration class to access the configured values for the Users implementation, the SSL boolean, and the server port.

//Get the configuration
Configuration config = Configuration.getInstance();

//Instantiate the singleton Users class
Users users = Users.getInstance(config.getUsersClassName(), config.getServerElement());

//Create the ServletSelector for the HttpServer
ServletSelector selector =
	new ServletSelector(
		new File("ROOT"),
		config.getRequireAuthentication());

//Add in the servlets
selector.addServlet("login", LoginServlet.class);
selector.addServlet("users", UserManagerServlet.class);
//etc.

//Instantiate the server.
int port = config.getServerPort();
boolean ssl = config.getServerSSL();
HttpServer httpServer = null;
try { httpServer = new HttpServer(ssl, port, selector); }
catch (Exception ex) {
	logger.error("Unable to instantiate the HTTP Server on port "+port, ex);
	System.exit(0);
}

1.2 Example Server Shutdown

If the application has servlets that must be shut down gracefully, the server's shutdown method should be called before the application exits. This causes the server to stop the ExecutorService's processing of HttpHandler requests and to call the ServletSelector's shutdown method, which in turn causes it to call each servlet's destroy method.

//Stop the server
httpServer.shutdown();

2 org.rsna.multipart

The multipart package provides support for multipart forms. The basic model is:

  • Test the content type of the HttpRequest.
  • Call the getParts method of the HttpRequest object.
  • Access the parameters and files.

2.1 Example Multipart Parsing

This example demonstrates how to obtain the parts of a multipart form.

String ct = req.getContentType().toLowerCase();
if (ct.contains("multipart/form-data")) {

	//Make a directory to receive any uploaded files
	File dir = new File("dirName");
	dir.mkdirs();

	//Get the parameter parts and the posted files
	int maxsize = 75 * 1024 * 1024; //max allowed upload in bytes
	LinkedList<UploadedFile> files = req.getParts(dir, maxsize);

	//At this point, any parameter parts are available through the
	//getParameter methods of the HttpRequest object.
	String param = req.getParameter("paramName", "defaultValue");

	//The file parts are obtained from the List object
	for (UploadedFile uFile : files) {
		File file = uFile.getFile();
		//do something with the file
	}
}

3 org.rsna.servlets

The servlets package includes the Servlet base class and an assortment of subclasses that are useful in system administration.

The Servlet base class is the parent class of all servlets.

When a servlet is added to the ServletSelector, the ServletSelector loads the class and calls the static init method, supplying the context and the server's root directory. This provides an opportunity for the servlet to initialize its environment before it might be called to service a request. This occurs only once for each servlet. The init method of the base class attempts to install an index.html file if one does not exist and an example-index.html file is found in its context directory (ROOT/{context}). This is done to allow modifications to the index.html file to be preserved during an upgrade. (This feature was intended to allow CTP sites to configure their home pages, but in practice, nobody does it.) When writing a servlet, the attempt to install an index.html file can be suppressed as shown in this example:

public static void init(File root, String context) { }

When the ServletSelector is called by the HttpHandler to find the appropriate servlet to handle a request, the ServletSelector uses the first path element in the request path to identify the servlet. That element is defined as the context of the servlet. It then instantiates the servlet's class by calling its constructor, providing the context and the root directory of the server.

In certain situations, it is necessary to switch the servlet's root so that it points not to the root of the server but to some other directory. In TFS, for example, the entire teaching file site's root is defined by an attribute of the MIRC plugin. This allows the server to be installed on one drive while all the teaching file data is served from another. This remapping is done in the constructor as shown in this example:

public PreferencesServlet(File root, String context) {
	super(root, context);
	//Override the root supplied by the HttpServer with the root of the MIRC plugin.
	this.root = MircConfig.getInstance().getRootDirectory();
}

The base class provides a doGet method that serves files from its context, a doPost method that returns the HTTP NotFound response code, and a destroy method that does nothing.

The doGet method of the base class works a little differently from the equivalent method in a normal servlet container.

  1. When it receives a request, it tries to find the file using the request path as a relative path from the servlet's root directory.
  2. If the file exists in that location, it serves the file from there.
  3. If the file does not exist in the root, it looks for the file in the Cache (see the Cache class description in the org.rsna.util package description).
  4. If the file exists in the Cache, it serves it from there.
  5. If the file does not exist in the Cache, it looks for the file on the Classpath.
  6. If the file exists on the Classpath, it copies the file to the Cache and serves it from there.
  7. If it can't find the file in any of the places above, it returns the HTTP NotFound response code.

The purpose of this design is to allow standard files to be provided in releases without overwriting modified versions that may have been created by individual sites. This feature is used by many sites, especially for templates in TFS and for scripts in CTP.

4 org.rsna.service

The service package includes the Service interface and the HttpService class. The HttpService opens a ServerSocket and listens for connections, passing the connections to a class that implements the Service interface. This package is used in CTP to implement the HttpImportService and DatabaseExportService pipeline stages and the Redirector plugin.

5 org.rsna.util

The util package provides a set of utility classes. The Javadocs provide descriptions of the individual methods provided. There are numerous examples of the use of these methods throughout the CTP and MIRC2 code. Here are a few top-level comments on the classes themselves:

AcceptAllHostnameVerifier
Used in SSL where no authentication of the host is required.
AcceptAllX509TrustManager
Used in SSL where no authentication of certificates is required.
Base64
A Base64 encoder/decoder.
Cache
This singleton class implements a cache that is accessed by the doGet method of the Servlet base class for serving files. It is also used by some methods of the FileUtil class.
CipherUtil
Used to decipher encrypted elements in DicomObjects.
ClasspathUtil
Used to control the classpath programmatically.
ClientHttpRequest
Used to POST an HTTP multipart form with cookies, parameters, and files.
DateUtil
Converts DICOM date strings into GregorianCalendars and DICOM time strings into a standard form.
DigestUtil
Computes hashes of strings.
FileUtil
A large collection of useful file handling methods.
HtmlUtil
Three methods for inserting close boxes into HTML pages.
HttpUtil
Methods for getting HttpURLConnections, using SSL if defined by the URL, and employing a ProxyServer if configured.
IPUtil
A method for obtaining the IP address of the local computer.
JarClassLoader
A specialized classloader that was used by CTP before the ClasspathUtil replaced it. This class is deprecated, but I had so much fun writing it that I can't bring myself to remove it.
JarUtil
A method for getting the attributes from a Jar file as a Hashtable.
JdbmUtil
Methods for accessing JDBM databases. This class is used throughout CTP and MIRC2 for many databases.
LdapUtil
A method for authenticating a user against an external LDAP server.
ProxyServer
A class to encapsulate the information required to access a network via a proxy server, setting up the necessary Java system parameters.
SerializerUtil
A set of methods to serialize and deserialize Java objects.
SSLConfiguration
A class to encapsulate the parameters necessary for SSL, including the keystore and truststore, and to set the neccessary Java system parameters.
StringUtil
A collection of useful string methods.
XmlUtil
A collection of useful XML and XSL methods.

6 Files

The util.jar file also contains a collection of resource files that are used by the servlets. They are in the root of the jar, so their context is /.

JSAJAX.js
A multi-browser AJAX object.
JSCookies
Provides access to cookies.
JSLoginPopup.js
Provides a popup for logging into the system.
JSMD5.js
Computes an MD5 hash.
JSMenu.js, JSMenu.css
Support for pull-down menus.
JSPage.js, JSPage.css
Functions used to insert page headers.
JSPopup.js, JSPopup.css
Functions to display popups.
JSScrollableTable.js
A table that scrolls the body but not the head.
JSSplitPane.js, JSSplitPane.css
A resizable split pane.
JSTableSort.js, JSTableSort.css
A function to sort a table on a column.
JSTree.js, JSTree.css
An expandable tree.
JSUser.js
A function to get the user's roles from the server.
JSUtil.js
Miscellaneous functions for finding objects on a page.

In addition, there is an icons directory containing icon images that are used on various servlet pages. The directory is in the root of the jar, so the context of the files it contains is '/icons/'.

These files are served by the Servlet base class from the jar file (via the Cache) when required. They are not installed.