public class HotReloader extends Object
A "shell" creates a HotReloader, and obtains app instances by calling
often to handle a task, e.g. to process an incoming http request.
getAppInstance() method returns the same instance
(for the same class name) until reloading occurs
-- if some source files are changed,
getAppInstance() may trigger
recompilation and reloading, returning a new app instance created in a new app class loader.
HotReloader is thread-safe, particularly,
getAppInstance() can be invoked concurrently.
If compiler C2 depends on C1 (e.g. C2 consumes the output files of C1), C2 should be added after C1.
getAppInstance() is invoked, if some source files are changed,
the corresponding compilers will be invoked to recompile them.
Recompilation may or may not trigger reloading.
The shell class loader (the one that loads the class of this HotReloader instance)
is required to be a
The app class loader is a sibling of the shell class loader, sharing the same parent.
The app class loader is also a URLClassLoader, initially containing the same class paths
as the shell's (usually the JVM's class paths). Additional class paths can be added
to the app class loader by
The shell and the app share the same classes loaded by the parent class loader, notably all the JRE classes.
In addition, the app needs to share some classes that's loaded by the shell.
Often, the shell interacts with the app instance through an interface, e.g.
HttpHandler, it's necessary that the app sees the same
for that interface, and all interfaces/classes referenced by that interface, recursively.
The shell also needs to share other classes to communicate with the app,
The shell can specify these classes by
When reloading occurs, the previous app class loader and app instances are dereferenced from this HotReloader. Hopefully they will be garbage collected soon.
If an app instance implements
close() method will be called upon unloading.
An unloaded app instance may still be functioning for some time, e.g. to continue processing previously accepted http requests.
Threads created by an app instance may persist after unloading; this is very bad,
especially because it likely references the app class loader, hindering garbage collection.
To avoid that, use thread executors with timeouts (even for core threads);
or do necessary cleanup/shutdown actions in the
close() method mentioned above.
|Constructor and Description|
Create a HotReloader instance.
Get the app instance for the class name.
Add a compiler.
Add shared classes.
Prepend a class path to the app class loader.
Reload on java file changes.
Reload on class file changes.
Reload on file changes.
Where diagnosis messages will be printed to.
Set where diagnosis messages will be printed to.
The same instance will be returned for the same class name, until reloading occurs, then a new instance will be created in a new class loader.
The app class must have a public 0-arg constructor.
public void addCompiler(HotCompiler compiler, boolean reload, Path... srcDirs)
reload=true, reloading is needed after this compiler recompiles some source files.
It is possible that a recompilation does not require reloading, because the app can handle the
new outputs of the compiler on-the-fly, without being reloaded.
public void addSharedClasses(Class<?>... sharedClasses)
Dependant classes referenced in the public/protected APIs of
will be added as shared classes as well (recursively).
path- refers to a class dir or a jar file
public HotReloader onJavaFiles(String... srcDirs) throws Exception
This method adds a
JavacCompiler. If java files under the
are changed, they will be recompiled, and reloading will occur.
The javac options are
The source directory structure must follow the package structure.
Each java file must contain only one top-level class.
For example, class
"foo.Bar" must be defined in a "foo/Bar.java" file
under one of the
The output dir for class files is determined by
The directory will be cleaned before the first compilation.
public HotReloader onClassFiles()
If class files under the JVM classpath are changed, reloading will occur.
public HotReloader onFiles(String fileDesc, String filePattern, String... srcDirs)
If files under
srcDirs matching the
filePattern are changed,
reloading will occur.
The format of
filePattern is specified in
Example use case: If the app reads
on startup and cache the information in memory,
it needs to be reloaded if some of the files are changed.
hotReloader.onFiles("prop file", "glob:**.properties", SRC_DIR)
fileDesc- description of the file type, used for diagnosis outputs
public Consumer<CharSequence> getMessageOut()
By default the messages will be printed to
setMessageOut(Consumer) to change that.