Embedded Tomcat : Tips, Tricks and Hacks

Introduction

Embedded Tomcat API comes handy where it only requires a few lines of code to start a tomcat instance within your application.  This makes the life easy for the users to get the useful features out from Apache Tomcat in their application.

In most cases, the users expect the embedded tomcat also to behave like the same way as a standalone tomcat instance. For example, if they want to change the global level server configuration, they expect the usage and support of server.xml for embedded tomcat as-well.

But when using embedded tomcat, this is not supported OOTB. You have to do some hacks and change the way you embed tomcat in to your application, to make this feature available.

Some of the useful features that are NOT supported OOTB in embedded tomcat are :

  1. Server descriptor (server.xml) support
  2. Global deployment descriptor (web.xml) support
  3. Webapp specific context descriptor (context.xml) support

In this post I will be explaining about how to overcome the above limitations in embedded tomcat by using some hacks and tricks.

The Tomcat class is what used in embedding tomcat in our applications. But it is better to extend this class and write it our-way. This will give the freedom to override some of the important methods which needs some custom changes. The following are the tips used in extending the Tomcat class to support the above mentioned limitations.

Tip 1 – Server descriptor (server.xml) support

The server.xml file is read/parsed and initialized by the class called Catalina in a standard tomcat instance. It creates a Digester class instance which in-turn parses the server.xml as a stream. So in our extended tomcat class, what we could do is, create an inner class which extends the Catalina and override only the method which creates the Digester instance.

    private static class MyExtendedCatalina extends Catalina {
        @Override
        public Digester createStartDigester() {
            return super.createStartDigester();
        }
    }

Then this can be used in parsing the server.xml which is given as an inputStream

    Digester digester = extendedCatalina.createStartDigester();
    digester.push(extendedTomcat);
    try {
        digester.parse(inputStreamOfServerXml);
    } catch (IOException e) {
        log.error("Error while parsing server.xml", e);
    }

You can see that the Digester is coupled with Tomcat instance in-order to configure it self with Tomcat during start-up. The above code segment should be executed in the initialization part of the Extended Tomcat instance.

Tip 2 – Global deployment descriptor (web.xml) support

This is known as the root/default deployment descriptor for all webapps. The webapps inherits the settings in this file and override those values with their webapp specific deployment descriptor values, if any. Having this file gives some advantages such as defining some global level properties for all webapps, such as , default session timeout, etc.

To get this global web.xml added to all webapps, you have to override the “addWebapp” method of Tomcat class. Then add the following into that overridden method.

    Context context = null;
    try {
        context = new StandardContext();
        context.setName(contextPath);
        context.setPath(contextPath);
        context.setDocBase(webappFilePath);
        context.setRealm(this.getHost().getRealm());
        ContextConfig contextConfig = new ContextConfig();
        context.addLifecycleListener(contextConfig);
        if (new File(pathToGlobalWebXml).exists()) {
            contextConfig.setDefaultWebXml(pathToGlobalWebXml);
        } else {
            contextConfig.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");
        }
        host.addChild(context);
    } catch (Exception e) {
        log.error("Error while deploying webapp", e);
    }

The above code segment adds/deploys a webapp to the server host as child. This is what happens with Tomcat class’s addWebapp as well. But the important thing to note here is the place (in bold) where we set global web.xml to the current webapps context using the setDefaultWebXml method. With the normal addWebapp method of Tomcat class, this is always set to NO_DEFAULT_XML.

Tip 3 – Webapp specific context descriptor (context.xml) support

As with global web.xml, there is also another useful feature, where each webapp can add some meta information using the context descriptor. It is added to the META-INF directory of the webapp. It is commonly used in adding context specific resources such as DataSource, JNDI Resources, Valves, ClassLoaders,  etc. which are specific to the webapp only. To get this support, what you should do is, add the following code segment to the same overridden addWebapp method of Tomcat class, before calling the addChild method with host object.

    JarFile webappWarFile = new JarFile(webappFilePath);
    JarEntry contextXmlFileEntry = webappWarFile.getJarEntry("META-INF/context.xml");
    if (contextXmlFileEntry != null) {
        context.setConfigFile(new URL("jar:file:" + webappFilePath + "!/" + "META-INF/context.xml"));
    }

The point to note here is the segment which is bold. It calls the setConfigFile method of the webapp’s context object, which in-turn adds the context.xml descriptor to the webapps context.

Conclusion

The above three are the most commonly used features in a normal tomcat environment. By following the above tips, you can overcome those limitations that we face when using tomcat as embedded mode. This can be further extended to get the other features supported, such as global context.xml, etc.

Advertisements

About kishanthan

I’m currently working as a Software Engineer at WSO2, an open source software company. I hold an Engineering degree, majoring in Computer Science & Engineering field, from University of Moratuwa, Sri Lanka.
This entry was posted in Java, Tomcat and tagged , , , , , , . Bookmark the permalink.

One Response to Embedded Tomcat : Tips, Tricks and Hacks

  1. Ramiro says:

    Good article, I am trying to implement something based on this, the only thing that I am not able to figure out is the jar file stuff, It seems to be the application’s war file, isn’t it? but the war file is not created until the test case passes or at least that is true for a maven environment. How should the test case be run to be able to access that war file? Thx for your help

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s