Features
Overview
Sometimes you have to load some set of JAR files to initialize some Plugin or a set of Plugins. And these JARs may be uploaded to the server folder where you watch for them one by one. The Feature
and the FeatureManager
is the mechanism to declare a set of JAR files required for some “feature” and start the initialization process when all necessary files appeared.
Feature of Plugins
Imagine we have an example feature which requires two plugins to be loaded. Create a server which initialization is delayed until both plugin JARs appeared in the ‘libs’ folder.
Plugins initialization
The code to initialize Plugins stays the same as in plugins example. However it will be called only when all necessary JARs appeared.
Filesystem tracker
At the first you need to create the instance of IFilesystemTracker
. It takes care on monitoring of the filesystem for already existing and new appearing files.
IFilesystemTracker jarFilesTracker = new FilesystemTracker(
(path) -> path.getPath().endsWith(".jar"),
ListenerTask::new
);
The first parameter to constructor is a filter of file paths. Here it waits only for JAR files ignoring other files. However, the Feature may wait for other resources, not only JARs.
The second parameter is the ListeningTaskFactory
which creates Runnable
to monitor the folder for new files in a background thread. The ListenerTask
is the predefined Runnable
of such kind, so it’s constructor is used as a factory and is passed as the second parameter.
Handling of new files
You can handle new files appearing in addition to the Feature functionality. Just add a lambda handler.
jarFilesTracker.addFileHandler((file) -> System.out.println("Found file: " + file));
Handling of errors
If any handler for new files failed, the error handler can be called.
jarFilesTracker.addErrorHandler((e) -> {
System.out.println("Failed to track files: " + e);
e.printStackTrace();
});
Feature manager
Then you create the FeatureManager
over the FilesystemTracker
.
IFeatureManager featureManager = new FeatureManager(jarFilesTracker);
Define Feature
Now you create a new named Feature
from FeatureManager
.
IFeature feature = featureManager.newFeature("example.feature");
You should define the set of files required by this feature. Here these are two plugins.
feature.requireFile("Plugin1.jar");
feature.requireFile("Plugin2.jar");
Note the filenames must be relative to the monitoring folder.
Action when all files present
Define the action to call when all required files appeared in the monitoring folder. Here it is the code to load plugins.
feature.whenPresent(files -> {
try {
pluginLoader.loadPlugin(files);
bootstrap.start();
} catch (Throwable e) {
throw new RuntimeException("Plugin loading failed.", e);
}
});
Subscribe for updates
Here Feature
adds it’s own handler to FilesystemTracker
to takes care about new files appearing in the folder.
feature.listen();
Start monitoring
Now start a thread to monitor the folder and notify Feature
when new files appear. Actually the file handler is called initially for files already existing in the folder and then is called when new file appeared in the folder.
IPath jarsDir = new Path("libs");
jarFilesTracker.start(jarsDir);
Note FilesystemTracker
and PluginLoader
works with instances of IPath
for file paths.
Sequence of operations
FilesystemTracker
(actually, it’sListenerTask
) starts monitoring the folder in the background thread.- For each existing file and each new created file the tracker calls registered handlers. One of the handler is
Feature
. Feature
tracks the added files. When not all required files appeared, it does nothing.- When all required files appeared
Feature
calls it’swhenPresent
callback. - The callback loads and initializes the plugins from the appeared JARs.
Note the background filesystem tracking thread continues running. And the Feature won’t calls the plugins initialization until all required files are appeared. So, the initialization may happen at any moment in a future when all necessary files appeared in the monitoring folder.
Code
The sources of this tutorial: