org.hyperic.util.PluginLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.util.PluginLoader.java

Source

/*
 * NOTE: This copyright does *not* cover user programs that use HQ
 * program services by normal system calls through the application
 * program interfaces provided as part of the Hyperic Plug-in Development
 * Kit or the Hyperic Client Development Kit - this is merely considered
 * normal use of the program, and does *not* fall under the heading of
 * "derived work".
 * 
 * Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
 * This file is part of HQ.
 * 
 * HQ is free software; you can redistribute it and/or modify
 * it under the terms version 2 of the GNU General Public License as
 * published by the Free Software Foundation. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 */

package org.hyperic.util;

import java.io.File;
import java.io.FilenameFilter;

import java.net.URLClassLoader;
import java.net.URL;
import java.net.JarURLConnection;
import java.net.MalformedURLException;

import java.util.jar.Attributes;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class PluginLoader extends URLClassLoader {

    private static Log _log = LogFactory.getLog(PluginLoader.class.getName());
    //XXX WAS 6.1 seems to have an issue with %20
    private static final boolean ESCAPE_SPACES = !"false".equals(System.getProperty("PluginLoader.ESCAPE_SPACES"));
    private Map addedURLs = new HashMap();
    private String pluginClassName = null;
    private String pluginName;
    private ThreadLocal<ClassLoader> previousClassLoader = new ThreadLocal<ClassLoader>();

    private static String toFileURL(String file) {
        if (ESCAPE_SPACES) {
            file = StringUtil.replace(file, " ", "%20");
        }
        return "file:" + file;
    }

    private static URL toJarURL(String file) throws MalformedURLException {

        return new URL("jar", "", toFileURL(file) + "!/");
    }

    private static URL toURL(String file) throws MalformedURLException {

        return new URL(toFileURL(file));
    }

    /*
     * if classname is a jar, try to configure the plugin using jar
     * attributes
     */
    public static String getPluginMainClass(URL url) throws Exception {

        JarURLConnection jarConn = (JarURLConnection) url.openConnection();
        Attributes attrs = jarConn.getMainAttributes();

        String pluginName = attrs.getValue(Attributes.Name.MAIN_CLASS);

        return pluginName;
    }

    /*
     * allow each plugin to have their own classpath.
     */
    public static PluginLoader create(String pluginName, ClassLoader parent) throws PluginLoaderException {

        String pluginClassName = null;
        URL[] classpath;

        if (pluginName.endsWith(".jar")) {
            ArrayList urls = new ArrayList();
            try {
                URL jarUrl = toJarURL(pluginName);

                urls.add(jarUrl); //note-to-self

                // Get the plugin name from the jar
                pluginClassName = getPluginMainClass(jarUrl);
            } catch (Exception e) {
                String msg = "failed to configure plugin jar=" + pluginName;
                throw new PluginLoaderException(msg, e);
            }

            classpath = (URL[]) urls.toArray(new URL[0]);
        } else {
            classpath = new URL[0];
        }

        PluginLoader loader = new PluginLoader(classpath, parent, pluginClassName);
        loader.pluginName = new File(pluginName).getName();
        return loader;
    }

    public static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }

    public static void resetClassLoader(Object obj) {
        ClassLoader cl = obj.getClass().getClassLoader();
        //XXX: should probably be dealt with by the caller
        if (cl instanceof PluginLoader) {
            PluginLoader pl = (PluginLoader) cl;

            setClassLoader(pl.previousClassLoader.get());

            pl.previousClassLoader.set(null);
        }
    }

    public static boolean setClassLoader(Object obj) {
        ClassLoader current = getClassLoader();
        ClassLoader cl = obj.getClass().getClassLoader();

        if (cl == current || !(cl instanceof PluginLoader)) {
            return false; //already set or not a PluginLoader
        }

        PluginLoader pl = (PluginLoader) cl;

        pl.previousClassLoader.set(current);

        setClassLoader(pl);

        return true;
    }

    public static void setClassLoader(ClassLoader loader) {
        Thread.currentThread().setContextClassLoader(loader);
    }

    public Class loadPlugin() throws ClassNotFoundException, PluginLoaderException {
        if (pluginClassName == null) {
            String msg = "No Main-Class attribute found in MANIFEST";
            throw new PluginLoaderException(msg);
        }

        return loadClass(pluginClassName);
    }

    public Class loadPlugin(String name, byte[] bytecode, int len) {
        return defineClass(name, bytecode, 0, len);
    }

    public void addURL(String url) throws PluginLoaderException {

        addURL(new File(url));
    }

    private static class ClassPathFilter implements FilenameFilter {
        private String start;
        private String end;

        private ClassPathFilter(String start, String end) {
            this.start = start;
            this.end = end;
        }

        public boolean accept(File file, String name) {
            return name.startsWith(this.start) && name.endsWith(this.end);
        }
    }

    private void logAddPath(File path, boolean isExpand) {
        if (_log.isDebugEnabled()) {
            if (isExpand) {
                String url = path.toString();
                if (addedURLs.get(url) == Boolean.TRUE) {
                    return; //only print once
                }
                _log.debug(this.pluginName + " expanding " + path + "...");
                addedURLs.put(url, Boolean.TRUE);
            } else {
                if (path.canRead()) {
                    _log.debug(this.pluginName + " += " + path);
                } else {
                    _log.debug(this.pluginName + " -= " + path + ": Permission denied");
                }
            }
        }
    }

    public static String[] expand(File file) {
        String name = file.getName();
        int ix = name.indexOf("*"); //good enough for version matching
        if (ix == -1) {
            return null;
        }
        File dir = file.getParentFile();
        if (!dir.isDirectory()) {
            return null;
        }

        //support: "repository/geronimo/jars/geronimo-management-*.jar"
        String start = name.substring(0, ix);
        String end = name.substring(ix + 1);

        return dir.list(new ClassPathFilter(start, end));
    }

    public void addURL(File file) throws PluginLoaderException {

        boolean isExpand = false;
        String[] jars = null;

        if (!file.exists()) {
            jars = expand(file);
            if (jars == null) {
                return;
            }
            isExpand = true;
            logAddPath(file, true);
            file = file.getParentFile();
        }

        if (isExpand || file.isDirectory()) {
            if (!isExpand) {
                boolean addedDir = addedURLs.get(file) == Boolean.TRUE;
                logAddPath(file, true);
                if (!addedDir) {
                    addedURLs.put(file, Boolean.TRUE);
                    //add the directory itself
                    //must end with '/' (see URLClassLoader)
                    try {
                        super.addURL(toURL(file.toString() + "/"));
                    } catch (Exception e) {
                        throw new PluginLoaderException(e.getMessage());
                    }
                }
                jars = file.list();
            }

            if (jars == null) {
                return;
            }

            for (int j = 0; j < jars.length; j++) {
                addURL(new File(file, jars[j]));
            }

            return;
        }

        if (!file.exists()) {
            return;
        }
        String url = file.toString();
        if (addedURLs.get(url) == Boolean.TRUE) {
            return;
        }
        addedURLs.put(url, Boolean.TRUE);

        try {
            logAddPath(file, false);
            addURL(toURL(url));
        } catch (Exception e) {
            throw new PluginLoaderException(e.getMessage());
        }
    }

    public void addURLs(String[] urls) throws PluginLoaderException {

        for (int i = 0; i < urls.length; i++) {
            addURL(urls[i]);
        }
    }

    public void addURLs(List urls) throws PluginLoaderException {

        for (int i = 0; i < urls.size(); i++) {
            addURL((String) urls.get(i));
        }
    }

    private PluginLoader(URL[] urls, ClassLoader parent, String name) {
        super(urls, parent);
        pluginClassName = name;
        previousClassLoader.set(getClassLoader());
    }

    public String toString() {
        URL[] urls = getURLs();
        String s = super.toString() + "/" + this.pluginName + "=[";

        for (int i = 0; i < urls.length; i++) {
            s += urls[i].getFile();
            if (i < urls.length - 1) {
                s += ", ";
            }
        }

        s += "]";
        return s;
    }

    //hook for plugins to avoid having to set
    //LD_LIBRARY_PATH, SHLIB_PATH, PATH, etc.
    //in order to find third-party native libs.
    protected String findLibrary(String libname) {
        String lib = System.getProperty("net.covalent.lib." + libname);

        if (lib != null) {
            return lib;
        }

        return super.findLibrary(libname);
    }
}