JRebel Gradle plugin

The JRebel Gradle plugin is designed to generate the rebel.xml configuration file for your projects.

Tip

When using a JRebel IDE plugin, it is recommended to generate rebel.xml using the IDE plugin. Generating rebel.xml using the JRebel Gradle plugin is intended for situations when generation using the IDE plugin is not available.


Download

Download the JRebel Gradle plugin at GitHub: https://github.com/zeroturnaround/gradle-jrebel-plugin. The source is available under Apache License 2.0.


Enabling JRebel for Gradle

Add the following to your build.gradle script. This adds a dependency for Gradle. Do not mix this up with your project’s project level dependencies – you need to put this code into the buildscript {..} block.

apply plugin: 'rebel'

buildscript {
  repositories {
     mavenCentral()
  }

  dependencies {
     classpath group: 'org.zeroturnaround', name: 'gradle-jrebel-plugin', version: '1.1.5'
  }
}

When using Gradle 2.1 or newer, add the following snippet to the top of your build.gradle script:

plugins {
  id "org.zeroturnaround.gradle.jrebel" version "1.1.5"
}

This will provide your Gradle build with a new task called generateRebel. You will probably also want to make the generateRebel task part of your main build flow, instead of executing it manually after every clean. A good place for it in is right before the task that generates the build archive (the JAR or the WAR).

When your project uses the Java plugin:

jar.dependsOn(generateRebel)

And when your project uses the WAR plugin:

war.dependsOn(generateRebel)

In many cases, this is all you need to do. The plugin should be able to read the locations of your classes and resources from Gradle’s project model and put them into your rebel.xml.


Additional configuration

In some cases, out of the box configuration is insufficient. You need to explicitly set some configuration. To get an idea about this, start off by having a look at the rebel.xml that was generated based on the defaults, and figure out where is it going wrong. You can override the default settings or customize the behavior of JRebel Gradle plugin by adding a rebel {} element into your build.gradle, specifying any of the parameters below:

rebel {
  /*
   * alwaysGenerate - default is false
   *
   * If 'false' - rebel.xml is generated if timestamps of build.gradle and the current rebel.xml file are not equal.
   * If 'true' - rebel.xml will always be generated
   */
  alwaysGenerate = true

  /*
   * showGenerated - default is false
   *
   * If set to true, generated rebel.xml will be printed out in console during the build, so you can immediately see what was generated.
   */
  showGenerated = true

  /*
   * rebelXmlDirectory - default is 'build/classes'
   *
   * Output directory for rebel.xml.
   */
  rebelXmlDirectory = "build/classes"

}

Configuring <classpath>

The <classpath> element in rebel.xml defines which locations are monitored by JRebel for new versions of your classes. If you use Gradle’s Java or WAR plugin, the plugin will ask for the classes output location from your Gradle project model. In many cases this will be sufficient to make class reloading work (you just have to check that your IDE is also auto-compiling your classes into that same directory). In that case, you can just completely leave out the classpath { .. } block and the defaults will be used.

If for some reason the plugin is not getting it right, or you want to add additional classpath locations to your rebel.xml or explicitly fine-tune excluded or included resources, you can do so by providing classpath { .. } section in your build.gradle DSL:

rebel {
  // (other config)

  classpath {
    resource {
      directory = "build/main/other-classes-dir"
      includes = ["**/*"]
      excludes = ["*.java", "*.properties"]
    }

    // the default element
    resource {}

    resource {
      directory = "build/integration-tests/classes"
    }
  }
}

Each resource {..} element will define one classpath search location in your rebel.xml. The empty resource {} element has a special meaning - this is a placeholder for the default classpath location asked from the Java or War plugin. It can be used to control the order where the default location will be placed in the generated rebel.xml. For example, the above configuration would generate a rebel.xml that instructs JRebel to search for a class from these directories in that same order:

  1. build/main/other-classes-dir
  2. [the default compilation output directory known by your Gradle Java plugin]
  3. build/integration-tests/classes

If you omit the empty resource {} block, the default classpath will be added as the first element into your rebel.xml. If you want the default classpath not to appear at all, use the omitDefault configuration option:

rebel {
  // (other config)

  classpath {
    // don't add the default classes target directory
    omitDefaultClassesDir = true

    // don't add the default resources directory
    omitDefaultResourcesDir = true

    resource {
      directory = "build/main/other-classes-dir"
      includes = ["**/*"]
      excludes = ["*.java", "*.properties"]
    }
  }

}

Customizing the root path

You have the option to explicitly specify the root path of your workspace, should the JRebel Gradle plugin for any reason get it incorrectly by default or should you have multiple symlinks/aliases for the same folder and you want to specify which one to use.

Notice that for default resources, the plugin will check if the default directories prepended by this workspace root path really exist. If they don’t, they will not be added to rebel.xml. Non-default locations (i.e. the ones that you define manually in your classpath { .. } and web { .. } blocks) are added even if they don’t exist at the moment when the plugin executes.

You can add the rootPath configuration option to your build.gradle as all the other configuration options:

rebel {
  rootPath = "/opt/my-project"
}

This would probably annoy your colleagues, who would get this configuration from the SCM although they might have different workspace path in their environment. Therefore, it is better to provide this configuration option in your personal gradle.properties file that is not added to the SCM:

rebel.rootPath = /opt/my-project

You can also provide it directly on the command line:

gradle build -Prebel.rootPath=/opt/myproject

You can use this option to generate a rebel.xml that contains a placeholder root directory so that every developer can specify the value for it via a JVM argument when executing their JRebel-enabled JVM:

build.gradle:

rebel {
  rootPath = "\${my-workspace}"
  classpath {
    resource {
      directory = "build/classes/main"
    }
  }
}

Fragment of the generated rebel.xml:

<classpath>
  <dir name="${my-workspace}/build/classes/main">
  </dir>
</classpath>

Now when running the JVM, each developer would just add an extra JVM argument like -Dmy-workspace=/opt/my-project.


Configuring <web>

The <web> element is valid for war projects and lets you map specific locations of your workspace against specific locations inside your war archive. An example: your war contains a folder /WEB-INF/jsps, the contents of which come from your workspace folder src/main/jsps. In order to make the changes you make to JSPs in your workspace immediately available in your deployed application, you have to define a mapping in your rebel.xml. The corresponding mapping is created by the first resource { .. } block in the example below:

rebel {
  // other config ..

  web {
    resource {
      directory = "src/main/jsps"
      target = "/WEB-INF/jsps"
    }

    resource { }

    resource {
      directory = "src/main/WEB-INF-resources"
      target = "/WEB-INF/"
      includes = ["**/*.xml"]
      excludes = ["*.java", "*.groovy", "*.scala"]
    }
  }
}

The empty resource {} block here has similar meaning and properties as the one in classpath {..} configuration block. It can be used to control the placement of the default resource-mapping element. The default resource-mapping maps your war’s root to the main webapp directory known by Gradle’s project model. Also here you can use the omitDefault setting to completely exclude the default configuration from the generated rebel.xml:

rebel {
  // other config ..

  web {
    omitDefault = true
    resource {
      directory = "src/main/jsps"
      target = "/WEB-INF/jsps"
    }
  }
}

Once again, omit the web {..} configuration block as a whole if you are satisfied with the defaults.


Configuring <war>

You can also add the element to your rebel.xml by adding the following to your build.gradle. Refer to JRebel manual for details on the meaning of the element.

rebel {
  war {
    path = "build/dist/my-other-webapp.war"
  }
}

IDE configuration

Please note that the Gradle Eclipse plugin does not seem to generate project files that would configure Eclipse to auto-compile your classes into the same folder where Gradle is compiling them. JRebel class reloading relies on your IDE to automatically re-compile your classes, so that JRebel can pick them up.

The compilation output directory of your IDE and the monitored classes directory have to match in order for the class reloading to work. Therefore, make sure that your IDE is compiling classes into the same directory where your Gradle project model and the rebel.xml file are expecting them (build/classes/main by default, as opposed to bin which is the default for Eclipse).


Writing a Gradle task

If for some reason the JRebel Gradle plugin does not fit your requirements, you can easily write a custom Gradle task to automatically generate rebel.xml during the build. For example:

task generateRebel << {
  def rebelFile = sourceSets.main.output.classesDir.absolutePath + '/rebel.xml'
  def srcWebApp = project.webAppDir.absolutePath
  def writer = new FileWriter(rebelFile)
  new MarkupBuilder(writer).application() {
    classpath{
      dir( name:sourceSets.main.output.classesDir.absolutePath )
    }
    web{
      link(target:'/'){
        dir(name:srcWebApp)
      }
    }
  }
}
war.dependsOn generateRebel