Tomcat administration

From Elvanör's Technical Wiki
Jump to: navigation, search

Tomcat is a servlet container, and as such can be used to deploy web applications using Java on the server side.

Tomcat 6 on Gentoo

General

  • To launch Tomcat with an LD_LIBRARY_PATH custom value, just export this environment variable in /etc/conf.d/tomcat-6. It will work, but strangely, only on the first load of a web-application. If you undeploy the webapp and redeploy it, you will get an UnsatisfiedLinkError exception.
  • Gentoo modifies the way Tomcat behaves with respect to logging. Normally the upstream commons-logging jar is a modified version, hardcoded to use standard Java logging. On Gentoo I think this jar is present in /usr/share/tomcat-6/bin.
  • Additional jars can be added to the common / shared classloader. The Gentoo guide is outdated in this respect, but in /etc/tomcat-6/catalina.properties you can configure the places where it looks for jars. By default these are the locations:
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
shared.loader=
  • /etc/conf.d/tomcat-7 allows two variables JAVA_OPTS and CATALINA_OPTS. However, using CATALINA_OPTS to set a Java System property like -DHUDSON_HOME=/srv/net.kameleoon-dev/hudson/ does not seem to work, since CATALINA_OPTS are appended to the Tomcat launch. JAVA_OPTS should thus be used.

Issues

  • Be very careful with the permissions on the server.xml file in /etc/tomcat-6. If the read permission is not set for the others, every file created by Tomcat will be created without read permission both for group and others! This is really strange, but this is actually true.
  • OpenRC init script can create strange bugs with Tomcat (apparently if Tomcat uses external native libraries). This is due to the --background option of start-stop-daemon. Using a hand-written script works around the problem.
  • If Tomcat is extremely slow to load (25 minutes...) at startup, add the following option:
-Djava.security.egd=file:/dev/./urandom

Configuration

General

  • To increase the memory size available to Tomcat on Gentoo, use the JAVA_OPTS variable on /etc/conf.d/tomcat-6. Eg:
JAVA_OPTS="-XX:MaxPermSize=512m"
  • By default, Tomcat has autoDeploy and deployOnStartup set to true. This means any application found in the webapps directory of a host will be deployed while Tomcat is running, and that any application found at startup time will also be deployed, respectively. Beware that autoDeploy also means auto-undeploy: if you remove the WAR file corresponding to an application, Tomcat automatically undeploys the application (and removes the associated expanded directory). Note that if you remove the expanded directory, Tomcat undeploys the application but leave the WAR file intact (which means a redeployment will be triggered the next time changes are checked, since a WAR file is present in the webapps directory).
  • autoDeploy can also be used for convenient upgrading of applications.

Controlling Tomcat

  • You can control Tomcat via the web GUI (the manager web-application), or with an useful set of Ant tasks. The jar containing the ant tasks is named catalina-ant.jar. You can deploy or redeploy (update) a web application, and generally achieve everything you usually need.
  • The error count in Tomcat manager application corresponds to the number of requests that did not achioeve an HTTP status of 2xx (eg, all 404s, 500, etc).

Virtual Hosts

  • Tomcat supports virtual hosts. This is a nice way to have several webapps hosted on the root context ("/"), but at different hostnames. Follow Tomcat documentation to create virtual hosts.
  • Unfortunately, the matching on the virtual hosts is very basic. No regexp or even wildcard (*) support. So you have to exactly match the host name.
  • You can deploy a WAR archive on a virtual host using the Manager Tomcat webapp. The easiest way to do this is to deploy the manager webapp to the new virtual host. On Gentoo this is done very easily just by copying a context file /etc/tomcat-6/Catalina/localhost/manager.xml to a context corresponding to the virtual host (eg /etc/tomcat-6/Catalina/newhost/manager.xml).
  • Be careful that the name of the virtual host is the one you must use on /etc/tomcat-6/Catalina/. The name is defined on server.xml in the "name" XML attribute of the Host node. If names don't match, you won't get access to the Manager.

Concepts

Tomcat Classloading

  • Tomcat seems to be picky about classloading. In particular put your JDBC driver (along with Hibernate) in common/lib. I had troubles when it was placed in shared/lib. This seems to happen when you let Tomcat do the pooling.

Session Mechanism

  • The cookie set by Tomcat is by default set on the path corresponding to the context path of the web-application. I did not yet find a way to change this (in Jetty you can control every aspect of the session cookie).
  • UPDATE: there is a way to set the session path to "/", which will usually always work. Set the emptySessionPath attribute to true in your Tomcat Connector configuration file.
  • UPDATE 2: starting with Tomcat-7 and the Servlet Specification 3.0, everything can now be controlled precisely. Be aware that as a result, the emptySessionPath attribute no longer exists.

Tomcat Logging

stdout logging

  • stdout logging is separated from internal and webapp logging. This is done by the init script on Gentoo and everything outputted to stdout will go to /var/log/tomcat-6/catalina.out.

Internal logging

  • By default, Tomcat logging uses JULI (a reimplemented version of java.util.logging) for its internal logging. The configuration file on Gentoo is at

/etc/tomcat-6/logging.properties.

  • Switching to log4j for internal logging on Gentoo is possible but complex, as the guide is outdated. I did not succeed to achieve this, below are old notes.
  • If Tomcat finds the log4j.jar on its classpath, it will use log4j as its logging system. And if log4j is not configured at all, *nothing* will be printed anywhere. So if you want to disable log4j logging and use JDK logging, you must delete the log4j.jar, it is not sufficient to delete or remove the log4j.properties file.
  • Warning: You need to emerge commons-logging (the base library Tomcat uses for logging) with the log4j USE flag enabled, else it won't be able to use log4j logging!

Web-app logging

  • It is important to understand that the webapp logging mechanism is different from the internal Tomcat logging, and can be configured per web-app. For example, configuration settings for the manager web-app are present in the (global) JULI configuration file.
  • Configuring logging per web-app is thus up to each web-app. Usually the WAR will pack the log4j.jar and log4j.properties, or will setup Log4J programmmatically as in Grails 1.1.

Tomcat Security

  • By default, Tomcat uses a Security Manager on Gentoo. This Security Manager policy is by default, very strict. In particular, it allows JDBC connections but does not allow any kind of write operations on the webapp directory. The security policy configuration file is located at /etc/tomcat-5.5/catalina.policy on Gentoo. Adding for example the following line will make the webapps directory writable by the servlets:
permission java.io.FilePermission "${catalina.home}/webapps/-", "read, write";

The "-" at the end indicates that every file recursively has the permission applied. Don't forget that standard UNIX permissions still apply, so the tomcat user must of course be able to write to the desired directories.

Tomcat Port and Connectors

  • The base configuration file for Tomcat is named server.xml. Here you define a server (the port attribute in the server node is used only to shut down Tomcat). Inside this server you can define several connectors. The most basic connector is the HTTP 1.1 connector. It has a port attribute that you can edit in order to change the port.

HTTP connector

  • There is no configuration based way to add standard HTTP headers to the responses the Tomcat connector generates. The only way is to use a servlet filter to programmatically set those headers. Grails filters work equally well for this.
  • Another option of course, if you are behind a lighttpd proxy, is to set the headers via lighttpd mod_header. This would also work with Apache httpd.
  • By default, Tomcat decodes URI by using an ISO-8859-1 encoding. This should be changed in most cases to UTF-8. This is done by adding the URIEncoding="UTF-8" attribute to the connector node. Note that this will affect values sent as parameters to Tomcat, since those are URI-encoded, but probably not values sent in a POST request. Also note that Jetty uses UTF-8 by default for URI decoding, so this can be a difference that can cause troubles.
  • There is a limit on the size of HTTP headers. This means there is a limit on the URIs length that are accepted by Tomcat. However, this is configurable so this can be increased if needed.

GZIP compression

  • Tomcat can do gzip compression; you need to add the following to the connector configuration (don't forget to edit the mime types):
compression="on" compressableMimeType="text/html,text/xml,text/plain,text/javascript"
  • There is an option to only compress over a given file size via the compressionMinSize attribute. However this only works if the content-length is known, which usually is not the case (mostly because by default I think Tomcat uses "Transfer-Encoding: chunked".
  • Also keep in mind that if Tomcat is used behind a proxy server, the proxy server can usually compress on its own which can be better.

Web Applications

Context Path

  • Every web application has a context path. To deploy to the root context (the "/" path), currently the only way I have found is to name the WAR file ROOT.war. In Grails I also had to rename the application name to "ROOT".

Mappings to Servlets

  • The mappings from URL to servlets are declared in the webapp deployment descriptor (web.xml file).
  • Be careful - mappings are not expected to be used with the default servlet. The default servlet is intented to serve static resources *that are not mapped*. You can still map files with extension based wildcards (*.css, *.png), however using path wildcards (/css/*) won't work with the default servlet. This is probably due to the fact that the default servlet (in Tomcat at least) look at the pathInfo data, not the full request path (servletPath + pathInfo).
  • Information regarding request path elements is available at section 3.4 of the Servlet Specification. What is important to understand is that it's the responsibility of the servlet container to fill the contextPath, servletPath and pathInfo elements. Depending on the mapping used (extension mapping, directory mapping, default "/" mapping), the repartition of the URL within those sections can be different and some servlets can react differently. This is the case for Spring dispatcher too apparently.

Deployment

  • There are basically three ways to deploy a Web application with Tomcat:
    • manually using the Tomcat Manager interface;
    • via the Tomcat Client Deployer, which uses Ant to send commands to Tomcat's manager (so the manager is also required);
    • automatically via simple file copying to the webapps directory of a Tomcat virtual host (usually a WAR file would be copied). This is probably the easiest and most efficient way in most cases, as it can be done manually or via scripting.

Manager

  • The URL to access the manager is: http://${hostName}:8080/manager/status.
  • Starting with Tomcat 7, the manager context file (manager.xml) cannot just be copied from /usr/share/tomcat-7/webapps/manager/META-INF/context.xml to the Tomcat virtual host, as you need to modify the docBase attribute of the context.

Troubleshooting

  • If Tomcat reports an application as successfully deployed and running, but obviously nothing is working, check if the web descriptor file is correct. What can happen is that if the web.xml file is wrong, the application is correctly loaded but normal initialization / setup (of Spring for instance) via listeners does not take place because Tomcat has not recognized the necessary elements in the descriptor. So Tomcat sees no problem but has not actually loaded / initialized anything.
  • One extremely useful tool to troubleshoot threads on the Java platform is jstack. For example, if some of your threads are caught in an infinite loop, you can run jstack and obtain stacktraces for the current running threads - this is extremely, extremely useful. Warning: you must not run jstack as root, run it as the same user running tomcat (using sudo). Running it as root will NOT work.
  • On Gentoo with the IcedTea JDK, you need to use icedtea (and not icedtea-bin), built with FEATURES="splitdebug", to get jstack to work.