Tuesday, April 5, 2011

Grails Goodness: Add Additional Web Application to Tomcat

In this post we learn how to add an additional WAR file to the Tomcat context when we use $ grails run-app. Recently I was working on a Alfresco Web Quick Start integration with Grails (current progress on GitHub). Alfresco offers inline editing of the web content with the help of an extra Java web application. This web application needs to be accessible from the Grails application, so at development time I want to deploy this web application when I use $ grails run-app at web context /awe. But this scenario is also applicable if for example we use SoapUI to create a WAR file with mocked web services and we want to access it from our Grails application.

I found two sources via Google: Grails, Tomcat and additional contexts and Run a Java web application within grails. It turns out Grails fires a configureTomcat event when we use $ grails run-app, with a Tomcat instance as argument. We can configure this Tomcat instance with additional information. With the addWebapp() method we can add an additional context to Tomcat. We can use a directory or WAR file and we must define the context name we want to use. And furthermore we can add extra directories that are added to the classpath of the additional web application. We must create a WebappLoader instance from Tomcat's classloader and then we can use the addRepository() method to add directories to the classpath.

For my use case I had a web application packaged as a WAR file: awe.war and it must be deployed with the context /awe. Furthermore extra configuration for the web application is done with a XML file found in the classpath, so we add an extra directory to the classpath of the web application.

import org.apache.catalina.loader.WebappLoader

eventConfigureTomcat = { tomcat ->
    // Use directory in the project's root for the
    // WAR file and extra directory to be included in the
    // web application's classpath.
    def aweWebappDir = new File(grailsSettings.baseDir, 'awe')
    def aweSharedDir = new File(aweWebappDir, 'shared')

    // Location of WAR file.
    def aweWarFile = new File(aweWebappDir, 'awe.war')

    // Context name for web application.
    def aweContext = '/awe'

    // Add WAR file to Tomcat as web application
    // with context /awe. 
    def context = tomcat.addWebapp(aweContext, aweWarFile.absolutePath)
    // Context is not reloadable, but can be 
    // if we want to.
    context.reloadable = false

    // Add extra directory to web application's
    // classpath for referencing configuration file needed
    // by the web application.
    def loader = new WebappLoader(tomcat.class.classLoader)
    loader.container = context

    // Add extra loader to context.
    context.loader = loader

We start our development Grails environment with $ grails run-app and the web application is available at http://localhost:8080/awe/, next to the Grails' application path.