There are two ways (that I know of) to integrate Tomcat with Apache 2:
Apache can be configured to proxy requests sent to Tomcat.
Apache can pass requests to Tomcat via connectors such as mod_jk2.
I don't know if mod_jk still works for Tomcat 5. I do know that mod_webapp development seems to have lapsed, so I shall concentrate on mod_jk2 integration. If you know of other methods of integrating Tomcat and Apache, please let me know !
The Jakarta group released version 2.0.4 of the mod_jk2 connector on 26 March 2004. This release includes source packages and RPM packages for Fedora Core 1, Suse 9 and Suse SLES 8. I will only be covering the source package compilation on Fedora Core for this document.
Binary RPM packages are available for Fedora Core 1, SUSE 9.0 and SUSE SLES at the Jakarta download site or mirrors. I'm not going to be covering the RPM packages in this document.
This release cleans up a lot of the bugs and annoyances that used to plague users of the previous release. It's actually quite good -- but then, Henri Gomez does good work !
The source package cannot be accessed directly from the links on the Jakarta site (strangely enough!). And because the Jakarta site will redirect you to the nearest mirror, the location of the source package may vary from site to site. My local mirror has the source tarball in this location: http://apache.oss.eznetsols.org/jakarta/tomcat-connectors/jk2/source/
If you clicked on the link for the JK2 Binary Releases on the Jakarta site, you will need to click on "Parent Directory", then "source" directory. (Note: your location may vary!)
This document has been updated for : jakarta-tomcat-connectors-jk2-2.0.4-src.tar.gz
Because many of the complexities of setting up mod_jk2 come from the environment itself, such as the layout of the Apache 2 files, or the availability of libraries, I will need to define my environment for this exercise. If your environment differs, please note that you may have to make some changes to the instructions here.
Table 8-1. Server Components
|Linux Distribution||Java Virtual Machine||Apache version||Tomcat version||mod_jk2 version|
|Fedora Core 1||IBM Java 2 SDK v1.4.1 SR1||2.0.48||5.0.18||2.0.4|
The following libraries were also installed:
I believe that they are all required for the compilation of the JK2 source packages. If you get a /lib/cpp sanity check failure when running the configure script, check that you have the following installed:
For all the above items, I am not sure if the version matters, but these versions worked for me.
Building the mod_jk2 binary and integrating Tomcat and Apache is easier if we define a set of steps.
Integrating Apache and Tomcat with JK2
Install Tomcat and verify that it is working properly
Install Apache 2 and verify that it is working properly
Shutdown both Tomcat and Apache
Download, build and install the mod_jk2 source files
Edit the configuration files httpd.conf, jk2.properties, workers2.properties and server.xml.
Verify that everything works
Install Tomcat and verify that all the servlet and JSP examples work properly. If you worked through the earlier sections, you should already have a working Tomcat setup.
I am using the RPM packages for Fedora Core for this exercise. I will not be covering the installation of the source tarball or other RPM packages. In any case, there are plenty of resources and documentation available for this task.
Verify that Apache 2 is working by starting and stopping the http daemon and try to access web pages on it.
If you are ready to build the mod_jk2 binary from the source package, you should shutdown both Tomcat and Apache. We will start them up momentarily, after we have finished the next step.
If you have already downloaded the package, navigate to the download directory and unpack the tarball.
[chongym@localhost mod_jk2]$ tar -zxvf jakarta-tomcat-connectors-jk2-2.0.4-src.tar.gz
Now, you will need to navigate to the build directory.
[chongym@localhost mod_jk2]$ cd jakarta-tomcat-connectors-jk2-2.0.4-src/jk/native2/ [chongym@localhost native2]$ ls aclocal.m4 BUILD.txt common INSTALL.txt scripts apr build.xml configure jni server autom4te.cache CHANGES.html configure.in Makefile.in STATUS.txt buildconf.sh CHANGES.txt include README.txt tomcat
For the next few steps, you will need to be root user to build the JK2 binary.
[chongym@localhost native2]$ su root Password: [root@localhost native2]# ./configure --with-apxs2=/usr/sbin/apxs \ --with-tomcat-41=/opt/tomcat \ --with-apr-lib=/usr/lib \ --with-java-home=/opt/IBMJava2-141 \ --with-jni
When you execute "configure", the switches that you will use will probably differ from mine if you are not using Fedora Core 1, or if you built your own Apache binaries. These are my settings:
APXS path: /usr/sbin/apxs
Tomcat path: /opt/IBMJava2-141 - yes, I am using the IBM Java 2 SDK
JAVA_HOME: I have enabled the JNI switch. Why? I'm not really sure. Just that every other documentation out there says so
If the configure completed successfully, you should see these lines at the end of the process:
config.status: creating server/apache13/Makefile config.status: creating server/apache13/Makefile.apxs config.status: creating server/apache2/Makefile config.status: creating server/apache2/Makefile.apxs config.status: creating ../build.properties config.status: creating scripts/build/unix/dummy config.status: executing depfiles commands
Now, you can execute the make command,
[root@localhost native2]# make
If it completes successfully, you should see something like this:
libtool: install: warning: remember to run `libtool --finish /usr/lib/httpd/modules' /bin/cp ../../../build/jk2/apache2//usr/lib/httpd/modules/libjkjni.so ../../../build/jk2/apache2/libjkjni.so make: Leaving directory `/home/chongym/downloads/jk2/jakarta-tomcat-connectors-jk2-2.0.4-src/jk/native2/server/apache2'
We don't really need to run 'libtool', but for the sake of completeness, let's execute it as suggested by the messages above.
[root@localhost native2]# libtool --finish ---------------------------------------------------------------------- Libraries have been installed in: If you ever happen to want to link against installed libraries in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use the `-LLIBDIR' flag during linking and do at least one of the following: - add LIBDIR to the `LD_LIBRARY_PATH' environment variable during execution - add LIBDIR to the `LD_RUN_PATH' environment variable during linking - use the `-Wl,--rpath -Wl,LIBDIR' linker flag - have your system administrator add LIBDIR to `/etc/ld.so.conf' See any operating system documentation about shared libraries for more information, such as the ld(1) and ld.so(8) manual pages. ----------------------------------------------------------------------
Now, we run the APXS tool against the mod_jk2 shared library.
[root@localhost native2]# cd ../build/jk2/apache2 [root@localhost apache2]# /usr/sbin/apxs -n jk2 -i mod_jk2.so
If the apxs command executed successfully, you should see this:
/usr/lib/httpd/build/instdso.sh SH_LIBTOOL='/bin/sh /usr/lib/apr/build/libtool' mod_jk2.so /usr/lib/httpd/modules /bin/sh /usr/lib/apr/build/libtool --mode=install cp mod_jk2.so /usr/lib/httpd/modules/ cp mod_jk2.so /usr/lib/httpd/modules/mod_jk2.so Warning! dlname not found in /usr/lib/httpd/modules/mod_jk2.so. Assuming installing a .so rather than a libtool archive. chmod 755 /usr/lib/httpd/modules/mod_jk2.so
At this point I would like to explain a little about what apxs is and what it does. According to the Apache documentation, APXS is the APache eXtenSion Tool. It's stated purpose is for building and installing extension modules to Apache by building Dynamic Shared Object (DSO) files from source files. What this means is that I can add extensions to the Apache web server without needing to re-compile it.
If the apxs command executed successfully, you should find both mod_jk2.so and libjkjni.so inside the current directory, which is: your_download_directory/jakarta-tomcat-connectors-jk2-2.0.4-src/jk/build/jk2/apache2
You should then copy those two files into /usr/lib/httpd/modules/. Note that you will have to rename libjkjni.so to jkjni.so.
[root@localhost apache2]# cp libjkjni.so /usr/lib/httpd/modules/jkjni.so [root@localhost apache2]# cp mod_jk2.so /usr/lib/httpd/modules/mod_jk2.so
At this point, you have to make a choice about the type of connection channel you want to use to connect Apache with Tomcat. There are 4 types of connections.
I will only be covering the configuration for UNIX sockets in this document.
You will need to set one environment variable inside catalina.sh. Locate the following code block inside the script:
# 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
Append below it the following lines and save the file.
# Set serverRoot serverRoot=/etc/httpd2 export serverRoot
Next, you will need to edit your configuration files. Try not to use the default files that come with your source download. The default files have a lot of options inside them, and may not work.
You MUST ensure that Apache and Tomcat have been shutdown before editing these files because they may be over-written when either daemon is started.
There are 3 files you will need to edit and one file you will need to check:
You will need to make Apache2 aware of the Tomcat connector. Add the following lines into httpd.conf, at the end of the LoadModule block:
LoadModule jk2_module /usr/lib/httpd/mod_jk2.so
Backup the existing jk2.properties file in this directory, then create a new jk2.properties implementing UNIX sockets, as shown below :
# jk2.properties # Configured for channel UNIX # Set the desired handler list handler.list=apr,request,channelUnix # UNIX Domain socket location channelUnix.file=/opt/tomcat/work/jk2.socket # Dynamic Library serverRoot=/etc/httpd apr.NativeSo=/usr/lib/httpd/modules/jkjni.so
A sample workers2.properties implementing UNIX sockets is shown below :
# workers2.properties # Shared memory handling. Needs to be set. [shm] info=Scoreboard. Required for reconfiguration and status with multiprocess serve rs file=/opt/tomcat/logs/jk2.shm size=1048576 debug=0 disabled=0 # UNIX domain socket [channel.un:/opt/tomcat/work/jk2.socket] tomcatId=localhost:8009 debug=0 # define the worker [ajp13:/opt/tomcat/work/jk2.socket] channel=channel.un:/opt/tomcat/work/jk2.socket # Announce a "status" worker [status:status] info=Status worker. Displays runtime information. [uri:/jkstatus/*] group=status:status # Uri mapping [uri:/examples/*] # Uri mapping for MyFirst [uri:/MyFirst/*]
You will need to ensure that the following code block in server.xml is uncommented. By default, it already is, but if you have been tinkering with your setup, you would probably want to check, just to be sure.
<!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 --> <Connector port="8009" enableLookups="false" redirectPort="8443" debug="0" protocol="AJP/1.3" />
Earlier, you defined a file jk2.shm in workers2.properties. This file should be automatically created when Tomcat is run. Start Tomcat now, if you have not already done so and verify that the file is created and is writeable by the UNIX user that the Tomcat daemon runs under. For this exercise, that user would be 'tomcat'.
This file is crucial for communications between Apache and Tomcat, and therefore must be readable and writeable by both the Apache user and the Tomcat user. This file is also automatically generated when Tomcat starts up. This file has read and write permissions for user and group. This means that you need to put both the Apache user and the Tomcat user in the same group. For this exercise, we will put Fedora's Apache user, called 'apache' inside the Tomcat group, called 'tomcat':
[root@localhost logs]# usermod -G apache,tomcat apache
For every web application, we must supply a Uri mapping. For example, for our MyFirst web application, we simply append to our workers2.properties the following:
Now, you are ready to test. Since both Apache and Tomcat are stopped, we will need to start them up.
For the configurations defined here, the startup processes for Apache 2 and Tomcat are "de-coupled", that is, when you start Apache, Tomcat does not automatically start up as well. This is important for troubleshooting. At this point we want to know if the basic configuration works at all.
The startup sequence for our configuration is : you start Tomcat first, then Apache. On startup, Tomcat reads its own configuration files such as server.xml and web.xml first, then reads jk2.properties, from which it extracts some important information :
Which handlers to use
The port it listens on
The hostname or IP address it expects the connection from Apache to originate from
To check on the progress of the startup, do this as tomcat user,
[tomcat@localhost tomcat]$ tail -f $CATALINA_HOME/logs/catalina.out
Once Tomcat has started up, you can start Apache. It would be a good idea to view the Apache logs for details and/or errors, at this point.
To do this, as root, execute the following command :
[root@localhost run]# tail -f /var/httpd2/logs/error_log
Open a browser and key in the URL. For our web application 'MyFirst', the URL to key in for the HelloWorld servlet would be http://localhost/MyFirst/HelloWorld. If you get an error, check to see if you followed the instructions closely. Another thing to check are the error logs.
If everything works, congratulations ! You have successfully integrated Apache and Tomcat !
If you don't want to mess with the complexities of mod_jk2, but you need to put Apache in front of Tomcat, there is an alternative, and that is to setup reverse proxying in Apache.
According to the ApacheWeek website, "a reverse proxy is a gateway for servers, and enables one web server to provide content from another transparently". This can sometimes result in better response times, because of caching, and also better security for servers behind a firewall.
With all these advantages, why would one want to use mod_jk2, you may ask ? Well, that's a question I really am not qualified to answer. The only drawback that I know of, for proxying, is that your web application sees only one client, and that is the Apache server. This makes it difficult for auditing and forensics -- should the need arise. There are ways to resolve this programmatically, of course, but I will not be covering that here.
These instructions cover :
Fedora Core 2
If you choose this method of integration, be aware that you may need to know your server's DNS name and interfaces. This is because I will be using the name-based virtual hosting method on Apache. Be aware of this difficulty, especially if you do not come from a networking background.
This tour of reverse proxying assumes the following:
I will be setting up reverse proxy for a host called www.virago.com.sg. There is, of course no such host or domain on the public Internet. You can substitute your favorite domain and hostname for this name.
Tomcat and Apache exist on the same machine. If your deployment has Apache and Tomcat on separate machines, you will need to refer to the VirtualHost stanza and locate where it defines Tomcat's server hostname in the ProxyPass and ProxyPassReverse directives and make the appropriate changes.
You will need to locate the following stanza and uncomment it.
<!-- Define a Proxied HTTP/1.1 Connector on port 8082 --> <!-- See proxy documentation for more information about using this. --> <!-- <Connector port="8082" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" acceptCount="100" debug="0" connectionTimeout="20000" proxyPort="80" disableUploadTimeout="true" /> -->
Note that in the stanza above, we are using port 8082 as the reverse proxy port for Tomcat. You will need to remember this when we edit Apache's configuration.
You need to ensure that the following proxy directives inside httpd.conf are uncommented (which they normally are by default)
LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_ftp_module modules/mod_proxy_ftp.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_connect_module modules/mod_proxy_connect.so
There are actually 2 ways that you can add reverse proxying to Apache. The first involves using the <Location> directive while the second involves setting up a Virtual Host definition. I will be covering the second option.
Suppose that we have a virtual host www.virago.com.sg, and its IP address is 192.168.0.200, and we want to use name-based virtual hosting.
We add the following line into httpd.conf:
Then we setup the virtual host stanza like so:
<VirtualHost 192.168.0.200:80> ServerName www.virago.com.sg DocumentRoot /var/www/html DirectoryIndex index.html index.shtml </VirtualHost>
We only need to add a couple of lines to enable reverse proxying. I'll just show the full virtual host stanza with the two lines included below:
<VirtualHost 192.168.0.200:80> ServerName www.virago.com.sg DocumentRoot /var/www/html DirectoryIndex index.html index.shtml ProxyPass /MyFirst http://www.virago.com.sg:8082/MyFirst ProxyPassReverse /MyFirst http://www.virago.com.sg:8082/MyFirst </VirtualHost>
Before we continue, let me explain the setup above:
I am assuming that both Tomcat and Apache are installed on the same box. This is why the ProxyPass and ProxyPassReverse refer to http://www.virago.com.sg:8082 which is the URL to reach Tomcat. In your deployment, that is probably not going to be the case, so you need to replace that URL with the URL for your Tomcat server.
For the ProxyPass and ProxyPassReverse directives, note that I defined two "arguments": the first "argument", /MyFirst refers to the URL that a user keys in, while the second "argument" refers to where that URL maps to. So, if a user opens a browser and keys in http://www.virago.com.sg/MyFirst, that request gets sent to Tomcat server URL http://www.virago.com.sg:8082/MyFirst via Apache's proxy.
Note that if your server needs to be accessible from the public Internet, there may be a few things you need to change in your DNS entries, especially if you wish to expose the Tomcat server to the Internet. It is perfectly all right to hide Tomcat behind a firewall, while the Apache server sits on the DMZ, but you need to ensure that the Apache server can resolve Tomcat's address and reach it.
This is not a comprehensive explanation of virtual hosting. If you require more clarification, or if you encounter problems with this setup, or if this setup is not what you want, please refer to the Apache documentation on virtual hosting or search for it on the Internet.
To test this setup, simply start your Tomcat and Apache servers and open a browser. Type in the URL http://www.virago.com.sg/MyFirst/HelloWorld and you should see the Tomcat page come up.
A lot of the time, the reason for most difficulties is that Apache cannot reach the Tomcat server, either because of firewall rules that block access on the proxy port (i.e. 8082 is Tomcat's default. Check what your proxy port is in server.xml), or because of name resolution problems. To see if this is the case, you need to look inside Tomcat's logs. If you are using a clean system, with no special logging defined, the access log is localhost_log.(some_date).txt. Check if any requests are coming into Tomcat. If none are, then you know you have a network problem. Speak to your network administrator to resolve it.