CONTENTS

Chapter 18. mod_jserv and Tomcat

Since the advent of the Servlets API, Java developers have been able to work behind a web server interface. For reasons of price, convenience, and ready availability, Apache has long been a popular choice for Java developers, holding its own in a programming world otherwise largely dominated by commercial tools.

The Apache-approved method for adding Java support to Apache is to use Tomcat. This is an open source version of the Java servlet engine that installs itself into Apache. The interpreter is always available, without being loaded at each call, to run your scripts. The old way to run Java with Apache was via JServ — which is now (again, in theory) obsolete on its own. JServ and Tomcat are both Java applications that talk to Apache via an Apache module (mod_jserv for JServ and mod_jk for Tomcat), using a socket to get from Apache to the JVM.

In practice, we had considerable difficulty with Tomcat. Since mod_jserv is still maintained and is not (all that) difficult to install, Java enthusiasts might like to try it. We will describe JServ first and then Tomcat. For more on Servlet development in general, see Jason Hunter's Java Servlet Programming (O'Reilly, 2001).

18.1 mod_jserv

figs/win32.gif

Windows users should get the self-installing .exe distribution from http://java.apache.org/.

figs/unix.gif

Download the gzipped tar file from http://java.apache.org/, and unpack it in a suitable place — we put it in /usr/src/mod_jserv.

The READMEfile says:

Apache JServ is a 100% pure Java servlet engine designed to implement the Sun Java Servlet API 2.0 specifications and add Java Servlet capabilities to the Apache HTTP Server.

For this installation to work, you must have:

Apache 1.3.9 or later.

But not Apache v2, which does not support mod_jserv.

A fully compliant Java 1.1 Runtime Environment

We decided to install the full Java Development Kit (which we needed anyway for Tomcat — see later on). We went to the FreeBSD site and downloaded the 1.1.8 JDK from ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/local-distfiles/nate/JDK1.1/jdk1.1.8_ELF.V1999-11-9.tar.gz.

If you are adventurous, 1.2 is available from http://www.freebsd.org/java/dists/12.html. When you have it, see Section 18.2.1 for what to do next. If you are using a different operating system from any of those mentioned, you will have to find the necessary package for yourself.

The Java servlet development kit (JSDK)

A range of versions is available at http://java.sun.com/products/servlet/download.html. As is usual with anything to do with Java, a certain amount of confusion is evident. The words "Java Servlet Development Kit" or "JSDK" are hard to find on this page, and when found they seem to refer to the very oldest versions rather than the newer ones that are called "Java Servlet." However, we felt that older is probably better in the fast-moving but erratic world of Java, and we downloaded v2.0 from http://java.sun.com/products/servlet/archive.html. This offered both Windows and "Unix (Solaris and others)" code, with the reassuring note: "The Unix download is labeled as being for Solaris but contains no Solaris specific code." The tar file arrived with a .Z extension, signifying that it needs to be expanded with the Unix utility uncompress. There is a FreeBSD JSDK available atftp://ftp.FreeBSD.org/pub/FreeBSD/branches/-current/ports/java/jsdk.tar.

A Java Compiler

If you downloaded the Runtime Environment listed earlier, rather than the JDK, you will also need a compiler — either Sun's Javac (see web site listed earlier) or the faster Jikes compiler from IBM at http://www.alphaworks.ibm.com/tech/jikes.

An ANSI-C compiler

If you have already downloaded the Apache source and compiled it successfully, you must have this component. But there is a hidden joke in that mod_jserv will not be happy with any old make utility. It must and will have a GNU make from ftp://ftp.gnu.org/gnu/make/. See the next section.

18.1.1 Making gmake

mod_jserv uses GNU make, which is incompatible with all other known makes. So, you may need to get (from http://www.gnu.org/software/make/make.html) and build GNU make before starting. If you do, here's how we did it.

Since you probably already have a perfectly good make, you don't want the new one to get mixed up with it. Just for safety's sake, you might want to back up your real make before you start.

Create a directory for the sources as usual, unpack them, and make gmake (cunningly not called make) with the commands:

./configure --program-prefix=g
make
make install

You should end up with /usr/local/bin/gmake.

18.1.2 Building JServ

Having created gmake, move to the mod_jserv source directory. Before you start, you need to have compiled Apache so that JServ can pass its configure checks. If you have got this far in the book, you probably will already have compiled Apache once or twice, but if not — now is a good time to start. Go to Chapter 1.

You then need to decide whether you want to build it into the Apache executable (recommended) or prepare it as a DSO. We took the first route and configured mod_jserv with this:

MAKE=/usr/local/bin/gmake ./configure --prefix=/usr/local --with-apache-src=/usr/src/
apache/apache_1.3.19 --with-jdk-home=/usr/src/java/jdk1.1.8 --with-JSDK=/usr/src/
jsdk/JSDK2.0/lib

Your paths in general will be different. --prefix invokes the location where you want the JServ bits to be put. Rather perversely, they appear in the subdirectory .../etc below the directory you specify. You might also think that you were required to put /src on the end of the Apache path, but you're not. If the process fails for any reason, take care to delete the file config.cache before you try again. You might want to write the necessary commands as a script since it is unlikely to work at the first attempt:

rm config.cache
MAKE=/usr/local/bin/gmake ./configure --prefix=/usr/local/bin --with-apache-src=/usr/src/
apache/apache_1.3.19 --with-jdk-home=/usr/src/java/jdk1.1.8 --with-JSDK=/usr/src/
jsdk/JSDK2.0/lib > log

If you use mod_ssl, you should add --enable-EAPI. The script's voluminous comments will appear in the file log; error messages will go the screen. Any mistakes in this script can produce rather puzzling error messages. For instance, on our first attempt we misspelled --with-JSDK as --with-JDSK. The error message was:

checking JSDK ... configure: error: Does not exist:
    '/usr/local/JSDK2.0

which was true enough. Yet it required a tour through the Configure file to realize that the script had failed to match --with-JDSK, said nothing about it, and had then gone to its default location for JSDK.

When ./configure has done its numerous things, it prints some sage advice on what to do next, which would normally disappear off the top of the screen, but which you will find at the bottom of the log file:

+-STEP 1-------------------------------------------------------+
|Run 'make; make install' to make a .jar file, compile the C   |
|code and copy the appropriate files to the appropriate        |
|locations.                                                    |
+--------------------------------------------------------------+

+-STEP 2-------------------------------------------------------+
|Then cd /usr/src/apache/apache_1.3.19 and run 'make; make install'
+--------------------------------------------------------------+

+-STEP 3-------------------------------------------------------+
|Put this line somewhere in Apache's httpd.conf file:          |
|Include /usr/src/jserv/ApacheJServ-1.1.2/etc/jserv.conf
|                                                              |
|Then start Apache and try visiting the URL:                   |
|http://my586.my.domain:SERVER_PORT/servlets/Hello
|                                                              |
|If that works then you have successfully setup Apache JServ.  |
|                                                              |
|If that does not work then you should read the                |
|troubleshooting notes referenced below.                       |
+--------------------------------------------------------------+

+-Troubleshooting----------------------------------------------+
|Html documentation is available in the docs directory.        |
|                                                              |
|Common Errors:                                                |
|    Make sure that the log files can be written to by the     |
|    user your httpd is running as (ie: nobody). If there are  |
|    errors in your configuration, they will be logged there.  |
|                                                              |
|Frequently asked questions are answered in the FAQ-O-Matic:   |
|                                                              |
|           http://java.apache.org/faq/                        |
+--------------------------------------------------------------+

You should carry on with:

gmake

Then:

gmake install

Now go to /usr/src/apache/apache_1.3.19 (or whatever your path is to the Apache sources). Do not go down to the src subdirectory as we did originally. Then:

./configure --activate-module=src/modules/jserv/libjserv.a
make
make install

We saw some complaints from make. This time the comments are output to stderr. You can capture them with:

 make install &> log2. 

The comments end with:

+--------------------------------------------------------+
| You now have successfully built and installed the      |
| Apache 1.3 HTTP server. To verify that Apache actually |
| works correctly you now should first check the         |
| (initially created or preserved) configuration files   |
|                                                        |
|   /usr/local/etc/httpd/httpd.conf
|                                                        |
| and then you should be able to immediately fire up     |
| Apache the first time by running:                      |
|                                                        |
|   /usr/local/sbin/apachectl start
|                                                        |
| Thanks for using Apache.       The Apache Group        |
|                                http://www.apache.org/  |
+--------------------------------------------------------+

This is not very helpful because:

But, in our view, building the executable is hard enough; one shouldn't expect the installation to work as well. The new httpd file is in .../src. Go there and check that everything worked by typing:

./httpd -l

A reference to mod_jserv.c among the "compiled-in modules" would be pleasing. Remember: if you forget ./, you'll likely run the httpd in /usr/local/bin, which probably won't know anything about JServ.) We then copied httpd to /usr/local/sbin/httpd_jserv.

If it is there, you can proceed to test that it all works by setting up site.jserv (a straight copy of site.simple) with this line in the Config file — making sure that the path suits:

Include /usr/local/bin/etc/jserv.conf

Finally, start Apache (as /usr/local/sbin/httpd_jserv), and visit http://www.butterthlies.com/servlets/Hello. You should see something like this:

Example Apache JServ Servlet
Congratulations, ApacheJServ 1.1.2 is working! 

Sadly, the Earth didn't quite move for both of us. Ben's first attempt failed. The problem was that his supplied jserv.conf was not quite set up correctly. The solution was to copy it into our own configuration file and edit it appropriately. The problem we saw was this:

Syntax error on line 43 of /usr/local/jserv/etc/jserv.conf:
ApJServLogFile: file '/home/ben/www3/NONE/logs/mod_jserv.log' can't be opened

We corrected this to be a sensible path, and then Apache started. But attempting to access the sample servlet caused an internal error in Apache. The error log said:

java.io.IOException: Directory not writable: //NONE/logs
    at org.apache.java.io.LogWriter.<init>(LogWriter.java:287)
    at org.apache.java.io.LogWriter.<init>(LogWriter.java:203)
    at org.apache.jserv.JServLog.<init>(JServLog.java:92)
    at org.apache.jserv.JServ.start(JServ.java:233)
    at org.apache.jserv.JServ.main(JServ.java:158)

We had to read the source to figure this one out, but it turned out that /usr/local/jserv/etc/jserv.properties had the line:

log.file=NONE/logs/jserv.log

presumably for the same reason that jserv.conf was wrong. To fix this we took our own copy of the properties file (which is used by the Java part of JServ) and changed the path. To use the new properties file, we had to change its location in our httpd.conf:

ApJServProperties /usr/local/jserv/etc/jserv.properties

This still didn't cure our problems. This time the error appeared in the jserv.log file we've just reconfigured earlier:

[28/04/2001 11:17:48:420 GMT] Error creating classloader for servlet zone root :
java.lang.IllegalArgumentException: Repository //NONE/servlets doesn't exist!

This error relates to a servlet zone, called root — this is defined in jserv.properties by two directives:

zones=root
root.properties=/usr/local/jserv/etc/zone.properties

So now the offending file is zone.properties, which we copied, changed its location in jserv.properties, and corrected:

repositories=NONE/servlets

We changed this to point at the example directory in the source of JServ, which has a precompiled example servlet in it, in our case:

repositories=/home/ben/software/unpacked/ApacheJServ-1.1.2/example

and finally, surfing to the Hello server (http://your.server/servlets/Hello) gave us a well-deserved "congratulations" page.

18.1.3 JServ Directives

JServ has its own Apache directives, which are documented in the jserv.conf file.

figs/win32.gif

To run JServ on Win32, tell Apache to load the Apache JServ communication module with:

...
LoadModule jserv_module modules/ApacheModuleJServ.dll
...

figs/unix.gif

If JServ is to be run as a Shared Object, tell Apache on Unix to load the Apache JServ communication module:

LoadModule jserv_module /usr/local/bin/libexec/mod_jserv.so

It would be sensible to wrap the JServ directives in this:

<IfModule mod_jserv.c>
ApJservManual  

ApJServManual [on/off]
Default: "Off"
 

Whether Apache should start JServ or not (On=Manual Off=Autostart). Somewhat confusingly, you probably want Off, meaning "start JServ." But since this is the default, you can afford to ignore the whole question.

ApJServProperties  

ApJServProperties [filename]
Default: "./conf/jserv.properties"
 

Properties filename for Apache JServ in automatic mode. In manual mode this directive is ignored.

Example

ApJServProperties /usr/local/bin/etc/jserv.properties
ApJServLogFile  

ApJServLogFile [filename] 
Default: "./logs/mod_jserv.log"
 

Log file for this module operation relative to Apache root directory. Set the name of the trace/log file. To avoid possible confusion about the location of this file, an absolute pathname is recommended. This log file is different from the log file that is in the jserv.properties file. This is the log file for the C portion of Apache JServ.

On Unix, this file must have write permissions by the owner of the JVM process. In other words, if you are running Apache JServ in manual mode and Apache is running as user nobody, then the file must have its permissions set so that that user can write to it.

When set to DISABLED, the log will be redirected to Apache error log.

   

   

Example

ApJServLogFile /usr/local/var/httpd/log/mod_jserv.log
ApJServLogFile  

ApJServLogLevel [debug|info|notice|warn|error|crit|alert|emerg]
Default: info    (unless compiled w/ JSERV_DEBUG, in which case it's debug)
 

Log Level for this module.

Example

ApJServLogLevel notice
ApJServDefaultProtocol  

ApJServDefaultProtocol [name]
Default: "ajpv12"
 

Protocol used by this host to connect to Apache JServ. As far as we know, the default is the only possible protocol, so the directive can be ignored. There is a newer version but it only works with mod_jk — see later.

Example

ApJServDefaultProtocol ajpv12
ApJServDefaultHost  

ApJServDefaultHost [hostname]
Default: "localhost"
 

Default host on which Apache JServ is running.

Example

ApJServDefaultHost java.apache.org
ApJServDefaultPort  

ApJServDefaultPort [number]
Default: protocol-dependant (for ajpv12 protocol this is "8007")
 

Default port to which Apache JServ is listening.

Example

ApJServDefaultPort 8007
ApJServVMTimeout  

ApJServVMTimeout [seconds] 
Default: 10 seconds
 

The amount of time to give to the JVM to start up, as well as the amount of time to wait to ping the JVM to see if it is alive. Slow or heavily loaded machines might want to increase this value.

Example

ApJServVMTimeout 10
ApJServProtocolParameter  

ApJServProtocolParameter [name] [parameter] [value]
Default: NONE
 

Passes parameter and value to specified protocol.

Currently no protocols handle this. Introduced for future protocols.

ApJServSecretKey  

ApJServSecretKey [filename]
Default: "./conf/jserv.secret.key"
 

Apache JServ secret key file relative to Apache root directory.

If authentication is DISABLED, everyone on this machine (not just this module) may connect to your servlet engine and execute servlet, bypassing web server restrictions.

   

   

Examples

ApJServSecretKey /usr/local/bin/etc/jserv.secret.key
ApJServSecretKey DISABLED
ApJServMount  

ApJServMount [name] [jserv-url]
Default: NONE
 

Mount point for Servlet zones (see documentation for more information on servlet zones)

[name] is the name of the Apache URI path on which to mount jserv-url. [jserv-url] is something like protocol://host:port/zone. If protocol, host, or port are not specified, the values from ApJServDefaultProtocol, ApJServDefaultHost, or ApJServDefaultPort will be used. If zone is not specified, the zone name will be the first subdirectory of the called servlet. For example:

ApJServMount /servlets /myServlets

If the user requests http://host/servlets/TestServlet, the servlet TestServlet in zone myServlets on the default host through default protocol on default port will be requested. For example:

 ApJServMount /servlets ajpv12://localhost:8007

If the user requests http://host/servlets/myServlets/TestServlet, the servlet TestServlet in zone myServlets will be requested. For example:

ApJServMount /servlets ajpv12://jserv.mydomain.com:15643/myServlets

If the user requests http://host/servlets/TestServlet, the servlet TestServlet in zone myServlets on host jserv.mydomain.com using "ajpv12" protocol on port 15643 will be executed.

ApJServMountCopy  

ApJServMountCopy [on/off]
Default: "On"
 

Whether <VirtualHost> inherits base host mount points or not.

This directive is meaningful only when virtual hosts are being used.

Example

ApJServMountCopy on
ApJServAction  

ApJServAction [extension] [servlet-uri]
Defaults: NONE
 

Executes a servlet passing filename with proper extension in PATH_TRANSLATED property of servlet request.

This is used for external tools.

Examples:

ApJServAction .jsp /servlets/org.gjt.jsp.JSPServlet
ApJServAction .gsp /servlets/com.bitmechanic.gsp.GspServlet
ApJServAction .jhtml /servlets/org.apache.
servlet.ssi.SSI
ApJServAction .xml /servlets/org.apache.cocoon.Cocoon

18.1.4 JServ Status

Enable the Apache JServ status handler with the URL of http://servername/jserv/ (note the trailing slash!). Change the deny directive to restrict access to this status page:

<Location /jserv/>
  SetHandler jserv-status
  order deny,allow
  deny from all
  allow from 127.0.0.1
</Location>

Remember to disable or otherwise protect the execution of the Apache JServ Status Handler on a production environment since this may give untrusted users the ability to obtain restricted information on your servlets and their initialization arguments, such as JDBC passwords and other important information. The Apache JServ Status Handler should be accessible only by system administrators.

18.1.5 Writing a Servlet

Now that we have JServ running, let's add a little servlet to it, just to show how its done. Of course, there's already a simple servlet in the JServ package, the Hello servlet mentioned earlier; the source is in the example directory, so take a look. We wanted to do something just a little more interesting, so here's another servlet called Simple, which shows the parameters passed to it. As always, Java requires plenty of code to make this happen, but there you are:

import java.io.PrintWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpUtils;

public class Simple extends HttpServlet
    {
    public void doGet(HttpServletRequest request,HttpServletResponse response) 
      throws ServletException, IOException
        {
        PrintWriter out;
        String qstring=request.getQueryString( );
        Hashtable query;

        if(qstring == null)
            qstring="";

        try
            {
            query=HttpUtils.parseQueryString(qstring);
            }
        catch(IllegalArgumentException e)
            {
            query=new Hashtable( );
            String tmp[]=new String[1];
            tmp[0]=qstring;
            query.put("bad query",tmp);
            }

        response.setContentType("text/html");
        out=response.getWriter( );
            
        out.println("<HTML><HEAD><TITLE>Simple Servlet</TITLE></HEAD>");
        out.println("<BODY>");
        out.println("<H1>Simple Servlet</H1>");

        for(Enumeration e=query.keys( ) ; e.hasMoreElements( ) ; )
            {
            String key=(String)e.nextElement( );
            String values[]=(String [])query.get(key);

            for(int n=0 ; n < values.length ; ++n)
                out.println("<B>"+key+"["+n+"]"+"=</B>"+values[n]+"<BR>");
            }

        out.println("</BODY></HTML>");
        out.close( );
        }
    }

We built this like so:

javac -classpath /home/ben/software/jars/jsdk-2.0.jar:/usr/local/jdk1.1.8/lib/
classes.zip Simple.java

That is, we supplied the path to the JSDK and the base JDK classes. All that is needed then is to enable it — the simplest way to do that is to add the directory Simple.java into the repository list for the root zone, by setting the following in zone.properties:

repositories=/home/ben/software/unpacked/ApacheJServ-1.1.2/example,/home/ben/work/
suppose-apachebook/samples/servlet-simple

That is, we added the directory to the existing one with a comma. We then test it by surfing to http://your.server/servlets/Simple.If we want, we can add some parameters, and they'll be displayed. For example,http://your.server/servlets/Simple?name=Ben&name=Peter&something=else should result in the following:

Simple Servlet
something[0]=else
name[0]=Ben
name[1]=Peter

If anything goes wrong with your servlet, you should find the error and stack backtrace in jserv.log.

Of course, you could create a completely new zone for the new servlet, but that struck us as overkill.

18.2 Tomcat

Tomcat, part of the Jakarta Project, is the modern version of JServ and is able to act as a server in its own right. But we feel that it will be a long time catching up with Apache and that it would not be a sensible choice as the standalone server for a serious web site.

The home URL for the Jakarta project is http://jakarta.apache.org/, where we are told:

The goal of the Jakarta Project is to provide commercial-quality server solutions based on the Java Platform that are developed in an open and cooperative fashion.

At the time of writing, Tomcat 4.0 was incompatible with Apache's mod-cgi, and in any case requires Java 1.2, which is less widely available than Java 1.1, so we decided to concentrate on Tomcat 3.2.

In the authors' experience, installing anything to do with Java is a very tiresome process, and this was no exception. The assumption seems to be that Java is so fascinating that proper explanations are unnecessary — devotees will immerse themselves in the holy stream and all will become clear after many days beneath the surface. This is probably because explanations are expensive and large commercial interests are involved. It contrasts strongly with the Apache site or the Perl CPAN network, both of which are maintained by unpaid enthusiasts and usually, in our experience, are easy to understand and work immaculately.

18.2.1 Installing the JDK

First, you need a Java Development Kit (JDK). We downloaded jdk1.1.8 for FreeBSD[1] from http://java.sun.com and installed it. Another source is ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/local-distfiles/nate/JDK1.1/jdk1.1.8_ELF.V1999-11-9.tar.gz. Installation is simple: you just unzip the tarball and then extract the files. If you read the README without paying close attention, you may get the impression that you need to unzip the src.zip file — you do not, unless you want to read the source code of the Java components. And, of course, you absolutely must not unzip classes.zip.

An essential step that may not be very clear from the documentation is to include the JDK, at ..../usr/src/java/jdk1.1.8/bin on your path, to set the environment variable CLASSPATH to /usr/src/java/jdk1.1.8/lib/classes.zip and to add the current directory to the path if it isn't already there.

Make sure that the directory names correspond with the situation on your machine and log in again to get it to work. A simple test to see whether you've got it all together is to write yourself a "hullo world" program:

public class hw
    {
    public static void main(String[] args)
        {
        System.out.println("Hello World");
        }
    }

Save it with the same name as the public class and the .java extension: hw.java. Compile it with:

javac hw.java

and run it with:

java hw

If Hello World appears on the screen, all is well.

18.2.2 Installation of Tomcat

Tomcat can work in three different ways:

  1. As a standalone servlet container. This is useful for debugging and testing, since it also acts a (rather crude) web server. We would not suggest you use it instead of Apache.

  2. As an in-process servlet container running inside Apache's address space. This gives good performance but is poor on scalability when your site's traffic grows.

  3. As an out-of-process servlet container, running in its own address space and communicating with Apache through TCP/IP sockets.

If you decide on 2 or 3, as you probably will, you have to choose which method to use and implement it accordingly.

Consequently, the installation of Tomcat involves two distinct processes: installing Tomcat and adapting Apache to link to it.

Normally we advocate building from source, but in the case of Java it can get tedious, so we decided to install Tomcat from the binary distribution, jakarta.-tomcat-3.3a.tar.gz in our case.

figs/unix.gif

Installation of Tomcat is pretty simple. Having unpacked it, all you have to do is to set the environment variables:

JAVA_HOME  to: /usr/src/java/jdk1.1.8
TOMCAT_HOME  to /usr/src/tomcat/jakarta-tomcat-3.3a

figs/unix.gif

(or the paths on your machine if they are different) and re-log in. Test that everything works by using the command:

ls $TOMCAT_HOME

figs/unix.gif

If it doesn't produce the contents of this directory, something is amiss.

figs/win32.gif

Installation on Win32 systems is very similar. Set the path to the Tomcat directory by typing:

set TOMCAT_HOME =\usr\src\tomcat\jakarta-tomcat-3.3a"

figs/win32.gif

The .../jakarta-tomcat-3.3a/bin directory contains two scripts: startup.sh, which sets Tomcat running, and shutdown.sh, which stops it. To test that everything is installed properly, go there and run the first. A good deal of screen chat ensues (after rather long pause). Note that the script detaches from the shell early on, so its hard to tell when its finished.

By default, Tomcat logs to the screen, which is not a good idea, so it is wise to modify conf/server.xml from:

...
<LogSetter name ="tc_log"
        verbosityLevel="INFORMATION"
/>
...

to:

...
<LogSetter name ="tc_log"
        path="logs/tomcat.log"
        verbosityLevel="INFORMATION"
/>
...

The result is to transfer the screen messages to the log file.

If you now surf to port 8080 on your machine — we went to http://www.butterthlies.com:8080 — Tomcat will show you its home page, which lives at $TOMCAT_HOME/webapps/ROOT/index.html. Note that the page itself erroneously claims to be at $TOMCAT_HOME/webapps/index.html.

When you have had enough of this excitement, you can stop Tomcat with $TOMCAT_HOME/bin/shutdown.sh. If you try to start Tomcat without shutting it down first, you will get a fatal Java error.

18.2.3 Tomcat's Directory Structure

In the .../jakarta-tomcat -- 3.3a directory you will find:

bin

Startup, shutdown scripts, tomcat.sh, and others

conf

Configuration files

doc

Various documents, including uguide — the file to print out and keep by you — and FAQ

lib

Jar files

logs

Log files

webapps

Sample web applications

work

Tomcat's own private stuff

We will look through the contents of these subdirectories that need comment.

18.2.3.1 Bin

The startup and shutdown scripts merely call the important one: tomcat.sh. This script does two things:

18.2.4 Conf

This subdirectory contains two important and useful files:

Server.xml

The first is server.xml. This file covers several issues, in most of which you will not have to interfere. For syntax, see the documentation on the default server we ran earlier (in http:/.../doc/serverxml.html).

apps-*.xml

Each file of the form apps-<somename>.xml is also parsed — this is enabled by the directive:

	<ContextXmlReader config="conf/apps.xml" />

which causes both conf/apps.xml and conf/apps-*.xml to be read and contexts to be loaded from them (see the example servlet later for how contexts are used).

18.2.5 Writing and Testing a Servlet

We use the Simple.java test servlet described earlier to demonstrate how to install a servlet. First of all we create a directory, .../site.tomcat, and in it a subdirectory called servlets — this is where we will end up pointing Tomcat. In .../site.tomcat/servlets, we create a directory WEB-INF (this is where Tomcat expects to find stuff). In WEB-INF we create another subdirectory called classes. Then we copy Simple.class to .../site.tomcat/servlets/WEB-INF/classes. We then associate the Simple class with a servlet unimaginatively called "test", by creating .../site.tomcat/servlets/WEB-INF/web.xml, containing:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
    <servlet>
        <servlet-name>
            test
        </servlet-name>
        <servlet-class>
            Simple
        </servlet-class>
    </servlet>
</web-app>

Finally, we make Tomcat aware of all this by associating the .../site.tomcat/servlets directory with a context by creating conf/apps-simple.xml (remember, this file will automatically be read by the default configuration) containing:

<?xml version="1.0" encoding="ISO-8859-1"?>
<webapps>
   <Context path="/simple" 
            docBase=".../site.tomcat/servlets" 
            debug="0" 
            reloadable="true" > 
              <LogSetter name="simple_tc.log" path="logs/simple.log" />
              <LogSetter name="simple_servlet_log" 
                         path="logs/simple_servlet.log" 
                         servletLogger="true"/>
  </Context>
</webapps>

Obviously, docBase must be set to the actual path of our directory. The path parameter specifies the first part of the URL that will access this context. The context can contain plain HTML, as well as servlets and JSPs. Servlets appear in the servlet subdirectory of the path, so to access the Simple servlet with the previous configuration, we would use the URL http://.../simple/servlet/test. Surfing to http://.../simple/servlet/test?a=b&c=d&c=e produces the following output:

Simple Servlet

c[0]=d
c[1]=e
a[0]=b

18.3 Connecting Tomcat to Apache

The basic document here is .../doc/tomcat-apache-howto.html. It starts with the discouraging observation:

Since the Tomcat source tree is constantly changing, the information herein may be out of date. The only definitive reference at this point is the source code.

As we have noted earlier, this may make you think that Tomcat is more suited to people who prefer the journey to the destination. You will also want to look at http://jakarta.apache.org/tomcat/tomcat-3.2-doc/uguide/tomcat_ug.html, though the two documents seem to disagree on various points.

18.3.1 mod_jk

The Tomcat interface in Apache is mod_jk. The first job is to get, compile, and install it into Apache. When we downloaded Tomcat earlier, we were getting Java, which is platform independent, and therefore the binaries would do. mod_jk is needed in source form and is distributed with the source version of Tomcat, so we went back to http://jakarta.apache.org/builds/jakarta-tomcat/release/v3.3a/src/ and downloaded jakarta-tomcat-3.3a-src.tar.gz. Things are looking up: when we first tried this, some months before, the tar files for the Tomcat binaries and sources had the same name. When you unpacked one, it obliterated the other.

Before starting, it is important that Apache has been compiled correctly, or this won't work at all. First, it must have been built using configure in the top directory, rather than src/Configure. Second, it must have shared object support enabled; that is, it should have been configured with at least one shared module enabled. An easy way to do this is to use:

./configure --enable-shared=example

Note that if you have previously configured Apache and are running a version prior to 1.3.24, you'll need to remove src/support/apxs to force a rebuild, or things will mysteriously fail. Once built, Apache should then be installed with this:

make install

Once this has been done, we can proceed.

Having unpacked the sources, we went down to the .../src directory. The documentation is in ..../jakarta-tomcat-3.3a-src/src/doc/mod_jk-howto.html.. Set the environment variable $APACHE_HOME (not $APACHE1_HOME despite the documentation) to /usr/local/apache. You also need to set JAVA_HOME as described earlier.

Descend into .../jakarta-tomcat-3.3a-src/src/native/mod_jk/apache-1.3, and execute:

./build-unix.sh

Unfortunately, this suffers from the "everything is Linux" syndrome and used weird options to the find utility. We fixed it by changing the line:

JAVA_INCLUDE="`find ${JAVA_HOME}/include -type d -printf \"-I %p \"`" ||  echo "find 
failed, edit build-unix.sh source to fix"

to:

JAVA_INCLUDE="`find ${JAVA_HOME}/include -type d | sed 's/^/-I /g'`" ||  echo "find 
failed, edit build-unix.sh source to fix"

which is substantially more portable. We also had to add this to .../jakarta-tomcat-3.3a-src/src/native/mod_jk/jk_jni_worker.c:

#ifndef RTLD_GLOBAL
# define RTLD_GLOBAL 0
#endif

With these two changes, build-unix.sh worked, and we ended up with a mod_jk.so as desired.

If you are running as an appropriately permitted user, build-unix.sh will install mod_jk.so in the libexec directory of the Apache installation (/usr/local/apache/libexec by default).

The next step is to configure Apache to use mod_jk. In fact, Tomcat comes with a sample set of config files for that in .../jakarta-tomcat-3.3a/conf/jk. There are two files that need tweaking to make it work. First, mod_jk.conf:

LoadModule jk_module /usr/local/apache/libexec/mod_jk.so

<IfModule mod_jk.c>

JkWorkersFile .../jakarta-tomcat-3.3a/conf/jk/workers.properties
JkLogFile  logs/jk.log
JkLogLevel error
JkMount /*.jsp ajp12
JkMount /servlet/* ajp12
JkMount /examples/* ajp12

</IfModule>

This is pretty straightforward — we just load mod_jk in the usual way. The JkWorkersFile directive specifies the location of a file with settings for the Java components of mod_jk. JkLogFile and JkLogLevel are self-explanatory. Finally, JkMount sets the mapping from URLs to Tomcat — ajp12 refers to the protocol used to communicate with Apache. In fact, ajp13 is the more modern protocol and should be used in preference, but despite the claims of the documentation, Tomcat's default setup uses ajp12. Simply change ajp12 to ajp13 to switch protocols.

The other file that needs tweaking is workers.properties (we've removed all the comments for brevity; see the real file for copious extra information):

workers.tomcat_home=.../jakarta-tomcat-3.3a
workers.java_home=/usr/local/jdk1.1.8
ps=/
worker.list=ajp12, ajp13
worker.ajp12.port=8007
worker.ajp12.host=localhost
worker.ajp12.type=ajp12
worker.ajp12.lbfactor=1
worker.ajp13.port=8009
worker.ajp13.host=localhost
worker.ajp13.type=ajp13
worker.ajp13.lbfactor=1
worker.loadbalancer.type=lb
worker.loadbalancer.balanced_workers=ajp12, ajp13
worker.inprocess.type=jni
worker.inprocess.class_path=$(workers.tomcat_home)$(ps)lib$(ps)tomcat.jar
worker.inprocess.cmd_line=start
worker.inprocess.jvm_lib=$(workers.java_home)$(ps)bin$(ps)javai.dll
worker.inprocess.stdout=$(workers.tomcat_home)$(ps)logs$(ps)inprocess.stdout
worker.inprocess.stderr=$(workers.tomcat_home)$(ps)logs$(ps)inprocess.stderr

The parts of this that need adjusting are workers.tomcat_home, workers.java_home, ps, and workers.inprocess.jvm_lib. The first two are self-explanatory; ps is simply the path separator for the operating system you are using (i.e., "\" for Windows and "/" for Unix). The last one, worker.inprocess.jvm_lib, should be adjusted according to OS and JVM, as commented in the sample file (but note that unless you are using the inprocess version of Tomcat, this setting won't be used — and by default, you won't be using it).

Finally, we write the actual configuration file for Apache — in this case, we decided to run it on port 8111, for no particular reason, and .../site.tomcat/conf/httpd.conf looks like this:

Port 8111
DocumentRoot .../site.tomcat/www
Include .../jakarta-tomcat-3.3a/conf/jk/mod_jk.conf

where the DocumentRoot points at some directory with HTML in it, and the Include is adjusted to point to the mod_jk.conf we altered earlier. Now all that is required is to start Tomcat and Apache in the usual way. Tomcat is started as described earlier, and Apache starts simply with:

httpd -d .../site.tomcat

You should then find that the example servlets are available. In fact, if you set the DocumentRoot to be .../jakarta-tomcat-3.3a/webapps/ROOT, then you should find that your Apache server looks exactly like your Tomcat server, only on a different port.

All that remains is to show how to add our example servlet to this configuration. Nothing could be easier. In mod_jk.conf or httpd.conf, add the line:

JkMount /simple/* ajp13

If everything is set up as we did for plain Tomcat earlier, then the Simple servlet should now work, exactly as it did for plain Tomcat. All we need is that the URL path in the JkMount matches the Context path in the apps-*.xml file.

[1]  This is the version of Unix we use — you would download the version appropriate to your OS.

CONTENTS