Package org.huihoo.willow.loader

Source Code of org.huihoo.willow.loader.WorkflowLoader

//----------------------------BEGIN LICENSE----------------------------
/*
* Willow : the Open Source WorkFlow Project
* Distributable under GNU LGPL license by gun.org
*
* Copyright (C) 2004-2010 huihoo.org
* Copyright (C) 2004-2010  ZosaTapo <dertyang@hotmail.com>
*
* ====================================================================
* Project Homepage : http://www.huihoo.org/willow
* Source Forge     : http://sourceforge.net/projects/huihoo
* Mailing list     : willow@lists.sourceforge.net
*/
//----------------------------END  LICENSE-----------------------------
package org.huihoo.willow.loader;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.Container;
import org.huihoo.willow.Context;
import org.huihoo.willow.Globals;
import org.huihoo.willow.Lifecycle;
import org.huihoo.willow.LifecycleException;
import org.huihoo.willow.LifecycleListener;
import org.huihoo.willow.Loader;
import org.huihoo.willow.Logger;
import org.huihoo.willow.core.StandardContext;
import org.huihoo.willow.util.JarFilenameFilter;
import org.huihoo.willow.util.LifecycleSupport;
import org.huihoo.willow.util.StringManager;
import org.huihoo.workflow.rules.ScriptContext;

/**
* @author Administrator
*
* To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
public class WorkflowLoader implements Lifecycle, Loader, PropertyChangeListener
{
  private static Log log = LogFactory.getLog(WorkflowLoader.class);

  // ----------------------------------------------------------- Constructors

  /**
   * Construct a new workflowLoader with no defined parent class loader
   * (so that the actual parent will be the system class loader).
   */
  public WorkflowLoader()
  {

    this(null);

  }

  /**
   * Construct a new workflowLoader with the specified class loader
   * to be defined as the parent of the ClassLoader we ultimately create.
   *
   * @param parent The parent class loader
   */
  public WorkflowLoader(ClassLoader parent)
  {
    super();
    this.parentClassLoader = parent;
  }

  // ----------------------------------------------------- Instance Variables

  /**
   * First load of the class.
   */
  private static boolean first = true;

  /**
   * The class loader being managed by this Loader component.
   */
  private WorkflowClassLoader classLoader = null;

  /**
   * The Container with which this Loader has been associated.
   */
  private Container container = null;

  /**
   * The debugging detail level for this component.
   */
  private int debug = 0;

  /**
   * The "follow standard delegation model" flag that will be used to
   * configure our ClassLoader.
   */
  private boolean delegate = false;

  /**
   * The descriptive information about this Loader implementation.
   */
  private static final String info = "org.huihoo.willow.loader.WorkflowLoader/1.0";

  /**
   * The lifecycle event support for this component.
   */
  protected LifecycleSupport lifecycle = new LifecycleSupport(this);

  /**
   * The Java class name of the ClassLoader implementation to be used.
   * This class should extend workflowClassLoader, otherwise, a different
   * loader implementation must be used.
   */
  private String loaderClass = "org.huihoo.willow.loader.WorkflowClassLoader";

  /**
   * The parent class loader of the class loader we will create.
   */
  private ClassLoader parentClassLoader = null;

  /**
   * The reloadable flag for this Loader.
   */
  private boolean reloadable = false;

  /**
   * The set of repositories associated with this class loader.
   */
  private String repositories[] = new String[0];

  /**
     * The string manager for this package.
     */
  protected static final StringManager sm = StringManager.getManager(Constants.PACKAGE);

  /**
   * Has this component been started?
   */
  private boolean started = false;

  /**
   * The property change support for this component.
   */
  protected PropertyChangeSupport support = new PropertyChangeSupport(this);

  /**
   * Classpath set in the loader.
   */
  private String classpath = null;

  // ------------------------------------------------------------- Properties

  /**
   * Return the Java class loader to be used by this Container.
   */
  public ClassLoader getClassLoader()
  {

    return ((ClassLoader) classLoader);

  }

  /**
   * Return the Container with which this Logger has been associated.
   */
  public Container getContainer()
  {

    return (container);
  }

  /**
   * Set the Container with which this Logger has been associated.
   *
   * @param container The associated Container
   */
  public void setContainer(Container container)
  {
    this.container = container;
  }
  /**
      * Return the debugging detail level for this component.
      */
  public int getDebug()
  {

    return (this.debug);

  }

  /**
   * Set the debugging detail level for this component.
   *
   * @param debug The new debugging detail level
   */
  public void setDebug(int debug)
  {

    int oldDebug = this.debug;
    this.debug = debug;
    support.firePropertyChange("debug", new Integer(oldDebug), new Integer(this.debug));

  }

  /**
   * Return the "follow standard delegation model" flag used to configure
   * our ClassLoader.
   */
  public boolean getDelegate()
  {

    return (this.delegate);

  }

  /**
   * Set the "follow standard delegation model" flag used to configure
   * our ClassLoader.
   *
   * @param delegate The new flag
   */
  public void setDelegate(boolean delegate)
  {

    boolean oldDelegate = this.delegate;
    this.delegate = delegate;
    support.firePropertyChange("delegate", new Boolean(oldDelegate), new Boolean(this.delegate));

  }

  /**
   * Return descriptive information about this Loader implementation and
   * the corresponding version number, in the format
   * <code>&lt;description&gt;/&lt;version&gt;</code>.
   */
  public String getInfo()
  {

    return (info);

  }

  /**
   * Return the ClassLoader class name.
   */
  public String getLoaderClass()
  {

    return (this.loaderClass);

  }

  /**
   * Set the ClassLoader class name.
   *
   * @param loaderClass The new ClassLoader class name
   */
  public void setLoaderClass(String loaderClass)
  {

    this.loaderClass = loaderClass;

  }

  /**
   * Return the reloadable flag for this Loader.
   */
  public boolean getReloadable()
  {

    return (this.reloadable);

  }

  /**
   * Set the reloadable flag for this Loader.
   *
   * @param reloadable The new reloadable flag
   */
  public void setReloadable(boolean reloadable)
  {

    // Process this property change
    boolean oldReloadable = this.reloadable;
    this.reloadable = reloadable;
    support.firePropertyChange(
      "reloadable",
      new Boolean(oldReloadable),
      new Boolean(this.reloadable));

  }

  // --------------------------------------------------------- Public Methods

  /**
   * Add a property change listener to this component.
   *
   * @param listener The listener to add
   */
  public void addPropertyChangeListener(PropertyChangeListener listener)
  {

    support.addPropertyChangeListener(listener);

  }

  /**
   * Add a new repository to the set of repositories for this class loader.
   *
   * @param repository Repository to be added
   */
  public void addRepository(String repository)
  {

    log.debug(sm.getString("workflowLoader.addRepository", repository));

    for (int i = 0; i < repositories.length; i++)
    {
      if (repository.equals(repositories[i]))
        return;
    }
    String results[] = new String[repositories.length + 1];
    for (int i = 0; i < repositories.length; i++)
      results[i] = repositories[i];
    results[repositories.length] = repository;
    repositories = results;

    if (started && (classLoader != null))
    {
      classLoader.addRepository(repository);
      setClassPath();
    }

  }

  /**
   * Return the set of repositories defined for this class loader.
   * If none are defined, a zero-length array is returned.
   * For security reason, returns a clone of the Array (since
   * String are immutable).
   */
  public String[] findRepositories()
  {

    return ((String[]) repositories.clone());

  }

  public String[] getRepositories()
  {
    return ((String[]) repositories.clone());
  }

  /** Extra repositories for this loader
   */
  public String getRepositoriesString()
  {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < repositories.length; i++)
    {
      sb.append(repositories[i]).append(":");
    }
    return sb.toString();
  }

  /**
   * Classpath, as set in wangll.willow.wfs_classpath context property
   *
   * @return The classpath
   */
  public String getClasspath()
  {
    return classpath;
  }

  /**
   * Has the internal repository associated with this Loader been modified,
   * such that the loaded classes should be reloaded?
   */
  public boolean modified()
  {

    return (classLoader.modified());

  }

  /**
   * Used to periodically signal to the classloader to release JAR resources.
   */
  public void closeJARs(boolean force)
  {
    if (classLoader != null)
    {
      classLoader.closeJARs(force);
    }
  }

  /**
   * Remove a property change listener from this component.
   *
   * @param listener The listener to remove
   */
  public void removePropertyChangeListener(PropertyChangeListener listener)
  {

    support.removePropertyChangeListener(listener);

  }

  /**
   * Return a String representation of this component.
   */
  public String toString()
  {

    StringBuffer sb = new StringBuffer("workflowLoader[");
    if (container != null)
      sb.append(container.getName());
    sb.append("]");
    return (sb.toString());

  }

  // ------------------------------------------------------ Lifecycle Methods

  /**
   * Add a lifecycle event listener to this component.
   *
   * @param listener The listener to add
   */
  public void addLifecycleListener(LifecycleListener listener)
  {

    lifecycle.addLifecycleListener(listener);

  }

  /**
   * Get the lifecycle listeners associated with this lifecycle. If this
   * Lifecycle has no listeners registered, a zero-length array is returned.
   */
  public LifecycleListener[] findLifecycleListeners()
  {

    return lifecycle.findLifecycleListeners();

  }

  /**
   * Remove a lifecycle event listener from this component.
   *
   * @param listener The listener to remove
   */
  public void removeLifecycleListener(LifecycleListener listener)
  {

    lifecycle.removeLifecycleListener(listener);

  }

  private boolean initialized = false;

  /**
   * Start this component, initializing our associated class loader.
   *
   * @exception LifecycleException if a lifecycle error occurs
   */
  public void start() throws LifecycleException
  {
    if (started)
    {
      throw new LifecycleException(sm.getString("workflowLoader.alreadyStarted"));
    }

    log.debug(sm.getString("workflowLoader.starting"));

    lifecycle.fireLifecycleEvent(START_EVENT, null);
    started = true;

    // Construct a class loader based on our current repositories list
    try
    {

      classLoader = createClassLoader();
      classLoader.setDebug(this.debug);
      classLoader.setDelegate(this.delegate);

      for (int i = 0; i < repositories.length; i++)
      {
        classLoader.addRepository(repositories[i]);
      }

      // Configure our repositories
      setRepositories();
      setClassPath();

      setPermissions();

      if (classLoader instanceof Lifecycle)
      {
        ((Lifecycle) classLoader).start();
      }

    }
    catch (Throwable t)
    {
      log.error("LifecycleException ", t);
      throw new LifecycleException("start: ", t);
    }

  }

  /**
   * Stop this component, finalizing our associated class loader.
   *
   * @exception LifecycleException if a lifecycle error occurs
   */
  public void stop() throws LifecycleException
  {

    // Validate and update our current component state
    if (!started)
    {
      throw new LifecycleException(sm.getString("workflowLoader.notStarted"));
    }

    log.debug(sm.getString("workflowLoader.stopping"));
    lifecycle.fireLifecycleEvent(STOP_EVENT, null);
    started = false;

    classLoader = null;

  }

  // ----------------------------------------- PropertyChangeListener Methods

  /**
   * Process property change events from our associated Container.
   *
   * @param event The property change event that has occurred
   */
  public void propertyChange(PropertyChangeEvent event)
  {

    // Validate the source of this event
    if (!(event.getSource() instanceof Container))
      return;

    // Process a relevant property change
    if (event.getPropertyName().equals("reloadable"))
    {
      try
      {
        setReloadable(((Boolean) event.getNewValue()).booleanValue());
      }
      catch (NumberFormatException e)
      {
        log.error(sm.getString("workflowLoader.reloadable", event.getNewValue().toString()));
      }
    }

  }

  // ------------------------------------------------------- Private Methods

  /**
   * Create associated classLoader.
   */
  private WorkflowClassLoader createClassLoader() throws Exception
  {

    Class clazz = Class.forName(loaderClass);
    WorkflowClassLoader classLoader = null;

    if (parentClassLoader == null)
    {
      parentClassLoader = Thread.currentThread().getContextClassLoader();
    }
    Class[] argTypes = { ClassLoader.class };
    Object[] args = { parentClassLoader };
    Constructor constr = clazz.getConstructor(argTypes);
    classLoader = (WorkflowClassLoader) constr.newInstance(args);
   
    return classLoader;

  }

  /**
   * Log a message on the Logger associated with our Container (if any)
   *
   * @param message Message to be logged
   */
  private void log(String message)
  {

    Logger logger = null;
    if (container != null)
    {
      logger = container.getLogger();
    }
    if (logger != null)
    {
      logger.log("workflowLoader[" + container.getName() + "]: " + message);
    }
    else
    {
      String containerName = null;
      if (container != null)
      {
        containerName = container.getName();
      }
      System.out.println("workflowLoader[" + containerName + "]: " + message);
    }

  }

  /**
   * Log a message on the Logger associated with our Container (if any)
   *
   * @param message Message to be logged
   * @param throwable Associated exception
   */
  private void log(String message, Throwable throwable)
  {

    Logger logger = null;
    if (container != null)
      logger = container.getLogger();
    if (logger != null)
    {
      logger.log("workflowLoader[" + container.getName() + "] " + message, throwable);
    }
    else
    {
      String containerName = null;
      if (container != null)
      {
        containerName = container.getName();
      }
      System.out.println("workflowLoader[" + containerName + "]: " + message);
      System.out.println("" + throwable);
      throwable.printStackTrace(System.out);
    }

  }

  /**
   * Configure associated class loader permissions.
   */
  private void setPermissions()
  {
    if (System.getSecurityManager() == null)
    {
      return;

    }
    if (!(container instanceof Context))
    {
      return;

    }

    // Tell the class loader the root of the context
    ScriptContext scriptContext = ((Context) container).getScriptContext();

    // Assigning permissions for the work directory
    File workDir = (File) scriptContext.getAttribute(Globals.ATTR_WORK_DIR);
    if (workDir != null)
    {
      try
      {
        String workDirPath = workDir.getCanonicalPath();
        classLoader.addPermission(new FilePermission(workDirPath, "read,write"));
        classLoader.addPermission(
          new FilePermission(workDirPath + File.separator + "-", "read,write,delete"));
      }
      catch (IOException e)
      {
        // Ignore
      }
    }

    try
    {

      URL rootURL = scriptContext.getResource("/");
      classLoader.addPermission(rootURL);

      String contextRoot = scriptContext.getRealPath("/");
      try
      {
        contextRoot = (new File(contextRoot)).getCanonicalPath();
        classLoader.addPermission(contextRoot);
      }
      catch (IOException e)
      {
        // Ignore
      }

      URL classesURL = scriptContext.getResource("/WEB-INF/classes/");
      classLoader.addPermission(classesURL);
      URL libURL = scriptContext.getResource("/WEB-INF/lib/");
      classLoader.addPermission(libURL);

      if (libURL != null)
      {
        File rootDir = new File(contextRoot);
        File libDir = new File(rootDir, "WEB-INF/lib/");
        try
        {
          String path = libDir.getCanonicalPath();
          classLoader.addPermission(path);
        }
        catch (IOException e)
        {
        }
      }
    }
    catch (MalformedURLException e)
    {
    }
  }
  /**
   * Configure the repositories for our class loader, based on the
   * associated Container.
   */
  private void setRepositories()
  {

    if (!(container instanceof Context))
    {
      return;
    }

    ScriptContext scriptContext = ((Context) container).getScriptContext();
    if (scriptContext == null)
    {
      return;
    }

    // Loading the work directory
    File workDir = (File) scriptContext.getAttribute(Globals.ATTR_WORK_DIR);
    if (workDir == null)
    {
      log.info("No work dir for " + scriptContext);
    }
    else
    {
      log.debug(sm.getString("workflowLoader.deploy", workDir.getAbsolutePath()));
      classLoader.setWorkDir(workDir);
    }

    // Setting up the class repository (/WEB-INF/classes), if it exists
    String classesPath = "/WEB-INF/classes";

    String absoluteClassesPath = scriptContext.getRealPath(classesPath);
    if (absoluteClassesPath != null)
    {
      File classRepository = new File(absoluteClassesPath);
      // Adding the repository to the class loader
      log.debug(
        sm.getString("workflowLoader.classDeploy", classesPath, classRepository.getAbsolutePath()));

      classLoader.addRepository(classesPath + "/", classRepository);

    }

    // Setting up the JAR repository (/WEB-INF/lib), if it exists
    String libPath = "/WEB-INF/lib";
    String absoluteLibPath = scriptContext.getRealPath(libPath);

    if (absoluteLibPath != null)
    {
      classLoader.addJarPath(libPath, new File(absoluteLibPath));

      File destDir = new File(absoluteLibPath);
      File[] files = destDir.listFiles(new JarFilenameFilter());

      for (int i = 0; i < files.length; ++i)
      {
        String filename = libPath + "/" + files[i].getName();

        File destFile = files[i];

        try
        {
          classLoader.addJar(filename, destFile);
        }
        catch (Exception ex)
        {
          // Catch the exception if there is an empty jar file
          // Should ignore and continute loading other jar files
          // in the dir
        }
      }
    }
  }

  /**
   * Set the appropriate context attribute for our class path.  This
   * is required only because Jasper depends on it.
   */
  private void setClassPath()
  {
    // Validate our current state information
    if (!(container instanceof Context))
    {
      return;
    }

    ScriptContext ScriptContext = ((Context) container).getScriptContext();

    if (ScriptContext == null)
    {
      return;
    }

    if (container instanceof StandardContext)
    {
      String baseClasspath = ((StandardContext) container).getCompilerClasspath();
      if (baseClasspath != null)
      {
        ScriptContext.setAttribute(Globals.ATTR_CLASS_PATH, baseClasspath);
        return;
      }
    }

    StringBuffer classpath = new StringBuffer();

    // Assemble the class path information from our class loader chain
    ClassLoader loader = getClassLoader();
    int n = 0;
    while (loader != null)
    {
      if (!(loader instanceof URLClassLoader))
      {
        String cp = getClasspath(loader);
        if (cp == null)
        {
          log.info("Unknown loader " + loader + " " + loader.getClass());
          break;
        }
        else
        {
          if (n > 0)
          {
            classpath.append(File.pathSeparator);
          }
          classpath.append(cp);
          n++;
        }

        break;
        //continue;
      }

      URL repositories[] = ((URLClassLoader) loader).getURLs();
      for (int i = 0; i < repositories.length; i++)
      {
        String repository = repositories[i].toString();
        if (repository.startsWith("file://"))
        {
          repository = repository.substring(7);
        }
        else if (repository.startsWith("file:"))
        {
          repository = repository.substring(5);
        }
        else
        {
          continue;
        }

        if (repository == null)
        {
          continue;

        }
        if (n > 0)
        {
          classpath.append(File.pathSeparator);
        }

        classpath.append(repository);
        n++;
      }

      loader = loader.getParent();
    }

    this.classpath = classpath.toString();

    // Store the assembled class path as a servlet context attribute
    ScriptContext.setAttribute(Globals.ATTR_CLASS_PATH, classpath.toString());
  }

  // try to extract the classpath from a loader that is not URLClassLoader
  private String getClasspath(ClassLoader loader)
  {
    try
    {
      Method m = loader.getClass().getMethod("getClasspath", new Class[] {
      });
      if (log.isTraceEnabled())
      {

      }
      log.trace("getClasspath " + m);
      if (m == null)
      {
        return null;
      }
      Object o = m.invoke(loader, new Object[] {
      });
      if (log.isDebugEnabled())
      {
        log.debug("gotClasspath " + o);
      }
      if (o instanceof String)
      {
        return (String) o;
      }

      return null;
    }
    catch (Exception ex)
    {
      if (log.isDebugEnabled())
      {
        log.debug("getClasspath ", ex);
      }
    }
    return null;
  }
}
TOP

Related Classes of org.huihoo.willow.loader.WorkflowLoader

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.