Use HotswapAgent with NetBeans to Speed up Development

Posted by Aminul Karim on July 12, 2018
With Java, it’s always necessary to rebuild the source code in bytecode, which can be then safely updated only by restarting the whole application. And all developers know that restoring the desired state of the application after a fresh restart takes time and is tedious.

Many developers know that JRebel can help a lot with updating the code on the fly. But recently they have stopped providing the free service with personal jRebel account.

Then I came across an open source alternative called HotswapAgent which has worked very well for me for my personal Java EE projects. In this post, I’m going to write up how I got it running in my IDE and Payara Server.

Getting started with HotswapAgent

HotswapAgent itself is just a java agent, which has to be attached to the application. It essentially scans the classpath to detect the presence of known frameworks and tries to refresh the frameworks after a code or resource change is detected. It’s also possible to write custom plugins for any project or even specifically for your application. However, getting the most of HotswapAgent requires installing an alternative DCEVM engine into your JRE installation, which does a much better job in reloading code changes than the standard VM engine in the HotSpot VM. The alternative VM engine is basically a patched version of the standard engine, which improves code hot swap via the debugging interface, without impacting other functions of the JVM.

Important Note

  • If you are currently using jRebel ,uninstall that plugin first
  • I have found that Hotswap agent only works on debug mode , Remember to debug your project after you have configured hotswap agent ,
  • In case you have added new class Or packages,You just need to click on Apply code changes button that's beside netbeans debug button. You will need to click on Apply code changes also in case any of your code changes in those newly added classes wasn't hotswaped
  • In case you have added multiple classes / renamed an existing class/ implemented new interface then goto each individual class and click on Apply code changes button and your code change should be applied with debugger console logs like following:
            
            Classes to reload:
            com.testpack.TestClass
            Code updated
            

Required Configurations

To use HotswapAgent with DCEVM while developing applications with NetBeans, the following changes are required:

1. Check your java version with java -version command and download appropriate DCEVM from https://github.com/dcevm/dcevm/releases Also download hotswap-agent-1.3.0.jar from https://github.com/HotswapProjects/HotswapAgent/releases

2. from your terminal run your DCEVM installer jar as an admin, with following command java -jar DCEVM-8u172-installer.jar, NB: DCEVM-8u172-installer.jar can have different version name based on you java installation

3. The DCEVM installer window will open, select the java installation and click on Install DCEVM as altjvm button, after clicking it will set the Installer altjvm field to yes

4. Now for Payara integration, copy hotswap-agent-1.3.0.jar, go to your payara server directory on disk, finally paste the jar into /glassfish/domains/domain1/lib/ext/ folder , finally the path would be glassfish/domains/domain1/lib/ext/hotswap-agent-1.3.0.jar

5. Again for linux user open glassfish/config/asenv.conf file into your notepad++ and specify that the AS_JAVA
environment variable points to the Java installation like : AS_JAVA = /path_to_jdk/ If you are using windows , open glassfish/config/asenv.bat file into your notepad++ and specify that the AS_JAVA
environment variable points to the Java installation like this set AS_JAVA=C:\Program Files\Java\jdk1.8.0_171

6. Now start your payara server from Netbeans-> Sevices -> Servers -> Payara , After the server has started open http://localhost:4848/ on your browser , click Configurations -> server-config -> JVM Settings , now click on JVM Options tab and next click Add JVM Options button

  • remove any -client Or -server option from JVM options list and click save
  • Add -XXaltjvm=dcevm option and click on Save button , restart your payara server from netbeans
  • Again add -javaagent:"/path_on_your_disk/hotswap-agent-1.3.0.jar" option , again restart payara server from netbeans
  • Now click the General tab just beside the Path Settings , enable Debug checkbox , set value of Debug Options: text box to -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9009

7. Now back to Netbeans , Click Debug menu -> click Attach Debugger , A Popup will open select Java Debugger , select SocketAttach as connector, set dt_socket as Transport , 9009 as port ,localhost as Host and finally click ok to save ,

  • Now goto Tools -> Options -> Java -> Java Debugger and tick the Apply code changes after save option
  • Right click on and open Project properties, goto Build -> Compile and click Compile on Save
  • Right click on the project and go to project properties -> Run -> Deselect Deploy on Save -> Click OK to save this settings
  • Right click on your WEB-INF folder and add the hotswap-agent.properties file, the contents of that file can be found in the Properties file Configuration section of this doc

8. Right click on your project , goto Project Properties -> Actions -> Debug project , At Set Properties text box add following new line
Env.MAVEN_OPTS=-XXaltjvm=dcevm -javaagent:"/path_on_your_disk/hotswap-agent-1.3.0.jar"

9. After following step 1-8 you are ready , Start to debug your project , if hotswap is properly configured you should see following logs at the beginning of your debug session

HOTSWAP AGENT: 16:04:39.142 INFO (org.hotswap.agent.HotswapAgent) - Loading Hotswap agent {1.3.0} - unlimited runtime class redefinition.
            HOTSWAP AGENT: 16:04:39.375 INFO (org.hotswap.agent.config.PluginRegistry) - Discovered plugins: [Hotswapper, JdkPlugin, AnonymousClassPatch, ClassInitPlugin, WatchResources]
            Listening for transport dt_socket at address: 9009
            

After you save changes to a class from netbeans, netbeans will show Updating Your App at the bottom then hotswap redefines changed class on run time and it shows logs like following:

Info:   HOTSWAP AGENT: 16:59:56.630 RELOAD (org.hotswap.agent.plugin.jvm.AnonymousClassPatchPlugin) - Class 'com/testPack/TestClass' has been enhanced with anonymous classes for hotswap.
            

Features available for current version of hotswap is listed here, it change method body, add/rename a method, field, The only unsupported operation is hierarchy change (change the superclass or remove an interface). You can use standard Java Hotswap from IDE in debug mode to reload changed class https://github.com/HotswapProjects/HotswapAgent#what-is-available

Properties file Configuration

create a file named "hotswap-agent.properties" inside your resources directory, see available properties and default values: https://github.com/HotswapProjects/HotswapAgent/blob/master/hotswap-agent-core/src/main/resources/hotswap-agent.properties

**I've set my hotswap-agent.properties file into WEB-INF folder like following **


# Default agent properties
# You can override them in your application by creating hotswap-agent.properties file in class root
# and specifying new property values.
            
# Add a directory prior to application classpath (load classes and resources).
#
# This may be useful for example in multi module maven project to load class changes from upstream project
# classes. Set extraClasspath to upstream project compiler output and .class file will have precedence to
# classes from built JAR file.
extraClasspath=target/classes/
            
# Watch for changes in a directory (resources only). If not set, changes of resources won't be observed.
#
# Similar to extraClasspath this property adds classpath when searching for resources (not classes).
# While extra classpath just modifies the classloader, this setting does nothing until the resource
# is really changed.
#
# Sometimes it is not possible to point extraClasspath to your i.e. src/main/resources, because there are multiple
# replacements of resources in a building step (maven filtering resource option).
# This setting will leave i.e. src/target/classes as default source for resources, but after the resource is modified
# in src/main/resources, the new changed resource is served instead.
watchResources=src/main/resources
            
# Load static web resources from different directory.
#
# This setting is dependent on application server plugin(Jetty, Tomcat, ...).
# Jboss and Glassfish are not yet supported.
# Use this setting to set to serve resources from source directory directly (e.g. src/main/webapp).
webappDir=src/main/webapp
            
            
# Comma separated list of disabled plugins
# Use plugin name - e.g. Hibernate, Spring, ZK, Hotswapper, AnonymousClassPatch, Tomcat, Logback ....
disabledPlugins=
            
# Watch for changed class files on watchResources path and reload class definition in the running application.
#
# Usually you will launch debugging session from your IDE and use standard hotswap feature.
# This property is useful if you do not want to use debugging session for some reason or
# if you want to enable hotswap at runtime environment.
#
# Internally this uses java Instrumentation API to reload class bytecode. If you need to use JPDA API instead,
# specify autoHotswap.port with JPDA port.
autoHotswap=true
            
# The base package prefix of your spring application (e.g. org.hotswap.).
# Needed when component scan is turned off, so we can still know which classes is your beans
# Can also be set to filter beans we handle to improve performance (So that we won't create proxy for thirty party lib's beans).
# Comma separated.
#spring.basePackagePrefix=
            
# Create Java Platform Debugger Architecture (JPDA) connection on autoHotswap.port, watch for changed class files
# and do the hotswap (reload) in background.
#
# You need to specify JPDA port at startup
# <pre>java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000</pre>
autoHotswap.port=9009
            
# Enables debugging in OsgiEquinox
# osgiEquinox.debugMode=true
            
# Setup reloading strategy of bean INSTANCE(s) in Weld CONTEXT(s). While bean class is redefined by DCEVM, reloading of bean instances
# can be customized by this parameter. Available values:
#   - CLASS_CHANGE - reload bean instance on any class modification, plus reaload on changes specified in
#     METHOD_FIELD_SIGNATURE_CHANGE and FIELD_SIGNATURE_CHANGE strategies
#   - METHOD_FIELD_SIGNATURE_CHANGE - reload bean instance on any method/field change. Includes changes specified in
#     strategy FIELD_SIGNATURE_CHANGE 
#   - FIELD_SIGNATURE_CHANGE - reload bean instance on any field signature change. Includes also field annotation changes
#   - NEVER - never reload bean (default)
# weld.beanReloadStrategy=NEVER
            
# Logger setup - use entries in the format of
# format:  LOGGER.my.package=LEVEL
# e.g.     LOGGER.org.hotswap.agent.plugin.myPlugin=trace
# root level
LOGGER=info
# DateTime format using format of SimpleDateFormat, default value HH:mm:ss.SSS
# LOGGER_DATETIME_FORMAT=HH:mm:ss.SSS
            
# Print output into logfile (with choice to append - false by default)
# LOGFILE=agent.log
# LOGFILE.append=true
            
# Comma separated list of class loaders to exclude from initialization, in the form of RegEx patterns.
#excludedClassLoaderPatterns=jdk.nashorn.*