Core Java

Eliminating Development Redeploys using Gradle

For service development, my team recently moved away from Grails to the Dropwizard framework. One of the things I really missed from the Grails stack, though, was auto-reloading: any changes to source files appear in the running app moments after saving, without a restart. It proved feasible to pull this functionality into Gradle builds as well.

Spring Loaded is the library that Grails uses under its hood. It supports reloading quite a few types of changes without restarting the JVM:
 
 
 

  • Add/modify/delete methods/fields/constructors
  • Change annotations on types/methods/fields/constructors
  • Add/remove/change values in enum types

The other piece I needed was a watch plugin: something to trigger Gradle tasks when source files change.

For the full working example, clone my demo Github repository.

The first piece of setup is adding an additional configuration. This isolates the spring-loaded.jar (which is only needed during development) from the standard configurations such as compile:

configurations {
    agent
}

The dependency block reads as follows:

configurations {
dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.3.4'
    compile 'io.dropwizard:dropwizard-core:0.7.1'
    compile 'com.sun.jersey:jersey-client:1.18'
    agent "org.springframework:springloaded:${springloadedVersion}"
}

The compile dependencies are the standard set one would expect in a Dropwizard project. The line starting with “agent” adds the Spring Loaded dependency to the agent configuration defined earlier. The build script uses this dependency to get the spring-loaded.jar onto the file system. springloadedVersion is a constant defined earlier in the build file.

task copyAgent(type: Copy) {
    from configurations.agent
    into "$buildDir/agent"
}
run.mustRunAfter copyAgent

The above copyAgent task will take the spring-loaded.jar file and copy it to the build directory for later use as a javaagent. run is also configured to follow copyAgent in the chain.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.bluepapa32:gradle-watch-plugin:0.1.3'
    }
}

apply plugin: 'watch'
watch {
    groovy {
        files files('src/main/groovy')
        tasks 'compileGroovy'
    }
}

task watchThread() << {
    Thread.start {
        project.tasks.watch.execute()
    }
}
run.mustRunAfter watchThread

The above script block adds and configures watch. The buildscript block adds the proper repository and the the watch plugin as a dependency. The watch block configures the plugin; whenever there are changes in src/main/groovy, the Groovy source will be recompiled. The watchThread task executes watch parallely. This is needed because the final job will execute two tasks which both run continuously: watch and run. watch would normally block run. Finally, the run task is configured to follow watchThread when both are part of the chain.

run {
    args = ['server', 'app.yaml']
    jvmArgs = ["-javaagent:${new File("$buildDir/agent/springloaded-${springloadedVersion}.jar").absolutePath}", '-noverify']
}

task reloading(dependsOn: [watchThread, copyAgent, run])

This final bit of code configures the run command with a javaagent flag. This tells the JVM to use Spring Loaded and let it do its magic. Spring Loaded also needs the noverifyflag. The reloading task is the actual task to run during development. It strings the tasks to copy the agent, spin up a thread watching for source changes, and running Dropwizard’s main method.

This configuration structure would also support frameworks outside of Dropwizard: anything with a main method, really. Though it doesn’t work for all kinds of code changes, it can eliminate a great many application restarts during development.

Reference: Eliminating Development Redeploys using Gradle from our JCG partner Matt Cholick at the Cholick.com blog.

Matt Cholick

Matt Cholick is a JVM developer who works primarily in Groovy. He focuses on web application development and spends his time contributing to all elements of the stack: JavaScript & markup, server side, build automation, deployment processes, and infrastructure automation code.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button