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("Errore 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("Errore 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.

0 comments :

Post a Comment

 
Top