Decorative image for blog on troubleshooting Tomcat errors
January 31, 2024

Troubleshooting Tomcat Errors (And How to Fix Them)

Tomcat
Web Infrastructure

Though it's one of the most popular app servers for Java developers, Apache Tomcat can be tricky to figure out at first. If you are troubleshooting Tomcat, you've come to the right place. In this blog, we'll cover some of the most common Tomcat errors and how to fix them. We'll also explain specific issues related to request routing, garbage collection, application logging, and session management and provide solutions so you can get the most out of your Tomcat deployments.  

Back to top

How to Approach Troubleshooting Tomcat

Troubleshooting can involve investigating various aspects such as Tomcat configuration, logs, server health, and network connectivity.

Knowing where to look is the first step in quickly diagnosing and resolving issues. Here are some common Tomcat errors and possible solutions.

Back to top

10 Common Tomcat Errors 

1. java.net.BindException: Address already in use: Verify if the port specified in the server xml configuration file is already in use. You can change the port in the Connector element.

2. java.lang.OutOfMemoryError: Modify the JVM memory settings in the catalina.sh or catalina.bat file. You can set the JAVA_OPTS environment variable to increase the heap size.

3. FAIL - Application at context path [/myApp] could not be started: Check the logs in the catalina.out or localhost.log files for more details on why the application failed to start. 

4. HTTP Status 404 - The requested resource is not available: Verify that the URL and the context path for your application are correct. Ensure that your application is deployed and the context path matches. More on this topic later in this blog.

5. java.util.zip.ZipException: invalid entry size: Check your WAR file for corruption. If you have unzip installed, try listing the files in the .war file by doing: unzip -l myapp.war.unzip will indicate if there is a structural issue with the .war file.

6. javax.net.ssl.SSLHandshakeException: Check your TLS configuration in the server.xml file. Ensure that the keystore and truststore files are correctly configured by using keytool located in the JDK bin directory to list the certificates in the store: keytool -v -list -keystore /path/to/keystore

Usually there is an intermediate certificate missing from the truststore that can cause handshake exceptions. It can be easy to forget the difference between a keystore and a truststore and import a certificate into the wrong store. A keystore is used to verify public certificates presented to Tomcat by a client, and a truststore defines which certificate authorities Tomcat can trust.

7. HTTP Status 403 - Access to the requested resource has been denied: Check your security constraints in the web.xml file and ensure that the user has the necessary roles and permissions. For example, the following constraint that requires authentication for any GET and POST requests is defined in the web.xml servlet: 

<servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.openlogic.MyServlet</servlet-class>
</servlet>

<servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myservlet</url-pattern>
</servlet-mapping>

<security-constraint>
            <web-resource-collection>
                    <web-resource-name>protected</web-resource-name>
                    <url-pattern>/*</url-pattern>
                    <http-method>GET</http-method>
                    <http-method>POST</http-method>
            </web-resource-collection>

<auth-constraint>
        <role-name>tomcat</role-name>
</auth-constraint>

The role-name in the auth-constraint,tomcat must be defined in the tomcat-users.xml file located in Tomcat’s conf directory:

<tomcat-users..>
        <role rolename="tomcat"/>
        <user username="jdoe" password="s3cret" roles="tomcat"/>
</tomcat-users>

8. org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class...: There are many causes of this exception. The first place to check is in the context.xml of the web application and verify the database connection pool configuration is correct. Ensure that the JDBC driver is correctly specified, which can vary among different drivers.

9. org.apache.jasper.JasperException: Unable to compile class for JSP: An error occurred at line: 13 in the jsp file: /WebContent/test.jsp

In this example, the syntax error is indicated on line 13 of test.jsp. The issue may start at line 13, but the actual syntax error may be further down the file. Typically code brackets ‘<%’ and ‘%>’ are not placed properly in the JSP file. Using a tool like 'vi' or 'nano'can be tough to pinpoint issues. Instead, use a code editor that has visual tools to highlight incorrect syntax.

10. java.security.AccessControlException: This usually indicates an issue in Tomcat's conf/catalina.policyfile. An application may be deployed needing access to classes or file system resources in Tomcat (not recommended). Decisions to modify this file should be taken into careful consideration since an attacker could take advantage of any policy changes.

Back to top

Get the Enterprise Guide to Apache Tomcat

In this free guide, OpenLogic Tomcat experts discuss key strategies for enterprises to improve their Tomcat deployments with real-life examples and analysis. 

Download

Back to top

Tomcat Request Routing 

Request routing is directing incoming HTTP requests to the correct application based on certain criteria.  Troubleshooting request routing depends on if URL mapping, virtual hosts, reverse proxy, or load balancing is being used. Let’s take a brief look at each one.

The first place to look is in catalina.out for any exceptions when the app is started and fix those. 

Check the URL mapping in the web.xmlof the application. If you have a servlet named MyServletmapped to "/myapp", all requests to http://mydomain.com/myapp/* will be routed to that servlet:

<servlet-mapping>
   <servlet-name>MyServlet</servlet-name>
   <url-pattern>/myapp/*</url-pattern>
</servlet-mapping>

Check the localhost_access_log.yyyy-mm-dd.txtfor any 404 or 50x errors.

Are virtual hosts being used to allow multiple domains to be configured on a single Tomcat instance? Inspect the server.xml for any XML snippets like the following:

<Host name="mydomainA.com" appBase="webapps/myappA">
   <Alias>www.mydomainA.com</Alias>
</Host>

In this example, there is one domain defined that is mapped to a directory in Tomcat’s webapps directory. Check the appBaselocation that is relative to Tomcat’s installation directory for the presence of these applications. 

If Tomcat is behind a reverse proxy server (e.g. Apache HTTP Server, NGINX), the proxy can handle the initial request and then forward it to Tomcat based on certain rules. The reverse proxy can perform request routing based on URL patterns, headers, or other criteria. The mod_proxymodule handles this logic for Apache HTTP Server. Verify the ProxyPass and ProxyPassReservedirectives in httpd.confof Apache HTTP Server:

ProxyPass /myapp http://localhost:8080/myapp
ProxyPassReverse /myapp http://localhost:8080/myapp

Check the error log located in /var/log/apache2/error.log or /var/log/httpd/error_log for mod_proxy issues:

LogLevel debug

If the Apache Tomcat Connector module, mod_jk, is used to connect Apache HTTP Server with Apache Tomcat, then there are a few things to check when troubleshooting.  

First, find the location of the mod_jk.logfile by searching any *.conf files including httpd.conf for the JkLogFile directive:

JkLogFile /etc/httpd/logs/mod_jk.log

Open this file and check for any connection related issues such as:

[error] ajp_get_reply::jk_ajp_common.c (2020): (node1) Timeout with waiting reply from tomcat. Tomcat is down, stopped or network problems (errno=110)

The JkLogLevel file can be changed from info to debugif needed for further troubleshooting. This change requires a restart of Apache HTTP Server. 

Verify that the workers defined in the worker.propertiesfile are correctly configured. The location of worker.properties file is set by the JkWorkersFile.

A typical worker.properties looks like:

worker.list=jkstatus,lb_router
worker.lb_router.type=lb
worker.jkstatus.type=status

worker.lb_router.balance_workers=tomcat-node1,tomcat-node2
worker.lb_router.sticky_session=1

worker.tomcat-node1.port=8009
worker.tomcat-node1.host=tomcat_node1.openlogic.com
worker.tomcat-node1.type=ajp13
worker.tomcat-node1.lbfactor=1

worker.tomcat-node2.port=8009
worker.tomcat-node2.host=tomcat_node2.openlogic.com
worker.tomcat-node2.type=ajp13
worker.tomcat-node2.lbfactor=1

In this example, Apache HTTP Server will distribute requests equally across two Apache Tomcat nodes on Tomcat’s AJP port (8009) with sticky sessions enabled.

Check the worker.<workername>.host and worker.<workername>.portsettings to make sure they match the Tomcat server's configuration in server.xml:

<Connector protocol="AJP/1.3"
              address="::1”
              port="8009" /

From the Apache HTTP Server node, verify that port 8009 is open to the Apache Tomcat nodes.

In addition, confirm that mod_jkhandles the routing for the application to Apache Tomcat by checking the JkMount directive:

JkMount /myapp/* lb_router

Also, make sure there is a route for each worker in Tomcat’s server.xml:

<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat-node1">

jvmRouteshould match one of the workers defined in worker.properties:

worker.lb_router.balance_workers=tomcat-node1,tomcat-node2

Back to top

Tomcat Garbage Collection

If you're seeing long JVM pause times related to garbage collection, it's usually the result of using an outdated or deprecated garbage collection algorithm or poor tuning. All of the following JVM arguments can be set in Tomcat’s bin/setenv file. A few key points to keep in mind:

  • It is invaluable to have a suite of repeatable performance test scripts to achieve a baseline and then to track performance as changes are made to the JVM arguments. Apache JMeter is an excellent open source tool to use to create these scripts. This is the most important takeaway of this section. 
     
  • Before any changes are made enable garbage collection logging by specifying the following in the JAVA_OPTS:
code

This will keep five historical files of size 100MB in /tmpand prints the system time and time of the garbage collection. There are several open source tools available to analyze this output for problems.

  • Use the G1GC algorithm since it outperforms the default garbage collector in JDK 8 (Parallel GC) or CMS by specifying -XX:+UseG1GC in the JAVA_OPTS variable. G1GC is the default on Java 11, but it is not the default on Java 8. Many people do see further improvements in Tomcat performance when using G1GC on JDK 11. Please note G1GC still does require tuning.  
     
  • Set the minimum heap size (-XX:InitialHeapSize) and the maximum heap size (-XX:MaxHeapSize)to the same value to avoid dynamic heap allocation during the runtime of the application.
     
  • Set a max pause time goal for garbage collection on the JVM. The default is set to 200 milliseconds, which is a soft goal. Try to stay within 200 to 500 milliseconds, and this is set by the following JVM argument: 

-XX:MaxGCPauseMillis=<milliseconds>

  • Set -XX:+UseStringDeduplicationto reduce memory footprint of String objects on the heap. This is disabled by default.
     
  • Set the max metaspace size for class metadata kept in native memory. Start with 256MB and test by specifying: -XX:MaxMetaspaceSize=256m

There are many other JVM arguments to consider setting, but this list is good to start with.

Back to top

Tomcat Application Tuning 

Tomcat application logging is a great way to trace exactly what is going on in an application, but it can be difficult to know what packages to enable logging on because there are so many components. It is imperative that an application keep its own log separate from Tomcat’s standard out (catalina.out), so that application logging does not clutter up catalina.outas this can make troubleshooting more difficult.

logging.propertiesin Tomcat’s conf directory is the main configuration file for logging. Knowing the format of the file including the log level, package, handler (file or console) is important to get just the right amount of output to troubleshoot an application without filling up the disk or bringing production down. If the log level is too high or the package to log is too broad (i.e.org.apache.level = FINEST), this can cause the JVM to be CPU-bound at 100% utilization or saturate the disk with write requests causing IO wait. 

There is the access log valve that can be configured in Tomcat’s server.xml that can assist with understanding what requests are coming into the server and how they are handled. 

Examples and discussion on these log mechanisms can be found in my blog on application logging in Tomcat.

Back to top

Tomcat Session Management

The easiest way to troubleshoot Tomcat session management is by increasing the log level for session-related activity or generating a heap dump to see what is actually being kept in session. 

To log all session-related activity in the servlet container (Catalina) in a single node deployment, declare this statement in Tomcat’s conf/logging.properties:

org.apache.catalina.session.level=ALL

If the HA clustering component of Tomcat is being used in a multi-node deployment, then specify the following:

org.apache.catalina.ha.level=FINE

This will produce a lot of logging statements in catalina.out, so make sure that enough disk space is available.

There are a few different mechanisms to capture a heap dump, but I will cover the easiest way in this blog. 

To generate a heap dump right when the JVM runs out of heap add the following JVM argument to the JAVA_OPTS variable in Tomcat's bin/setenv:

JAVA_OPTS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof”

Ensure the path of the heap dump has plenty of disk available as multi-gigabyte heaps can cause an undesirable “disk is full” situation. Tomcat must be restarted for this change to take effect. 

A heap dump can also be triggered on demand by issuing the following command on a running JVM:

jmap -dump:live,format=b,file=/path/to/dump.hprof <JVM process ID>

jmapis located in the bin directory of the JDK.

Users sometimes store objects in heap that are much larger than what they anticipated, which include JDBC result sets or log handlers. 

Back to top

Final Thoughts

Hopefully, these fixes will help you troubleshoot Tomcat and keep things running smoothly. For more information about working with Tomcat, including best practices for patching, upgrading, memory configuration, and security, download the Enterprise Guide to Apache Tomcat.

Still Struggling With Tomcat? Talk to an OpenLogic Expert

Technical support from OpenLogic is backed by SLAs and provided directly from Enterprise Architects with 15+ years of open source experience. We can help you troubleshoot and optimize your Tomcat deployment(s) for long-term success.

Explore Tomcat Support  

Additional Resources:

Back to top