com.varaneckas.hawkscope.plugin.PluginManager.java Source code

Java tutorial

Introduction

Here is the source code for com.varaneckas.hawkscope.plugin.PluginManager.java

Source

/*
 * Copyright (c) 2008-2009 Tomas Varaneckas
 * http://www.varaneckas.com
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.varaneckas.hawkscope.plugin;

import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Method;
import java.net.Proxy;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.TabFolder;

import com.varaneckas.hawkscope.Constants;
import com.varaneckas.hawkscope.Version;
import com.varaneckas.hawkscope.cfg.Configuration;
import com.varaneckas.hawkscope.cfg.ConfigurationFactory;
import com.varaneckas.hawkscope.gui.listeners.FolderMenuItemListener;
import com.varaneckas.hawkscope.gui.settings.AbstractSettingsTabItem;
import com.varaneckas.hawkscope.gui.settings.SettingsWindow;
import com.varaneckas.hawkscope.menu.FileMenuItem;
import com.varaneckas.hawkscope.menu.FolderMenu;
import com.varaneckas.hawkscope.menu.MainMenu;
import com.varaneckas.hawkscope.plugin.openwith.OpenWithPlugin;

/**
 * Plugin Manager
 * 
 * Works as a broker between Hawkscope and it's plugins
 * 
 * @author Tomas Varaneckas
 * @version $Id$
 */
public class PluginManager {

    /**
     * Hawkscope configuration
     */
    private final Configuration cfg = ConfigurationFactory.getConfigurationFactory().getConfiguration();

    /**
     * Singleton instance
     */
    private static PluginManager instance = null;

    /**
     * Logger
     */
    private static final Log log = LogFactory.getLog(PluginManager.class);

    /**
     * Available plugin updates
     */
    private final Map<String, String> availableUpdates = new HashMap<String, String>();

    /**
     * Private singleton constructor
     */
    private PluginManager() {
        reloadPlugins();
    }

    public Map<String, String> getAvailableUpdates() {
        return this.availableUpdates;
    }

    /**
     * Reloads the plugins
     */
    public void reloadPlugins() {
        plugins.clear();
        addBuiltInPlugins();
        findExternalPlugins();
        for (final Plugin p : plugins) {
            log.debug("Checking if plugin is enabled: " + p.getName());
            try {
                String pluginClass = (p instanceof DisabledPlugin) ? ((DisabledPlugin) p).getClassName()
                        : p.getClass().getName();
                p.setEnabled(isPluginEnabled(pluginClass));
                if (availableUpdates.containsKey(p.getName())
                        && availableUpdates.get(p.getName()).equals(p.getVersion())) {
                    availableUpdates.remove(p.getName());
                }
            } catch (final Exception e) {
                log.warn("Failed checking if plugin enabled: " + p.getName());
            }
        }
    }

    /**
     * Checks configuration to see if plugin is enabled
     * 
     * @param pluginClass Plugin class (getClass().getName())
     * @return true / false
     */
    private boolean isPluginEnabled(final String pluginClass) {
        final String enabled = cfg.getProperties().get("plugin." + pluginClass + ".enabled");
        if (enabled != null) {
            return enabled.equals("1");
        }
        //here we assume that there is no property, so plugin is new, enable it
        //by default.
        return true;
    }

    /**
     * Finds and loads external plugins
     */
    private void findExternalPlugins() {
        final File pluginDir = cfg.getPluginLocation();
        if (pluginDir == null) {
            return;
        }
        String[] pluginJars = pluginDir.list();
        if (pluginJars == null) {
            log.debug("Found 0 plugins");
            return;
        }
        log.debug("Found " + pluginJars.length + " plugins");
        for (final String jar : pluginJars) {
            if (jar.toLowerCase().endsWith(".jar")) {
                processPlugin(pluginDir, jar);
            }
        }
        pluginJars = null;
    }

    private void processPlugin(final File pluginDir, final String jar) {
        try {
            final File jarFile = new File(pluginDir.getAbsolutePath() + "/" + jar);
            final URLClassLoader classLoader = new URLClassLoader(new URL[] { jarFile.toURI().toURL() });
            final Reader r = new InputStreamReader(classLoader.getResourceAsStream("plugin.loader"));
            String pluginClass = "";
            int c = 0;
            while ((c = r.read()) != -1) {
                pluginClass += (char) c;
            }
            r.close();
            log.debug("Processing Plugin: " + pluginClass);
            if (!isPluginEnabled(pluginClass)) {
                log.debug("Plugin disabled, skipping");
                getAllPlugins().add(new DisabledPlugin(pluginClass));
                return;
            }
            Class<?> p = classLoader.loadClass(pluginClass);
            Method creator = null;
            try {
                creator = p.getMethod("getInstance", new Class[] {});
            } catch (final NoSuchMethodException no) {
                log.debug("Plugin does not implement a singleton getter");
            }
            Plugin plugin;
            if (creator == null) {
                plugin = (Plugin) p.newInstance();
            } else {
                plugin = (Plugin) creator.invoke(p, new Object[] {});
            }
            creator = null;
            p = null;
            if (plugin != null) {
                log.debug("Adding plugin: " + plugin);
                getAllPlugins().add(plugin);
            }
        } catch (final Exception e) {
            log.warn("Failed loading plugin: " + jar, e);
        }
    }

    /**
     * Adds built-in plugins
     */
    private void addBuiltInPlugins() {
        plugins.add(OpenWithPlugin.getInstance());
    }

    /**
     * List of available {@link Plugin}s
     */
    private final List<Plugin> plugins = new LinkedList<Plugin>();

    /**
     * Gets the singleton instance of {@link PluginManager}
     * 
     * @return singleton instance
     */
    public static PluginManager getInstance() {
        if (instance == null) {
            instance = new PluginManager();
        }
        return instance;
    }

    /**
     * Gets all {@link Plugin}s
     * 
     * @return
     */
    public List<Plugin> getAllPlugins() {
        return plugins;
    }

    /**
     * Gets the list of active {@link Plugin}s
     * 
     * @return active plugins
     */
    public List<Plugin> getActivePlugins() {
        final List<Plugin> active = new LinkedList<Plugin>();
        for (final Plugin p : plugins) {
            if (p.isEnabled()) {
                active.add(p);
            }
        }
        return active;
    }

    /**
     * Enhances {@link FolderMenu} with Plugins
     * 
     * @param file
     * @param menu
     * @param submenu
     * @param listener
     */
    public void enhanceFolderMenu(final File file, final MenuItem menu, final Menu submenu,
            final FolderMenuItemListener listener) {
        for (final Plugin plugin : getActivePlugins()) {
            try {
                if (plugin.canEnhanceFolderMenu()) {
                    plugin.enhanceFolderMenu(file, menu, submenu, listener);
                }
            } catch (final Exception e) {
                log.error("Could not enchance folder menu", e);
            }
        }
    }

    /**
     * Enhances {@link FileMenuItem} with Plugins
     * 
     * @param menuItem
     * @param file
     */
    public void enhanceFileMenuItem(final MenuItem menuItem, final File file) {
        for (final Plugin plugin : getActivePlugins()) {
            try {
                if (plugin.canEnhanceFileMenuItem()) {
                    plugin.enhanceFileMenuItem(menuItem, file);
                }
            } catch (final Exception e) {
                log.error("Could not enchance file menu item", e);
            }
        }
    }

    /**
     * Hooks in before Quick Access menu
     * 
     * @param mainMenu
     */
    public void beforeQuickAccess(final MainMenu mainMenu) {
        for (final Plugin plugin : getActivePlugins()) {
            try {
                if (plugin.canHookBeforeQuickAccessList()) {
                    plugin.beforeQuickAccess(mainMenu);
                }
            } catch (final Exception e) {
                log.error("Could not load plugin before quick access", e);
            }
        }
    }

    /**
     * Enhances Quick Access item with plugins
     * 
     * @param fm
     * @param custom
     */
    public void enhanceQuickAccessItem(final FolderMenu fm, final File custom) {
        for (final Plugin plugin : getActivePlugins()) {
            try {
                if (plugin.canEnhanceQuickAccessItem()) {
                    plugin.enhanceQuickAccessItem(fm, custom);
                }
            } catch (final Exception e) {
                log.error("Could not enchance quick access item", e);
            }
        }
    }

    /**
     * Hooks before About menu item
     * 
     * @param mainMenu
     */
    public void beforeAboutMenuItem(final MainMenu mainMenu) {
        for (final Plugin plugin : getActivePlugins()) {
            try {
                if (plugin.canHookBeforeAboutMenuItem()) {
                    plugin.beforeAboutMenuItem(mainMenu);
                }
            } catch (final Exception e) {
                log.error("Could not load plugin before about menu item", e);
            }
        }
    }

    /**
     * Intercepts file click with Plugins
     * 
     * @param file target file
     * @return
     */
    public boolean interceptClick(final File file) {
        boolean proceed = true;
        for (final Plugin plugin : getActivePlugins()) {
            if (plugin.canInterceptClick()) {
                proceed = plugin.interceptClick(file);
                if (!proceed) {
                    break;
                }
            }
        }
        return proceed;
    }

    /**
     * Enhances {@link SettingsWindow}'s {@link TabFolder}
     * 
     * @param settingsTabFolder
     * @param tabList
     */
    public void enhanceSettings(final TabFolder settingsTabFolder, final List<AbstractSettingsTabItem> tabList) {
        for (final Plugin plugin : getActivePlugins()) {
            try {
                plugin.enhanceSettings(settingsTabFolder, tabList);
            } catch (final Exception e) {
                log.warn("Failed enhancing settings tab for plugin: " + plugin.getName(), e);
            }
        }
    }

    /**
     * Tells to check for plugin updates
     * 
     * @param pluginVersionCheckUrl
     * @param proxy can be null
     */
    public void checkPluginUpdates(final String pluginVersionCheckUrl, final Proxy proxy) {
        try {
            final URL pluginCheckUrl = new URL(Version.PLUGIN_VERSION_CHECK_URL);
            final URLConnection conn;
            if (proxy == null) {
                conn = pluginCheckUrl.openConnection();
            } else {
                conn = pluginCheckUrl.openConnection(proxy);
            }
            conn.setConnectTimeout(Constants.CONNECTION_TIMOUT);
            conn.setReadTimeout(Constants.CONNECTION_TIMOUT);
            final InputStream io = conn.getInputStream();
            int c = 0;
            final StringBuilder version = new StringBuilder();
            while ((c = io.read()) != -1) {
                version.append((char) c);
            }
            final String[] plugs = version.toString().split("\n");
            final Map<String, String> currentVersions = new HashMap<String, String>();
            for (final Plugin p : getActivePlugins()) {
                currentVersions.put(p.getName(), p.getVersion());
            }
            for (final String plug : plugs) {
                final String[] plugData = plug.split(":");
                if (currentVersions.containsKey(plugData[0])) {
                    if (currentVersions.get(plugData[0]).compareTo(plugData[1]) < 0) {
                        availableUpdates.put(plugData[0], plugData[1]);
                    }
                }
            }
            if (availableUpdates.size() > 0) {
                log.info("Plugin updates available: " + availableUpdates);
            }
        } catch (final Exception e) {
            log.error("Failed getting plugin updates");
        }
    }

}