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.
Imagine we have a 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.
The code to initialize Plugins stays the same as in plugins example. However it will be called only when all necessary JARs appeared.
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.
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));
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(); });
Then you create the FeatureManager over the FilesystemTracker.
IFeatureManager featureManager = new FeatureManager(jarFilesTracker);
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.
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); } });
Here Feature adds it’s own handler to FilesystemTracker to takes care about new files appearing in the folder.
feature.listen();
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.
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.