Tomcat 5.5 / Apache 2.0 / PostgreSQL 8.1 Configuration
Sample server.xml, web.xml, httpd.conf, workers.properties and pg_hba.conf files that work.
Few configuration problems have taken me more time to figure out than getting database pooling working under Apache Tomcat 5.5. For
some reason, configuring Tomcat is a horribly convoluded process. What you state in one config file might not be listened to because
some other setting somewhere obscure is overriding it. It would seem that if you don't want to do exactly what Tomcat suggests in
their sample config files, you are basically on your own. Heck, sometimes even if you do what they suggest, things don't work! Its
amazing Tomcat has the takeup rate it does! But I digress.
What I wanted from Tomcat was a configuration suitable for servlet development and another for production that both support database
pooling. I wanted to serve pages through Apache's http server using mod_jk for the dynamic pages. I would think this would be a
common problem that has been solved many times over, but a quick scan of the net has revealed numerous pages just like this one
where people list various configuration examples tied to specific versions of Tomcat for doing what I'm trying to do. It seems like
every other example I have come across expects that I'm deploying WAR files and shows off the auto deploy feature. Well, I don't use
WAR files and I don't want to auto deploy anything. I'm developing an application that I probably will never package up like that.
All I will ever do is copy a class tree over to production. In my development framework, I want to have class files sitting right
next to source files because that's where the compiler puts them. (and I'm lazy and I don't want to have to move things around)
What follows is a configuration I slaved over for about 2 weeks. I wanted to use Apache's HTTP Server and Apache's Tomcat 5.5 servlet
runner. With the HTTP Server, I wanted to use mod_jk to trap specific requests and have Tomcat fulfill them. In Tomcat I wanted to be
able to describe a global database resource and then have one or more host configurations use them. I didn't want to use the WAR auto
deploy capability of Tomcat because I wanted to have a development friendly setup.
Generally I serve more than one domain from an Apache setup so I have a directory off of the root called /sites that contains one
directory per virtual server named the FQDN. (ie: /sites/www.example.com) Within each directory are directories named "web" for the
document tree, "logs" for the access and error logs and "conf" for site specific configuration files such as htaccess and htpasswd.
Apache HTTP Server 2.0 Setup
In this example, HTTP Server is installed in /usr/local/apache2 and serves documents from /sites/www.example.com/web. Here
we load the JK Module and define a virtual host for www.example.com with JkMount directives. (most of the standard httpd.conf file is
stripped out of this example)
/usr/local/apache2/conf/httpd.conf
...
# load and configure JK Module
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /usr/local/apache2/conf/jk-workers.properties
JkLogFile /usr/local/apache2/logs/mod_jk.log
# Virtual hosts
<VirtualHost www.example.com:80>
ServerAdmin webmaster@example.com
DocumentRoot /sites/www.example.com/web
ServerName www.example.com
ErrorLog /sites/www.example.com/logs/error.log
CustomLog /sites/www.example.com/logs/access.log common
JkMount /*.jsp web01-worker
JkMount /index.html web01-worker
JkMount /login web01-worker
JkMount /logout web01-worker
JkMount /show/* web01-worker
</VirtualHost>
|
The jk-workers.properties file describes the attributes of the worker we defined in the httpd.conf.
/usr/local/apache2/conf/jk-workers.properties
worker.list=web01-worker
worker.web01-worker.type=ajp13
worker.web01-worker.host=localhost
worker.web01-worker.port=8908
|
PostgreSQL 8.1 Setup
I'm not going to go into this much as it's covered elsewhere, but you will need the JDBC driver for your version of
PostgreSQL in the common library area. Mine is in:
/usr/local/tomcat/common/lib/postgresql-8.1-404.jdbc2.jar
Your pg_hba.conf file that controlls access to PostgreSQL might open up to all hosts supplying a password:
/usr/local/pgsql/data/pg_hba.conf
# TYPE DATABASE USER CIDR-ADDRESS METHOD
local all all password
host all all 0.0.0.0/0 password
|
Apache Tomcat 5.5 Setup
Tomcat is installed in /usr/local/tomcat and generally configured with two files plus a web.xml for each instance.
Server-wide configuration is done in /usr/local/tomcat/conf/server.xml and /usr/local/tomcat/conf/web.xml and site
specific configuration is done in /sites/www.example.com/web/WEB-INF/web.xml. I have tried to mantain this
seperation as much as possible but Tomcat idiosyncrasies have limited me. If you find a better and more clear way
of doing this that works universally, please let me know via the email address at the bottom right of this page.
In the server.xml file we define the Global Naming Resource "jdbc/testdb-global". Any number of Host declarations
(here we have one) can refrence that global resource and make it available under a new name with ResourceLink. In
this example, we call the database resource "jdbc/testdb". I also use a random string and a non-standard port for
server shutdown so its a little harder for local users to shut things down and cause problems.
/usr/local/tomcat/conf/server.xml
<?xml version='1.0' encoding='utf-8'?>
<Server port="8907" shutdown="2FA35BC73A9C65A985C8E45CA4B97ABC"
debug="0">
<Listener
className="org.apache.catalina.mbeans.ServerLifecycleListener"
debug="0"/>
<Listener
className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"
debug="0"/>
<GlobalNamingResources>
<Resource name="jdbc/testdb-global" auth="Container"
type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://1.2.3.4/testdb"
username="testdb" password="superSecretPassword" />
</GlobalNamingResources>
<Service name="service">
<Connector name="connector" address="127.0.0.1" port="8908"
protocol="AJP/1.3" enableLookups="false" />
<Engine name="engine" defaultHost="www.example.com"
debug="0">
<Host name="www.example.com" debug="0"
appBase="/sites/www.example.com/web" unpackWARs="false"
autoDeploy="false">
<Logger className="org.apache.catalina.logger.FileLogger"
directory="/sites/www.example.com/logs"
prefix="tomcat-" suffix=".log" timestamp="true"/>
<Context path="" docBase="" reloadable="true"
swallowOutput="true">
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<Logger className="org.apache.catalina.logger.FileLogger"
prefix="www-example-com-log." suffix=".txt"
timestamp="true"/>
<ResourceLink name="jdbc/testdb" global="jdbc/testdb-global"
type="javax.sql.DataSource"/>
</Context>
</Host>
</Engine>
</Service>
</Server>
|
We set some server-wide conventions in Tomcat's main web.xml file. In this example, we set some servlet mappings
for common things like jsp and set some other usefull defaults.
/usr/local/tomcat/conf/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
|
For each site, we create a site specific web.xml file. We reiterate our jndi resource "jdbc/testdb" and set up
our main servlet with some URLs mapped to it.
/sites/www.example.com/web/WEB-INF/web.xml
<web-app>
<display-name>Example Application</display-name>
<description>Just serves as an example.</description>
<resource-ref>
<description>
Resource reference to a factory for java.sql.Connection
instances that may be used for talking to the database
that is configured in server.xml.
</description>
<res-ref-name>jdbc/testdb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>handler</servlet-name>
<description>Handles everything thrown at it.</description>
<servlet-class>com.example.handler</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>handler</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>handler</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>handler</servlet-name>
<url-pattern>/logout</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>handler</servlet-name>
<url-pattern>/show/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
|
Notes:
All your class files would live in /sites/www.example.com/web/WEB-INF/classes in this example.
For example, the handler servlet described in the example would live in:
/sites/www.example.com/web/WEB-INF/classes/com/example/handler.class
You will find the output of System.err.println( "..." ) in:
/usr/local/tomcat/logs/catalina.out
Ideally STDERR and STDOUT would be split up by site and live in the logs directory of each site (/sites/www.example.com/logs) but
we are still working on a configuration that makes that happen reliably. If you have any input on
this, please contact me through the address at the bottom right of this page. I'll update this as soon
as we have a working solution.
If you can't get the resource ref going in /usr/local/tomcat/conf/server.xml, try declaring it in the
/usr/local/tomcat/conf/context.xml file. In my example above, I don't use a context.xml file but this seems to be needed
in one of our instances. To be clear, my example above comes from Tomcat 5.5.12 and when Tomcat 5.5.12 is installed on
another instance, the context.xml seems to be required. We can't seem to figure out what the critical differience is
though we haven't done an exhaustive search.
I'm trying to clean this up right now but haven't delved much into this. If anyone has tested and working examples of
a configuration that writes logs into the site specific log directory on a site by site basis, I'd be interested to
hear from you.
Some of my jsp definitions can probably be stripped out of the site specific web.xml and moved into the server-wide
web.xml in this example. I'm still working on cleaning these things up but all code on this page comes from running
production instances so it can be considered fully tested. I'll change this page once I clean up my code and test to make
sure everything is still working.
Keep in mind that the url-pattern in the servlet-mapping section is horribly restricted. You would think you could
trap a URL like /abc*.html with it but you can't. You are allowed to have an asterisk as either the first character or the
last, but nowhere else. The JkMount directive in the Apache httpd configuration is significantly more flexible. If you need
to trap a URL such as /abc*.html, you can trap /abc*.html in the Apache httpd configuration and then trap just *.html in the
url-pattern in your WEB-INF/web.xml file. Its somewhat of a hack but it can get you around the servlet-mapping limitations
in some situations.
|