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).
Windows users should get the self-installing .exe distribution from http://java.apache.org/.
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:
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.
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.
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.
JServ has its own Apache directives, which are documented in the jserv.conf file.
To run JServ on Win32, tell Apache to load the Apache JServ communication module with:
... LoadModule jserv_module modules/ApacheModuleJServ.dll ...
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.
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.
TIP: When set to DISABLED, the log will be redirected to Apache error log.
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.
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.
ApJServDefaultProtocol ajpv12
ApJServDefaultHost |
ApJServDefaultHost [hostname] Default: "localhost"
Default host on which Apache JServ is running.
ApJServDefaultHost java.apache.org
ApJServDefaultPort |
ApJServDefaultPort [number] Default: protocol-dependant (for ajpv12 protocol this is "8007")
Default port to which Apache JServ is listening.
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.
ApJServVMTimeout 10
ApJServProtocolParameter |
ApJServProtocolParameter [name] [parameter] [value] Default: NONE
TIP: 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.
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.
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)
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 /myServletsIf 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:8007If 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/myServletsIf 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.
TIP: This directive is meaningful only when virtual hosts are being used.
ApJServMountCopy on
ApJServAction |
ApJServAction [extension] [servlet-uri] Defaults: NONE
Executes a servlet passing filename with proper extension in PATH_TRANSLATED property of servlet request.
TIP: This is used for external tools.
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
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>
WARNING: 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.
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.
Copyright © 2003 O'Reilly & Associates. All rights reserved.