home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Apache The Definitive Guide, 3rd EditionApache: The Definitive GuideSearch this book

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

Figure

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

Figure

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 make s. 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:

  • The Config file is a variant of the enormous Apache "include everything" file which we think is confusing and retrograde.

  • The Config file actually said nothing about JServ.

  • The command /usr/local/sbin/apachectl start didn't work because Apache looked for the Config file in the wrong place.

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.

Figure

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

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

Figure

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.

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


Example

ApJServLogFile /usr/local/var/httpd/log/mod_jserv.log
TIP: Currently no protocols handle this. Introduced for future protocols.

WARNING: 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
NOTE: [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.

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.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.