Published on Apr 16 2014 in Control Panels Java Tomcat

Custom error pages may help you to unify look of all content served by your website no matter if it is served by Tomcat, by Apache or both. In our environment error pages are either served by Apache webserver or by your application server (Tomcat in below examples) depending on mapping scheme you set.

In Java Control Panel you can define which URLs are served by webserver and which ones by application server (let's refer to it as Tomcat from now on). By default, when Tomcat is down then generic error message will be produced by Apache. In case of WHM/cPanel server generic messages are defined in /etc/httpd/conf/includes/errordocument.conf.

Let's review most common error messages:

503 - Service Temporarily Unavailable This is generic message from webserver when Apache cannot contact Tomcat to proxy requests to it.

404 - The requested resource is not available Can be generated by Tomcat or by Apache depending on which of them is set to serve the request.

403 - Forbidden Generated by Apache if permissions do not allow access to the resource.

Defining custom error pages for paths/URLs served by Apache

To change the generic messages to your own ones login to cPanel and choose Advanced - Error Pages. For example we can change the generic 503 shown when our Tomcat is down or overloaded to "We seem to be overloaded. Check again in 15 minutes or better report the problem to user at mailbox dot com".

This action will save 503.shtml in your public_html directory. But if all your requests are proxied to Tomcat (catch-all mapping for root URL '/' is our default setting) this file will be unreachable. In such case you need to additionally ensure /503.shtml is unmapped from being proxied to Tomcat. Go to Java Control Panel - Mappings and add /503.shtml to Exclude section.

Now shutdown your Tomcat and access your website. You should get the message that you defined in cPanel in custom 503.shtml page. You can also edit the file directly in public_html.

backend down 503

Defining error pages for paths/URLs served by Tomcat

In Tomcat, error pages are defined in web.xml - either global one or application specific one. Before Servlet 3.0 specification (Tomcat 7.0.x) you could define error page for either exception or response code using error-page element this way:

<error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <!-- it may point to a handler -->
    <location>/ErrorHandler</location>
</error-page>

<error-page>
    <error-code>404</error-code>
    <location>/ErrorHandler</location>
    <!-- <location>/error/404.html</location> -->
</error-page>

Starting from Servlet 3.0 (Tomcat 7) you can also use general error page/handler:

<error-page>
    <location>/ErrorHandler</location>
</error-page>

To verify it, add error-page element to webapps/ROOT/WEB-INF/web.xml and create webapps/ROOT/error/404.html with your content. Then access a non-existent URL on a path served by your Tomcat to see your custom message.

An example of custom exception and error handler servlet

Save the below code into webapps/ROOT/WEB-INF/classes/com/jvmhost/ErrorHandler.java

package com.jvmhost;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ErrorHandler extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processError(request, response); }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processError(request, response); }

private void processError(HttpServletRequest request, HttpServletResponse response) throws IOException {

Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
Throwable throwable = (Throwable)request.getAttribute("javax.servlet.error.exception");
String servletName = (String)request.getAttribute("javax.servlet.error.servlet_name");
if (servletName == null) { servletName = "Undetermined"; }
String requestUri = (String) request.getAttribute("javax.servlet.error.request_uri");
if (requestUri == null) { requestUri = "Undetermined"; }
  
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.write("<html><head><title>Exception or Error</title></head><body>");
if(statusCode != 500){ // not Exception
    out.write("<h1>Error</h1>");
    out.write("<b>Status Code:</b>"+statusCode+"<br>");
    out.write("<b>Request URI:</b>"+requestUri);
}else{
    out.write("<h1>Exception</h1>");
    out.write("<b>Servlet Name:</b>"+servletName+"<br/>");
    out.write("<b>Exception Name:</b>"+throwable.getClass().getName()+"<br/>");
    out.write("<b>Request URI:</b>"+requestUri+"<br/>");
    out.write("<b>Exception Message:</b>"+throwable.getMessage());
}
out.write("<br><br></body></html>");
}
}

Enter the jvmhost directory and compile the code, for example with

javac -cp ~/appservers/apache-tomcat-7.0.39/lib/servlet-api.jar ErrorHandler.java

Remove any other error-page elements and insert into webapps/ROOT/WEB-INF/web.xml:

<error-page>
    <location>/ErrorHandler</location>
</error-page>
<servlet>
    <servlet-name>ErrorHandler</servlet-name>
    <servlet-class>com.jvmhost.ErrorHandler</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ErrorHandler</servlet-name>
    <url-pattern>/ErrorHandler</url-pattern>
</servlet-mapping>

Restart Tomcat and access a non-existent URL on a path served by your Tomcat

Other hints

If you would like to receive only error messages defined on Apache level (either generic or custom) you may request adding ProxyErrorOverride On by support. This way you will not get 404 message (and few other ones) from Tomcat but from Apache. It may help you to unify look of your error messages as you will only be defining them on Apache level.

For SEO purposes you may also want to add <meta name="robots" content="noindex, nofollow" /> to your custom error pages.

Feel free to add any comments or questions to the article.