Package com.valhalla.pluginmanager

Source Code of com.valhalla.pluginmanager.PluginLoader

/*
Copyright (C) 2003 Adam Olsen

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 1, 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package com.valhalla.pluginmanager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Properties;
import java.util.jar.JarEntry;

import com.valhalla.settings.Settings;

/**
* This class supports the loading of custom plugins from jar files. The main
* class in the Jar should be named after the jar filename and should be in the
* "package" String passed to the PluginLoader package. For example, the plugin
* LookAndFeel would be in a jar file called LookAndFeel.jar, and if the package
* parameter was com.valhalla.jbother.plugins there would need to be a class
* called <code>com.valhalla.jbother.plugins.LookAndFeel</code> in the jar
* file
*
* @author Adam Olsen
* @version 1.0
*/
public class PluginLoader extends ClassLoader {
    private static PluginLoader instance = null;

    private static int pluginAPIVersion = 91214;

    private String pluginDir = null;

    private Hashtable loadedClasses = new Hashtable(); // contains information
                                                       // about what classes
                                                       // have

    // already been loaded
    private static ArrayList availablePlugins = new ArrayList(); // the list of
                                                                 // available
                                                                 // plugins

    private static ArrayList invalidPlugins = new ArrayList();

    private static ArrayList incompatiblePlugins = new ArrayList();

    private static ArrayList installedPlugins = new ArrayList();

    private static Hashtable loadedPlugins = new Hashtable();

    /**
     * The default constructor
     *
     * @param pluginDir
     *            the directory to search for plugins in .jar format
     */
    private PluginLoader() {
    }

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

    public static PluginLoader getNewInstance() {
        instance = new PluginLoader();
        return instance;
    }

    /**
     * Finds which plugin contains the specified native library, extracts it to
     * a temporary location, and returns the path to that location or null if
     * the native library could not be found.
     *
     * @param the
     *            name of the library
     * @return the location of the newly created temporary file
     */
    public String findLibrary(String lib) {
        String l = super.findLibrary(lib);
        if (l != null)
            return l;
        PluginJAR jar = getPluginThatContains("native/" + lib);
        if (jar == null)
            return null;

        try {
            JarEntry entry = jar.getJarEntry("native/" + lib);

            int index = lib.lastIndexOf('.');
            String suffix = lib.substring(index, lib.length());
            lib = lib.substring(0, index);
            File temporaryDll = File.createTempFile(lib, suffix);

            InputStream inputStream = jar.getInputStream(entry);

            FileOutputStream outputStream = new FileOutputStream(temporaryDll);
            byte[] array = new byte[8192];
            for (int i = inputStream.read(array); i != -1; i = inputStream
                    .read(array)) {
                outputStream.write(array, 0, i);
            }

            outputStream.close();
            temporaryDll.deleteOnExit();
            com.valhalla.Logger.debug(temporaryDll.getPath());
            return temporaryDll.getPath();
        } catch (Exception ex) {
            com.valhalla.Logger.logException(ex);
            return null;
        }
    }

    /**
     * @return a list of currently loaded plugins
     */
    public Hashtable getLoadedPlugins() {
        return loadedPlugins;
    }

    /**
     * @return a list of available plugins
     */
    public ArrayList getAvailablePlugins() {
        return availablePlugins;
    }

    /**
     * @return a list of installed plugins
     */
    public ArrayList getInstalledPlugins() {
        return installedPlugins;
    }

    /**
     * @return a list of invalid plugins
     */
    public ArrayList getInvalidPlugins() {
        return invalidPlugins;
    }

    /**
     * @return a list of incompatible plugins
     */
    public ArrayList getIncompatiblePlugins() {
        return incompatiblePlugins;
    }

    /**
     * Returns the jar file for the specified plugin name
     *
     * @param name
     *            the plugin name
     * @return the jar file corresponding to the name
     */
    public PluginJAR getPlugin(String name) {
        PluginJAR jar = null;
        for (int i = 0; i < availablePlugins.size(); i++) {
            PluginJAR temp = (PluginJAR) availablePlugins.get(i);
            if (temp.getName().equals(name))
                jar = temp;
        }

        return jar;
    }

    /**
     * Returns the PluginJAR that contains <tt>file</tt>
     *
     * @param file
     *            the file to search for
     * @return the PluginJAR that contains <tt>file</tt>
     */
    private PluginJAR getPluginThatContains(String file) {
        PluginJAR jar = null;
        for (int i = 0; i < availablePlugins.size(); i++) {
            PluginJAR temp = (PluginJAR) availablePlugins.get(i);
            if (temp.contains(file))
                jar = temp;
        }

        return jar;
    }

    /**
     * Returns the plugin that is represented by location
     *
     * @param location
     *            the location of the plugin
     * @return the PluginJAR representing the specified location
     */
    private PluginJAR getPluginFromLocation(String location) {
        PluginJAR jar = null;

        for (int i = 0; i < availablePlugins.size(); i++) {
            PluginJAR temp = (PluginJAR) availablePlugins.get(i);
            if (temp.getLocation().equals(location))
                jar = temp;
        }

        return jar;
    }

    /**
     * Gets the current plugin API version
     *
     * @return the current plugin API version
     */
    public static int getAPIVersion() {
        return pluginAPIVersion;
    }

    /**
     * Attempts to load the available plugins
     */
    public void loadPlugins() {
        for (int i = 0; i < availablePlugins.size(); i++) {
            PluginJAR jar = (PluginJAR) availablePlugins.get(i);

            if (!jar.getLoaded()) {
                try {
                    jar.loadPlugin();
                } catch (Exception e) {
                    com.valhalla.Logger.logException(e);
                }
            } else {
                com.valhalla.Logger.debug("Plugin is already loaded.");
            }
        }
    }

    /**
     * Reads in all the available plugins and the information about them
     */
    public void findPlugins(String d) {
        this.pluginDir = d;
        String files[] = null;
        File pluginDir = null;

        installedPlugins.clear();
        ArrayList newAvailable = new ArrayList();
        invalidPlugins.clear();
        incompatiblePlugins.clear();

        try {
            // open up the plugin directory
            pluginDir = new File(this.pluginDir);
            if (!pluginDir.isDirectory())
                return;
            files = pluginDir.list();
        } catch (Exception exception) {
            return;
        }

        boolean ignoreIncompatible =
            Settings.getInstance().getBoolean("ignoreIncompatiblePlugins");

        for (int i = 0; i < files.length; i++) {
            // if it's a jar file, it might be a plugin
            if (files[i].endsWith(".jar")) {
                try {
                    PluginJAR jar = null;
                    String loc = pluginDir.getPath() + File.separatorChar
                            + files[i];
                    jar = getPluginFromLocation(loc);

                    if (jar == null || !jar.getLoaded()) {
                        jar = new PluginJAR(pluginDir.getPath()
                                + File.separatorChar + files[i]);
                    }

                    Properties props = jar.getProperties();
                    String name = props.getProperty("name");

                    if (props == null || props.getProperty("mainClass") == null
                            || name == null)
                        invalidPlugins.add(files[i]);
                    else if (ignoreIncompatible && !checkPlatform (props)) {
                        incompatiblePlugins.add(name);
                        com.valhalla.Logger.debug("Incompatible plugin found: " +
                                                  name + " (" + files[i] + ")");
                    } else {
                        int version = Integer.parseInt(props
                                .getProperty("APIVersion"));
                        if (version != pluginAPIVersion) {
                            invalidPlugins.add(name);
                        } else {
                            newAvailable.add(jar);
                        }

                        installedPlugins.add(props);
                    }
                } catch (Exception ex) {
                    com.valhalla.Logger.debug("Invalid plugin found: "
                            + files[i]);
                }
            }
        }

        availablePlugins = newAvailable;
    }

    /**
     * Gets a resource as a stream
     *
     * @param resource
     *            the resource to get
     * @return the stream
     */
    public InputStream getResourceAsStream(String resource) {
        InputStream stream = null;
        stream = this.getClass().getClassLoader().getResourceAsStream(resource);
        if (stream != null)
            return stream;

        PluginJAR jar = getPluginThatContains(resource);

        if (jar != null) {
            // find the entry in the Jar file
            JarEntry entry = jar.getJarEntry(resource);

            stream = jar.getInputStream(entry);
        } else {
            com.valhalla.Logger.debug("Stream was null for " + resource);
        }

        return stream;
    }

    protected Hashtable getLoadedClasses() {
        return loadedClasses;
    }

    /**
     * Loads the Class out of the plugin file
     *
     * @param className
     *            the class name to load
     * @param resolveIt
     *            true to resolve the class (load dependancies)
     * @return the Class if it was found
     * @throws <code>ClassNotFoundException</code> if the class could not be
     *             found in the system or any of the plugin files
     */
    public synchronized Class loadClass(String className, boolean resolveIt)
            throws ClassNotFoundException {
        Class result = null;

        String origName = className;

        // if the Class was already loaded, return it
        result = (Class) loadedClasses.get(origName);
        if (result != null)
            return result;

        // check to see if it's a system class
        try {
            result = this.getClass().getClassLoader().loadClass(className);
            //result = super.findSystemClass( className );

            return result;
        } catch (ClassNotFoundException exception) {
        }

        className = className.replaceAll("\\.", "/");
        className += ".class";

        // if the class name starts with "java", we will not load it
        // because of the security risks involved. A class in the package
        // java. or javax. will be able to access protected members of system
        // Classes, and this isn't good.
 
/*        *REMOVED* as official JBother plugins are checked by devel team
*        anyways...
*        if (className.startsWith("java"))
            throw new ClassNotFoundException();
*/
        PluginJAR jar = getPluginThatContains(className);

        if (jar == null)
            throw new ClassNotFoundException();

        try {
            // find the entry in the Jar file
            JarEntry entry = jar.getJarEntry(className);
            InputStream stream = jar.getInputStream(entry);

            // read it into a class
            int len = (int) entry.getSize();
            int offset = 0;
            int success = 0;
            byte[] contents = new byte[len];

            while (success < len) {
                len -= success;
                offset += success;
                success = stream.read(contents, offset, len);
                if (success == -1)
                    throw new ClassNotFoundException();
            }

            stream.read(contents, 0, (int) entry.getSize());

            // actually define the class
            result = defineClass(origName, contents, 0, contents.length);

            if (resolveIt)
                resolveClass(result);

            // mark this class as already loaded and put it in the cache
            loadedClasses.put(origName, result);

            return result;
        } catch (Exception e) {
            throw new ClassNotFoundException(className);
        }
    }

    /**
     *  Checks whether the current platform (OS name and architecture) is
     *  compatible with the given plugin's platform or not.
     *
     *  @param pluginProps  plugin properties
     *  @return true if the plugin is compatible and false otherwise
     */
    static boolean checkPlatform (Properties pluginProps)
    {
        // @todo it may be worth to introduce 'os.match' and 'arch.match'
        // plugin properties to perform more flexible regexp match

        String os = pluginProps.getProperty("os");
        String arch = pluginProps.getProperty("arch");

        boolean ok = os == null || os.equals("all") ||
                     System.getProperty("os.name").startsWith(os);

        if (ok) {
            ok = arch == null || arch.equals("all") ||
            System.getProperty("os.arch").equals(arch);
        }

        return ok;
    }
}
TOP

Related Classes of com.valhalla.pluginmanager.PluginLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.