Showing posts with label JRebel. Show all posts
Showing posts with label JRebel. Show all posts

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:



Sunday, November 18, 2012

Save Time - Integrating JRebel Development with Alfresco Customization


For the past two months or so, my team has been using JRebel 5.0.1 with our custom Alfresco development to minimize application redeploys during development.    It has been a LIFESAVER TIMESAVER!  I am hooked and won't ever go back to doing Java development without it!  Personally for the past 30 days, JRebel has prevented at least 314 redeploys/restarts saving me about 12.7 hours. (Which it tells you each time you start your application server locally).

I'm working on an Alfresco 4.0.2 customization project built with Alfresco web scripts where we store content in the Alfresco repository, Spring configuration to inject our custom Java-backed web scripts, and Spring's JDBC template to store and retrieve metadata about the content.  In conjunction with debug options turned on and attaching to the remote java app, multiple JUnit integration tests for each of the 18 custom web scripts we are writing, and the key - JRebel development integration - this has started being one of the most awesome development experiences I've only just had the opportunity to enjoy!

Here's a small blurb from the JRebel page on what it does:
"Save. Refresh. Enjoy. And stop redeploying in Java!
JRebel is a JVM-plugin that makes it possible for Java developers to instantly see any code change made to an app without redeploying. JRebel lets you see code changes instantly, versioning classes and resources individually and updating one at a time instead of as a lump application redeploy. When developers make a change to any class or resource in their IDE, the change is immediately reflected in the deployed application, skipping the build and redeploy phases and preventing an average of 5.25 work weeks per year in redeploys!"
For a detailed list, please refer here.

JRebel has detailed instructions on how to install with your IDE of choice, here's what I had to do:
  • View the instructions to install the plugin for your IDE (eclipse in our case) - http://zeroturnaround.com/software/jrebel/download/installing-jrebel-plugin-for-eclipse/
  • Install with Eclipse update manager
  • Restart Eclipse, window should pop up to Activate JRebel license.  Can also Open JRebel perspective and go to ‘Activation’ tab
  • Point to JRebel license, or use the link from the JRebel server, download client.lic and point to that file
  • Open JRebel perspective, navigate to ‘Overview’ tab, choose the project to have JRebel recognize
  • Right click on project root, JRebel, ensure that ‘Automatically generate jrebel.xml’ is enabled.  This creates the jrebel.xml file (tells what classes to auto deploy) in /src/main/resources
  • Ensure Project -> Build Automatically is enabled
  • Configure tomcat server where Alfresco is deployed.  In JRebel perspective, navigate to ‘Overview’ tab.  If you use 'External Server', click there to get the JVM options you will need to add to your tomcat JVM.  I have an alfresco.bat file that starts up my local tomcat server.  Add the JRebel JVM options to your JAVA_OPTS.
rem set debug options
set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=n

rem set jrebel options
set JREBEL_OPTS=-javaagent:"C:\Apps\springsource\sts-2.9.2.RELEASE\plugins\org.zeroturnaround.eclipse.embedder_5.0.1.RELEASE-201207191833\jrebel\jrebel.jar" -Drebel.workspace.path="C:\workspaces\opm-team" -Drebel.log.file="C:\Users\jhsu\.jrebel\jrebel.log" -Drebel.properties="C:\Users\jhsu\.jrebel\jrebel.properties"

rem Set any default JVM options
set JAVA_OPTS=%DEBUG_OPTS% -Xms512m -Xmx512m -Xss512k -XX:MaxPermSize=256m -XX:NewSize=256m -server -Dalfresco.home=%ALF_HOME% -Dcom.sun.management.jmxremote %JREBEL_OPTS%
  • Deploy once more from scratch so the jrebel.xml will be on the target
  • NOTE:  Refreshes anything that goes in target/classes or target/test-classes (as defined in rebel.xml)
  • NOTE: Won’t refresh the web script descriptor files yet, have to do that manually
NOTE: There is a bug that I noticed with version 5.0.1 last week when I changed code inside the constructor.  Since both adding / removing constructors and changes to method bodies are claimed to be supported by JRebel, I was surprised at this.  *Edit:  We are using spring's default scope of singleton for this particular spring bean definition.*
  • We're not using Spring constructor injection so it's not this: http://zeroturnaround.com/forums/topic/does-jrebel-see-constructor-implementation-change-for-spring-component/
  • This article says re-invoking the constructor will break the existing bean’s state so this is very unlikely that this will ever be implemented, then why does JRebel state say adding / removing constructors is supported? http://zeroturnaround.com/forums/topic/jrebel-5-0-x-does-it-invoke-constructor-on-spring-bean-on-reloading-of-it/