### HotswapAgent Startup Log Example Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt An example of the expected log output when HotswapAgent successfully loads and discovers its plugins. ```log # Expected startup log: # HOTSWAP AGENT: INFO (HotswapAgent) - Loading Hotswap agent 2.0.4-SNAPSHOT - unlimited runtime class redefinition. # HOTSWAP AGENT: INFO (PluginRegistry) - Discovered plugins: [HibernatePlugin, SpringPlugin, LogbackPlugin, ...] ``` -------------------------------- ### Component Scan Configuration Example Source: https://github.com/hotswapprojects/hotswapagent/blob/master/plugin/hotswap-agent-spring-plugin/README.md Example of configuring component scanning in Spring. This setup is supported by the HotswapAgent plugin for reloading components registered via classpath scanning. ```xml ``` -------------------------------- ### Custom Hotswap Agent Plugin Example Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt A comprehensive example of a custom Hotswap Agent plugin. It demonstrates hooking into framework bootstrap classes, reacting to class redefinitions and new class files, and monitoring resource file changes. ```java // File: src/main/java/com/example/plugin/MyPlugin.java package com.example.plugin; import org.hotswap.agent.annotation.*; import org.hotswap.agent.command.*; import org.hotswap.agent.javassist.*; import org.hotswap.agent.util.PluginManagerInvoker; import org.hotswap.agent.watch.Watcher; import java.net.URL; @Plugin( name = "MyPlugin", description = "Reloads MyFramework on class/resource changes.", testedVersions = {"1.5"}, expectedVersions = {"1.x"} ) public class MyPlugin { @Init Watcher watcher; @Init Scheduler scheduler; @Init ClassLoader appClassLoader; // ── Step 1: hook into the framework's bootstrap class ────────────────── @OnClassLoadEvent(classNameRegexp = "com\.example\.framework\.FrameworkContext") public static void patchFrameworkContext(ClassPool cp, CtClass ct) throws NotFoundException, CannotCompileException { CtMethod start = ct.getDeclaredMethod("start"); start.insertBefore(PluginManagerInvoker.buildInitializePlugin(MyPlugin.class)); start.insertAfter(PluginManagerInvoker.buildCallPluginMethod( MyPlugin.class, "onFrameworkStarted", "this", "java.lang.Object" )); } // ── Step 2: post-start setup ─────────────────────────────────────────── public void onFrameworkStarted(Object frameworkContext) { System.out.println("MyPlugin active for: " + frameworkContext.getClass().getName()); } // ── Step 3: react to class redefinitions ─────────────────────────────── @OnClassLoadEvent( classNameRegexp = "com\.example\.app\..*", events = LoadEvent.REDEFINE ) public void onClassRedefined(Class clazz) { scheduler.scheduleCommand( new ReflectionCommand(this, "doReload", appClassLoader, clazz.getName()), 300 ); } // ── Step 4: react to new class files ────────────────────────────────── @OnClassFileEvent(classNameRegexp = "com\.example\.app\..*", events = FileEvent.CREATE) public void onNewClass(CtClass ctClass) throws Exception { System.out.println("New class: " + ctClass.getName()); scheduler.scheduleCommand( new ReflectionCommand(this, "doReload", appClassLoader, ctClass.getName()), 300 ); } // ── Step 5: react to config resource changes ─────────────────────────── @OnResourceFileEvent(path = "/", filter = ".*.properties", events = FileEvent.MODIFY) public void onPropertiesChanged(URL url) { scheduler.scheduleCommand( new ReflectionCommand(this, "doReloadConfig", appClassLoader, url.toString()), 500 ); } // ── Reload methods — run in the APP classloader ──────────────────────── public void doReload(String className) { System.out.println("Reloading framework for class: " + className); // Call framework reload API here via reflection or direct call } public void doReloadConfig(String configUrl) { System.out.println("Reloading config from: " + configUrl); } } ``` ```properties ``` -------------------------------- ### Start JVM with JPDA Enabled Source: https://github.com/hotswapprojects/hotswapagent/blob/master/hotswap-agent-core/src/main/java/org/hotswap/agent/plugin/hotswapper/README.md Launch the Java application with JPDA (Java Platform Debugger Architecture) enabled. This allows for debugging and hotswapping capabilities. ```bash java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 --XXaltjvm=dcevm -javaagent:HotswapAgent.jar MainClass ``` -------------------------------- ### Spring Boot Configuration Reload Example Source: https://github.com/hotswapprojects/hotswapagent/blob/master/plugin/hotswap-agent-spring-boot-plugin/README.md Demonstrates the structure of properties files, configuration classes, and service beans that benefit from dynamic configuration reloading. Changes in properties files will trigger bean updates. ```properties ####hotswap-demo.properties properties.l10.l1=hotswap-demo ``` ```java @Configuration @PropertySource("classpath:hotswap-demo.properties") @EnableConfigurationProperties(value = {Test10Properties.class}) public class ApplicationConfiguration { @Value("${application.name}") private String applicationName; ... } ``` ```java @ConfigurationProperties(prefix = "properties.l10") public class Test10Properties { private String l1; ... } ``` ```java @Component public class Test50Service { @Value("${properties.l10.l1}") private String l1; ... } ``` -------------------------------- ### Configure IBatis SqlMapClientFactoryBean Source: https://github.com/hotswapprojects/hotswapagent/blob/master/plugin/hotswap-agent-ibatis-plugin/README.md Configure the SqlMapClientFactoryBean to specify the locations of IBatis configuration and mapping files. This setup is necessary for the plugin to monitor and reload changes. ```xml classpath:conf/ibatis/sqlMapConfig.xml sqlmap/**/*.xml ...... ``` -------------------------------- ### Get Javassist Version Source: https://github.com/hotswapprojects/hotswapagent/blob/master/hotswap-agent-core/src/main/java/org/hotswap/agent/javassist/package.html Execute this command to display the version number of the Javassist library. ```bash java -jar javassist.jar ``` -------------------------------- ### Load HotswapAgent via JVM Startup Arguments Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Demonstrates various ways to load the HotswapAgent during JVM startup, including different Java versions and configurations like auto-hotswap and disabling specific plugins. Required for all HotswapAgent functionality. ```bash java -XX:+AllowEnhancedClassRedefinition \ -XX:HotswapAgent=fatjar \ -jar myapp.jar ``` ```bash java -XX:+AllowEnhancedClassRedefinition \ -XX:HotswapAgent=external \ -javaagent:/path/to/hotswap-agent.jar \ -jar myapp.jar ``` ```bash java -XX:HotswapAgent=fatjar -jar myapp.jar ``` ```bash java -XXaltjvm=dcevm \ -javaagent:/path/to/hotswap-agent.jar \ -jar myapp.jar ``` ```bash java -XXaltjvm=dcevm \ -javaagent:/path/to/hotswap-agent.jar=autoHotswap=true \ -jar myapp.jar ``` ```bash java -XX:HotswapAgent=fatjar \ -javaagent:hotswap-agent.jar=disablePlugin=Hibernate,disablePlugin=Spring \ -jar myapp.jar ``` ```bash java -XX:HotswapAgent=fatjar \ -Dhotswapagent.disablePlugin=Hibernate,Spring \ -jar myapp.jar ``` -------------------------------- ### Logback Plugin Initialization and Watching Output Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Expected startup log messages indicating the Logback plugin initialization and the URL it is actively watching for configuration changes. ```bash # Expected startup: # HOTSWAP AGENT: INFO (LogbackPlugin) - Logback plugin initialized. # HOTSWAP AGENT: DEBUG (LogbackPlugin) - Watching 'file:/path/logback.xml' URL for changes. # After editing logback.xml and saving: ``` -------------------------------- ### Launch Application with Logback Plugin Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Command to launch an application with the Hotswap Agent's fatjar, which includes the Logback plugin for automatic configuration reloading. ```bash # Launch — Logback plugin is included in fatjar automatically java -XX:HotswapAgent=fatjar -jar myapp.jar ``` -------------------------------- ### Accessing PluginManager Singleton and Services Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Shows how to access the PluginManager singleton and its sub-services like PluginRegistry, Scheduler, and Watcher. Also demonstrates programmatic initialization and method calls for plugins. ```java import org.hotswap.agent.config.PluginManager; import org.hotswap.agent.config.PluginRegistry; import org.hotswap.agent.command.Scheduler; import org.hotswap.agent.watch.Watcher; // ── Access the singleton ──────────────────────────────────────────────────── PluginManager pm = PluginManager.getInstance(); // ── Retrieve a plugin instance for a specific classloader ────────────────── ClassLoader appCL = Thread.currentThread().getContextClassLoader(); // By class reference (agent classloader must know the plugin class) MyFrameworkPlugin plugin = (MyFrameworkPlugin) pm.getPlugin(MyFrameworkPlugin.class.getName(), appCL); // ── Initialise a plugin programmatically (called from injected bytecode) ──── // PluginManagerInvoker generates the equivalent source snippet for Javassist String initCode = PluginManagerInvoker.buildInitializePlugin(MyFrameworkPlugin.class); // Produces: org.hotswap.agent.config.PluginManager.getInstance() // .getPluginRegistry().initializePlugin("com.example.MyFrameworkPlugin", // getClass().getClassLoader()); // ── Call a plugin method from transformed bytecode ───────────────────────── String callCode = PluginManagerInvoker.buildCallPluginMethod( MyFrameworkPlugin.class, "onFrameworkReady", // plugin method "this", "java.lang.Object" // arg name, arg type (as seen in Javassist source) ); // ── Access sub-services ───────────────────────────────────────────────────── PluginRegistry registry = pm.getPluginRegistry(); Scheduler scheduler = pm.getScheduler(); Watcher watcher = pm.getWatcher(); // ── Manually hotswap a class (requires Instrumentation API) ──────────────── // (normally triggered by IDE debugger or autoHotswap file watcher) // pm.hotswap(new ClassDefinition(MyClass.class, newBytecode)); ``` -------------------------------- ### Watch .class File Creation Events Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Fires when a new .class file is created on disk. Inspects annotations before the class is loaded by the JVM. Requires `com.example.app.*` package matching. ```java import org.hotswap.agent.annotation.*; import org.hotswap.agent.javassist.*; @Plugin(name = "MyFramework", testedVersions = {"2.x"}) public class MyFrameworkPlugin { @Init Scheduler scheduler; @Init ClassLoader appClassLoader; private static CtClass baseEntityClass; /** Capture the base class once it's loaded so we can check subtypes below */ @OnClassLoadEvent(classNameRegexp = "com\.example\.framework\.BaseEntity") public static void captureBaseEntity(CtClass ctClass) { baseEntityClass = ctClass; } /** * Fires when a NEW .class file appears on disk (e.g. developer adds a new entity). * Uses CtClass to inspect annotations before the class is loaded by the JVM. */ @OnClassFileEvent( classNameRegexp = "com\.example\.app\..*", events = {FileEvent.CREATE} ) public void onNewClass(CtClass ctClass, ClassLoader loader) throws Exception { boolean isEntity = false; for (Object ann : ctClass.getAnnotations()) { if (ann.toString().contains("Entity")) { isEntity = true; break; } } boolean isSubtype = baseEntityClass != null && ctClass.subtypeOf(baseEntityClass); if (isEntity || isSubtype) { System.out.println("New entity class detected: " + ctClass.getName()); Class loaded = loader.loadClass(ctClass.getName()); scheduler.scheduleCommand( new ReflectionCommand(this, "registerNewEntity", appClassLoader, ctClass.getName()), 100 ); } } /** * Fires when an existing .class file is modified on disk (outside IDE hotswap). */ @OnClassFileEvent( classNameRegexp = "com\.example\.app\..*", events = {FileEvent.MODIFY}, timeout = 200 ) public void onClassModified(String className, URI uri) { System.out.println("Class file modified: " + className + " at " + uri); } } ``` -------------------------------- ### Hotswap Agent Initialization Log Source: https://github.com/hotswapprojects/hotswapagent/blob/master/README.md This log output indicates that the Hotswap Agent has loaded successfully and discovered its available plugins, including core JVM plugins and framework-specific plugins like Spring and Hibernate. ```log HOTSWAP AGENT: 9:49:29.548 INFO (org.hotswap.agent.HotswapAgent) - Loading Hotswap agent - unlimited runtime class redefinition. HOTSWAP AGENT: 9:49:29.725 INFO (org.hotswap.agent.config.PluginRegistry) - Discovered plugins: [org.hotswap.agent.plugin.hotswapper.HotswapperPlugin, org.hotswap.agent.plugin.jvm.AnonymousClassPatchPlugin, org.hotswap.agent.plugin.hibernate.HibernatePlugin, org.hotswap.agent.plugin.spring.SpringPlugin, org.hotswap.agent.plugin.jetty.JettyPlugin, org.hotswap.agent.plugin.tomcat.TomcatPlugin, org.hotswap.agent.plugin.zk.ZkPlugin, org.hotswap.agent.plugin.logback.LogbackPlugin] ... HOTSWAP AGENT: 9:49:38.700 INFO (org.hotswap.agent.plugin.spring.SpringPlugin) - Spring plugin initialized - Spring core version '3.2.3.RELEASE' ``` -------------------------------- ### Constructing ReflectionCommand for Cross-Classloader Invocation Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Demonstrates various ways to construct ReflectionCommand to call static or instance methods across classloaders. Ensure parameters are types known to the target classloader. ```java import org.hotswap.agent.command.*; import org.hotswap.agent.config.PluginManager; // ── Construction forms ────────────────────────────────────────────────────── // 1. Call a static method on a class in appClassLoader ReflectionCommand cmd1 = new ReflectionCommand( pluginInstance, // plugin — used to resolve the app classloader "com.example.app.Reloader", // class name in the app classloader "reloadAll", // method name (must be public) appClassLoader // explicit target classloader // optional varargs params... ); // 2. Call a method with parameters ReflectionCommand cmd2 = new ReflectionCommand( pluginInstance, "com.example.app.Reloader", "reloadBean", appClassLoader, "com.example.app.MyService" // String parameter — type known to both loaders ); // 3. Call a method on a specific object instance Object targetObject = ...; // object whose classloader is the target ReflectionCommand cmd3 = new ReflectionCommand( targetObject, "refresh", // method on targetObject "extra-param" // optional params ); ``` ```java // 4. Attach a result listener cmd2.setCommandExecutionListener(result -> { System.out.println("Reload completed, result: " + result); }); // ── Schedule immediately ──────────────────────────────────────────────────── PluginManager.getInstance().getScheduler().scheduleCommand(cmd2, 200); // ── Execute directly (synchronous, rare) ─────────────────────────────────── cmd1.executeCommand(); ``` -------------------------------- ### Launch Spring Boot Application with Hotswap Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Command to launch a Spring Boot application with Hotswap Agent support, including enhanced class redefinition and reduced reload delay. ```bash # Launch a Spring Boot application with full hotswap support java -XX:+AllowEnhancedClassRedefinition \ -XX:HotswapAgent=fatjar \ -DSpringReloadDelayMillis=500 \ -jar spring-boot-app.jar ``` -------------------------------- ### Hotswap Agent Command-Line Syntax Source: https://github.com/hotswapprojects/hotswapagent/blob/master/README.md The general syntax for applying Hotswap Agent options via the command line. Options are specified as key-value pairs after the agent JAR path. ```bash -javaagent:[yourpath/]hotswap-agent.jar=[option1]=[value1],[option2]=[value2] ``` -------------------------------- ### HotswapAgent Configuration via properties file Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Configuration options for HotswapAgent using a properties file. This file controls classpaths, resource watching, plugin states, logging, and framework-specific settings. ```properties # src/main/resources/hotswap-agent.properties # Extra classpath directories (for loading .class files outside JARs) # Useful in multi-module Maven projects to pick up upstream compiled classes extraClasspath=/path/to/module-a/target/classes,/path/to/module-b/target/classes # Watch these directories for resource changes (non-class files) watchResources=/path/to/src/main/resources,/path/to/other/resources # Serve static web resources from source instead of build output # (requires Jetty/Tomcat/Undertow plugin) webappDir=/path/to/src/main/webapp # Disable specific plugins for this classloader (comma-separated plugin names) disabledPlugins=Logback,ZK # Enable automatic hotswap (watch .class files and reload without IDE debugger) autoHotswap=false # JPDA port for autoHotswap via debugger protocol instead of Instrumentation API # autoHotswap.port=8000 # Spring: restrict bean reload to specific base packages (improves performance) spring.basePackagePrefix=com.mycompany.myapp # Weld/OWB CDI: bean instance reload strategy # Values: NEVER (default), CLASS_CHANGE, METHOD_FIELD_SIGNATURE_CHANGE, FIELD_SIGNATURE_CHANGE weld.beanReloadStrategy=CLASS_CHANGE # Logging: root level (trace|debug|info|warn|error) LOGGER=info # Per-package level override LOGGER.org.hotswap.agent.plugin.spring=debug LOGGER.com.mycompany.myapp=trace # Write logs to a file LOGFILE=hotswap-agent.log LOGFILE.append=true LOG_TO_CONSOLE=true ``` -------------------------------- ### Hotswap Agent JVM Options by Java Version Source: https://github.com/hotswapprojects/hotswapagent/blob/master/README.md Use these JVM options to launch your application with the Hotswap Agent, depending on your Java version. Ensure the correct Hotswap Agent JAR is available. ```bash java -XX:+AllowEnhancedClassRedefinition -XX:HotswapAgent=fatjar -jar your-application.jar ``` ```bash java -XX:HotswapAgent=fatjar -jar your-application.jar ``` ```bash java -XXaltjvm=dcevm -javaagent:hotswap-agent.jar -jar your-application.jar ``` -------------------------------- ### Hotswap-agent.properties Configuration Source: https://github.com/hotswapprojects/hotswapagent/blob/master/plugin/hotswap-agent-osgiequinox-plugin/README.md Set up the hotswap-agent.properties file with the extraClasspath and debugMode settings. This ensures the agent knows where to find compiled classes and operates in debug mode. ```properties extraClasspath=[extra_classpath] osgiEquinox.debugMode=true ``` -------------------------------- ### HotswapAgent Configuration Properties Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Configure HotswapAgent by setting plugin packages, watched resources, and logger levels in this properties file. ```properties pluginPackages=com.example.plugin watchResources=/path/to/src/main/resources LOGGER.com.example.plugin=debug ``` -------------------------------- ### Configure Logback Plugin Properties Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Properties for the Logback plugin to enable configuration file reloading. Enable debug logging for detailed trace events. ```properties # hotswap-agent.properties watchResources=/path/to/src/main/resources # Enable debug logging for Logback plugin LOGGER.org.hotswap.agent.plugin.logback=debug ``` -------------------------------- ### Watch Filesystem Resources with @OnResourceFileEvent Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Use this instance method annotation to monitor filesystem resource changes (CREATE, MODIFY, DELETE) on watched paths. It supports merging multiple events within a timeout window. Supported parameter types include URI, URL, ClassLoader, and ClassPool. ```java import org.hotswap.agent.annotation.*; import java.net.URL; import java.net.URI; @Plugin(name = "MyFramework", testedVersions = {"2.x"}) public class MyFrameworkPlugin { @Init Scheduler scheduler; @Init ClassLoader appClassLoader; /** * Fires when any .xml file changes anywhere on the classpath. * The 500 ms timeout merges rapid IDE save-bursts into a single event. */ @OnResourceFileEvent( path = "/", filter = ".*.xml", events = {FileEvent.MODIFY, FileEvent.CREATE}, timeout = 500 ) public void onXmlChange(URL url) { System.out.println("XML resource changed: " + url); scheduler.scheduleCommand( new ReflectionCommand(this, "reloadXmlConfig", appClassLoader, url.toString()), 500 ); } /** * Fires when a .properties file is modified, passing both URI and ClassLoader. */ @OnResourceFileEvent(path = "/", filter = ".*.properties", events = {FileEvent.MODIFY}) public void onPropertiesChange(URI uri, ClassLoader classLoader) { System.out.println("Properties changed: " + uri + " in " + classLoader); } /** * Watch a specific directory prefix for YAML configuration changes. */ @OnResourceFileEvent(path = "/config", filter = ".*.ya?ml", events = {FileEvent.MODIFY}) public void onYamlChange(URL url) { System.out.println("YAML config changed: " + url); } } ``` -------------------------------- ### Specify JPDA Port Source: https://github.com/hotswapprojects/hotswapagent/blob/master/hotswap-agent-core/src/main/java/org/hotswap/agent/plugin/hotswapper/README.md Configure the `autoHotswap.port` property to match the JPDA port specified during JVM startup. The default is 8000. ```properties autoHotswap.port=8000 ``` -------------------------------- ### Maven Release Commands Source: https://github.com/hotswapprojects/hotswapagent/blob/master/README.md Commands to prepare and perform a release of the Hotswap Agent project. Ensure JAVA_HOME is set and Java 11 with DCEVM is available. ```bash mvn release:prepare mvn release:perform ``` -------------------------------- ### Access Plugin Configuration Properties Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Provides typed access to properties from hotswap-agent.properties with parent-classloader fallback. Use @Init injection for preferred usage. ```java import org.hotswap.agent.config.PluginConfiguration; // ── @Init injection (preferred) ───────────────────────────────────────────── @Plugin(name = "ConfigDemo", testedVersions = {"1.0"}) public class ConfigDemoPlugin { @Init PluginConfiguration pluginConfiguration; @Init ClassLoader appClassLoader; @Init public void init() { // Read raw property with default String extra = pluginConfiguration.getProperty("extraClasspath", ""); // Read watchResources as a URL array java.net.URL[] watched = pluginConfiguration.getWatchResources(); // Read extraClasspath as a URL array java.net.URL[] extra2 = pluginConfiguration.getExtraClasspath(); // Check if a plugin is disabled for this classloader boolean disabled = pluginConfiguration.isDisabledPlugin("Hibernate"); // Read Spring base package prefix(es) String[] prefixes = pluginConfiguration.getBasePackagePrefixes(); System.out.println("WatchResources count : " + watched.length); System.out.println("ExtraClasspath count : " + extra2.length); System.out.println("Spring prefixes : " + java.util.Arrays.toString(prefixes)); System.out.println("Hibernate disabled : " + disabled); } } // ── Direct construction (outside plugin context) ──────────────────────────── PluginConfiguration cfg = new PluginConfiguration( Thread.currentThread().getContextClassLoader() ); String prop = cfg.getProperty("myPlugin.someKey", "defaultValue"); ``` -------------------------------- ### Logback Plugin Bytecode Transformation Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt This snippet shows the bytecode transformation used by the Logback plugin to intercept GenericConfigurator.doConfigure. No application changes are needed as it works entirely through bytecode manipulation. ```java // The plugin works entirely through bytecode transformation — no application changes needed. // Internal reload path for reference (executed by the plugin in app classloader): // // 1. GenericConfigurator.doConfigure(URL) is intercepted // 2. Plugin registers WatchEventListener on the config URI // 3. On MODIFY event: context.reset() then configurator.doConfigure(url) // // The relevant transformer injection (from LogbackPlugin source): @OnClassLoadEvent(classNameRegexp = "ch.qos.logback.core.joran.GenericConfigurator") public static void registerConfigurator(ClassPool classPool, CtClass ctClass) throws NotFoundException, CannotCompileException { CtMethod m = ctClass.getDeclaredMethod("doConfigure", new CtClass[]{ classPool.get("java.net.URL") }); m.insertAfter(PluginManagerInvoker.buildInitializePlugin(LogbackPlugin.class)); m.insertAfter(PluginManagerInvoker.buildCallPluginMethod( LogbackPlugin.class, "initLogback", "this", "java.lang.Object", "url", "java.net.URL")); } ``` -------------------------------- ### Eclipse.ini Configuration for OsgiEquinox Hotswap Source: https://github.com/hotswapprojects/hotswapagent/blob/master/plugin/hotswap-agent-osgiequinox-plugin/README.md Configure these options in eclipse.ini for the debugee Eclipse instance to enable hotswap support. Ensure PATH_TO_AGENT points to your hotswap-agent.jar. ```ini # use application classloader for the framework -Dosgi.frameworkParentClassloader=app # development classpath that is added to each plugin classpath -Dosgi.dev=[extra_classpath] # use dcevm as JVM -XXaltjvm=dcevm # enable hotswapagent -javaagent:PATH_TO_AGENT/hotswap-agent.jar # enable remote debugging on port 8000 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 ``` -------------------------------- ### @OnResourceFileEvent Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Fires when a file on a watched path changes (CREATE, MODIFY, DELETE). Supports filtering by path and file extension, and merging multiple events within a timeout window. ```APIDOC ## @OnResourceFileEvent — Watch Filesystem Resources Instance method annotation that fires when a file on a watched path changes (CREATE, MODIFY, DELETE). Triggered for resources known to the classloader and for paths listed in `watchResources`. Supported method parameter types: `URI`, `URL`, `ClassLoader`, `ClassPool`. Multiple identical events within the `timeout` window are merged into one call. ```java import org.hotswap.agent.annotation.*; import java.net.URL; import java.net.URI; @Plugin(name = "MyFramework", testedVersions = {"2.x"}) public class MyFrameworkPlugin { @Init Scheduler scheduler; @Init ClassLoader appClassLoader; /** * Fires when any .xml file changes anywhere on the classpath. * The 500 ms timeout merges rapid IDE save-bursts into a single event. */ @OnResourceFileEvent( path = "/", filter = ".*.xml", events = {FileEvent.MODIFY, FileEvent.CREATE}, timeout = 500 ) public void onXmlChange(URL url) { System.out.println("XML resource changed: " + url); scheduler.scheduleCommand( new ReflectionCommand(this, "reloadXmlConfig", appClassLoader, url.toString()), 500 ); } /** * Fires when a .properties file is modified, passing both URI and ClassLoader. */ @OnResourceFileEvent(path = "/", filter = ".*.properties", events = {FileEvent.MODIFY}) public void onPropertiesChange(URI uri, ClassLoader classLoader) { System.out.println("Properties changed: " + uri + " in " + classLoader); } /** * Watch a specific directory prefix for YAML configuration changes. */ @OnResourceFileEvent(path = "/config", filter = ".*.ya?ml", events = {FileEvent.MODIFY}) public void onYamlChange(URL url) { System.out.println("YAML config changed: " + url); } } ``` ``` -------------------------------- ### Configure Spring Plugin Properties Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Configuration properties for the Spring plugin to enable live bean reloading. Set base package prefixes for faster, restricted scanning. ```properties # hotswap-agent.properties for a Spring application watchResources=/path/to/src/main/resources extraClasspath=/path/to/target/classes # Restrict Spring reload scanning to your packages (faster, avoids third-party) spring.basePackagePrefix=com.mycompany.myapp # Optional: lower reload delay (ms). Default 1600; ~500 is usually reliable # SpringReloadDelayMillis=500 # Log Spring plugin at DEBUG to trace reload events LOGGER.org.hotswap.agent.plugin.spring=debug ``` -------------------------------- ### Dependency Injection for Plugins with @Init Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Use the @Init annotation for field or method injection of agent services like Watcher, Scheduler, and ClassLoader. Static @Init methods act as callbacks for new classloaders. ```java @Plugin(name = "DemoInit", testedVersions = {"1.0"}) public class DemoInitPlugin { // Field injection @Init Watcher watcher; @Init Scheduler scheduler; @Init HotswapTransformer hotswapTransformer; @Init ClassLoader appClassLoader; // the app (child) classloader @Init PluginConfiguration pluginConfiguration; // Method injection — all listed argument types are resolved automatically @Init public void init(PluginManager pluginManager, Watcher watcher, Scheduler scheduler) { String extra = pluginConfiguration.getProperty("extraClasspath", ""); System.out.println("Extra classpath: " + extra); System.out.println("App classloader: " + appClassLoader); System.out.println("PluginManager instance: " + pluginManager); } // Static @Init — invoked each time a new classloader is initialised @Init public static void onNewClassLoader(ClassLoader classLoader) { System.out.println("New app classloader registered: " + classLoader); } } ``` -------------------------------- ### Schedule Commands with Deferred and Merged Execution Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt The `Scheduler` service runs commands asynchronously with configurable delays and deduplication. Equal commands scheduled within a timeout window are merged, preventing redundant executions. ```java import org.hotswap.agent.command.*; @Plugin(name = "MyFramework", testedVersions = {"2.x"}) public class MyFrameworkPlugin { @Init Scheduler scheduler; @Init ClassLoader appClassLoader; @OnResourceFileEvent(path = "/", filter = ".*.xml") public void onXmlChange(java.net.URL url) { // Schedule with default 100 ms timeout; duplicate commands are merged scheduler.scheduleCommand( new ReflectionCommand(this, "reloadAll", appClassLoader) ); // Schedule with explicit 500 ms timeout scheduler.scheduleCommand( new ReflectionCommand(this, "reloadXml", appClassLoader, url.toString()), 500 ); // Schedule but SKIP if an identical command is already pending scheduler.scheduleCommand( new ReflectionCommand(this, "reloadXml", appClassLoader, url.toString()), 500, Scheduler.DuplicateSheduleBehaviour.SKIP ); // Schedule and execute only after ALL pending class redefinitions are done // (useful to run reload after a batch of hotswapped classes) scheduler.scheduleCommandOnClassesRedefinedOrTimeout( new ReflectionCommand(this, "reloadAfterBatch", appClassLoader), 1000 ); } // These methods run in the APP classloader (loaded by appClassLoader) public void reloadAll() { System.out.println("Reload all triggered"); } public void reloadXml(String xmlPath) { System.out.println("Reload XML: " + xmlPath); } public void reloadAfterBatch() { System.out.println("Post-batch reload"); } } ``` -------------------------------- ### Configure Hotswap Agent Logging Source: https://github.com/hotswapprojects/hotswapagent/blob/master/README.md These properties can be added to `hotswap-agent.properties` to control Hotswap Agent's logging behavior, directing output to a file and managing console logging. ```properties LOGFILE=agent.log ``` ```properties LOGFILE.append=true ``` ```properties LOG_TO_CONSOLE=false ``` -------------------------------- ### @OnClassLoadEvent Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Intercepts class load (DEFINE) or hotswap (REDEFINE) events matching a class name regular expression. Static methods can bootstrap plugin initialization, while instance methods handle redefine events. ```APIDOC ## @OnClassLoadEvent — Intercept Class Load / Hotswap Method annotation (static or instance) that triggers on class load (`DEFINE`) or hotswap (`REDEFINE`) events matching a class name regexp. Static methods execute even before the plugin is fully initialised and are the standard way to bootstrap plugin initialisation by transforming the framework's own classes. Supported method parameter types: `byte[]`, `ClassLoader`, `String` (classname), `Class` (being redefined), `ProtectionDomain`, `ClassPool`, `CtClass`, `LoadEvent`. ```java import org.hotswap.agent.annotation.*; import org.hotswap.agent.javassist.*; import org.hotswap.agent.util.PluginManagerInvoker; @Plugin(name = "MyFramework", testedVersions = {"2.x"}) public class MyFrameworkPlugin { @Init Watcher watcher; @Init ClassLoader appClassLoader; /** * Static transformer — intercepts MyFramework's bootstrap class at load time * and injects a call that initialises this plugin. * Runs in the AGENT classloader before the plugin instance exists. */ @OnClassLoadEvent(classNameRegexp = "com\.example\.framework\.Bootstrap") public static void patchBootstrap(ClassPool classPool, CtClass ctClass) throws NotFoundException, CannotCompileException { CtMethod initMethod = ctClass.getDeclaredMethod("initialize"); // Inject plugin initialization at the start of Bootstrap.initialize() initMethod.insertBefore( PluginManagerInvoker.buildInitializePlugin(MyFrameworkPlugin.class) ); // Inject a callback so the plugin can register watchers after init initMethod.insertAfter( PluginManagerInvoker.buildCallPluginMethod( MyFrameworkPlugin.class, "afterFrameworkInit", "this", "java.lang.Object" ) ); } /** * Instance method — called on every class REDEFINE (hotswap) for classes * matching "com.example.framework.*" . * Runs in the APP classloader via scheduled ReflectionCommand. */ @OnClassLoadEvent( classNameRegexp = "com\.example\.framework\..*", events = LoadEvent.REDEFINE ) public void onFrameworkClassRedefined(Class redefinedClass, ClassLoader loader) { System.out.println("Framework class redefined: " + redefinedClass.getName()); // Trigger framework-specific reload logic here } /** Called by the injected code in Bootstrap.initialize() */ public void afterFrameworkInit(Object bootstrap) { System.out.println("MyFramework bootstrapped — plugin fully active."); } } ``` ``` -------------------------------- ### Enable Auto Hotswap in Properties Source: https://github.com/hotswapprojects/hotswapagent/blob/master/hotswap-agent-core/src/main/java/org/hotswap/agent/plugin/hotswapper/README.md Set `autoHotswap` to `true` in `hotswap-agent.properties` to enable background class reloading. Ensure the JPDA port is correctly specified. ```properties autoHotswap=true ``` -------------------------------- ### Intercept Class Load/Hotswap with @OnClassLoadEvent Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Use this static method annotation to intercept class load (DEFINE) or hotswap (REDEFINE) events. It's ideal for bootstrapping plugin initialization by transforming framework classes. Static methods run before the plugin is fully initialized. Supported parameter types include byte[], ClassLoader, String, Class, ProtectionDomain, ClassPool, CtClass, and LoadEvent. ```java import org.hotswap.agent.annotation.*; import org.hotswap.agent.javassist.*; import org.hotswap.agent.util.PluginManagerInvoker; @Plugin(name = "MyFramework", testedVersions = {"2.x"}) public class MyFrameworkPlugin { @Init Watcher watcher; @Init ClassLoader appClassLoader; /** * Static transformer — intercepts MyFramework's bootstrap class at load time * and injects a call that initialises this plugin. * Runs in the AGENT classloader before the plugin instance exists. */ @OnClassLoadEvent(classNameRegexp = "com\.example\.framework\.Bootstrap") public static void patchBootstrap(ClassPool classPool, CtClass ctClass) throws NotFoundException, CannotCompileException { CtMethod initMethod = ctClass.getDeclaredMethod("initialize"); // Inject plugin initialization at the start of Bootstrap.initialize() initMethod.insertBefore( PluginManagerInvoker.buildInitializePlugin(MyFrameworkPlugin.class) ); // Inject a callback so the plugin can register watchers after init initMethod.insertAfter( PluginManagerInvoker.buildCallPluginMethod( MyFrameworkPlugin.class, "afterFrameworkInit", "this", "java.lang.Object" ) ); } /** * Instance method — called on every class REDEFINE (hotswap) for classes * matching "com.example.framework.*". * Runs in the APP classloader via scheduled ReflectionCommand. */ @OnClassLoadEvent( classNameRegexp = "com\.example\.framework\..*", events = LoadEvent.REDEFINE ) public void onFrameworkClassRedefined(Class redefinedClass, ClassLoader loader) { System.out.println("Framework class redefined: " + redefinedClass.getName()); // Trigger framework-specific reload logic here } /** Called by the injected code in Bootstrap.initialize() */ public void afterFrameworkInit(Object bootstrap) { System.out.println("MyFramework bootstrapped — plugin fully active."); } } ``` -------------------------------- ### Declare a HotswapAgent Plugin with @Plugin Source: https://context7.com/hotswapprojects/hotswapagent/llms.txt Use the @Plugin annotation to mark a class as a HotswapAgent plugin. Specify compatibility versions and a description. The plugin is instantiated once per application classloader. ```java import org.hotswap.agent.annotation.*; import org.hotswap.agent.command.Scheduler; import org.hotswap.agent.watch.Watcher; import org.hotswap.agent.config.PluginConfiguration; /** * Example custom plugin that watches a framework's config file and * reloads the framework when it changes. * * Register the package in hotswap-agent.properties: * pluginPackages=com.mycompany.plugin * Maven dependency (provided scope — must NOT be in app runtime): * * org.hotswapagent * hotswap-agent-core * 2.0.4-SNAPSHOT * provided * */ @Plugin( name = "MyFramework", description = "Reloads MyFramework configuration on file change.", testedVersions = {"2.3", "2.4"}, expectedVersions = {"2.x"} ) public class MyFrameworkPlugin { // Agent services injected automatically by the @Init mechanism @Init Watcher watcher; @Init Scheduler scheduler; @Init ClassLoader appClassLoader; @Init PluginConfiguration pluginConfiguration; // Called once when the plugin is initialised for an app classloader @Init public void init() { System.out.println("MyFrameworkPlugin initialised for " + appClassLoader); } } ```