And people think dependancy loading is easy. Close #381
This commit is contained in:
parent
33d315b719
commit
b1e3f6a75b
@ -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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 );
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user