PluginManager.java :  » Wiki-Engine » VeryQuickWiki » vqwiki » Java Open Source

Java Open Source » Wiki Engine » VeryQuickWiki 
VeryQuickWiki » vqwiki » PluginManager.java
/**
 * @author garethc
 * Date: Jan 7, 2003
 */
package vqwiki;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import vqwiki.lex.LexExtender;
import vqwiki.utils.Utilities;

import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.net.URL;
import java.net.URLDecoder;
import java.security.AccessControlException;
import java.io.UnsupportedEncodingException;


/**
 * Manager for loading plugins from zip files. The plugins directory is a directory under
 * the Wiki home directory (where the topic contents live) named "plugins". A plugin occupies
 * one zip file and needs to include a file named the same as the zip file but with
 * and "xml" file extension in the WEB-INF/classes directory.
 * So if the plugin file is called test.zip, then the included file
 * should be called test.xml.
 * <p/>
 * The plugin zip is extracted directly to the wiki installation directory, so any class files
 * should be in the path WEB-INF/classes and any jars should be in WEB-INF/lib.
 * <p/>
 * The plugin xml file can contain various entries that will cause mappings to be inserted in the
 * different mapping repositories.
 */
public class PluginManager {

    private static PluginManager instance;
    public static final String PLUGINS_DIR = "plugins";
    private static final Logger logger = Logger.getLogger(PluginManager.class);
    private static final String TAG_PLUGIN = "plugin";
    private static final String TAG_EXTERNAL_LEX = "external-lex";
    private static final String TAG_TOPIC_LISTENER = "topic-listener";
    private static final String TAG_ACTION = "action";
    private static final String ATTR_TAG = "tag";
    private static final String ATTR_CLASS = "class";
    private static final String ATTR_NAME = "name";
    private static final String ATTR_REDIRECT = "redirect";
    private static final String ATTR_PSEUDOTOPIC = "pseudotopic";

    /** Listeners for the WikiBase to use */
    private List topicListeners = new ArrayList();
    /** Number of millis to wait after a plugin has been unzipped so that the class loader has a chance to load the classes */
    private static final long PAUSE = 3000;

    /**
     * Get an instance of the manager
     *
     * @return singleton instance
     */
    public synchronized static PluginManager getInstance() {
        if (instance == null) {
            instance = new PluginManager();
        }
        return instance;
    }

    /**
     * Hide default constructor
     */
    private PluginManager() {
    }

    /**
     * Look for plugins and install them as required
     */
    public void installAll() {
        File pluginDir = new File(Environment.dir(), PLUGINS_DIR);
        // create if necessary
        pluginDir.mkdir();
        logger.debug("Looking for plugins in " + pluginDir);
        File[] files = pluginDir.listFiles();
        if (files == null) {
            return;
        }
        if (files.length == 0) {
            return;
        }
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            String name = file.getName();
            if (name.endsWith(".zip")) {
                name = name.substring(0, name.length() - 4);
                logger.debug("Plugin found: " + name);
                install(name, file);
            }
        }
    }

    /**
     * Return the name the plugin XML file will have
     *
     * @param name plugin name
     * @return name
     */
    private String getPluginPropertiesFilename(String name) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("/");
        buffer.append(name);
        buffer.append(".xml");
        String pluginPropertiesFilename = buffer.toString();
        return pluginPropertiesFilename;
    }

    /**
     * Install the plugin
     *
     * @param name plugin name
     * @param file zip file containing the plugin
     */
    private void install(String name, File file) {
        try {
            String installedVersion = getInstalledPluginVersion(name);
            String newVersion = readPluginAttributeFromPlugin(name, file, "version");
            logger.debug("installed plugin version: " + installedVersion + ", new version: " + newVersion);
            if (installedVersion == null || newVersion.compareTo(installedVersion) > 0) {
                logger.info("Installing plugin: " + name);
                String realPath = Environment.getInstance().getRealPath();
                if (realPath == null) {
                    logger.error(
                        "installation directory is null, plugin manager must be running without any requests having been made!"
                    );
                    return;
                }
                Utilities.unzip(file, new File(realPath));
                // give the app server a chance to load the classes
                Thread.sleep(PAUSE);
            } else {
                logger.info("plugin of same version already installed: " + name);
            }
        } catch (Exception e) {
            logger.error("unable to determine current version of plugin: " + name, e);
        }
        Document doc;
        String pluginPropertiesFilename = null;
        String res = null;
        try {
            pluginPropertiesFilename = getPluginPropertiesFilename(name);
            URL resource = PluginManager.class.getResource(pluginPropertiesFilename);
            if (resource == null) {
                logger.error("plugin properties file not found: " + pluginPropertiesFilename);
                return;
            } else {
                String defaultencoding = null;
                try {
                    defaultencoding = System.getProperty("file.encoding");
                } catch (AccessControlException ae) {
                    logger.warn("This application server doesn't allow to access " +
                                "file.encoding with System.getProperty. Set default " +
                                "encoding for filename-URL to UTF-8");
                    defaultencoding = "UTF-8";
                }
                try {
                    res = URLDecoder.decode(resource.getFile(), defaultencoding);
                } catch (UnsupportedEncodingException e) {
                    logger.error("The platform's default encoding is not supported in the JDK.", e);
                    try {
                        res = URLDecoder.decode(resource.getFile(), "UTF-8");
                    } catch (UnsupportedEncodingException e1) {
                        logger.fatal("Even UTF-8 is not supported by this JDK!", e1);
                    }
                }
            }
            doc = Utilities.parseDocumentFromFile(res);
        } catch (Exception e) {
            logger.error("Error parsing plugin properties XML file: " + pluginPropertiesFilename, e);
            return;
        }
        logger.debug("Reading plugin configuration");
        NodeList rootList = doc.getElementsByTagName(TAG_PLUGIN);
        if (rootList.getLength() == 0) {
            return;
        }
        Element root = (Element) rootList.item(0);
        NodeList externalLexEntries = root.getElementsByTagName(TAG_EXTERNAL_LEX);
        for (int i = 0; i < externalLexEntries.getLength(); i++) {
            logger.debug("Making plugin external lex entry");
            Element externalLexElement = (Element) externalLexEntries.item(i);
            String className = externalLexElement.getAttribute(ATTR_CLASS);
            String tagName = externalLexElement.getAttribute(ATTR_TAG);
            try {
                LexExtender.getInstance().addLexerEntry(tagName, className);
            } catch (IOException e) {
                logger.error("error adding lexer entry", e);
            }
        }
        NodeList actionEntries = root.getElementsByTagName(TAG_ACTION);
        for (int i = 0; i < actionEntries.getLength(); i++) {
            Element actionElement = (Element) actionEntries.item(i);
            String actionName = actionElement.getAttribute(ATTR_NAME);
            String className = actionElement.getAttribute(ATTR_CLASS);
            try {
                ActionManager.getInstance().addMapping(actionName, className);
            } catch (IOException e) {
                logger.error("error adding action", e);
            }
            String pseudotopic = actionElement.getAttribute(ATTR_PSEUDOTOPIC);
            if (pseudotopic != null && !"".equals(pseudotopic)) {
                PseudoTopicHandler.getInstance().addMapping(pseudotopic, "Wiki?action=" + actionName);
            }
        }
        NodeList topicListeners = root.getElementsByTagName(TAG_TOPIC_LISTENER);
        for (int i = 0; i < topicListeners.getLength(); i++) {
            Element topicListenerElement = (Element) topicListeners.item(i);
            String className = topicListenerElement.getAttribute(ATTR_CLASS);
            logger.debug("registering topic listener: " + className);
            try {
                Class clazz = Class.forName(className);
                TopicListener listener = (TopicListener) clazz.newInstance();
                this.topicListeners.add(listener);
            } catch (Exception e) {
                logger.error("error creating topic listener and registering it", e);
            }
        }
    }

    /**
     * Get the version of the currently installed version of the plugin
     *
     * @param name plugin name
     * @return version or null if not installed
     * @throws IOException
     * @throws ParserConfigurationException
     * @throws SAXException
     */
    private String getInstalledPluginVersion(String name) throws IOException, ParserConfigurationException, SAXException {
        InputStream in = getClass().getResourceAsStream("/" + name + ".xml");
        if (in == null) {
            return null;
        }
        Document doc = Utilities.parseDocumentFromInputStream(in);
        return getPluginAttributeFromDocument(doc, "version");
    }

    /**
     * Read an attribute from the top-level plugin element in the plugin's descriptor XML
     *
     * @param pluginName    name of the plugin
     * @param pluginZipFile the zip file containing the plugin
     * @param attributeName attribute name
     * @return attribute value or null if not found
     */
    private String readPluginAttributeFromPlugin(String pluginName, File pluginZipFile, String attributeName) {
        Enumeration entries;
        ZipFile zipFile;
        try {
            zipFile = new ZipFile(pluginZipFile);
            entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();
                if (entry.getName().equals("WEB-INF/classes/" + pluginName + ".xml")) {
                    logger.debug("found descriptor");
                    InputStream xmlIn = null;
                    try {
                        xmlIn = zipFile.getInputStream(entry);
                        Document doc = Utilities.parseDocumentFromInputStream(xmlIn);
                        return getPluginAttributeFromDocument(doc, attributeName);
                    } catch (Exception e) {
                        logger.error("", e);
                    } finally {
                        if (xmlIn != null) {
                            xmlIn.close();
                        }
                    }
                }
            }
            zipFile.close();
        } catch (IOException ioe) {
            logger.error("Unzipping error: " + ioe);
        }
        return null;
    }

    /**
     * Return a plugin attribute from a plugin descriptor document
     *
     * @param doc           document
     * @param attributeName attribute name
     * @return value or null if not found
     */
    private String getPluginAttributeFromDocument(Document doc, String attributeName) {
        NodeList pluginElements = doc.getElementsByTagName(TAG_PLUGIN);
        if (pluginElements.getLength() != 1) {
            logger.error("there must be one and only one plugin element in descriptor");
            return null;
        }
        Element element = (Element) pluginElements.item(0);
        String attribute = element.getAttribute(attributeName);
        logger.debug("found attribute " + attributeName + " = " + attribute);
        return attribute;
    }

    /**
     * Topic listeners found in plugins
     *
     * @return listeners
     */
    public List getTopicListeners() {
        return topicListeners;
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.