//----------------------------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.core;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.util.ArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.Container;
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.util.LifecycleSupport;
import org.huihoo.willow.util.StringManager;
/**
* @author reic
*
* To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
public abstract class ContainerBase implements Container, Lifecycle, Serializable
{
protected static Log log =LogFactory.getLog(ContainerBase.class);
// ----------------------------------------------------- Instance Variables
/**
* The debugging detail level for this component.
*/
protected int debug = 0;
/**
* The processor delay for this component.
*/
protected int backgroundProcessorDelay = 300;
/**
* The lifecycle event support for this component.
*/
protected LifecycleSupport lifecycle = new LifecycleSupport(this);
/**
* The container event listeners for this Container.
*/
protected ArrayList listeners = new ArrayList();
/**
* The Loader implementation with which this Container is associated.
*/
protected Loader loader = null;
/**
* The Logger implementation with which this Container is associated.
*/
protected Logger logger = null;
/**
* The human-readable name of this Container.
*/
protected String name = null;
/**
* The parent Container to which this Container is a child.
*/
protected Container parent = null;
/**
* The parent class loader to be configured when we install a Loader.
*/
protected ClassLoader parentClassLoader = null;
/**
* The string manager for this package.
*/
protected static StringManager sm = StringManager.getManager(Constants.PACKAGE);
/**
* Has this component been started?
*/
protected boolean started = false;
/**
* The property change support for this component.
*/
protected PropertyChangeSupport support = new PropertyChangeSupport(this);
/**
* The background thread.
*/
protected Thread thread = null;
/**
* The background thread completion semaphore.
*/
protected boolean threadDone = false;
// ------------------------------------------------------------- Properties
/**
* 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));
}
/**
* Get the delay between the invocation of the backgroundProcess method on
* this container and its children. Child containers will not be invoked
* if their delay value is not negative (which would mean they are using
* their own thread). Setting this to a positive value will cause
* a thread to be spawn. After waiting the specified amount of time,
* the thread will invoke the executePeriodic method on this container
* and all its children.
*/
public int getBackgroundProcessorDelay()
{
return backgroundProcessorDelay;
}
/**
* Set the delay between the invocation of the execute method on this
* container and its children.
*
* @param delay The delay in seconds between the invocation of
* backgroundProcess methods
*/
public void setBackgroundProcessorDelay(int delay)
{
backgroundProcessorDelay = delay;
}
/**
* Return descriptive information about this Container implementation and
* the corresponding version number, in the format
* <code><description>/<version></code>.
*/
public String getInfo()
{
return this.getClass().getName();
}
/**
* Return the Loader with which this Container is associated. If there is
* no associated Loader, return the Loader associated with our parent
* Container (if any); otherwise, return <code>null</code>.
*/
public Loader getLoader()
{
if (loader != null)
return (loader);
if (parent != null)
return (parent.getLoader());
return (null);
}
/**
* Set the Loader with which this Container is associated.
*
* @param loader The newly associated loader
*/
public synchronized void setLoader(Loader loader)
{
// Change components if necessary
Loader oldLoader = this.loader;
if (oldLoader == loader)
return;
this.loader = loader;
// Stop the old component if necessary
if (started && (oldLoader != null) && (oldLoader instanceof Lifecycle))
{
try
{
((Lifecycle) oldLoader).stop();
}
catch (LifecycleException e)
{
log.error("ContainerBase.setLoader: stop: ", e);
}
}
// Start the new component if necessary
if (loader != null)
{
loader.setContainer(this);
}
if (started && (loader != null) && (loader instanceof Lifecycle))
{
try
{
((Lifecycle) loader).start();
}
catch (LifecycleException e)
{
log.error("ContainerBase.setLoader: start: ", e);
}
}
// Report this property change to interested listeners
support.firePropertyChange("loader", oldLoader, this.loader);
}
/**
* Return the Logger with which this Container is associated. If there is
* no associated Logger, return the Logger associated with our parent
* Container (if any); otherwise return <code>null</code>.
*/
public Logger getLogger()
{
if (logger != null)
return (logger);
if (parent != null)
return (parent.getLogger());
return (null);
}
/**
* Set the Logger with which this Container is associated.
*
* @param logger The newly associated Logger
*/
public synchronized void setLogger(Logger logger)
{
// Change components if necessary
Logger oldLogger = this.logger;
if (oldLogger == logger)
{
return;
}
this.logger = logger;
// Stop the old component if necessary
if (started && (oldLogger != null) && (oldLogger instanceof Lifecycle))
{
try
{
((Lifecycle) oldLogger).stop();
}
catch (LifecycleException e)
{
log.error("ContainerBase.setLogger: stop: ", e);
}
}
// Start the new component if necessary
if (logger != null)
{
logger.setContainer(this);
}
if (started && (logger != null) && (logger instanceof Lifecycle))
{
try
{
((Lifecycle) logger).start();
}
catch (LifecycleException e)
{
log.error("ContainerBase.setLogger: start: ", e);
}
}
// Report this property change to interested listeners
support.firePropertyChange("logger", oldLogger, this.logger);
}
/**
* Return an object which may be utilized for mapping to this component.
*/
public Object getMappingObject()
{
return this;
}
/**
* Return a name string (suitable for use by humans) that describes this
* Container. Within the set of child containers belonging to a particular
* parent, Container names must be unique.
*/
public String getName()
{
return (name);
}
/**
* Set a name string (suitable for use by humans) that describes this
* Container. Within the set of child containers belonging to a particular
* parent, Container names must be unique.
*
* @param name New name of this container
*
* @exception IllegalStateException if this Container has already been
* added to the children of a parent Container (after which the name
* may not be changed)
*/
public void setName(String name)
{
String oldName = this.name;
this.name = name;
support.firePropertyChange("name", oldName, this.name);
}
/**
* Return the Container for which this Container is a child, if there is
* one. If there is no defined parent, return <code>null</code>.
*/
public Container getParent()
{
return (parent);
}
/**
* Set the parent Container to which this Container is being added as a
* child. This Container may refuse to become attached to the specified
* Container by throwing an exception.
*
* @param container Container to which this Container is being added
* as a child
*
* @exception IllegalArgumentException if this Container refuses to become
* attached to the specified Container
*/
public void setParent(Container container)
{
Container oldParent = this.parent;
this.parent = container;
support.firePropertyChange("parent", oldParent, this.parent);
}
/**
* Return the parent class loader (if any) for this web application.
* This call is meaningful only <strong>after</strong> a Loader has
* been configured.
*/
public ClassLoader getParentClassLoader()
{
if (parentClassLoader != null)
return (parentClassLoader);
if (parent != null)
{
return (parent.getParentClassLoader());
}
return (ClassLoader.getSystemClassLoader());
}
/**
* Set the parent class loader (if any) for this web application.
* This call is meaningful only <strong>before</strong> a Loader has
* been configured, and the specified value (if non-null) should be
* passed as an argument to the class loader constructor.
*
*
* @param parent The new parent class loader
*/
public void setParentClassLoader(ClassLoader parent)
{
ClassLoader oldParentClassLoader = this.parentClassLoader;
this.parentClassLoader = parent;
support.firePropertyChange("parentClassLoader", oldParentClassLoader, this.parentClassLoader);
}
// ------------------------------------------------------ Container Methods
/**
* Add a property change listener to this component.
*
* @param listener The listener to add
*/
public void addPropertyChangeListener(PropertyChangeListener listener)
{
support.addPropertyChangeListener(listener);
}
/**
* Remove a property change listener from this component.
*
* @param listener The listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener)
{
support.removePropertyChangeListener(listener);
}
// ------------------------------------------------------ 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);
}
/**
* Prepare for active use of the public methods of this Component.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents it from being started
*/
public synchronized void start() throws LifecycleException
{
// Validate and update our current component state
if (started)
{
log.info(sm.getString("containerBase.alreadyStarted", logName()));
return;
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
started = true;
// Start our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle))
{
((Lifecycle) loader).start();
}
if ((logger != null) && (logger instanceof Lifecycle))
{
((Lifecycle) logger).start();
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(START_EVENT, null);
// Start our thread
threadStart();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
/**
* Gracefully shut down active use of the public methods of this Component.
*
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
public synchronized void stop() throws LifecycleException
{
// Validate and update our current component state
if (!started)
{
log.info(sm.getString("containerBase.notStarted", logName()));
return;
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
// Stop our thread
threadStop();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
// Stop our subordinate components, if any
if ((logger != null) && (logger instanceof Lifecycle))
{
((Lifecycle) logger).stop();
}
if ((loader != null) && (loader instanceof Lifecycle))
{
((Lifecycle) loader).stop();
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
}
/**
* Execute a periodic task, such as reloading, etc. This method will be
* invoked inside the classloading context of this container. Unexpected
* throwables will be caught and logged.
*/
public void backgroundProcess()
{
}
// ------------------------------------------------------ Protected Methods
/**
* Log the specified message to our current Logger (if any).
*
* @param message Message to be logged
*/
protected void log(String message)
{
Logger logger = getLogger();
if (logger != null)
{
logger.log(logName() + ": " + message);
}
else
{
log.info(message);
}
}
/**
* Log the specified message and exception to our current Logger
* (if any).
*
* @param message Message to be logged
* @param throwable Related exception
*/
protected void log(String message, Throwable throwable)
{
Logger logger = getLogger();
if (logger != null)
{
logger.log(logName() + ": " + message, throwable);
}
else
{
log.error(message, throwable);
}
}
/**
* Return the abbreviated name of this container for logging messsages
*/
protected String logName()
{
String className = this.getClass().getName();
int period = className.lastIndexOf(".");
if (period >= 0)
className = className.substring(period + 1);
return (className + "[" + getName() + "]");
}
/**
* Start the background thread that will periodically check for
* session timeouts.
*/
protected void threadStart()
{
}
/**
* Stop the background thread that is periodically checking for
* session timeouts.
*/
protected void threadStop()
{
}
}