Mostly working plugin loader.

keep-around/d31701866686f66088b78de2e29736ae36e55a68
Pingex aka Raphaël 9 years ago
parent 107bf41a76
commit fcef6deeeb

@ -32,11 +32,15 @@ public class DiscordCommandableFramework
// Load plugins
PluginLoader.getInstance().discoverPluginsDirectory();
PluginLoader.getInstance().bulkLoadPlugins();
// Set up initial connection
ClientBuilder builder = new ClientBuilder();
if(Configuration.isConnectionToken()) builder.withToken(Configuration.CONNECTION_TOKEN);
else builder.withLogin(Configuration.CONNECTION_USERNAME, Configuration.CONNECTION_PASSWORD);
GatewayConnectionsManager.getInstance().registerConnection(builder);
// Run plugins
PluginLoader.getInstance().bulkRunPlugins();
}
}

@ -11,12 +11,12 @@ public interface IPlugin
* Load basic parameters, instantiate some objects, load configuration
* @param configuration Configuration from dcf.properties
*/
void load(ImmutableConfiguration configuration);
void load(ImmutableConfiguration configuration) throws Throwable;
/**
* Power up objects, shedule tasks, and so on
* Power up objects, schedule tasks, and so on
*/
void run();
void run() throws Throwable;
/**
* Stop everything, doesn't mean we'll unload the plugin

@ -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);
}
}

@ -5,11 +5,6 @@ package net.pingex.dcf.modularity;
*/
public enum PluginState
{
/**
* The plugin does not have an associated {@code IPlugin} instance.
*/
VOID,
/**
* The plugin is unloaded, ie. the loader did not run {@code load()}, or executed {@code unload()} on the associated instance.
*/

@ -42,15 +42,15 @@ public class PluginWrapper
*/
private PluginState state;
public PluginWrapper(Class<? extends IPlugin> pluginClass, URLClassLoader classLoader)
public PluginWrapper(Class<? extends IPlugin> pluginClass, URLClassLoader classLoader) throws IllegalAccessException, InstantiationException
{
this.id = pluginClass.getAnnotation(Plugin.class).id();
this.version = pluginClass.getAnnotation(Plugin.class).version();
this.description = pluginClass.getAnnotation(Plugin.class).description();
this.pluginClass = pluginClass;
this.classLoader = classLoader;
this.instance = null;
this.state = PluginState.VOID;
this.instance = pluginClass.newInstance();
this.state = PluginState.UNLOADED;
}
// GETTERS