TOMCAT

Last year we had Tomcat working on one of our machines with multiple virtual hosts. It was good, then this year we upgraded to a new version of linux, bringing everything else up with it as well and suddenly things did not work. The new specs included:
New System
Old System
  • Red Hat 9.0 - The Operating System
  • Apache 2.0.49 - The Web server
  • Tomcat 5.0.25 - Java backend
  • Mod_JK2 - Java connector
  • Red Hat 7.3 - The Operating System
  • Apache 1.3 - The Web server
  • Tomcat 4.0 - Java backend
  • Mod_JK - Java connector

So it was a fairly significant upgrade and not surprising that things broke. This is the story of how I got things working.


APACHE

Apache is the webserver of choice. I prefer to compile from source. First step is you'll need to get the source. Grab the md5sum file while you're there as well.

# wget http://apache.mirror.pacific.net.au/httpd/httpd-2.0.49.tar.gz
# wget http://www.apache.org/dist/httpd/httpd-2.0.49.tar.gz.md5

Check the md5sum of the downloaded file against what it is suppose to be and make sure they match. Assuming they do, you'll want to decompress the file, then compile it. Incidently, I put all my source files into /usr/local/src and all of the programs go into /usr/local with symbolic links pointing from the major program to the actual name. ie) If I install Program_Version_1.2.3 that will be installed in /usr/local/program_1.2.3 but then I'll create a symbolic link /usr/local/program back to that path.

Before I get started compiling, I create an Apache user and group. This is the userid that Apache will run on. (I usually call it "apache". I am nothing if not original). You'll need to edit your password, group and maybe your shadow file. Or you can use the adduser scripts to do it nicely for you.

# tar -xvf httpd-2.0.49.tar.gz |tar -xvf -
# cd httpd-2.0.49
# ./configure --prefix=/usr/local/httpd-2.0.49 --enable-suexec --with-suexec-bin=/usr/sbin/suexec --with-suexec-docroot=/usr/local/httpd/htdocs --with-suexec-logfile --with-suexec-safepath --enable-module=so --enable-ssl --with-ssl=/usr/include/openssl CFLAGS="-I/usr/kerberos/include" --with-suexec-userdir=WWW --with-suexec-caller=apache --with-suexec-uidmin=101 --with-suexec-gidmin=101 --enable-module=mod_jk --enable-deflate --enable-maintainer-mode --enable-module=mod_dav --enable-module=mod_dav_fs --enable-dav --enable-dav-fs --enable-module=mod_rewrite --enable-rewrite
# make
# make install

The configure line of Apache is worth a mention. It says to install Apache in /usr/local/httpd-2.0.49; compile in the suexec options (which we use); enable webdav; enable mod_rewrite and the maintainer mode allows for debugging. (Good in this particular case).

Assuming everything worked in the lines above, you'll now have apache installed in /usr/local/httpd-2.0.49. The next step I perform is a bundle of symbolic links. (Link /use/local/httpd to /usr/local/httpd-2.0.49; link /etc/httpd to /usr/local/httpd/conf for example) exactly what depends on your personal preference and the installation.

Edit /usr/local/httpd/conf/httpd.conf (or /etc/httpd/httpd.conf). I'm not going to go into details about that. It is relatively straightforward and the configuration file is very well documented.

Now you're ready to start Apache. Run the apachectl command

# /usr/local/httpd/bin/apachectl start

and point your browser to http://hostname and you should see the Apache welcome page. If not, look in the error logs and see what the problem is.


TOMCAT

Tomcat is "a servlet container that is used in the official Reference Implementation for the Java Servlet and JavaServer Pages technologies" (Tomcat Home Page). Essentially it allows you to use Java via a web interface.

Installing it is actually quite easy.

Get the source from http://apache.ausgamers.com/jakarta/tomcat-5/v5.0.25/bin/jakarta-tomcat-5.0.25.tar.gz, uncompress and copy to /usr/local and create a symbolic link. You'll also need to create a user to run the tomcat process under as well. I use the user/group "tomcat" :

# wget http://apache.ausgamers.com/jakarta/tomcat-5/v5.0.25/bin/jakarta-tomcat-5.0.25.tar.gz
# gzip -dc jakarta-tomcat-5.0.25.tar.gz |tar -xvf -
# cp -a jakarta-tomcat-5.0.25 /usr/local/
# cd /usr/local/
# chown -R tomcat:tomcat jakarta-tomcat-5.0.25
# ln -s jakarta-tomcat-5.0.25 tomcat

Now you've got tomcat installed. Before you start it, you'll need to set an environmental variable JAVA_HOME. All the documentation says to set this in the shell startup scripts, however, I have more luck setting it directly in the Tomcat startup script. That is called catalina.sh and is located at /usr/local/tomcat/bin/catalina.sh. Just add the line export JAVA_HOME="/usr/java/jdk" to that file. That is assuming that your java directory is located in /usr/java/jdk. While you are there, at about line 70, under the block which gets the environmental variables add serverRoot=/etc/httpd and export serverRoot. This gets used later.

# Get standard environment variables
PRGDIR=`dirname "$PRG"`
CATALINA_HOME=`cd "$PRGDIR/.." ; pwd`
if [ -r "$CATALINA_HOME"/bin/setenv.sh ]; then
. "$CATALINA_HOME"/bin/setenv.sh
fi
# Added these lines
serverRoot=/etc/httpd
export serverRoot

Once you've done that, start tomcat:

# /usr/local/tomcat/bin/catalina.sh start

and point your browser to http://hostname:8080 and you should see the tomcat welcome page. If not, then you've got problems. Find the examples on the left hand side of the page and double check that they work as well.


MOD_JK2

Mod_jk2 is a java connector. Essentially it acts as a translator, allowing Apache and Tomcat to talk to each other. Unfortunately it is a pain to set up.

Essentially its like anything else: download, compile and install. However, under Red Hat the configure script leaves out some libraries, so you need to manually edit one of the Makefiles to include them. First you need to get the source and prepare it:

# wget http://apache.ausgamers.com/jakarta/tomcat-connectors/jk2/source/jakarta-tomcat-connectors-jk2-2.0.4-src.tar.gz
# gzip -dc jakarta-tomcat-connectors-jk2-2.0.4-src.tar.gz | tar -xvf -
# cd jakarta-tomcat-connectors-jk2-2.0.4-src/jk/native2
# ./configure --with-apxs2=/usr/local/httpd/bin/apxs \
--with-apr-lib=/usr/lib \
--with-tomcat-41=/usr/local/tomcat \
--with-java-home=/usr/java/jdk \
--with-jni

Now you need to find the file source_directory/jk/native2/server/apache2/Makefile inside that file you need to add some libraries.

Find the block -->
ifdef APR_LIBDIR_LA
JK_LDFLAGS=-L${APACHE2_LIBDIR} -lcrypt
else
JK_LDFLAGS=-lcrypt ${APR_LIBS}
endif
And change it to this:
ifdef APR_LIBDIR_LA
JK_LDFLAGS=-L${APACHE2_LIBDIR} -lcrypt
else
JK_LDFLAGS=-lcrypt ${APR_LIBS} JK_LDFLAGS=-lcrypt ${APR_LIBS} -L/usr/local/httpd-2.0.49/lib -laprutil-0 -lgdbm -ldb-4.0 -lexpat
endif

Note the extra LDFLAGS --> JK_LDFLAGS=-lcrypt ${APR_LIBS} -L/usr/local/httpd-2.0.49/lib -laprutil-0 -lgdbm -ldb-4.0 -lexpat

Finding out which flags can be a bit of a challenge. Exactly which flags will depends on your own installation.

The simple answer is to run apu-config, however there may be different versions. I discovered I had two versions installed. One in my path in /usr/bin/apu-config and another in /usr/local/httpd/bin/apu-config. You'll want to run the one which Apache uses, which was the one inside the httpd directory in my situation.

/usr/local/httpd/bin/apu-config --link-ld --libs returned a value of:

-L/usr/local/httpd/bin -laprutil-0 -lgdbm -ldb-4.2 -lexpat

These are the libraries which were compilied into apu-utils. If you don't specify these, you will get library errors when you try to start tomcat, resulting in the apr not loading.

In my case, it asked for the db4.2 library. I tried that, but when I compiled, it could not be found, so I did some searching and tried -ldb4.0 instead. That worked. I am guessing I forgot to add the path for db-4.2 to my /etc/ld.so.conf file.

Once you have edited that line, run a make command. Assuming that works you need to then run apxs and then you can copy the created modules from the tomcat source directory into the Apache module directory.

# cd ../build/jk2/apache2
# /usr/local/httpd/bin/apxs -n jk2 -i mod_jk2.so
# cp libjkjni.so /usr/local/httpd/modules/jkjni.so
# cp mod_jk2.so /usr/local/httpd/modules/
Things to take note of:


CONFIGURATION

Once everything is in place, there are four files that need to be modified:

In httpd.conf you need to tell Apache to load the mod_jk2 module. Some documentation says to add this all to httpd.conf, however, I find it neater to add it to a seperate file and then tell Apache to load the file. In this case, you add the line include /etc/httpd/include/jk2_mod.conf to /etc/httpd/httpd.conf and then create a file /etc/httpd/include/jk2_mod.conf which looks like this: /etc/httpd/include/jk2_mod.conf
	
# Sample mod_jk2.conf  08-Feb-2004, located at:
# http://www.gknw.com/development/apache/docs/win32/mod_jk2/mod_jk2x.conf
#
# Load the mod_jk2 module
#
LoadModule jk2_module modules/mod_jk2.so



    #-----------------------------------------------
    # Set the Apache2 logger level
    #-----------------------------------------------
    #
    JkSet2 logger level debug

    #-----------------------------------------------
    # Set the alternate log file
    #-----------------------------------------------
    #
    JkSet2 logger.file:0 file ${serverRoot}/logs/jk2.log
    JkSet2 logger.file:0 level debug

    #-----------------------------------------------
    # Set the scoreboard file
    #-----------------------------------------------
    #
    JkSet2 shm: file ${serverRoot}/logs/jk2.shm
    JkSet2 shm: size 1048576

    #-----------------------------------------------
    # Where to find the workers2.properties file
    #-----------------------------------------------
    #
    #JkSet config.file ${serverRoot}/conf/workers2.properties

    #-----------------------------------------------
    # Alternatively setup the workers here
    #-----------------------------------------------
    #
    JkSet2 channel.socket:localhost:8009 info "Ajp13 forwarding over socket"
    JkSet2 status: info "Status worker, displays runtime information"
    JkSet2 workerEnv: logger logger.file:0

    #-----------------------------------------------
    # Configure the /jkstatus handler
    #-----------------------------------------------
    #
    
        JkUriSet group status:
        JkUriSet info "Map the /jkstatus handler to the Web server uri space"
        Order Deny,Allow
        Deny from all
        Allow from 192.168.1
    

    #-----------------------------------------------
    # Configure Tomcat Example Applications
    #-----------------------------------------------
    #
    
        JkUriSet group lb:lb
        JkUriSet info "Map the Tomcat examples to the Web server uri space"
    

    #-----------------------------------------------
    # Configure Tomcat Documentation
    #-----------------------------------------------
    #
    
        JkUriSet group lb:lb
        JkUriSet info "Map the Tomcat docs to the Web server uri space"
    


	

The key thing to see here is the setting up of the worker. Make a note of the port, 8009 on localhost is standard, but you can move it if you wish. Also notice the URI setups.

/etc/httpd/workers2.properties
	
# workers2.properties

# Shared memory handling. Needs to be set.
[shm]
info=Scoreboard.  Required for reconfiguration and status with multiprocess servers
file=/usr/local/tomcat/logs/jk2.shm
size=1048576
debug=0
disabled=0

#UNIX domain socket
[channel.un:/usr/local/tomcat/work/jk2.socket]
tomcatId=localhost:8009
debug=0

# define the worker
[ajp13:/usr/local/tomcat/work/jk2.socket]
channel=channel.un:/usr/local/tomcat/work/jk2.socket
# Not currently using sockets, using channels instead
#[ajp13:localhost:8009]
#channel=channel.socket:localhost:8009

# Announce a "status" worker
pstatus:status]
info=Status worker.  Displays runtime information.

[uri:/jkstatus/*]
group=status:status

# Uri mapping
[uri:/examples/*]


### The default website
[uri:projects.csse.uwa.edu.au/*.jsp]
worker=ajp13:localhost:8009

### Perth agentcity site
[uri:perth-agentcity.csse.uwa.edu.au/*.jsp]
worker=ajp13:localhost:8009

	

Notice here the channel definition. This tells tomcat where to go. We're using channels, not sockets. The other thing to notice here is the defitinions of the uris of the virtual sites. This is where you can tell it to associate files with tomcat. In our case, any file which ends in .jsp will be passed to tomcat.

/etc/tomcat/jk2.properties
	
# jk2.properties
# Configured for channel UNIX

# Set the desired handler list
handler.list=apr,request,channelUnix

# UNIX Domain socket location
channelUnix.file=/usr/local/tomcat/work/jk2.socket

# Dynamic library
serverRoot=/etc/httpd
apr.NativeSo=/usr/local/httpd/modules/jkjni.so

#channelSocket.port=8009
	

Notice here how the sockets ports are commented out, since we're using file channels instead. Also notice here the loading of the jkjni.so module. This is the one which was renamed previously.

/etc/tomcat/server.xml
	
<!-- Example Server Configuration File -->
<!-- Note that component elements are nested corresponding to their
     parent-child relationships with each other -->

<Server port="8005" shutdown="SHUTDOWN" debug="0">


  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener"
            debug="0"/>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"
            debug="0"/>

  <!-- Global JNDI resources -->
  <GlobalNamingResources>

    <!-- Test entry for demonstration purposes -->
    <Environment name="simpleValue" type="java.lang.Integer" value="30"/>

    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users -->
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
       description="User database that can be updated and saved">
    </Resource>
    <ResourceParams name="UserDatabase">
      <parameter>
        <name>factory</name>
        <value>org.apache.catalina.users.MemoryUserDatabaseFactory</value>
      </parameter>
      <parameter>
        <name>pathname</name>
        <value>conf/tomcat-users.xml</value>
      </parameter>
    </ResourceParams>

  </GlobalNamingResources>

  <!-- Define the Tomcat Stand-Alone Service -->
  <Service name="Catalina">

    <!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8080 -->
    <Connector port="8080"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               debug="0" connectionTimeout="20000"
               disableUploadTimeout="true" />
    <!-- Note : To disable connection timeouts, set connectionTimeout value
     to 0 -->

    <!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 -->
    <Connector port="8009"
               enableLookups="false" redirectPort="8443" debug="0"
               protocol="AJP/1.3" />

    <!-- Define the top level container in our container hierarchy -->
    <Engine name="Catalina" defaultHost="localhost" debug="0">

      <!-- Global logger unless overridden at lower levels -->
      <Logger className="org.apache.catalina.logger.FileLogger"
              prefix="catalina_log." suffix=".txt"
              timestamp="true"/>

      <!-- Because this Realm is here, an instance will be shared globally -->

      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                 debug="0" resourceName="UserDatabase"/>

      <!-- Define the default virtual host -->
      <Host name="localhost" debug="0" appBase="webapps"
       unpackWARs="true" autoDeploy="true"
       xmlValidation="false" xmlNamespaceAware="false">

        <Context path="" docBase="/usr/local/tomcat/webapps/ROOT/" debug="0" reloadable="true" crossContext="true"/>

        <Logger className="org.apache.catalina.logger.FileLogger"
                 directory="logs"  prefix="localhost_log." suffix=".txt"
            timestamp="true"/>

      </Host>

      <!-- perth-agentcity.csse.uwa.edu.au  -->
      <Host name="perth-agentcity.csse.uwa.edu.au" debug="1"
                appBase="/home/projects/misc/perth-agentcity/public_html"
                unpackWARs="true">
      <!--  <Alias>perth-agentcity.csse.uwa.edu.au</Alias>         -->

        <Logger className="org.apache.catalina.logger.FileLogger"
                 directory="logs"  prefix="perth-agentlog." suffix=".txt"
                timestamp="true"/> -->

        <!--  Root Context -->
        <Context path="" docBase="/home/projects/misc/perth-agentcity/public_html" debug="1"
         reloadable="true" crossContext="true"  />
      </Host>

      <!-- projects.csse.uwa.edu.au  -->
      <Host name="projects.csse.uwa.edu.au" debug="1"
                appBase="/usr/local/httpd/htdocs"
                unpackWARs="true">
        <Logger className="org.apache.catalina.logger.FileLogger"
                 directory="logs"  prefix="projects." suffix=".txt"
                timestamp="true"/> -->
        <Context path="" docBase="/usr/local/httpd/htdocs" debug="1"
         reloadable="true" crossContext="true"  />
      </Host>
    </Engine>

  </Service>

</Server>
	

I've stripped out all most of the comments, but there should be a template one you can follow in your Tomcat configuration. About all you'll need to do is edit the file paths and make sure they match up. Any virtual hosts you want need to be added to the bottom so the base directories are defined and that appears to be all you need to do.

Once you have set all of those, start it up and give it a test. A test .jsp file you can use will look like:
	
<HTML>
<BODY>
<H1><%= "It works" %></h1><%= "at " + java.util.Calendar.getInstance().getTime() %>
</BODY>
</HTML>
	

You should see the message "It works" on the screen along with the current date. If you don't see that, then you've got problems.


DEBUGGING

My biggest problem was missing libraries. This was evident when tomcat was starting. The logs had a line:
INFO: APR not loaded, disabling jni components: java.io.IOException: java.lang.UnsatisfiedLinkError: /usr/local/httpd-2.0.49/modules/jkjni.so: /usr/local/httpd-2.0.49/modules/jkjni.so: undefined symbol: apr_md5_final
in them. If you see anything similiar and APR is not loading, then your .jsp files are not going to work. To solve the library problem, use the apu-config command as has been detailed above. You can also check with the ldd command to see what libraries have been compiled in.
# ldd /usr/local/httpd/modules/jkjni.so
libcrypt.so.1 => /lib/libcrypt.so.1 (0x40046000)
libapr-0.so.0 => /usr/local/httpd-2.0.49/lib/libapr-0.so.0 (0x40074000)
libaprutil-0.so.0 => /usr/local/httpd-2.0.49/lib/libaprutil-0.so.0 (0x40097000)
libgdbm.so.2 => /usr/lib/libgdbm.so.2 (0x400ab000)
libdb-4.0.so => /lib/libdb-4.0.so (0x400b2000)
libexpat.so.0 => /usr/lib/libexpat.so.0 (0x4015a000)
libc.so.6 => /lib/i686/libc.so.6 (0x4017a000)
librt.so.1 => /lib/i686/librt.so.1 (0x402b4000)
libm.so.6 => /lib/i686/libm.so.6 (0x402c6000)
libnsl.so.1 => /lib/libnsl.so.1 (0x402e8000)
libpthread.so.0 => /lib/i686/libpthread.so.0 (0x402fd000)
libdl.so.2 => /lib/libdl.so.2 (0x4034d000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)
The other problem I faced were typos in the configuration files, but they should be relatively easy to diagnose.

Useful links:


Back to index page