This plugin helps you configure your application for Hot Swapping (also called
                    "Hot Reloading") and let you easily register listeners 
                    so you are able to run custom code when modifications are made on classes or on regular files.
                
Hot Swapping is really useful during development: you change a class and the modifications are immediately available, without having to restart the application or the server. This leads to a good development experience!
Java doesn't provide hot swapping out-of-the-box, and this is why third-party solutions have emerged. The most known of those solutions is JRebel which is a very nice product but that is way, way too expensive. From the other available solutions, the more mature is HotswapAgent + DCEVM, which is free and works very well too! It is the solution used by this plugin...
In short, here's what this plugin has to offer:
.yaml or .properties files, for
                            example). This plugin provides an easy way to register listeners
                            for files modifications.
                        HotswapAgent, when properly installed, will make most of the changes made to your code available immediately, without having to restart the application! In addition to this, this current plugin allows you to register listeners, that will be called when classes are redefined by HotswapAgent.
                     Note that if the application is not started with the HotswapAgent agent,
                    all features related to classes redefinitions and the associated listeners will be
                    automatically disabled. This is the behavior you want for an application running
                    on any environment other than locally. Classes redefinitions is indeed something
                    in general only done during development.
                    Note that if the application is not started with the HotswapAgent agent,
                    all features related to classes redefinitions and the associated listeners will be
                    automatically disabled. This is the behavior you want for an application running
                    on any environment other than locally. Classes redefinitions is indeed something
                    in general only done during development.
                
Registering a class redefinition listener
                    You can register listeners to be called when some specific classes are
                    redefined by HotswapAgent. To do so, you simply bind them in your application
                    Guice module using the associated HotSwapClassesRedefinitionsListener
                    multibinder:
                    
Multibinder<HotSwapClassesRedefinitionsListener> multibinder =
        Multibinder.newSetBinder(binder(), HotSwapClassesRedefinitionsListener.class);
        
multibinder.addBinding().to(AppMainClassClassesRedefinitionsListener.class)
                        .in(Scopes.SINGLETON); 
                        
                    
                     A classes redefinitions listener (such as "
 
                    A classes redefinitions listener (such as "AppMainClassClassesRedefinitionsListener" in the above code) is
                    a class that implements 
                    the HotSwapClassesRedefinitionsListener
                    interface. There are three methods to provide when you create such listeners:
                    
                    
Set<Class<?>> getClassesToWatch()
                            This is where you specify for which classes you want to be called, when modifications are made. A listener can be interested in a single class or in multiple classes.
void classRedefined(Class<?> redefinedClass)
                            The method that is called when a class is modified. This is your hook! In this method, you can reload/call some code to make use of the newly redefined class.
boolean isEnabled()
                            
                                    The listener will only be registered if this method returns 
                                    true.
                                
                                     Note that all classes redefinitions listeners are automatically
                                    disabled if the application is started without 
                                    HotswapAgent! But this method allows you fine grain control,
                                    during development.
                                    Note that all classes redefinitions listeners are automatically
                                    disabled if the application is started without 
                                    HotswapAgent! But this method allows you fine grain control,
                                    during development.
                                
Classes redefinitions listener example
                    Here's an example of a classes redefinitions listener. It listens to changes to an
                    "AppRoutes" class which may be the place where the routes of
                     an application are defined. When this
                    class changes, the listener makes sure the routes cache is cleared and routes are reloaded!
                    
                    
public class AppMainClassClassesRedefinitionsListener implements HotSwapClassesRedefinitionsListener {
    private final AppRouter appRouter;
    @Inject
    public AppMainClassClassRedefinitionListener(AppRouter appRouter) {
        this.appRouter = appRouter;
    }
    protected AppRouter getAppRouter() {
        return this.appRouter;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
    @Override
    public Set<Class<?>> getClassesToWatch() {
        return Sets.newHashSet(AppRoutes.class);
    }
    @Override
    public void classRedefined(Class<?> redefinedClass) {
        getAppRouter().clearCacheAndReloadRoutes();
    }
} 
                        
                        Explanation :
HotswapAgent!).
                                AppRoutes class, which is responsible (in this example) for defining 
                                    the application's routes.
                                AppRoutes class
                                    is modified, the classRedefined() method is called. In this method, we tell our
                                    router to reload the cached routes. Note that we only
                                    watch one class ("AppRoutes") in this example, so there is no need to use
                                    the "redefinedClass" parameter to validate if the current modification
                                    has been made on that class or on another!
                                
                     Remember that you may register as many listeners as you want. Each can listen to a different
                    class or they may share some. If one of your listeners is interested in more than
                    one class, you then may have to use the "
                    Remember that you may register as many listeners as you want. Each can listen to a different
                    class or they may share some. If one of your listeners is interested in more than
                    one class, you then may have to use the "redefinedClass" parameter, provided
                    when the classRedefined() method is called, to validate which class
                    was actually modified!
                
In addition to classes redefinitions listeners, this plugin also provides an easy way of listening to modifications made on regular files.
                    For example, during development, you may change a app-config.yaml
                    file to tweak some configurations of your application.
                    By registering a listener for this file, you would be able to refresh
                    the AppConfigs object uses to access the configurations in your Java code.
                
Registering a file modifications listener
                    To register a new files modifications listener, you simply bind it in your application
                    Guice module using the associated HotSwapFilesModificationsListener
                    multibinder:
                    
Multibinder<HotSwapFilesModificationsListener> multibinder =
        Multibinder.newSetBinder(binder(), HotSwapFilesModificationsListener.class);
        
multibinder.addBinding().to(AppConfigFileModificationsListener.class)
                        .in(Scopes.SINGLETON); 
                        
                    
                     A files modifications listener (such as "
 
                    A files modifications listener (such as "AppConfigFileModificationsListener" in the above example), is
                    a class that implements 
                    HotSwapFilesModificationsListener.
                    There are three methods to provide when you create such listener:
                    
                    
Set<FileToWatch> getFilesToWatch()
                            
                                    This is where you specify which files you want to watch. A listener can be interested in 
                                    a single file or in multiple files. The files can be on the file system or on the classpath.
                                    We'll have a look at FileToWatch and how to define the watched files soon.
                                
void fileModified(File modifiedFile)
                            The method is called when a file is modified. This is your hook! For example, you can programmatically delete some cache in your application.
boolean isEnabled()
                            
                                    The listener will only be registered if this method returns 
                                    true. Most of the time, you only want to listen to
                                    files modifications during development.
                                    A common pattern is therefore to make use of 
                                    isDevelopmentMode() :
                                    
@Override
public boolean isEnabled() {
    return getSpincastConfig().isDevelopmentMode();
} 
                                        
                                    Files modifications listener example
                    Here's an example of a files modifications listener. It listens on changes to
                    the "app-configs.yaml" configurations file.
                    When this file changes, it then clears the configurations cache so the new
                    values are used:
                    
                    
public class AppConfigFileModificationsListener implements HotSwapFilesModificationsListener {
    private final AppConfigs appConfigs;
    @Inject
    public AppConfigFileModificationListener(AppConfigs appConfigs) {
        this.appConfigs = appConfigs;
    }
    protected AppConfigs getAppConfigs() {
        return this.appConfigs;
    }
    @Override
    public boolean isEnabled() {
        return getAppConfigs().isDevelopmentMode();
    }
    @Override
    public Set<FileToWatch> getFilesToWatch() {
        return Sets.newHashSet(FileToWatch.ofClasspath("app-config.yaml"));
    }
    @Override
    public void fileModified(File modifiedFile) {
        getAppConfigs().clearConfigCache();
    }
} 
                        
                        Explanation :
app-config.yaml" file, which is located at the root of the
                                    classpath. In the next section, we'll learn how to use FileToWatch
                                    to define the files to watch!
                                app-config.yaml" file
                                    is modified, we tell AppConfigs to clear its cache.
                                    Note that, in this example, we only listen to one file so there is no need to use
                                    the "modifiedFile" parameter to validate what file was modified.
                                
                     You may have as many listeners as you want. Each one can listen to different
                    files or share some of them. If one of your listeners is interested in more than
                    one file, you may have to use the "
                    You may have as many listeners as you want. Each one can listen to different
                    files or share some of them. If one of your listeners is interested in more than
                    one file, you may have to use the "modifiedFile" parameter provided
                    when the fileModified() method is called, to validate which one exactly
                    has been modified.
                
                    The getFilesToWatch() method has to return a set of 
                    FileToWatch.
                    A FileToWatch is able to specify if the files to watch are on the file system or on the
                    classpath, and it also allows you to target files using a regular expression.
                
                    Let's see the three methods you can use to create an instance of FileToWatch:
                    
                    
FileToWatch ofFileSystem(String fileAbsolutePath)
                            
                                    Creates a FileToWatch from the absolute path of a file
                                    on the file system. For example:
                                    
                                    
FileToWatch file = FileToWatch.ofFileSystem("/home/stromgol/dev/some-file-to-watch.json");
 
                                        
                                    FileToWatch ofClasspath(String classpathFilePath)
                            
                                    Creates a FileToWatch from a classpath path. The path can start
                                    with a "/" or not, it doesn't make any difference.
                                    
                                    
FileToWatch file = FileToWatch.ofClasspath("app-configs.yaml");
 
                                        
                                    FileToWatch ofRegEx(String dirPath, String fileNameRegEx, boolean isClassPath)
                            This factory is the most powerful of the three! It allows you to specify a regular expression to use for the names of the files to watch. Those files can be on the file system or on the classpath.
Note that using a regular expression is available for the names of the files, but you still have to specify the directory where those files are located.
FileToWatch fileToWatch = FileToWatch.ofRegEx("/home/stromgol/news/", 
                                              "news-[0-9]+\\.yaml", 
                                              false);
 
                                        
                                        
                                         As you can see in this example, a
                                        As you can see in this example, a FileToWatch
                                        may represent more than one file! Here, any files
                                        matching the provided regular expression (such as "news-12.yaml", 
                                        "news-687.yaml", etc.) will be watched. In this situation, you may have to
                                        check what file actually changed by using the modifiedFile
                                        parameter provided when the fileModified() hook is
                                        called!
                                        
                                    Installing the plugin itself is very simple, but you also need to have DCEVM and HotswapAgent for it to work. The time you spend installing those will be well rewarded, as you'll then have in place the best free Java Hot Swapping solution out there!
1. Add this Maven artifact to your project:
<dependency>
    <groupId>org.spincast</groupId>
    <artifactId>spincast-plugins-hotswap</artifactId>
    <version>2.2.0</version>
</dependency> 
                        
                    2. Add an instance of the SpincastHotSwapPlugin plugin to your Spincast Bootstrapper:
Spincast.configure()
        .plugin(new SpincastHotSwapPlugin())
        // ...
                            
                        
                    The suggested way to use DCEVM with Java 17 is
                    to download a JetBrains JDK release.
                    These already include DCEVM. Download a recent JDK 17 from that page (search for "jbr17").
                    At the time of writing this document, the "Release 17_0_2-b315.1" version was used.
                
In other words, if you want to use the HotSwap plugin, you currently need to use such JDK from Jetbrain during development (of course any JDK 17 will be fine when your application is deployed and hot swapping is not required anymore).
                    1. Download the latest 
                    Hotswap Agent version 
                    .jar file.
                    At the time of writing this document, the latest release is hotswap-agent-1.4.2-SNAPSHOT.jar and
                    is the one to use for Java 17. Hopefully, by the time you read this, a stable version 
                    compatible with Java 17 will have been released.
                
                    2. Rename the .jar file you downloaded to hotswap-agent.jar and move it to
                    lib/hotswap/hotswap-agent.jar inside the Jetbrain JDK you unzipped/installed. For example,
                    if you downloaded and installed jbrsdk-17_0_2-windows-x64-b315.1 from Jetbrains, you
                    have to copy the .jar file to C:\jbrsdk-17_0_2-windows-x64-b315.1\lib\hotswap\hotswap-agent.jar
                    (Windows example). Create the "hotswap" subfolder if required.
                
                    2. When you start your application inside an IDE, during development, 
                    add those VM arguments: "-XX:+AllowEnhancedClassRedefinition -XX:HotswapAgent=fatjar". 
                
Here's an example in Eclipse:
When you start your application with this in place, any modification to a class will make this class being automatically redefined. And if you have registered some listeners, they will be called.
                     Make sure you read the HotswapAgent documentation
                    if you have issues.
                    Make sure you read the HotswapAgent documentation
                    if you have issues.