|
|
|
@ -12,8 +12,10 @@ import java.nio.file.Path;
|
|
|
|
|
import java.nio.file.Paths;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.util.jar.Attributes;
|
|
|
|
|
import java.util.jar.JarFile;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class that load the plugins, a very powerful thingy.
|
|
|
|
@ -33,7 +35,7 @@ public class PluginLoader
|
|
|
|
|
/**
|
|
|
|
|
* All plugins
|
|
|
|
|
*/
|
|
|
|
|
Map<String, PluginWrapper> plugins;
|
|
|
|
|
private Map<String, PluginWrapper> plugins;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gives the singleton instance
|
|
|
|
@ -59,32 +61,27 @@ public class PluginLoader
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Discovering all IPlugin classes in plugins directory.");
|
|
|
|
|
|
|
|
|
|
int counter = 0;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Files.walk(Paths.get(Configuration.PLUGINS_DIR))
|
|
|
|
|
Set<Path> files = Files.walk(Paths.get(Configuration.PLUGINS_DIR))
|
|
|
|
|
.filter(Files::isRegularFile)
|
|
|
|
|
.filter(path -> FilenameUtils.getExtension(path.toString()).equals("jar"))
|
|
|
|
|
.forEach(path -> {
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
loadJarFile(path);
|
|
|
|
|
}
|
|
|
|
|
catch(IOException e)
|
|
|
|
|
{
|
|
|
|
|
LOGGER.catching(e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
for(Path i : files) counter += loadJarFile(i);
|
|
|
|
|
}
|
|
|
|
|
catch(IOException e)
|
|
|
|
|
{
|
|
|
|
|
LOGGER.error("IO error while discovering plugins.", e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGGER.debug("Loaded {} plugins from {}", counter, Paths.get(Configuration.PLUGINS_DIR).toAbsolutePath());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Look in a .jar file for IPlugins.
|
|
|
|
|
* Look in a .jar file for IPlugins and preload them.
|
|
|
|
|
*/
|
|
|
|
|
public void loadJarFile(Path target) throws IOException
|
|
|
|
|
public int loadJarFile(Path target) throws IOException
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Looking up {} for plugins.", target.getFileName());
|
|
|
|
|
|
|
|
|
@ -94,13 +91,14 @@ public class PluginLoader
|
|
|
|
|
if(jarAttributes.getValue("DCF-Plugins") == null)
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Jar file has no `DCF-Plugins` attribute in its manifest. Ignoring archive.");
|
|
|
|
|
return;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String[] pluginsToLoad = jarAttributes.getValue("DCF-Plugins").split(" ");
|
|
|
|
|
LOGGER.debug("Manifest returned {} plugins to load.", pluginsToLoad.length);
|
|
|
|
|
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{target.toUri().toURL()});
|
|
|
|
|
|
|
|
|
|
int counter = 0;
|
|
|
|
|
for(String item : pluginsToLoad)
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Attempting to load {}.", item);
|
|
|
|
@ -113,18 +111,179 @@ public class PluginLoader
|
|
|
|
|
catch(ClassNotFoundException e) // Class with that name doesn't exist.
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("No class with name {} was found.", item);
|
|
|
|
|
return;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Class doesn't implement IPlugin, or doesn't have @Plugin annotation.
|
|
|
|
|
if(!clazz.isAnnotationPresent(Plugin.class) || !IPlugin.class.isAssignableFrom(clazz))
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Class does not implement IPlugin or doesn't gave @Plugin annotation.");
|
|
|
|
|
return;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
plugins.put(clazz.getAnnotation(Plugin.class).id(), new PluginWrapper(clazz.asSubclass(IPlugin.class), classLoader));
|
|
|
|
|
LOGGER.debug("Plugin {} {} preloaded.", clazz.getAnnotation(Plugin.class).id(), clazz.getAnnotation(Plugin.class).version());
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
catch(IllegalAccessException | InstantiationException e)
|
|
|
|
|
{
|
|
|
|
|
LOGGER.warn("Plugin " + clazz.getAnnotation(Plugin.class).id() + " failed to preload.", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGGER.debug("Loaded up {} plugins from {}.", counter, target.getFileName());
|
|
|
|
|
return counter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load all plugins that aren't.
|
|
|
|
|
*/
|
|
|
|
|
public void bulkLoadPlugins()
|
|
|
|
|
{
|
|
|
|
|
LOGGER.info("Bulk loading plugins.");
|
|
|
|
|
plugins.entrySet().stream()
|
|
|
|
|
.filter(i -> i.getValue().getState().equals(PluginState.UNLOADED))
|
|
|
|
|
.forEach(i -> loadPlugin(i.getKey()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load a plugin.
|
|
|
|
|
* @param id The unique ID of the plugin.
|
|
|
|
|
*/
|
|
|
|
|
public void loadPlugin(String id)
|
|
|
|
|
{
|
|
|
|
|
// Check for plugin existence.
|
|
|
|
|
if(!plugins.containsKey(id))
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Plugin with ID {} doesn't exist.", id);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGGER.debug("Loading plugin {}.", id);
|
|
|
|
|
PluginWrapper plugin = plugins.get(id);
|
|
|
|
|
|
|
|
|
|
// Check if plugin state is UNLOADED
|
|
|
|
|
if(!plugin.getState().equals(PluginState.UNLOADED))
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Current plugin state does not allow it to be loaded.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
plugin.getInstance().load(Configuration.getStore());
|
|
|
|
|
plugin.setState(PluginState.LOADED);
|
|
|
|
|
LOGGER.info("Loaded plugin {}.", id);
|
|
|
|
|
}
|
|
|
|
|
catch(Throwable throwable)
|
|
|
|
|
{
|
|
|
|
|
LOGGER.warn("Plugin " + id + " failed to load. Entering FAILED state.", throwable);
|
|
|
|
|
plugin.setState(PluginState.FAILED);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Run all plugins that aren't
|
|
|
|
|
*/
|
|
|
|
|
public void bulkRunPlugins()
|
|
|
|
|
{
|
|
|
|
|
LOGGER.info("Bulk running plugins.");
|
|
|
|
|
plugins.entrySet().stream()
|
|
|
|
|
.filter(i -> i.getValue().getState().equals(PluginState.LOADED) || i.getValue().getState().equals(PluginState.STOPPED))
|
|
|
|
|
.forEach(i -> runPlugin(i.getKey()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Run a plugin.
|
|
|
|
|
* @param id The unique ID of the plugin.
|
|
|
|
|
*/
|
|
|
|
|
public void runPlugin(String id)
|
|
|
|
|
{
|
|
|
|
|
// Check for plugin existence.
|
|
|
|
|
if(!plugins.containsKey(id))
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Plugin with ID {} doesn't exist.", id);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGGER.debug("Attempting to run plugin {}.", id);
|
|
|
|
|
PluginWrapper plugin = plugins.get(id);
|
|
|
|
|
|
|
|
|
|
// Check if plugin state is LOADED or STOPPED
|
|
|
|
|
if(!plugin.getState().equals(PluginState.LOADED) && !plugin.getState().equals(PluginState.STOPPED))
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Current plugin state does not allow it to run.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
plugin.getInstance().run();
|
|
|
|
|
LOGGER.info("Plugin {} is now running. (last state: {})", id, plugin.getState());
|
|
|
|
|
plugin.setState(PluginState.RUNNING);
|
|
|
|
|
}
|
|
|
|
|
catch(Throwable throwable)
|
|
|
|
|
{
|
|
|
|
|
LOGGER.warn("Plugin " + id + " failed to run. Entering FAILED state. (last state: " + plugin.getState() + ")", throwable);
|
|
|
|
|
plugin.setState(PluginState.FAILED);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stop a plugin from running.
|
|
|
|
|
* @param id The unique ID of the plugin.
|
|
|
|
|
*/
|
|
|
|
|
public void stopPlugin(String id)
|
|
|
|
|
{
|
|
|
|
|
// Check for plugin existence.
|
|
|
|
|
if(!plugins.containsKey(id))
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Plugin with ID {} doesn't exist.", id);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGGER.debug("Plugin {} {} preloaded.", clazz.getAnnotation(Plugin.class).id(), clazz.getAnnotation(Plugin.class).version());
|
|
|
|
|
plugins.put(clazz.getAnnotation(Plugin.class).id(), new PluginWrapper(clazz.asSubclass(IPlugin.class), classLoader));
|
|
|
|
|
LOGGER.debug("Stopping plugin {}.", id);
|
|
|
|
|
PluginWrapper plugin = plugins.get(id);
|
|
|
|
|
|
|
|
|
|
// Check if plugin state is RUNNING
|
|
|
|
|
if(!plugin.getState().equals(PluginState.RUNNING))
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Current plugin state does not allow it to stop.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
plugin.getInstance().stop();
|
|
|
|
|
LOGGER.info("Stopped plugin {}.", id);
|
|
|
|
|
plugin.setState(PluginState.STOPPED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Unload a plugin.
|
|
|
|
|
* @param id The unique ID of the plugin.
|
|
|
|
|
*/
|
|
|
|
|
public void unloadPlugin(String id)
|
|
|
|
|
{
|
|
|
|
|
// Check for plugin existence.
|
|
|
|
|
if(!plugins.containsKey(id))
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Plugin with ID {} doesn't exist.", id);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOGGER.debug("Unloading plugin {}.", id);
|
|
|
|
|
PluginWrapper plugin = plugins.get(id);
|
|
|
|
|
|
|
|
|
|
// Check if plugin state is STOPPED
|
|
|
|
|
if(!plugin.getState().equals(PluginState.STOPPED))
|
|
|
|
|
{
|
|
|
|
|
LOGGER.debug("Current plugin state does not allow it to be unloaded.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
plugin.getInstance().unload();
|
|
|
|
|
LOGGER.info("Unloaded plugin {}.", id);
|
|
|
|
|
plugin.setState(PluginState.UNLOADED);
|
|
|
|
|
}
|
|
|
|
|
}
|