ToolAgentLoader.java :  » Workflow-Engines » shark » org » enhydra » shark » toolagent » Java Open Source

Java Open Source » Workflow Engines » shark 
shark » org » enhydra » shark » toolagent » ToolAgentLoader.java
package org.enhydra.shark.toolagent;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.StringTokenizer;

import org.enhydra.shark.api.internal.working.CallbackUtilities;

/**
 * Use the static <code>load</code> method to load classes that implement tool
 * agents from jars encountered in CLASSPATH like properties or located in a
 * plugin directory.
 * 
 * There a three properties that may be set in Shark's configuration file
 * (conf/Shark.conf).
 * <ol>
 * <li><code>ToolAgentClassPath</code>: This property specifies a colon separated list of
 * bytecode containers, i.e. jar files or directories containing class files.
 * Tool agent implementations found in one of these bytecode containers are
 * loaded by a single static class loader, i.e. they can not be reloaded during
 * the lifetime of the engine.</li>
 * <li><code>ToolAgentPluginPath</code>: This property specifies a colon separated list of
 * bytecode containers, i.e. jar files or directories containing class files.
 * Tool agent implementations found in one of these bytecode containers are
 * loaded by individual class loaders, i.e. they are reloaded on each tool agent
 * invocation.</li>
 * <li><code>ToolAgentPluginDir</code>: This property specifies a directory that may contain
 * jar files which are loaded as if they were encountered in the
 * <code>ToolAgentPluginPath</code>.</li>
 * </ol>
 * Tool agent implementations are first looked up by the default class loader,
 * then if not found by the static class loader according to the
 * <code>ToolAgentClassPath</code> and finally by an individual class loader according to the
 * <code>ToolAgentPluginPath</code>.
 * <p>
 * That means that if you want your tool agent implementation to be reloadable
 * it must neither be encountered in the <code>ToolAgentClassPath</code> nor in the default
 * <code>CLASSPATH</code>.
 * <p>
 * If any of the properties mentioned above do not exist or are empty they will
 * not be used for the search.
 * <p>
 * Note that classes that depend on your tool agent implementation are normally
 * loaded by the same class loader that looked up the tool agent's byte code.
 * That means that you can make even these classes being reloaded on each tool
 * agent invocation if the are encountered in the <code>ToolAgentPluginPath</code>.
 * <p>
 * This feature is nice in the phase of development as you need not restart the
 * whole workflow engine after you modified your tool agent classes or those
 * classes it depends on.
 * <p>
 * If you think your classes do not change anymore move them from the
 * <code>ToolAgentPluginPath</code> to the <code>ToolAgentClassPath</code> to avoid unnecessary class
 * loading operations.
 * 
 * @author Dirk Hoffmann, H+BEDV (www.antivir.de)
 */
public class ToolAgentLoader {

    private static ClassLoader staticClassLoader;

    /**
     * Load a tool agent class.
     * 
     * @param cus
     *            callback utilities used to query shark's properties
     * @param name
     *            name of the class
     * @return the tool agent class
     * @throws ClassNotFoundException
     *             if tool agent class could not be found.
     * @throws IOException
     *             if plugin dir could not be listed
     */
    public static Class load(CallbackUtilities cus, String name)
            throws ClassNotFoundException, IOException {

        /*
         * Create the static URLClassLoader. Provide the class loader that
         * loaded this class as the parent class loader (the one that uses
         * CLASSPATH). This is necessary to allow tool agents to use common
         * classes.
         */
        ClassLoader staticCl = provideStaticClassLoader(cus,
                ToolAgentLoader.class.getClassLoader());

        /*
         * Create the URLClassLoader. Provide the static class loader that
         * loaded this class as the parent class loader.
         */
        ClassLoader cl = provideDynamicClassLoader(cus, staticCl);

        // Finally load class
        return cl.loadClass(name);
    }

    /**
     * Returns the one and only static class loader that loads classes according
     * to the ToolAgentClassPath property such that they cannot be reloaded
     * during the workflow engine's life time.
     * 
     * @param cus
     *            callback utilities used to query shark's properties
     * @param parentClassLoader
     *            the parent class loader for delegation
     * @return the static class loader
     * @throws IOException
     */
    private static ClassLoader provideStaticClassLoader(CallbackUtilities cus,
            ClassLoader parentClassLoader) throws IOException {
        if (staticClassLoader == null) {
            String toolAgentClassPathPropVal = cus.getProperty(
                    "ToolAgentClassPath", "");
            URL[] toolAgentClassSearchList = makeSearchList(
                    toolAgentClassPathPropVal, null);

            staticClassLoader = new URLClassLoader(toolAgentClassSearchList,
                    parentClassLoader);
        }
        return staticClassLoader;
    }

    /**
     * Return a new class loader ready to load a tool agent class according to
     * the ToolAgentPluginPath property or from the directory specified by the
     * ToolAgentPluginDir property such that it can be reloaded on the next
     * invocation.
     * 
     * @param cus
     *            callback utilities used to query shark's properties
     * @param parentClassLoader
     *            the parent class loader for delegation
     * @return the dynamic class loader
     * @throws IOException
     */
    private static ClassLoader provideDynamicClassLoader(CallbackUtilities cus,
            ClassLoader parentClassLoader) throws IOException {
        String toolAgentPluginPathPropVal = cus.getProperty(
                "ToolAgentPluginPath", "");
        String toolAgentPluginDirPropVal = cus.getProperty(
                "ToolAgentPluginDir", null);

        URL[] toolAgentPluginSearchList = makeSearchList(
                toolAgentPluginPathPropVal, toolAgentPluginDirPropVal);

        ClassLoader cl = new URLClassLoader(toolAgentPluginSearchList,
                parentClassLoader);
        return cl;
    }

    /**
     * @param classPath
     * @param libDir
     * @return
     * @throws IOException
     */
    private static URL[] makeSearchList(String classPath, String libDir)
            throws IOException {
        URL[] searchList;
        // Convert ToolAgentPluginPath property into tokenizer.
        StringTokenizer tk = new StringTokenizer(classPath, ":");

        File[] libDirFiles;

        if (libDir == null || libDir.length() == 0) {
            libDirFiles = new File[0];
        } else {
            // Search the lib dir for jars and property files.
            FilenameFilter jarAndPropFilesFilter = new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return name.endsWith(".jar")
                            || name.endsWith(".properties");
                }
            };

            libDirFiles = (new File(libDir)).listFiles(jarAndPropFilesFilter);

            if (libDirFiles == null) {
                throw new IOException("Could not list files in " + libDir);
            }
        }

        // Create the search list which will be used by the URLClassLoader
        searchList = new URL[libDirFiles.length + tk.countTokens()];

        int i = 0;

        // Add jars encountered in the class path to the search list.
        for (; tk.hasMoreTokens(); ++i) {
            searchList[i] = new URL("file:" + tk.nextToken());
        }

        // Add jars found in the lib dir to the search list.
        for (; i < libDirFiles.length; ++i) {
            // urls[i] = files[i].toURL();
            // ,-----------------^^^^^^^
            // `-- depends on the user.dir property which may be corrupted.

            // Therefore we construct the URL using string methods.
            searchList[i] = new URL("file:" + libDirFiles[i].getPath());
        }
        return searchList;
    }
}
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.