⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pluginmanager.java

📁 基于Jabber协议的即时消息服务器
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
/**
 * $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 + -