Thursday, December 17, 2009

Run a Java web application within grails

Ever needed to run an existing java web application side by side with your grails project? I found myself in exactly that position this week, and discovered that it (like most things in grails) was rather simple to accomplish. Considering I work in a predominately Java environment, I often need to run a java web application side by side with my grails work. To date, I would simply run grails in the embedded tomcat engine (or Jetty in grails <1.2) bundled with grails, and use a local install of tomcat to power my java web apps.

I wanted to see if I could streamline the environment, and have my locally installed tomcat run my grails application. It is possible to use the excellent tomcat plugin to export a WAR of your project to an external tomcat instance via tomcat's manager application. The problem with this configuration is that you lose the "hot-deploy" features of grails which was a deal breaker for me.

After quite a bit of head scratching, googling, and yelling at my computer, I finally got pointed in the right direction from Graeme Rocher via the grails-user mailing list. If you are not aware, grails uses tomcat's embedded version under the hood as of 1.2. I was aware that grails emits numerous lifecycle events that you can hook into, but was unaware that the tomcat plugin uses this system to announce a "TomcatConfigured" event that exposes the underlying tomcat API. During startup of your grails project, the tomcat plugin will configure the embedded engine, and then announce the "TomcatConfigured" event, and pass along the newly configured tomcat. This can be used to manage and configure every aspect of the tomcat engine.

My current implementation supports only one specific web application. In the future, I plan on updating this code to support multiple applications which will be configurable via Config.groovy, which should be rather trivial.

Begin by adding your project specific configuration to Config.groovy:

grails.myproject.contextRoot = "/myproject"
grails.myproject.build.path = "/myproject/WEB-INF/classes"
grails.myproject.web.root = "/myproject/"

Next we need to add the event handler to our Grails application. This is done by simply creating a new file named _Events.groovy and placing it in the /scripts directory of your project.

import org.apache.catalina.*
import org.apache.catalina.connector.*
import org.apache.catalina.loader.WebappLoader
import org.codehaus.groovy.grails.commons.ConfigurationHolder

eventConfigureTomcat = {tomcat ->
println "### Starting load of custom application"
def contextRoot = ConfigurationHolder.config.grails.myproject.contextRoot
def buildroot= ConfigurationHolder.config.grails.myproject.build.path
def webroot  = ConfigurationHolder.config.grails.myproject.web.root

File appDir = new File(webroot);
context = tomcat.addWebapp(contextRoot, appDir.getAbsolutePath());
context.reloadable = true

WebappLoader loader = new WebappLoader(tomcat.class.classLoader)

loader.addRepository(new File(buildroot).toURI().toURL().toString());
context.loader = loader
loader.container = context

println "### Ending load of custom application"
}

You can now start you application with the grails run-app command, and your java web application will be available under the context you set in Config.groovy.

As a side note, if you need to support having an apache web server sit in fron of your application, you can add the following to turn on AJP connections for the embedded tomcat container.

// enable AJP to allow apache to front tomcat
def ajpConnector = new Connector("org.apache.jk.server.JkCoyoteHandler")
ajpConnector.port = 8009
ajpConnector.setProperty("redirectPort", "8443")
ajpConnector.setProperty("protocol", "AJP/1.3")
ajpConnector.setProperty("enableLookups", "false")

tomcat.service.addConnector ajpConnector