[Maven is not so evil] Avoid webapp redeployment with hot reloading of your Maven project in Eclipse and IDEA. For free? Yes, with Spring-loaded and Tomcat7 :)

This might be the most important feature we have ever added to the (Maven) Alfresco SDK, which explains why we are getting such an amazing feedback following yesterday Tech Talk Live, where we presented the fully hot reloading (no webapp-context reload) early 2.0 version of the SDK.

BTW, if you are an Alfresco Developer and you have missed the talk, I (and not just myself) really recommend you take a hour of your time and check out the recording of the talk and / or the slide deck, because, if you are used to the sometimes frustrating world of WAR reloading (especially with a 100M+ webapp like Alfresco), I really do hope the demo of the new SDK will be no less than mind boggling for you…

Or, well, at least half as excited as I was when I first saw Ole‘s early attempts and when I finally got it working integrated in the SDK just a couple of days ago. Eureka! was my reaction to say the least (and in honor of my Sicilian past, a reference to Archimedes never hurts :).

I figured though that it’s not only Alfresco Developers that can benefit from this new approach, but more in general the community at large of Java developers using Maven to develop J2EE webapps in conjunction with an IDE (tested on Eclipse, IDEA, Netbeans).

And I know first hand the pain it takes to wait for neverending reloads, being this one of the main concerns around Java development with respect to newer / interpreted frameworks which make RAD (Rapid Application Development) their mantra.

So I figured I shared with all of you, eager blog readers, the couple of (simple) tricks that we used to define what, hopefully once and for all, can become the standard de facto to boost rapid develop Java webapps on Maven, in a completely IDE independent (but integrated) way and without the need for proprietary tools like JRebel.

So here we go. Assuming you have a WAR project, follow this steps to forget once and for all lengthy and boring WAR reloading (and contextually drink less coffeee!):

  1. The first building block is spring-loaded, a library from Spring.io (used to provide hot reloading to the GRails2 framework). This library (free of charge) provides a JVM agent which, transparently, provides class files reloading. All you need to do is get the spring-loaded.jar (e.g. from here) and then run your build with the agend enabled as such:
    MAVEN_OPTS="-javaagent:/path/to/downloads/springloaded-1.2.0.RELEASE.jar -noverify" 
mvn <goals>
  2. The second building block is based on the Tomcat7 VirtualWebapp features. This allows to: configure, via webapp context.xml, your webapp to dynamically load classpath resources (with the and webapp Loader component) and web resources (via ResourcesVirtualDirContext)  from outside a physical WAR / exploded folder. This way it’s very easy to configure your Maven <war> packaging project to automatically load classpath and web-resources directly from where Maven (or your IDE) would compile them, i.e. ${project.build.outputDirectory} (for your classpath resources) and ${project.build.directory}/${project.build.finalName} for your webapp resources. Basically you just need to create a context.xml file in your Maven project (e.g. in this example in the ${project.basedir}/tomcat folder that looks like this:
    <?xml version="1.0" encoding="UTF-8"?>
    <Context docBase="${alfresco.client.war.folder}" path="${alfresco.client.contextPath}">
      <Resources className="org.apache.naming.resources.VirtualDirContext" 
      extraResourcePaths="/=${project.build.directory}/${project.build.finalName}" />
    <Loader searchVirtualFirst="true" className="org.apache.catalina.loader.VirtualWebappLoader" virtualClasspath="${project.build.outputDirectory}" />
    </Context>
  3. Once the two building blocks are in place, you just need to wire this in the Maven pom.xml for your war packaging project. You could use Cargo but my dark memories of pain using that plugin suggest that a better, easier and more maintained route is to use the shiny tomcat7-maven-plugin. All you need to do is telling tomcat to use the previously defined context file by specifying:
     <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>run-embedded</id>
            <goals>
              <goal>run</goal>
            </goals>
            <phase>pre-integration-test</phase>
            <configuration>
               <useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader>
               <contextFile>${project.basedir}/tomcat/context.xml</contextFile>
            </configuration>
          </execution>
        </executions>
      </plugin>

    NOTE: this is going to bind the tomcat7 plugin to run your WAR, with the RAD configuration, during the pre-integration-test phase.

  4. Now you can run your build with:
    mvn clean integration-test

    and you will see your webapp run in Tomcat7. Nothing spectacular yet, but go the next point 🙂

  5. Now import your Maven project in Eclipse (Luna has already the Maven integration by default) or IDEA and there the fun begins! If you make a change to an existing Java class or web resource, all it takes is for you to hit refresh on your browser and AUTOMAGICALLY changes are there! No more webapp reloads, no more waiting time, just have fun!
  6. If you want an example of how this approach can save you huge amounts of time (and hairloss :P) check out the video below:

Hope you enjoyed what you see and hope you can leverage this ASAP! Feedback and comments are welcome, but in the meanwhile a nice collective farewell to silly webapp reloads 🙂

4 thoughts on “[Maven is not so evil] Avoid webapp redeployment with hot reloading of your Maven project in Eclipse and IDEA. For free? Yes, with Spring-loaded and Tomcat7 :)

  1. Thanks for the tutorial

    I think for MAVEN_OPTS it should be javaagent not javagent.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *