And people think dependancy loading is easy. Close #381

This commit is contained in:
md_5 2013-06-05 18:24:33 +10:00
parent 33d315b719
commit b1e3f6a75b
4 changed files with 65 additions and 67 deletions

View File

@ -76,11 +76,11 @@ public class Plugin
* @param description the description that describes this plugin * @param description the description that describes this plugin
* @param jarfile this plugins jar or container * @param jarfile this plugins jar or container
*/ */
final void init(ProxyServer proxy, PluginDescription description, File file) final void init(ProxyServer proxy, PluginDescription description)
{ {
this.proxy = proxy; this.proxy = proxy;
this.description = description; this.description = description;
this.file = file; this.file = description.getFile();
this.logger = new PluginLogger( this ); this.logger = new PluginLogger( this );
} }
} }

View File

@ -1,5 +1,6 @@
package net.md_5.bungee.api.plugin; package net.md_5.bungee.api.plugin;
import java.io.File;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -35,4 +36,8 @@ public class PluginDescription
* Plugin hard dependencies. * Plugin hard dependencies.
*/ */
private Set<String> depends = new HashSet<>(); private Set<String> depends = new HashSet<>();
/**
* File we were loaded from.
*/
private File file = null;
} }

View File

@ -10,6 +10,7 @@ import java.net.URLClassLoader;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
@ -38,8 +39,9 @@ public class PluginManager
/*========================================================================*/ /*========================================================================*/
private final Yaml yaml = new Yaml(); private final Yaml yaml = new Yaml();
private final EventBus eventBus; private final EventBus eventBus;
private final Map<String, Plugin> plugins = new HashMap<>(); private final Map<String, Plugin> plugins = new LinkedHashMap<>();
private final Map<String, Command> commandMap = new HashMap<>(); private final Map<String, Command> commandMap = new HashMap<>();
private Map<String, PluginDescription> toLoad = new HashMap<>();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public PluginManager(ProxyServer proxy) public PluginManager(ProxyServer proxy)
@ -130,23 +132,37 @@ public class PluginManager
return plugins.get( name ); return plugins.get( name );
} }
/** public void loadAndEnablePlugins()
* Enable all plugins by calling the {@link Plugin#onEnable()} method.
*/
public void enablePlugins()
{ {
Map<Plugin, Boolean> pluginStatuses = new HashMap<>(); Map<PluginDescription, Boolean> pluginStatuses = new HashMap<>();
for ( Map.Entry<String, Plugin> entry : plugins.entrySet() ) for ( Map.Entry<String, PluginDescription> entry : toLoad.entrySet() )
{ {
Plugin plugin = entry.getValue(); PluginDescription plugin = entry.getValue();
if ( !this.enablePlugin( pluginStatuses, new Stack<Plugin>(), plugin ) ) if ( !enablePlugin( pluginStatuses, new Stack<PluginDescription>(), plugin ) )
{ {
ProxyServer.getInstance().getLogger().warning( "Failed to enable " + entry.getKey() ); ProxyServer.getInstance().getLogger().warning( "Failed to enable " + entry.getKey() );
} }
} }
toLoad.clear();
toLoad = null;
for ( Plugin plugin : plugins.values() )
{
try
{
plugin.onEnable();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
{
plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor()
} );
} catch ( Throwable t )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), t );
}
}
} }
private boolean enablePlugin(Map<Plugin, Boolean> pluginStatuses, Stack<Plugin> dependStack, Plugin plugin) private boolean enablePlugin(Map<PluginDescription, Boolean> pluginStatuses, Stack<PluginDescription> dependStack, PluginDescription plugin)
{ {
if ( pluginStatuses.containsKey( plugin ) ) if ( pluginStatuses.containsKey( plugin ) )
{ {
@ -157,9 +173,9 @@ public class PluginManager
boolean status = true; boolean status = true;
// try to load dependencies first // try to load dependencies first
for ( String dependName : plugin.getDescription().getDepends() ) for ( String dependName : plugin.getDepends() )
{ {
Plugin depend = this.plugins.get( dependName ); PluginDescription depend = toLoad.get( dependName );
Boolean dependStatus = depend != null ? pluginStatuses.get( depend ) : Boolean.FALSE; Boolean dependStatus = depend != null ? pluginStatuses.get( depend ) : Boolean.FALSE;
if ( dependStatus == null ) if ( dependStatus == null )
@ -167,11 +183,11 @@ public class PluginManager
if ( dependStack.contains( depend ) ) if ( dependStack.contains( depend ) )
{ {
StringBuilder dependencyGraph = new StringBuilder(); StringBuilder dependencyGraph = new StringBuilder();
for ( Plugin element : dependStack ) for ( PluginDescription element : dependStack )
{ {
dependencyGraph.append( element.getDescription().getName() ).append( " -> " ); dependencyGraph.append( element.getName() ).append( " -> " );
} }
dependencyGraph.append( plugin.getDescription().getName() ).append( " -> " ).append( dependName ); dependencyGraph.append( plugin.getName() ).append( " -> " ).append( dependName );
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: " + dependencyGraph ); ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: " + dependencyGraph );
status = false; status = false;
} else } else
@ -186,7 +202,7 @@ public class PluginManager
{ {
ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[] ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]
{ {
depend.getDescription().getName(), plugin.getDescription().getName() depend.getName(), plugin.getName()
} ); } );
status = false; status = false;
} }
@ -202,15 +218,23 @@ public class PluginManager
{ {
try try
{ {
plugin.onEnable(); URLClassLoader loader = new PluginClassloader( new URL[]
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
{ {
plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor() plugin.getFile().toURI().toURL()
} );
Class<?> main = loader.loadClass( plugin.getMain() );
Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance();
clazz.init( proxy, plugin );
plugins.put( plugin.getName(), clazz );
clazz.onLoad();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[]
{
plugin.getName(), plugin.getVersion(), plugin.getAuthor()
} ); } );
} catch ( Throwable t ) } catch ( Throwable t )
{ {
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), t ); proxy.getLogger().log( Level.WARNING, "Error enabling plugin " + plugin.getName(), t );
status = false;
} }
} }
@ -218,51 +242,12 @@ public class PluginManager
return status; return status;
} }
/**
* Load a plugin from the specified file. This file must be in jar format.
* This will not enable plugins, {@link #enablePlugins()} must be called.
*
* @param file the file to load from
* @throws Exception Any exceptions encountered when loading a plugin from
* this file.
*/
public void loadPlugin(File file) throws Exception
{
Preconditions.checkNotNull( file, "file" );
Preconditions.checkArgument( file.isFile(), "Must load from file" );
try ( JarFile jar = new JarFile( file ) )
{
JarEntry pdf = jar.getJarEntry( "plugin.yml" );
Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml" );
try ( InputStream in = jar.getInputStream( pdf ) )
{
PluginDescription desc = yaml.loadAs( in, PluginDescription.class );
URLClassLoader loader = new PluginClassloader( new URL[]
{
file.toURI().toURL()
} );
Class<?> main = loader.loadClass( desc.getMain() );
Plugin plugin = (Plugin) main.getDeclaredConstructor().newInstance();
plugin.init( proxy, desc, file );
plugins.put( desc.getName(), plugin );
plugin.onLoad();
ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[]
{
desc.getName(), desc.getVersion(), desc.getAuthor()
} );
}
}
}
/** /**
* Load all plugins from the specified folder. * Load all plugins from the specified folder.
* *
* @param folder the folder to search for plugins in * @param folder the folder to search for plugins in
*/ */
public void loadPlugins(File folder) public void detectPlugins(File folder)
{ {
Preconditions.checkNotNull( folder, "folder" ); Preconditions.checkNotNull( folder, "folder" );
Preconditions.checkArgument( folder.isDirectory(), "Must load from a directory" ); Preconditions.checkArgument( folder.isDirectory(), "Must load from a directory" );
@ -271,9 +256,17 @@ public class PluginManager
{ {
if ( file.isFile() && file.getName().endsWith( ".jar" ) ) if ( file.isFile() && file.getName().endsWith( ".jar" ) )
{ {
try try ( JarFile jar = new JarFile( file ) )
{ {
loadPlugin( file ); JarEntry pdf = jar.getJarEntry( "plugin.yml" );
Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml" );
try ( InputStream in = jar.getInputStream( pdf ) )
{
PluginDescription desc = yaml.loadAs( in, PluginDescription.class );
desc.setFile( file );
toLoad.put( desc.getName(), desc );
}
} catch ( Exception ex ) } catch ( Exception ex )
{ {
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load plugin from file " + file, ex ); ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load plugin from file " + file, ex );

View File

@ -195,7 +195,7 @@ public class BungeeCord extends ProxyServer
public void start() throws Exception public void start() throws Exception
{ {
pluginsFolder.mkdir(); pluginsFolder.mkdir();
pluginManager.loadPlugins( pluginsFolder ); pluginManager.detectPlugins( pluginsFolder );
config.load(); config.load();
if ( reconnectHandler == null ) if ( reconnectHandler == null )
{ {
@ -203,7 +203,7 @@ public class BungeeCord extends ProxyServer
} }
isRunning = true; isRunning = true;
pluginManager.enablePlugins(); pluginManager.loadAndEnablePlugins();
startListeners(); startListeners();