📄 pluginmanager.java
字号:
/**
* $RCSfile$
* $Revision: 3001 $
* $Date: 2005-10-31 05:39:25 -0300 (Mon, 31 Oct 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.container;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jivesoftware.admin.AdminConsole;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.Version;
import org.jivesoftware.wildfire.XMPPServer;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.ZipFile;
/**
* Loads and manages plugins. The <tt>plugins</tt> directory is monitored for any
* new plugins, and they are dynamically loaded.<p>
* <p/>
* An instance of this class can be obtained using:
* <p/>
* <tt>XMPPServer.getInstance().getPluginManager()</tt>
*
* @author Matt Tucker
* @see Plugin
* @see org.jivesoftware.wildfire.XMPPServer#getPluginManager()
*/
public class PluginManager {
private File pluginDirectory;
private Map<String, Plugin> plugins;
private Map<Plugin, PluginClassLoader> classloaders;
private Map<Plugin, File> pluginDirs;
private ScheduledExecutorService executor = null;
private Map<Plugin, PluginDevEnvironment> pluginDevelopment;
private Map<Plugin, List<String>> parentPluginMap;
private Map<Plugin, String> childPluginMap;
private Set<String> devPlugins;
private PluginMonitor pluginMonitor;
/**
* Constructs a new plugin manager.
*
* @param pluginDir the plugin directory.
*/
public PluginManager(File pluginDir) {
this.pluginDirectory = pluginDir;
plugins = new ConcurrentHashMap<String, Plugin>();
pluginDirs = new HashMap<Plugin, File>();
classloaders = new HashMap<Plugin, PluginClassLoader>();
pluginDevelopment = new HashMap<Plugin, PluginDevEnvironment>();
parentPluginMap = new HashMap<Plugin, List<String>>();
childPluginMap = new HashMap<Plugin, String>();
devPlugins = new HashSet<String>();
pluginMonitor = new PluginMonitor();
}
/**
* Starts plugins and the plugin monitoring service.
*/
public void start() {
executor = new ScheduledThreadPoolExecutor(1);
// See if we're in development mode. If so, check for new plugins once every 5 seconds.
// Otherwise, default to every 20 seconds.
if (Boolean.getBoolean("developmentMode")) {
executor.scheduleWithFixedDelay(pluginMonitor, 1, 5, TimeUnit.SECONDS);
}
else {
executor.scheduleWithFixedDelay(pluginMonitor, 1, 20, TimeUnit.SECONDS);
}
}
/**
* Shuts down all running plugins.
*/
public void shutdown() {
// Stop the plugin monitoring service.
if (executor != null) {
executor.shutdown();
}
// Shutdown all installed plugins.
for (Plugin plugin : plugins.values()) {
try {
plugin.destroyPlugin();
}
catch (Exception e) {
Log.error(e);
}
}
plugins.clear();
pluginDirs.clear();
classloaders.clear();
pluginDevelopment.clear();
childPluginMap.clear();
pluginMonitor = null;
}
/**
* Installs or updates an existing plugin.
*
* @param in the input stream that contains the new plugin definition.
* @param pluginFilename the filename of the plugin to create or update.
* @return true if the plugin was successfully installed or updated.
*/
public boolean installPlugin(InputStream in, String pluginFilename) {
try {
byte[] b = new byte[1024];
int len;
// Absolute path to the plugin file
String absolutePath = pluginDirectory + File.separator + pluginFilename;
// Save input stream contents to a temp file
OutputStream out = new FileOutputStream(absolutePath + ".part");
while ((len = in.read(b)) != -1) {
//write byte to file
out.write(b, 0, len);
}
out.close();
// Delete old .jar (if it exists)
new File(absolutePath).delete();
// Rename temp file to .jar
new File(absolutePath + ".part").renameTo(new File(absolutePath));
// Ask the plugin monitor to update the plugin immediately.
pluginMonitor.run();
}
catch (IOException e) {
Log.error("Error installing new version of plugin: " + pluginFilename, e);
return false;
}
return true;
}
/**
* Returns true if the specified filename, that belongs to a plugin, exists.
*
* @param pluginFilename the filename of the plugin to create or update.
* @return true if the specified filename, that belongs to a plugin, exists.
*/
public boolean isPluginDownloaded(String pluginFilename) {
return new File(pluginDirectory + File.separator + pluginFilename).exists();
}
/**
* Returns a Collection of all installed plugins.
*
* @return a Collection of all installed plugins.
*/
public Collection<Plugin> getPlugins() {
return Collections.unmodifiableCollection(plugins.values());
}
/**
* Returns a plugin by name or <tt>null</tt> if a plugin with that name does not
* exist. The name is the name of the directory that the plugin is in such as
* "broadcast".
*
* @param name the name of the plugin.
* @return the plugin.
*/
public Plugin getPlugin(String name) {
return plugins.get(name);
}
/**
* Returns the plugin's directory.
*
* @param plugin the plugin.
* @return the plugin's directory.
*/
public File getPluginDirectory(Plugin plugin) {
return pluginDirs.get(plugin);
}
/**
* Loads a plug-in module into the container. Loading consists of the
* following steps:<ul>
* <p/>
* <li>Add all jars in the <tt>lib</tt> dir (if it exists) to the class loader</li>
* <li>Add all files in <tt>classes</tt> dir (if it exists) to the class loader</li>
* <li>Locate and load <tt>module.xml</tt> into the context</li>
* <li>For each jive.module entry, load the given class as a module and start it</li>
* <p/>
* </ul>
*
* @param pluginDir the plugin directory.
*/
private void loadPlugin(File pluginDir) {
// Only load the admin plugin during setup mode.
if (XMPPServer.getInstance().isSetupMode() && !(pluginDir.getName().equals("admin"))) {
return;
}
Log.debug("Loading plugin " + pluginDir.getName());
Plugin plugin;
try {
File pluginConfig = new File(pluginDir, "plugin.xml");
if (pluginConfig.exists()) {
SAXReader saxReader = new SAXReader();
saxReader.setEncoding("UTF-8");
Document pluginXML = saxReader.read(pluginConfig);
// See if the plugin specifies a version of Wildfire
// required to run.
Element minServerVersion = (Element)pluginXML.selectSingleNode("/plugin/minServerVersion");
if (minServerVersion != null) {
String requiredVersion = minServerVersion.getTextTrim();
Version version = XMPPServer.getInstance().getServerInfo().getVersion();
String hasVersion = version.getMajor() + "." + version.getMinor() + "." +
version.getMicro();
if (hasVersion.compareTo(requiredVersion) < 0) {
String msg = "Ignoring plugin " + pluginDir.getName() + ": requires " +
"server version " + requiredVersion;
Log.warn(msg);
System.out.println(msg);
return;
}
}
PluginClassLoader pluginLoader;
// Check to see if this is a child plugin of another plugin. If it is, we
// re-use the parent plugin's class loader so that the plugins can interact.
Element parentPluginNode = (Element)pluginXML.selectSingleNode("/plugin/parentPlugin");
String pluginName = pluginDir.getName();
String webRootKey = pluginName + ".webRoot";
String classesDirKey = pluginName + ".classes";
String webRoot = System.getProperty(webRootKey);
String classesDir = System.getProperty(classesDirKey);
if (webRoot != null) {
final File compilationClassesDir = new File(pluginDir, "classes");
if (!compilationClassesDir.exists()) {
compilationClassesDir.mkdir();
}
compilationClassesDir.deleteOnExit();
}
if (parentPluginNode != null) {
String parentPlugin = parentPluginNode.getTextTrim();
// See if the parent is already loaded.
if (plugins.containsKey(parentPlugin)) {
pluginLoader = classloaders.get(getPlugin(parentPlugin));
pluginLoader.addDirectory(pluginDir, classesDir != null);
}
else {
// See if the parent plugin exists but just hasn't been loaded yet.
// This can only be the case if this plugin name is alphabetically before
// the parent.
if (pluginDir.getName().compareTo(parentPlugin) < 0) {
// See if the parent exists.
File file = new File(pluginDir.getParentFile(), parentPlugin + ".jar");
if (file.exists()) {
// Silently return. The child plugin will get loaded up on the next
// plugin load run after the parent.
return;
}
else {
file = new File(pluginDir.getParentFile(), parentPlugin + ".war");
if (file.exists()) {
// Silently return. The child plugin will get loaded up on the next
// plugin load run after the parent.
return;
}
else {
String msg = "Ignoring plugin " + pluginDir.getName() + ": parent plugin " +
parentPlugin + " not present.";
Log.warn(msg);
System.out.println(msg);
return;
}
}
}
else {
String msg = "Ignoring plugin " + pluginDir.getName() + ": parent plugin " +
parentPlugin + " not present.";
Log.warn(msg);
System.out.println(msg);
return;
}
}
}
// This is not a child plugin, so create a new class loader.
else {
pluginLoader = new PluginClassLoader();
pluginLoader.addDirectory(pluginDir, classesDir != null);
}
// Check to see if development mode is turned on for the plugin. If it is,
// configure dev mode.
PluginDevEnvironment dev = null;
if (webRoot != null || classesDir != null) {
dev = new PluginDevEnvironment();
System.out.println("Plugin " + pluginName + " is running in development mode.");
Log.info("Plugin " + pluginName + " is running in development mode.");
if (webRoot != null) {
File webRootDir = new File(webRoot);
if (!webRootDir.exists()) {
// Ok, let's try it relative from this plugin dir?
webRootDir = new File(pluginDir, webRoot);
}
if (webRootDir.exists()) {
dev.setWebRoot(webRootDir);
}
}
if (classesDir != null) {
File classes = new File(classesDir);
if (!classes.exists()) {
// ok, let's try it relative from this plugin dir?
classes = new File(pluginDir, classesDir);
}
if (classes.exists()) {
dev.setClassesDir(classes);
pluginLoader.addURL(classes.getAbsoluteFile().toURL());
}
}
}
pluginLoader.initialize();
String className = pluginXML.selectSingleNode("/plugin/class").getText();
plugin = (Plugin)pluginLoader.loadClass(className).newInstance();
if (parentPluginNode != null) {
String parentPlugin = parentPluginNode.getTextTrim();
// See if the parent is already loaded.
if (plugins.containsKey(parentPlugin)) {
pluginLoader = classloaders.get(getPlugin(parentPlugin));
classloaders.put(plugin, pluginLoader);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -