Default Tomcat installations run on port 8080 so you get urls like:
Some firewalls block port 8080 so I wanted my site to be available on port 80 so that it uses urls like:
One option was to modify the Tomcat configuration to listen on port 80. However, I already have Apache installed and listening on port 80 (to serve other content) so I couldn’t that. Instead I configured Apache to route requests for my web application to Tomcat. I’ve been through this process a number of times before, and it never seems to go smoothly, so I document it here. I am running Apache 2.2 and Tomcat 6 on 32 bit Ubuntu Linux.
The steps are:
- Download the Apache jk connector module (mod_jk.so).
- Create Apache module configuration files for the jk connector (jk.load and jk.conf) and enable the module.
- Create a worker.properties file to configure the Tomcat worker for the connector.
- Define an AJP connector in your Tomcat configuration file (server.xml)
- Assign urls to Tomcat in your Apache virtual hosts file.
Download the Apache jk connector module (mod_jk.so)
Apache uses the jk connector module to talk to Tomcat. I downloaded it from a subdirectory of http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/. I wasn’t sure which OS I was running and whether or not I was running a 32 bit version (i586 directory) or a 64 bit version (x86_64). To find this out, I ran:
file /usr/bin/file /usr/bin/file: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped
So I downloaded: http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/linux/jk-1.2.28/i586/mod_jk-1.2.28-httpd-2.2.X.so
I chose that version because I am running Apache 2.2. There are different versions for different versions of Apache.
I put the file in my Apache modules directory (/usr/lib/apache2/modules/) and renamed it to mod_jk.so.
Create Apache module configuration files for the jk connector (jk.load and jk.conf) and enable the module
In order to get Apache to load and configure the jk connector module, I created jk.load and jk.conf files (in /etc/apache2/mods-available/) and then enabled them. jk.load just tells Apache where to find the module:
LoadModule jk_module /usr/lib/apache2/modules/mod_jk.so
jk.conf ties everything together by configuring the jk connector module:
# Where to find workers.properties # Update this path to match your conf directory location (put workers.properties next to httpd.conf) JkWorkersFile /etc/apache2/workers.properties # Where to put jk shared memory # Update this path to match your local state directory or logs directory JkShmFile /var/log/apache2/mod_jk.shm # Where to put jk logs # Update this path to match your logs directory location (put mod_jk.log next to access_log) JkLogFile /var/log/apache2/mod_jk.log # Set the jk log level [debug/error/info] JkLogLevel info # Select the timestamp log format JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
For more information about the Apache jk connector module configuration, see the Tomcat Connector – Apache Webserver HowTo.
Initially I set the JkLogLevel to debug, so I could see any error messages, but then changed it to info once I had everything working.
After creating the files, I enabled the module using:
sudo a2enmod jk
That creates symlinks to the jk.load and jk.conf files in the mods-enabled directory where my Apache is configured to look for modules to load.
Define an AJP connector in your Tomcat configuration file (server.xml)
AJP is an efficient protocol that Apache and Tomcat can be configured to use to talk to each other. I set up an AJP connector in my Tomcat configuration file (/etc/tomcat6/server.xml). The default configuration file has the connector defined but commented out, so I uncommented it:
<!-- Define an AJP 1.3 Connector on port 8009 --> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
For details see the Tomcat AJP Connector documentation.
Create a worker.properties file to configure the Tomcat ajp worker for the connector
“A Tomcat worker is a Tomcat instance that is waiting to execute servlets or any other content on behalf of some web server”.
Note: this quote from the documentation is a bit curious, because, nowhere in the Tomcat configuration files do I tell Tomcat about the worker. I think that the worker is actually a process that the jk connector spawns.
I configured a worker to listen to Apache requests by creating a worker.properties file in the same directory as the Apache configuration file (/etc/apache2/workers.properties).
# Define 1 real worker using ajp13 worker.list=worker1 # Set properties for worker1 (ajp13) worker.worker1.type=ajp13 worker.worker1.host=localhost worker.worker1.port=8009
The jk connector knows how to talk to this worker, because the file name is specified in the Apache jk connector configuration file (/etc/apache2/mods-available/jk.conf). For more information see the Tomcat Connector Quick Start or the Tomcat Connector Reference Guide.
Assign urls to Tomcat in your Apache virtual hosts file
After I configured a Tomcat worker to listen to AJP requests and configured Apache to use the jk connector module to talk to that worker, the last thing that was needed was to configure my web site’s virtual host (/etc/apache2/sites-available/default) to route urls to Tomcat:
<VirtualHost *:80> ... JkMount /lms/* worker1 ... </VirtualHost>
Note that worker1 is the name I gave to the worker I set up in the workers.properties file. Note that by using a * mask, I routed all requests (including static files) through Tomcat. Alternatively I could have configured only jsp requests to be routed to Tomcat, using:
<VirtualHost *:80> ... JkMount /lms/*.jsp worker1 ... </VirtualHost>
Restart Tomcat and Apache
sudo /etc/init.d/tomcat6 restart sudo /etc/init.d/apache2 restart
To the best of my understanding, the relevant lifecycle is:
- When Tomcat starts up, it begins listening for AJP requests on port 8009 (because the connector is defined in /etc/tomcat6/server.xml).
- When Apache starts up, it loads the jk connector module (because it is defined in /etc/apache2/mods-enabled/jk.load).
- When Apache loads the jk connector, its configuration file (/etc/apache2/mods-enabled/jk.conf) tells it to send requests to the specified Tomcat worker and to use shared memory to do that.
- It is not clear to me whether or not the Tomcat worker gets spawned when Apache starts up or on each request. I don’t see how it could get spawned when Tomcat starts up since Tomcat has no way of knowing about it.
- Apache receives a request for a url that is mapped to Tomcat (in the virtual host file – /etc/apache2/sites-enabled/default).
- Apache uses the jk connector module (mod_jk.so) to generate a request to send to Tomcat via a Tomcat worker.
- The Tomcat worker communicates with Tomcat using the protocol (AJP) and port (8009) defined in the workers configuration file (/etc/apache2/workers.properties).
- Tomcat processes the request and returns the response back through the worker and connector to Apache which returns it to the client.
Kind of complicated, huh? And of course, this is just my best guess.
Questions and problems
- Not knowing which version of the JK connector to download
- Forgetting I needed to configure the AJP connector in the Tomcat configuration file
- Initially I routed only requests for JSP pages to Tomcat and so my stylesheets and images did not show up