Thursday, March 24, 2016

Integrating JRebel into a Spring Boot Gradle run

This post talks about integrating JRebel with a Spring Boot Gradle run; it builds upon my last post on How to remote debug a gradle run.

Environment:
Windows 7
Gradle 2.9
Java 1.8.0_66

Step 1: Update build.gradle file

I am using gradle version 2.9, so was able to put the following after the buildscript {} script block in build.gradle:
plugins {
  id "org.zeroturnaround.gradle.jrebel" version "1.1.3"
}

My project is building a war file, so I added the following after apply plugin: 'war', right before the applicationDefaultJvmArgs configuration:

war.dependsOn(generateRebel)

Now, after running 'gradle build' or 'gradle war', I see the rebel.xml file is generated here and with the following contents:
$ find . -name rebel.xml -print
./build/classes/main/rebel.xml

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd">

  <classpath>
    <dir name="D:/workspaces/cherryshoe-team/cherryshoe/cherryshoe-app/build/resources/main">
    </dir>
    <dir name="D:/workspaces/cherryshoe-team/cherryshoe/cherryshoe-app/build/classes/main">
    </dir>
  </classpath>

  <web>
    <link target="/">
      <dir name="D:/workspaces/cherryshoe-team/cherryshoe/cherryshoe-app/src/main/webapp">
      </dir>
    </link>
  </web>

</application>

This article helped a lot.

NOTE:  You could check in the rebel.xml in your code base at this point, and add in a ${workspace.home} variable and then add in the workspace.home variable to your JVM, so each developer could use the generated rebel.xml file at this point.  Or we could have each developer run 'gradle build' or 'gradle war' one time to generate this file themselves for their specific environment.

Step 2: Activate jrebel for standalone agent
Install JRebel without installing any IDE plugins
  • Download current stable version of jrebel no setup zip https://zeroturnaround.com/software/jrebel/download/
  • Standalone install: http://manuals.zeroturnaround.com/jrebel/standalone/index.html
  • Extract the jrebel stand alone zip to C:\Apps
  • Read readme.txt, which says to run bin\activate-gui.cmd to activate the license (Use an activation code, company license server url, or your stand-alone jrebel license file)
  • Add in JREBEL_HOME to system environment variables

Step 3:  IDE configuration (if using eclipse for example)
  • Project -> Build automatically is checked
  • The compilation output directory of the eclipse IDE and the JRebel monitored classes directory (defined in rebel.xml) 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 both 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).
    • Right click on Project -> Build Path -> Configure Build Path -> Source Tab -> Default output folder change it from <project name>/bin to <project name>/build/classes/main
    • NOTE:  I have a problem right now when I change the default output folder in eclipse where when I run JUnit unit or JUnit integration tests, eclipse says it can't find the compiled class files.  I still need to figure this out while also having JRebel work, so I don't have to switch back and forth.

Step 4:  Enable jrebel javaagent with the JVM options for the java application
I decided to solve this by updating JVM options via the build.gradle application plugin applicationDefaultJvmArgs property.
  • Update applicationDefaultJvmArgs to include jrebel configuration
OLD:
applicationDefaultJvmArgs = [
    "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
]

NEW:
applicationDefaultJvmArgs = [
    "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005", "-javaagent:C:\\Apps\\jrebel\\jrebel.jar"
]

Make sure the parameters are in this format:  ["-Dparam1", "-Dparam2"].  I originally updated it to the below, but got an error since the gradle task was trying to fork another process [detailed error below].

WRONG:
applicationDefaultJvmArgs = [
    "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -javaagent:C:\\Apps\\jrebel\\jrebel.jar"
]

ERROR:
$ gradle bootRun
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:findMainClass
:bootRun
FATAL ERROR in native method: JDWP No transports initialized, jvmtiError=AGENT_ERROR_TRANSPORT_INIT(197)
ERROR: transport error 202: bind failed: Cannot assign requested address
ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510)
JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialized [debugInit.c:750]
:bootRun FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':bootRun'.
> Process 'command 'C:\Apps\Java\jdk1.8.0_66\bin\java.exe'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 10.85 secs


You don't need to add any configuration in <HOME>/.gradle/gradle.properties to have this work.

Helpful documentation:



3 comments:

  1. Hi, How do I configure my social license for the Jrebel for standalone application. I have set up mu IDE, could connect to my production environment. But on the server jrebel log is saying "No license found".

    ReplyDelete
    Replies
    1. What steps have you done so far for the Jrebel social license?

      Delete
  2. Hi, How do I configure my social license for the Jrebel for standalone application. I have set up mu IDE, could connect to my production environment. But on the server jrebel log is saying "No license found".

    ReplyDelete

I appreciate your time in leaving a comment!