Othello Servlet Container - Java™-like servlets for mono and dotNet™

Manfred Hein

The Othello Servlet Container (short Othello [OTHELLO]) provides Java™ servlet functionality [SUNSERVLET] for mono [MONO]/dotNet™[DOTNET] compatible platforms.

Servlets are basically classes that implement a certain interface (the servlet interface) allowing a class to be executed in a servlet container. The servlet container itself provides several functions like session handling, a proper interface for accessing http-headers and -parameters, load balancing, etc.

The technology is proven and well known in the context of the Java programming language. It is also part of Sun™'s J2EE-Specification [SUNJ2EE]. J2EE defines several standards for servlet-/application- server technology. In comparison to Microsoft™'s approach (aka. asp.Net™), it gives the possibility to properly separate web servers and web application execution environments.


Table of Contents
1. Introduction
1.1. History and predecessors
1.2. Structure of the software
2. Installation
2.1. Core Components
2.2. Optional Components
2.3. Compiling the sources
2.4. Core Installation
2.5. Webserver integration
2.5.1. Integration into Apache 1.3
2.5.2. Integration into Apache 2
2.5.3. Integration into IIS™
3. Using Othello
3.1. Test the setup
3.2. Othello Parameters
3.3. Writing a servlet
3.3.1. The NAnt build file
3.3.2. The servlet configuration
3.3.3. The servlet code
3.4. Using the control service console
References

1. Introduction

Please note that this is a first version of the documentation for the Othello Servlet Container. Thus, it is currently quite beta. It will be enhanced frequently, but it should be sufficient to get a head start.


1.1. History and predecessors

Servlets are pretty well known in the Java world. Sun™ introduced the Java Servlet Development Kit (Java SDK [SUNSERVLET]) in 1997. The Servlet definition has been enhanced several times since then. The current version is 2.4. The SDK contains interfaces for all basic items that are needed to write web applications:

  • Servlet: Every servlet has to implement the methods defined in this class to initialise and destruct a servlet instance. Furthermore it provides methods for request handling.

  • ServletConfig: Configuration information for a servlet implementation on startup.

  • ServletRequest: Covers all functions to read information from an incoming request (headers, cookies, request parameters)

  • ServletResponse: Contains methods for constructing a response towards the client (setting status codes, writing output data as stream to the client, etc.)

  • Session: Methods to handle session data (writing and reading session-related information).

  • Cookie: Wrapping cookie information for session handling

and a bunch of Listeners and Events for reacting on startup,shutdown and status changes of a servlet and its components.

The sum of the interfaces and classes in the SDK provide a powerful set of functions that are triggered by a servlet-container on certain events, e.g. initialisiation, incoming requests, session handling, etc. As a result, the servlet implementation can react on these events to cover the functionality that the web application should provide.

The Othello Servlet Container is based upon the Winstone Servlet Engine [WINSTONE], a lean and fast Java-based servlet container by Rick Knowles.


1.2. Structure of the software

The exchange of information between a servlet container and a servlet requires a structured way of accessing the servlet's methods and the servlet engine services: A Servlet API.

As a result, the Othello servlet engine contains out of two basic parts:

  • The servlet API (Mono Servlet API)

  • and the servlet container (Othello Servlet Container)

As a matter of fact, your servlet must use the same Servlet API library as the servlet container. Thus, the Othello Servlet Container and a servlet library that should run in the container must always implement the same version of the MonoServletAPI.dll.

Please note that Othello and it's components are tested with mono only, since it does not contain proprietary components and is published under open source licences. In addition, the Mono Servlet API does not comply fully to Sun's SDK v.2.4, since there are a few structural differences between cSharp and Java.


2. Installation

You can decide, wether you want to compile the Othello sources yourself or use the binary distribution. The Othello distribution always contains both. The binaries can be found in the dist subdirectory while the sources are located under src.


2.1. Core Components

If you want to run the binary Othello distribution, you need the following software packages:

  • mono, version 1.1.8 or later, please grab a copy at [MONO]

  • An AJP v1.3 capable web server (currently Apache 1.3, Apache 2 are tested, Microsoft's IIS should work without problems, but is currently not tested). For using Apache, please download a copy at [APACHE]

  • An AJP v1.3 interface module installed with your web server [AJP13]


2.2. Optional Components

If you want to compile the Othello sources, make sure you have the following packages additonally:

  • nAnt, version 0.85 or later, the .NET build tool [NANT]

  • docbook-tools used for building html-documentation, grab a copy at [DOCBOOKTOOLS]

If you use the Debian GNU/Linux distribution, you should find all the modules as Debian packages.


2.3. Compiling the sources

The NAnt build files *.build are located in the top of the source tree under mono-servlet-api, othello-container/Othello and othello-container/Testservlet.

Please run the mono <PATH_TO_NANT>/bin/NAnt.exe build command within the mono-servlet-api directory first. It will build the MonoServletAPI.dll in the dist-directory. This library contains the Servlet-API for mono. Copy the dll-file into the othello-container/Othello/lib/ and othello-container/TestServlet/lib/-directories to make the library accesible for the next compile stages.

Afterwards, run mono <PATH_TO_NANT>/bin/NAnt.exe build in othello-container/Othello/ to build the Othello Servlet Container and in othello-container/TestServlet/ to build the TestServlet.


2.4. Core Installation

Install mono and the web server including an AJP13 module as described in the referring documentation.


2.5. Webserver integration

To get Othello integrated with your web server, you need to configure AJP13 in order to let the web server talk to the Othello servlet container. Due to a lack of access to Windows™ machines, we only describe the Apache integration here. Help with testing on Microsoft is highly appreciated.

The following configuration examples configure the AJP-connector to connect to localhost on port 8009. This is the default configuration for most of the servlet containers.


2.5.1. Integration into Apache 1.3

First, add

LoadModule jk_module /usr/lib/apache/1.3/mod_jk.so
to your /etc/apache/modules.conf (if not done already by the debian package).

The /etc/apache/workers.properties file should look like this:

worker.list=worker1

# Set properties for worker1 (ajp13)
 worker.worker1.type=ajp13
 worker.worker1.host=localhost
 worker.worker1.port=8009
 worker.worker1.lbfactor=50
 worker.worker1.cachesize=10
 worker.worker1.cache_timeout=600
 worker.worker1.socket_keepalive=1
 worker.worker1.socket_timeout=300
The /etc/apache/conf.d/jk.conf file should look like this:
# workers.properties file: where httpd.conf is
JkWorkersFile /etc/apache/workers.properties

# Where to put jk logs
JkLogFile /var/log/apache/jk.log

# Set the jk log level [debug/error/info]
JkLogLevel info

# Select the log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "

# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories

# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"

# Send everything for context /examples to worker named worker1 (ajp13)
JkMount /test/* worker1


2.5.2. Integration into Apache 2

The /etc/apache2/workers2.properties file should look like:

[lb:lb]
info=Default load balancer.
debug=0

[channel.socket:localhost:8009]
info=Ajp13 forwarding over socket
debug=0
tomcatId=localhost:8009

[status:]
info=Status worker, displays runtime informations

[uri:/jkstatus/*]
info=Display status information and checks the config file for changes.
group=status:

[uri:/test/*]
info=Map the whole webapp
	  

The /etc/apache2/mods-enabled/jk2.conf file:

# To enable mod_jk2, customize workers2.properties* from
# /usr/share/doc/libapache2-mod-jk2/examples and copy it to
# /etc/apache2/workers2.properties. Then uncomment the following line:
JkSet config.file /etc/apache2/workers2.properties
	  


3. Using Othello

To start Othello, unpack the binary distribution or compile the Othello and TestServlet sources.

Change into into the Othello distribution directory (othello-container/Othello/dist) and run mono ./Othello.exe --ajp13Port=8009 --controlPort=9000 --peacefile=../../TestServlet/dist/TestServlet.dll

This line actually runs Othello, opens an ajp13 connector on port 8009 and a control port connector on port 9000. Furthermore the TestServlet.dll is exposed via the servlet container and will be accessible via the web server.


3.1. Test the setup

If you have the ajp13 configuration in place and Othello is active, you will be able to connect to the web application by using your browser. Point your browser to: http://localhost/test/index.html. You should see a HTML-test page with two test forms that trigger the Test Servlet. The TestServlet will show its result based on the incoming requests.


3.2. Othello Parameters

If you start Othello with an empty parameter list or with the parameters --help or --usage, you'll get a list of provided options.

Currently Othello provides the following set of options:

--peacefile              = set location of peace file to get servlets(s) from.

Other options:
--logfile                = redirect othello log messages to this file
--debug                  = set the level of debug msgs (1-9). Default is 5 (INFO level)

--ajp13Port              = set the ajp13 listening port. -1 to disable, Default is 8009
--controlPort            = set the shutdown/control port. -1 to disable, Default disabled

--handlerCountStartup    = set the no of worker threads to spawn at startup. Default is 5
--handlerCountMax        = set the max no of worker threads to allow. Default is 300
--handlerCountMaxIdle    = set the max no of idle worker threads to allow. Default is 50

--directoryListings      = enable directory lists (true/false). Default is true
--usage / --help         = show this message
	


3.3. Writing a servlet

We would like to show a simple example based on the test servlet, that comes with Othello. You need a mono compiler and the NAnt build tool.


3.3.1. The NAnt build file

Let's have a look at the NAnt build file:

<?xml version="1.0"?>
 <project name="TestServlet" default="build" basedir=".">
    <description>Building a TestServlet for Othello Servlet Container</description>
    <property name="debug" value="false" overwrite="false" />
    <property name="dist" value="dist"/>

    <target name="clean" description="remove dist">
        <delete dir="${dist}" failonerror="false" />
    </target>

    <target name="build" description="compiles the source code">
	<mkdir dir="${dist}" failonerror="false"/>

        <csc target="library" output="${dist}/TestServlet.dll" debug="${debug}">
            <sources basedir="src">
                <include name="*.cs" />
            </sources>
	    <references>
	        <include name="lib/*.dll" />
	    </references>
	    <resources>
	        <include name="etc/*" />
	    </resources>
        </csc>
    </target>
</project>
This build file has two different tasks: clean to clean up any files that have been generated during the compilation and build to build the servlet library. Please note that Othello currently only works with mono-/dotNetlibraries (dlls). The generated dll will contain the compiled cSharp files, the libraries used for the web application (located in subfolder lib) and resources (e.g. .html files, located in subfoler etc).


3.3.2. The servlet configuration

As known for Java servlets, each servlet needs a configuration. This configuration is read by the servlet container to determine how to expose the servlet. This file is called web.xml and needs to be added add resource to a peace file. Let's have a look at the servlet configuration for our Test Servlet:

 <?xml version="1.0" encoding="ISO-8859-1"?>
 <web-app>
  <context-param>
    <param-name>ContextParam</param-name> 
    <param-value>An example context parameter</param-value> 
  </context-param>
  <servlet>
	<servlet-name>TestServlet</servlet-name>
	<servlet-class>TestServlet</servlet-class> 
	<load-on-startup>1</load-on-startup>
  </servlet>
  <welcome-file-list>
	<welcome-file>welcome.html</welcome-file>
	<welcome-file>index.html</welcome-file>
  </welcome-file-list>

  <servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/test/TestServlet</url-pattern>
  </servlet-mapping>
</web-app>
			
The first section of the xml-file below web-app is context-param. This element contains a name/value pair with information that is passed to a servlet on initialisation as parameter in the context.

The next section servlet configures the servlet itself. It contains the name of the servlet and the name of the serlvet-class. The servlet class definition needs to be configured with full namespace. Furthermore, it is optionally possible to define parameters that are passed to the servlet on initialisation (init-param). Additionally, the parameter load-on-startup defines, wether the servlet should be started when the servlet container starts or on first request towards the servlet.

The next major section welcome-file-list defines, which files the servlet container should check for in the servlet resources and display, even only the servlet path is given. The order of the welcome-file- list matters.

The most important section in the servlet configuration is servlet-mapping. It maps the URL that is exposed by the servlet container to the servlet. In this example, we map test/TestServlet on TestServlet.


3.3.3. The servlet code

Let's now have a look at a typical simple servlet. First of all: Every servlet has to extend the class HttpServlet and needs to implement its abstract methods: Init (called on servlet initialisation),Destroy (called on servlet shutdown),DoGet and DoPost (called when a request comes in from a client). DoGet and DoPost have two parameters of type HttpServletRequest and HttpServletResponse.

These parameters contain the incoming information from the client (Request) while the response to the client is stored in the Response parameter.

In this example, we simply print out some of the information that we get on the initialisation of the servlet into the Othello standard log channel (method Init). On a request, we write some of the information that we received from the request into the Servlet Output Stream and therewith to the client. Since most of the clients are HTML-enabled (web browsers), the output is contructed and returned as HTML (methods DoGet and DoPost).

using MonoServletAPI;

using System; 
using System.Collections;
using MonoServletAPI.Http;

public class TestServlet:HttpServlet {
	public override void Init() {		
		// print servlet name, context and init parameters
		Console.WriteLine("ServletName:"+this.config.GetServletName());
		Console.WriteLine("ServletName:"+this.config.GetServletContext());
		Console.WriteLine("InitParameters");
		IEnumerator it=this.config.GetInitParameterNames();
		while(it.MoveNext()) {
			string param=(string)it.Current;
			Console.WriteLine(param+"="+this.config.GetInitParameter(param));
		}
	}
	
	public override void Destroy() {
		Console.WriteLine("DESTROY CALLED");
	}
	
	public override void DoGet(HttpServletRequest req, HttpServletResponse res) {
		DoPost(req,res); // get and post shall behave the same way, just pass it over
	}
	
	public override void DoPost(HttpServletRequest req, HttpServletResponse res) {
		// get output stream and set content type
		ServletOutputStream s=res.GetOutputStream();
		res.SetContentType("text/html");
	
		// some general request information	
		s.WriteLine("<h1>Some output from test servlet...</h1>");
		s.WriteLine("<br><b>GetProtocol: </b>"+req.GetProtocol());
		s.WriteLine("<br><b>QueryString: </b>"+req.GetQueryString());
		s.WriteLine("<br><b>RemoteAddr: </b>"+req.GetRemoteAddr());
		s.WriteLine("<br><b>RemoteHost: </b>"+req.GetRemoteHost());
		s.WriteLine("<br><b>RequestURI: </b>"+req.GetRequestURI());
		s.WriteLine("<br><b>RequestURL: </b>"+req.GetRequestURL());
		s.WriteLine("<br><b>Session: </b>"+req.GetSession());
		s.WriteLine("<br><b>RequestedSessionId: </b>"+req.GetRequestedSessionId());
		
		// print the parameters given from the request
		s.WriteLine("<p>Parameters<br>");
		it=req.GetParameterNames();
		while(it.MoveNext()) {
			string paramName=(string)it.Current;
			s.WriteLine("<b>"+paramName+"</b>="+req.GetParameter(paramName)+"<br>");	
		}

		// print the cookies, if any
		s.WriteLine("<p>Cookies<br>");
		Cookie[] cookies=req.GetCookies();
		for(int i=0;i<cookies.Length;i++) {
			s.WriteLine(
			"Name="+cookies[i].GetName()+
			", Value="+cookies[i].GetValue()+
			", Path="+cookies[i].GetPath()+
			", Secure="+cookies[i].GetSecure()+ 
			", Type="+cookies[i].GetType()+
			", Version="+cookies[i].GetVersion()+
			", Comment="+cookies[i].GetComment()+
			", Domain="+cookies[i].GetDomain()+
			", HashCode="+cookies[i].GetHashCode()+
			", MaxAge="+cookies[i].GetMaxAge()+
			"<br>");
		}
		s.Close();
	}
}
If you compile the servlet using the build file, you will create a dll that can be used with Othello as described above that we might call peace file.


3.4. Using the control service console

If you added a control port on start of Othello, you will be able to connect the Othello control service console, e.g. via telnet.

$ telnet localhost 9000
Trying 127.0.0.1...
Connected to localhost.localdomain.
Escape character is '^]'.
Othello Servlet Container- Control Service Help
h=display this help
s=stop servlet container
r=reload and activate peacefile
Currently, the three options 'h', 's' and 'r' are implemented, that you can call by just pressing the reverring key (be careful with the 's'-button!). The reload functionality is pretty useful, if you built a newer version of your peace file and want to activate it.


References

[OTHELLO] http://sourceforge.net/projects/othelloservlet/: Othello homepage at sourceforge.net.

[WINSTONE] http://sourceforge.net/projects/winstone/: Winstone homepage at sourceforge.net.

[MONO] http://www.mono-project.com: The mono project homepage.

[SERVLET] http://java.sun.com/products/servlet/: Java Servlet Technology Homepage.

[J2EE] http://java.sun.com/j2ee/: Java 2 Platform, Enterprise Edition (J2EE).

[DOTNET] http://www.microsoft.com/net/: Microsoft .net.

[APACHE] http://httpd.apache.org: The Apache Webserver Homepage.

[AJP13] http://jakarta.apache.org/tomcat/connectors-doc/: AJP13 documentation and download area at Apache Jakarta.

[NANT] http://nant.sourceforge.net: NAnt homepage.

[DOCBOOKTOOLS] http://sources.redhat.com/docbook-tools/: DocBook tools homepage.