Installing Tomcat 4.1.18

This is a log of my install of Tomcat 4.1.18 binary under Red Hat Linux 8.0
Jan Labanowski, Mar. 8, 2003
1. The problem with Red Hat Linux is that when you ask it to install
   Everything, it actually does not install everything. You will be
   missing a lot of useful stuff. If something does not work, just
   install DEVELOPMENT version of it. 
      
2. First you need Java SDK. I installed the latest greatest 1.4.1.
   Point your browser to http://java.sun.com/ and click on J2SE 1.4.1
   (Standard Edition) download. While you can download Java install
   files to /tmp you may want to put it in a more permanent directory
   in case you want to reinstall Java later. Click on J2SE downloads.
   Click on J2SE 1.4.1. You will see a table. Do not take anything from
   JRE column since JRE is a part of SDK. I retrieved 
   "Linux self-extracting file" since it can be unpacked in any directory,
   while RPM is easier, but goes to a predefined directory.
   - Accept the license
   - Click on Download j2sdk-1_4_1_02-linux-i586.bin .
     Filesize = 42,250,657 bytes. 
   I saved it in the /root/j2sdk-1_4_1_02-linux-i586.bin
   Then I ran:

      cd /root
      sh j2sdk-1_4_1_02-linux-i586.bin
  
    Answered Yes to license terms
    This created a directory:
      /root/j2sdk1.4.1_02
    I moved this directory  to /usr/local:

      cd /root
      mv j2sdk1.4.1_02 /usr/local
      cd /usr/local
      ln -s j2sdk1.4.1_02 j2sdk1.4.1

    I appended the following to the files in home directories of accounts
    which will use this Java SDK:
    Appended to .bashrc

      JAVA_HOME=/usr/local/j2sdk1.4.1
      PATH=${JAVA_HOME}/bin:${PATH}
      MANPATH=${JAVA_HOME}/man:${MANPATH}
      export JAVA_HOME PATH MANPATH

    You you should also run these commands in your current terminal window
    to set necessary environment variables. Also, add lines below to .chsrc
    of accounts which run csh or tcsh shell:

      setenv JAVA_HOME /usr/local/j2sdk1.4.1
      setenv PATH ${PATH}:/usr/local/j2sdk1.4.1/bin
      setenv MANPATH /usr/local/j2sdk1.4.1/man:${MANPATH}

    Default distribution on Java SDK comes with weak encryption (some
    governments do not like you to install strong encryption and they want
    to read your stuff -- they say it is for your protection, since
    they want to read files of bad guys -- the problem is that
    bad guys are using strong encryption and do not give a damn about what
    government wants. Moreover, government knows it very well).
    To upgrade your encryption, you need to download another file from
    Sun. Go to: http://java.sun.com/j2se/1.4.1/download.html and get
    to the bottom of the page and get Java Cryptography Extension (JCE)
            Unlimited Strength Jurisdiction Policy Files:
    You will get a file of 9715 Bytes: jce_policy-1_4_1.zip
    Unzip the file anywhere: (e.g., you can use a command:

      /usr/local/j2sdk1.4.1/bin/jar xvf jce_policy-1_4_1.zip

    and use the Java jar utility you have just installed. You can also use 
    the regular unzip program which comes with Linux.

      unzip jce_policy-1_4_1.zip  


    In any case, the jce directory will be created with the following
    files:
      README.txt            This file
      COPYRIGHT.html        Copyright information
      local_policy.jar      Unlimited strength local policy file
      US_export_policy.jar  Unlimited strength US export policy file

    Copy these files to lib/security directory of your Java Runtime
    Environment. In short, I did (Yes, I did set up the JAVA_HOME
    variable as described above):

       cd /wherever/it/is/.../jce 
       mkdir ${JAVA_HOME}/jre/lib/security/limited-strength
       cp -p ${JAVA_HOME}/jre/lib/security/*.jar \
          ${JAVA_HOME}/jre/lib/security/limited-strength
       cp * ${JAVA_HOME}/jre/lib/security

    Now, I also downloaded docs for Java. With my browser went to:
         http://java.sun.com/j2se/1.4.1/download.html
    Toward the bottom, under the heading: J2SE v 1.4.1 Documentation
    there is the [Download] entry under JRE column (unfortunately, there is
    no easy download for documents for J2SDK). Click, accept the license,
    and download the zip file with docs: Download j2sdk-1_4_1-doc.zip 
    (32,765,641 bytes).
    Then, I unpacked the zipped docs in JAVA_HOME:

       cd $JAVA_HOME
       unzip /full/path/j2sdk-1_4_1-doc.zip
   
    This created a docs directory under JAVA_HOME. Later, I will link
    this directory to Tomcat webapps later. To see if my Java install worked,
    I created a simple application which prints the Java version and
    vendor. Get it HERE or cut&paste from below:
--------------- Cut and put into file: HelloWorld.java ---------------

         class HelloWorld {
            public static void main(String[] args) {
               String systemProperties [] = {
                    "java.vendor", 
                    "java.vendor.url", 
                    "java.version",
                    "java.class.version",
                    "os.name",
                    "os.arch",
                    "os.version"
               };

               for (int i=0; i < systemProperties.length; i++) {
                  try {
                    String key = systemProperties[i];
                    String value = System.getProperty(key);
                    System.out.println("\nProperty: " +  key +
                       "\nValue: " + value);
                  } catch ( Exception e ) {
                    System.err.println("Exception " + e);
                  }
               }
            }
         }

---------------- end of file: HelloWorld.java ---------------
    Then I compiled the source code as:

          javac javac  HelloWorld.java

    and  I ran it:

          java  HelloWorld

    It happily produced:

Property: java.vendor
Value: Sun Microsystems Inc.

Property: java.vendor.url
Value: http://java.sun.com/

Property: java.version
Value: 1.4.1_02

Property: java.class.version
Value: 48.0

Property: os.name
Value: Linux

Property: os.arch
Value: i386

Property: os.version
Value: 2.4.18-24.8.0

     (I told you, I did the upgrades of RH8.0, didn't I?).


3. Before you do anything, explore Jakarta site:
   http://jakarta.apache.org/ and find out what is
   the latest STABLE release of Tomcat. In this case it is 4.1.18,
   but in a few weeks, it will not be the latest. 
   Also READ the documents and HOWTOs.

   It is convenient to run Tomcat as a user different than root.
   If you need to run Tomcat on the regular "well known ports", i.e,.
   80 (for HTTP), and 443 (for HTTPS) you have to run Tomcat as root.
   Server ports with numbers less than 1025 can only be opened by
   a superuser. Native code can do the trick by opening lower ports as
   root user and changing itself to another non-root user (this operation
   is called setuid). Code written in Pure Java cannot perform the setuid
   operation and therefore once started as root, will run as root.
   However, running as root, is DANGEROUS!!!. It is ESPECIALLY dangerous
   to run Web server as root, since you have no control over data which
   are fed to your server. While Tomcat is very safe, your own code in Java
   might not be. Small mistakes (even gurus make them), and your Java Server
   Page or servlet may be used to attack your computer (or other computers
   using your computer as a lunch pad).  It is therefore a VERY GOOD
   PRACTICE to run Tomcat as user other than root.

   There are many ways to serve standard Web ports (80, 443) by Tomcat,
   yet run Tomcat as a non-root user. You need to put a piece of native
   code (written usually in C) between your Tomcat running as non-root and
   the real world out there. This topic is too long to cover here, but
   essentially, you have some options:

     a) Use Apache (or some other Web server), running as native code,
        as a front, and connect Tomcat to the Web server via a connector
        module (or run Tomcat inside a native web server process as a thread)
        Search web for words: Apache connector module tomcat

     b) Use a firewall software capable of translating port numbers.
        Assuming that your HTTP port for Tomcat is 8080, and HTTPS
        port is 8443, you can run Tomcat as non-root, and the firewall
        will rewrite the destination port number in the incoming TCP
        packet (request) from 80 to 8080, (or 443 to 8443), and do the
        reverse for the source port number in the outgoing packet (response).
        Redhat comes with iptables firewall. Write a set of rules to remap
        ports (check the following URLs and search http://google.com:
        http://www.wrox.com/books/sample-chapters/SampleChapter_1861008309.pdf,
        http://www.projectforge.org/Administration.pdf,
        http://www.netikus.net/documents/Linux-Cocoon.pdf). 

     c) Use some specialized piece of native code to do this. Check
        perl archives (CPAN) for a piece of code called tunnel.pl
        (check for example: 
  http://www.ks.uiuc.edu/Research/biocore/localServer/install/sample/webTunnel.
        )

4. Create a new user and group to run tomcat on your machine.
   I use the user webrun or tomcat or tomcat41180 or whatever. 
   It is convenient to have a specific user associated with the given
   version of tomcat, so when you need to kill or restart Tomcat you
   can easily see what is running and what is not. First, check if
   the group and user (and the corresponding uid and gid) exist by
   grepping or looking at /etc/passwd and /etc/group. Then create 
   a new group and a new user:

      groupadd -g 41180 tomcat41180
      useradd -c 'Tomcat 4.1.18' -d /usr/local/tomcat-4.1.18 -g tomcat41180 \
             -m -s /bin/bash -u 41180 tomcat41180
      passwd tomcat41180

   This will create group for and user id for tomcat41180,
   create home directory, and change the password to what you want it to be.
   The home directory will be /usr/local/tomcat-4.1.18

   Then log out as root and log in as tomcat41180.

5. Once you are logged in user tomcat41180 add this to .bashrc file in the
   HOME directory:

      JAVA_HOME=/usr/local/j2sdk1.4.1
      PATH=${JAVA_HOME}/bin:${PATH}
      MANPATH=${JAVA_HOME}/man:${MANPATH}
      export JAVA_HOME PATH MANPATH

   and log out and log in again as tomcat41180.
   I retrieved BINARY Tomcat distribution from Jakarta
   Apache site: http://jakarta.apache.org/ being in HOME
   directory of tomcat41180. Note that there are two distributions
   of Tomcat: the one with Xerces XML parser (regular distribution)
   and the Lightweight distribution which does not bundle the Services parser.
   The "regular" distribution is will not run out of the box with the
   J2SE 1.4.1 JDK since JDK has its own XML parser (JAXP). You would have
   to reconfigure several things to make it run. The solution is to use
   "Lightweight" binary distribution which does not have XML parser bundled
   and will run out of the box with the J2SE 1.4.1 JDK. So why should anyone
   ever look at "regular" distribution?. It is a separate topic, but:
      a) People often use other JDKs than Sun's (Why? Since they can
         be redistributed with your application, while Sun's cannot).
  
      b) Xerces has a lot of functionality which JAXP does not have (yet...).

      c) Java 1.4.X is not yet ported to all computer architectures and
         systems, and for these systems you need to use the older versions
         of Java which do not yet bundle the JAXP.
   I used wget to retrieve the binary distribution (but you can
   just use regular browser). Note, I used the gzipped tar distribution.
   There is also RPMs, but if you want to have a freedom where you place
   your Tomcat files, use the tgz.  I logged in as tomcat41180 and did:

      wget http://jakarta.apache.org/builds/jakarta-tomcat-4.0/release/v4.1.18/bin/jakarta-tomcat-4.1.18-LE-jdk14.tar.gz
      tar zxvf jakarta-tomcat-4.1.18-LE-jdk14.tar.gz
      cd jakarta-tomcat-4.1.18-LE-jdk14
      mv * ..    # copy files one dir up
      cd ..      # go one up
      rmdir jakarta-tomcat-4.1.18-LE-jdk14   # remove it - it is empty

   i.e., I moved all components to the HOME directory of tomcat41180.
   In the .bashrc file I added lines at the end :

      TOMCAT_HOME=/usr/local/tomcat-4.1.18 
      CATALINA_HOME=/usr/local/tomcat-4.1.18 
      PATH=${TOMCAT_HOME}/bin:${PATH}
      export TOMCAT_HOME PATH CATALINA_HOME

   and logged out and logged in again as tomcat41180
   Now, making tomcat RUN. You definitely need to read files:
   ${TOMCAT_HOME}/RUNNING.txt ${TOMCAT_HOME}/README.txt which come
   with the distribution. I started from making sure that my port
   assignments do not collide with other servers which I run on the machine.
   I edited the  ${TOMCAT_HOME}/conf/server.xml file and made all ports
   original starting from 80xx to start from 418xx. In my case these
   ports did not collide with anything else running on the machine, which
   I checked by doing:

          netstat -n | grep 412

   I made following changes in ${TOMCAT_HOME}/conf/server.xml:
         8005 --> 41805
         8008 --> 41808
         8009 --> 41809
         8080 --> 41880
         8082 --> 41882
         8083 --> 41883
         8443 --> 41843
   Note that most statements using these ports are commented out.
   But I changed just in case, so it is consistent in case I later
   need to uncomment some connectors. The server.xml file patched
   as described above is available HERE

6. Testing tomcat. Before starting Tomcat, you need to set the
   JAVA_HOME environmental variable (read $TOMCAT_HOME/bin/catalina.sh
   script for more information). In my case the JAVA_HOME and TOMCAT_HOME
   should have been already set in the .bashrc for the tomcat41180 user.
   To start the Tomcat server, I did:

     cd ${TOMCAT_HOME}/bin
     ./startup.sh


   Then, when I pointed my browser at: 
         http://my.machine.com:41880
   Note, there will be a slight delay when serving first page after 
   startup, since Tomcat needs to initialize a lot of stuff (e.g., 
   random number generator seed) and compile JSP pages (check the
   ${TOMCAT_HOME}/work subdirectories for compiled/translated JSP
   pages and servlets). In the URL above put of course, the actual
   name or IP address of your machine. To use it on a local host,
   just use the URL: http://127.0.0.1:41880
   In my case, I am behind the firewall which only lets through the
   authorized ports. Port 41880 obviously is not one of them.
   I had to add a rule to my IPTABLES to let the
   packets through. Check your firewall if things should work and
   do not (e.g., if everything works fine with http://localhost41880
   but you cannot access the server from another computer).
   After I played with examples and pages (whose sources are in the
   ${TOMCAT_HOME}/webapps directory) I stopped Tomcat  

     cd ${TOMCAT_HOME}/bin
     ./shutdown.sh



7. There may be a situation when you want all developers to have 
   a password to the tomcat41180 (or whatever) account which owns
   all files of Tomcat. But, you may also want to have the situation
   when people own/can modify subdirectories under ${TOMCAT_HOME}/webapps,
   i.e., the individual web applications. This is not really 100% secure
   solution if you wanted to protect the integrity of the Tomcat 
   installation, since people can always write a Java code which can
   do anything the user "tomcat41180" can do (e.g., delete/modify files
   which are owned/writable by user which runs Tomcat). If security
   is the issue, you have no recourse, but to create separate Tomcat
   installations each owned by the respective developer. However, in
   a regular development environment, it is not an issue, and it is
   rather a case when we need to sensibly protect people against mistakes
   (not malice) made by others.   
  
   If you create webapps owned by individual developers so they do not
   get into each other way, there is still the problem that Tomcat needs
   to be restarted, or killed sometimes. Some profoundly messed up Java
   code can affect Tomcat performance or even hang it (imagine something
   which runs tight infinite loop, for example). Moreover, sometimes,
   especially when you use compile time includes in JSP, the easiest way
   to make Tomcat aware of the changes is to restart Tomcat, and even
   delete temporary files in the directory. 

   These actions can only be done by the user who has privileges,
   i.e., root or the "tomcat41180" who owns Tomcat files. 
   To allow other users to perform these actions, I created a few
   scripts and C-wrappers. In UNIX, when you set up a SETUID bit on
   an executable file, it will execute as user which owns it. To ensure
   that both the real and effective user id is changed, I use few more
   lines in the C wrapper. While in the beginning of time, UNIXES allowed the
   shell scripts to have SETUID bit set and active, this option is 
   disabled by default on most installation to enhance security.
   The way around is to write a C program (a C wrapper) which executes
   the script by calling a system library function. The full path
   to the script should be used, and script should not be writable by
   anyone but root of the owner of the setuid C wrapper program.
   C wrapers work, since SETUID but works by default on binary executables.

   I created shell scripts and corresponding wrappers which
   are given below as links. If you use some other account and other
   paths/file locations, you need to modify these scripts. 

     start_tomcat41180.sh -- a shell script which sets some
         environment variables. It then calls the startup.sh script, which
         comes with Tomcat. It is assumed that the script resides in the
         /usr/local/tomcat-4.1.18/bin directory and is owned and
         writable only by user "tomcat41180" and that user and group
         id of tomcat41180 is 41180. Remember to change mode of this
         script once you put it where it belongs:

             cd /usr/local/tomcat-4.1.18/bin
             chmod 755 start_tomcat41180.sh



     start_tomcat41180.c -- a C wrapper for the start_tomcat.sh script.
         It should be compiled, and installed as below when you are logged
         in as root:

             gcc -o start_tomcat41180 start_tomcat41180.c
             chown root:root  start_tomcat41180
             chmod 711 start_tomcat41180
             chmod ug+s start_tomcat41180
             mv start_tomcat41180 /usr/local/bin


         Before you start tomcat, make sure that it does not already run
         (run script: ps-tomcat41880 described below), or just run
         stop_tomcat41180 program as described below. Once you are sure
         that tomcat is not running, type:

             start_tomcat41180

         to start Tomcat. 

          
     stop_tomcat41180.sh -- a shell script which sets some
         environment variables. It then calls the shutdown.sh script, which
         comes with Tomcat. It is assumed that the script resides in the
         /usr/local/tomcat-4.1.18/bin directory and is owned and
         writable only by user "tomcat41180" and that user and group
         id of tomcat41180 is 41180. Remember to change mode of this
         script once you put it where it belongs:

             cd /usr/local/tomcat-4.1.18/bin
             chmod 755 stop_tomcat41180.sh



     stop_tomcat41180.c -- a C wrapper for the stop_tomcat.sh script.
         It should be compiled, and installed as below, when you are logged
         in as root:

             gcc -o stop_tomcat41180 stop_tomcat41180.c
             chown root:root  stop_tomcat41180
             chmod 711 stop_tomcat41180
             chmod ug+s stop_tomcat41180
             mv stop_tomcat41180 /usr/local/bin

         To stop Tomcat server, type:

             stop_tomcat41180

         You may ask why I did: chown root:root stop_tomcat41180 while
         chown tomcat41180:tomcat41180 stop_tomcat41180 would
         also work? I strongly believe that files in standard public
         directories like /bin, /usr/bin, /etc, etc... should be owned by
         root, so only root can change them. What if some jerk replaced
         the stop_tomcat41180 with a script "rm -r *" and removed the setuid
         bits? It is in principle possible from within a servlet or JSP page
         run by Tomcat. The unsuspecting soul would have all its files
         removed from below the current directory. If files are owned by
         root the only thing which can be done is that the stop_tomcat41180.sh
         script (or shutdown.sh, or whatever it calls) can be messed up, 
         but they only execute as user tomcat41180 and can only mess up
         files writable by this user. In the production (as opposed to
         development environment described here) all files/directories
         under Tomcat should be owned by root with exception of the ones
         which Tomcat changes itself, i.e., subdirectories work,
         temp, and logs. 
       

     ps-tomcat41180.sh -- a shell script which lists process 
         ids (in 2nd column) of all processes which run as user tomcat41180.
         Put it in /usr/local/bin and change its mode to 755. It is a simple
         shell script which has only one line:

            ps -u tomcat41180 -fww

         It is useful to check if your (or whoever) code creates some
         stray processes from within Tomcat. Such things can be created
         (e.g., by the Runtime facility of Java in your servlets or JSPs).
         They will run with the id of the user which runs Tomcat, i.e., 
         tomcat41180. Stopping Tomcat may or may not stop all these processes.
         You may need to kill some of them manually. If something does not work
         right, and you have problems when restarting Tomcat, check what still
         runs after you Tomcat with stop_tomcat41180 command (just type: 

            ps-tomcat41180.sh

         and see what it lists). 
     
     kill_tomcat41180.c -- a C setuid program which kills processes
         owned by tomcat41180. If after stopping Tomcat with the stop_tomcat
         command the ps-tomcat41180.sh script still shows some processes,
         you may need to kill them. Be careful though, it may be the
         administrator logged in as tomcat41180, or some other legitimate
         process. Look carefully at the output of the ps-tomcat41180.sh script.
         It should be compiled, and installed as below. You need to be
         a root to run this:

             gcc -o kill_tomcat41180 kill_tomcat41180.c
             chown root:root  kill_tomcat41180
             chmod 711 kill_tomcat41180
             chmod ug+s kill_tomcat41180
             mv kill_tomcat41180 /usr/local/bin


         The program takes one or more process ids (numbers from the
         second column of the ps-tomcat41180.sh output).
         Since it is setuid tomcat41180 it should be relatively safe
         and should not kill other processes but only the ones owned by
         tomcat41180. For example if you want to kill process 23455 23458
         just type:

             kill_tomcat41180 23455 23458


     rm_work_tomcat41180.c -- a C setuid program which removes all files under the
         /usr/local/tomcat-4.1.18/work directory. The files there
         are JSP pages converted to servlets, and the Java classes crated
         from them. If they are missing, they will be recreated by Tomcat.
         Please use this script only when Tomcat is not running, since
         otherwise you may confuse Tomcat that class is there while it was
         just deleted. Why you would ever use this thing? Sometimes when
         we include files into servlets/JSPs and we change these include
         files, the change is not propagated to the compiled classes.
         Tomcat only checks the Java source code and if did not change,
         it may assume that no recompilation is necessary. In such case
         cleaning work directory may help (it will not help though for
         classes which you compiled yourself -- these you need to recompile
         yourself). You deal with it as with all others, i.e.:

             gcc -o rm_work_tomcat41180 rm_work_tomcat41180.c
             chown root:root  rm_work_tomcat41180
             chmod 711 rm_work_tomcat41180
             chmod ug+s rm_work_tomcat41180
             mv rm_work_tomcat41180 /usr/local/bin


         You clean work directory by just typing:

              rm_work_tomcat41180


8. You may want to start tomcat at boot up of your machine.
   Assuming that start_tomcat41180 and stop_tomcat41180 are in the
   /usr/local/bin directory, you can place the following very
   simple script tomcat41180
   in the /etc/rc.d/init.d directory:
------------- cut here and put into /etc/rc.d/init.d/tomcat41180 -------
#!/bin/bash
#
# Startup script for the Tomcat Web Server
#
# chkconfig: 345 84 16
# description: Tomcat is a World Wide Web server.  It is used to serve \
#              HTML, JSP, and servlets, and CGI if needed.
# processname: java

# Source function library.
. /etc/rc.d/init.d/functions

if [ -f /etc/sysconfig/tomcat41180 ]; then
        . /etc/sysconfig/tomcat41180
fi

case "$1" in
  start)
        /usr/local/bin/start_tomcat41180
        ;;
  stop)
        /usr/local/bin/stop_tomcat41180
        ;;
  *)
        echo $"Usage: tomcat41180 {start|stop}"
        exit 1
esac

exit 0
------------- cut here: end of file /etc/rc.d/init.d/tomcat41180 -------

      then do (obviously as root):

           cd /etc/rc.d/init.d
           chmod 755 tomcat41180 
           chkconfig --add tomcat41180
           chkconfig --list tomcat41180
           

    Tomcat should now start automatically at boot.

=================
If you got this far, send corrections/typos/curses/thankyous to jkl@ccl.net.