Author: cziegeler
Date: Tue Oct 19 12:10:38 2004
New Revision: 55087
Added:
cocoon/whiteboard/ecmplus/src/java/
cocoon/whiteboard/ecmplus/src/java/org/
cocoon/whiteboard/ecmplus/src/java/org/apache/
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/AbstractComponentHandler.java
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceManager.java
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceSelector.java
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentFactory.java
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentsSelector.java
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/PoolableComponentHandler.java
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/RoleManager.java
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/SingleThreadedComponentHandler.java
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ThreadSafeComponentHandler.java
Log:
First version of ECM++
Added:
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/AbstractComponentHandler.java
==============================================================================
--- (empty file)
+++
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/AbstractComponentHandler.java
Tue Oct 19 12:10:38 2004
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import org.apache.avalon.excalibur.logger.LoggerManager;
+import org.apache.avalon.excalibur.pool.Poolable;
+import org.apache.avalon.framework.component.Composable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.thread.SingleThreaded;
+import org.apache.avalon.framework.thread.ThreadSafe;
+
+/**
+ * This class acts like a Factory to instantiate the correct version
+ * of the component handler that you need.
+ *
+ * @version CVS $Revision: 1.6 $ $Date: 2004/02/28 11:47:14 $
+ */
+public abstract class AbstractComponentHandler {
+
+ private final Object referenceSemaphore = new Object();
+ private int references = 0;
+
+ protected final Logger logger;
+
+ /** This factory is used to created new objects */
+ protected final ComponentFactory factory;
+
+ /** State management boolean stating whether the Handler is disposed or
not */
+ protected boolean disposed = false;
+
+ /** State management boolean stating whether the Handler is initialized or
not */
+ protected boolean initialized = false;
+
+ /**
+ * Looks up and returns a component handler for a given component class.
+ *
+ * @param role The role name of the component. This must be
+ * a fully-qualified classname
+ * @param componentClass Class of the component for which the handle is
+ * being requested.
+ * @param configuration The configuration for this component.
+ * @param serviceManager The service manager which will be managing the
service.
+ * @param context The current context object.
+ * @param logger The current logger
+ * @param loggerManager The current LoggerManager.
+ *
+ * @throws Exception If there were any problems obtaining a
ComponentHandler
+ */
+ public static AbstractComponentHandler getComponentHandler( final String
role,
+ final Class
componentClass,
+ final Configuration
configuration,
+ final ServiceManager
serviceManager,
+ final Context context,
+ final Logger logger,
+ final LoggerManager
loggerManager,
+ final RoleManager
roleManager)
+ throws Exception {
+ int numInterfaces = 0;
+
+ if( SingleThreaded.class.isAssignableFrom( componentClass ) ) {
+ numInterfaces++;
+ }
+
+ if( ThreadSafe.class.isAssignableFrom( componentClass ) ) {
+ numInterfaces++;
+ }
+
+ if( Poolable.class.isAssignableFrom( componentClass ) ) {
+ numInterfaces++;
+ }
+
+ if( numInterfaces > 1 ) {
+ throw new Exception( "[CONFLICT] More than one lifecycle interface
in "
+ + componentClass.getName() + " May implement
no more than one of "
+ + "SingleThreaded, ThreadSafe, or Poolable" );
+ }
+
+ // Early check for Composable
+ if ( Composable.class.isAssignableFrom( componentClass ) ) {
+ throw new Exception("Interface Composable is not supported
anymore. Please change class "
+ + componentClass.getName() + " to use
Serviceable instead.");
+ }
+
+ // Create the factory to use to create the instances of the Component.
+ ComponentFactory factory = new ComponentFactory( role,
+ componentClass,
+ configuration,
+ serviceManager,
+ context,
+ logger,
+ loggerManager,
+ roleManager);
+
+ AbstractComponentHandler handler;
+
+ if( Poolable.class.isAssignableFrom( componentClass ) ) {
+ handler = new PoolableComponentHandler( logger, factory,
configuration );
+ } else if( ThreadSafe.class.isAssignableFrom( componentClass ) ) {
+ handler = new ThreadSafeComponentHandler( logger, factory );
+ } else {
+ // This is a SingleThreaded component
+ handler = new SingleThreadedComponentHandler( logger, factory );
+ }
+
+ return handler;
+ }
+
+ /**
+ * Creates a new ComponentHandler.
+ */
+ public AbstractComponentHandler(Logger logger, ComponentFactory factory) {
+ this.logger = logger;
+ this.factory = factory;
+ }
+
+ /**
+ * Get an instance of the type of component handled by this handler.
+ * <p>
+ * Subclasses should not extend this method but rather the doGet method
below otherwise
+ * reference counts will not be supported.
+ * <p>
+ *
+ * @return an instance
+ * @exception Exception if an error occurs
+ */
+ public final Object get() throws Exception {
+ if( !this.initialized ) {
+ throw new IllegalStateException(
+ "You cannot get a component from an uninitialized handler." );
+ }
+ if( this.disposed ) {
+ throw new IllegalStateException( "You cannot get a component from
a disposed handler." );
+ }
+
+ final Object component = this.doGet();
+
+ synchronized( this.referenceSemaphore ) {
+ this.references++;
+ }
+
+ return component;
+ }
+
+ /**
+ * Put back an instance of the type of component handled by this handler.
+ * <p>
+ * Subclasses should not extend this method but rather the doPut method
below otherwise
+ * reference counts will not be supported.
+ * <p>
+ *
+ * @param component a service
+ * @exception Exception if an error occurs
+ */
+ public final void put( Object component )
+ throws Exception {
+ if( !this.initialized ) {
+ throw new IllegalStateException(
+ "You cannot put a component to an uninitialized handler." );
+ }
+ // The reference count must be decremented before any calls to doPut.
+ // If there is another thread blocking, then this thread could stay
deep inside
+ // doPut for an undetermined amount of time until the thread
scheduler gives it
+ // some cycles again. (It happened). All ComponentHandler state
must therefor
+ // reflect the thread having left this method before the call to
doPut to avoid
+ // warning messages from the dispose() cycle if that takes place
before this
+ // thread has a chance to continue.
+ synchronized( this.referenceSemaphore ) {
+ this.references--;
+ }
+
+ try {
+ this.doPut( component );
+ } catch( Throwable t ) {
+ this.logger.error("Exception during putting back a component.", t);
+ }
+ }
+
+ /**
+ * Concrete implementation of getting a component.
+ *
+ * @return a service
+ * @exception Exception if an error occurs
+ */
+ protected abstract Object doGet() throws Exception;
+
+ /**
+ * Concrete implementation of putting back a component.
+ *
+ * @param component a <code>Component</code> value
+ * @exception Exception if an error occurs
+ */
+ protected abstract void doPut( Object component ) throws Exception;
+
+ /**
+ * Returns <code>true</code> if this component handler can safely be
+ * disposed (i.e. none of the components it is handling are still
+ * being used).
+ *
+ * @return <code>true</code> if this component handler can safely be
+ * disposed; <code>false</code> otherwise
+ */
+ public final boolean canBeDisposed() {
+ return ( this.references == 0 );
+ }
+
+ /**
+ * Dispose of the component handler and any associated Pools and Factories.
+ */
+ public void dispose() {
+ this.disposed = true;
+ }
+
+ /**
+ * Initialize this handler
+ */
+ public void initialize() throws Exception {
+ if( this.initialized ) {
+ return;
+ }
+ this.initialized = true;
+ if( this.logger.isDebugEnabled() ) {
+ this.logger.debug( "ThreadSafeComponentHandler initialized for: "
+ this.factory.getCreatedClass().getName() );
+ }
+ }
+}
Added:
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceManager.java
==============================================================================
--- (empty file)
+++
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceManager.java
Tue Oct 19 12:10:38 2004
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.avalon.excalibur.logger.LoggerManager;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.activity.Initializable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.components.SitemapConfigurable;
+import org.apache.cocoon.components.SitemapConfigurationHolder;
+
+/**
+ * Default component manager for Cocoon's components.
+ *
+ * @version CVS $Revision: 1.6 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class CocoonServiceManager
+ extends AbstractLogEnabled
+ implements ServiceManager,
+ Configurable,
+ Contextualizable,
+ Initializable,
+ Disposable {
+
+ /** The parent ComponentLocator */
+ private final ServiceManager parentManager;
+
+ /** The classloader used for this system. */
+ private final ClassLoader loader;
+
+ /** The application context for components */
+ private Context context;
+
+ /** Static component mapping handlers. */
+ private final Map componentMapping = Collections.synchronizedMap(new
HashMap());
+
+ /** Used to map roles to ComponentHandlers. */
+ private final Map componentHandlers = Collections.synchronizedMap(new
HashMap());
+
+ /** added component handlers before initialization to maintain
+ * the order of initialization
+ */
+ private final List newComponentHandlers = new ArrayList();
+
+ /** RoleInfos. */
+ private RoleManager roleManager;
+
+ /** LogKitManager. */
+ private LoggerManager loggerManager;
+
+ /** Is the Manager disposed or not? */
+ private boolean disposed;
+
+ /** Is the Manager initialized? */
+ private boolean initialized;
+
+ /** The [EMAIL PROTECTED] SitemapConfigurationHolder}s */
+ private Map sitemapConfigurationHolders = new HashMap(15);
+
+ /** Temporary list of parent-aware components. Will be null for most of
+ * our lifecycle. */
+ private ArrayList parentAwareComponents = new ArrayList();
+
+ /** Create the ComponentLocator with a Classloader and parent
ComponentLocator */
+ public CocoonServiceManager( final ServiceManager parent,
+ final ClassLoader loader ) {
+ if( null == loader ) {
+ this.loader = Thread.currentThread().getContextClassLoader();
+ } else {
+ this.loader = loader;
+ }
+
+ this.parentManager = parent;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.service.ServiceManager#lookup(java.lang.String)
+ */
+ public Object lookup( final String role )
+ throws ServiceException {
+ if( !this.initialized ) {
+ if( this.getLogger().isWarnEnabled() ) {
+ this.getLogger().warn(
+ "Looking up component on an uninitialized
CocoonServiceManager [" + role + "]" );
+ }
+ }
+
+ if( this.disposed ) {
+ throw new IllegalStateException(
+ "You cannot lookup components on a disposed
CocoonServiceManager" );
+ }
+
+ if( role == null ) {
+ final String message =
+ "CocoonServiceManager attempted to retrieve service with null
role.";
+
+ if( this.getLogger().isErrorEnabled() ) {
+ this.getLogger().error( message );
+ }
+ throw new ServiceException( role, message );
+ }
+
+ AbstractComponentHandler handler =
(AbstractComponentHandler)this.componentHandlers.get( role );
+
+ // Retrieve the instance of the requested component
+ if ( handler == null )
+ {
+ if( this.parentManager != null ) {
+ try {
+ return this.parentManager.lookup( role );
+ } catch( Exception e ) {
+ if( this.getLogger().isWarnEnabled() ) {
+ final String message =
+ "ComponentLocator exception from parent CM during
lookup.";
+ this.getLogger().warn( message, e );
+ }
+ // ignore. If the exception is thrown, we try to
+ // create the component next
+ }
+ }
+
+ if( this.roleManager != null ) {
+ final String className =
this.roleManager.getDefaultClassNameForRole( role );
+
+ if( className != null ) {
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( "Could not find
ComponentHandler, attempting to create "
+ + "one for role [" + role + "]" );
+ }
+
+ try {
+ final Class componentClass = this.loader.loadClass(
className );
+
+ final Configuration configuration = new
DefaultConfiguration( "", "-" );
+
+ handler =
AbstractComponentHandler.getComponentHandler( role,
+ componentClass,
+ configuration,
+ this,
+ context,
+ this.getLogger(),
+ this.loggerManager,
+ this.roleManager);
+
+ handler.initialize();
+ } catch (ServiceException se) {
+ throw se;
+ } catch( final Exception e ) {
+ final String message = "Could not find component";
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( message + " for role: " +
role, e );
+ }
+ throw new ServiceException( role, message, e );
+ }
+
+ this.componentHandlers.put( role, handler );
+ }
+ } else {
+ this.getLogger().debug( "Component requested without a
RoleManager set.\n"
+ + "That means setRoleManager() was not called during
initialization." );
+ }
+ }
+
+ if( handler == null ) {
+ final String message = "Could not find component";
+ if( this.getLogger().isDebugEnabled() )
+ {
+ this.getLogger().debug( message + " for role: " + role );
+ }
+ throw new ServiceException( role, message );
+ }
+
+ Object component = null;
+
+ try {
+ component = handler.get();
+ } catch( final IllegalStateException ise ) {
+ try {
+ handler.initialize();
+ component = handler.get();
+ } catch( final ServiceException ce ) {
+ // Rethrow instead of wrapping a ComponentException with
another one
+ throw ce;
+ } catch( final Exception e ) {
+ final String message = "Could not access the Component";
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( message + " for role [" + role +
"]", e );
+ }
+
+ throw new ServiceException( role, message, e );
+ }
+ } catch ( ServiceException se) {
+ throw se;
+ } catch( final Exception e ) {
+ final String message = "Could not access the Component";
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( message + " for role [" + role + "]",
e );
+ }
+
+ throw new ServiceException( role, message, e );
+ }
+
+ // Add a mapping between the component and its handler.
+ // In the case of a ThreadSafeComponentHandler, the same component
will be mapped
+ // multiple times but because each put will overwrite the last, this
is not a
+ // problem. Checking to see if the put has already been done would
be slower.
+ this.componentMapping.put( component, handler );
+
+ if ( null != component && component instanceof SitemapConfigurable) {
+
+ // FIXME: how can we prevent that this is called over and over
again?
+ SitemapConfigurationHolder holder;
+
+ holder =
(SitemapConfigurationHolder)this.sitemapConfigurationHolders.get( role );
+ if ( null == holder ) {
+ // create new holder
+ holder = new DefaultSitemapConfigurationHolder( role );
+ this.sitemapConfigurationHolders.put( role, holder );
+ }
+
+ try {
+ ((SitemapConfigurable)component).configure(holder);
+ } catch (ConfigurationException ce) {
+ throw new ServiceException(role, "Exception during setup of
SitemapConfigurable.", ce);
+ }
+ }
+ return component;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.service.ServiceManager#hasService(java.lang.String)
+ */
+ public boolean hasService( final String role ) {
+ if( !this.initialized ) return false;
+ if( this.disposed ) return false;
+
+ boolean exists = this.componentHandlers.containsKey( role );
+
+ if( !exists && null != this.parentManager ) {
+ exists = this.parentManager.hasService( role );
+ }
+
+ return exists;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.service.ServiceManager#release(java.lang.Object)
+ */
+ public void release( final Object component ) {
+ if( null == component ) {
+ return;
+ }
+
+ // The componentMapping StaticBucketMap itself is threadsafe, and
because the same component
+ // will never be released by more than one thread, this method does
not need any
+ // synchronization around the access to the map.
+
+ final AbstractComponentHandler handler =
+ (AbstractComponentHandler)this.componentMapping.get( component );
+
+ if ( handler != null ) {
+ // ThreadSafe components will always be using a
ThreadSafeComponentHandler,
+ // they will only have a single entry in the m_componentMapping
map which
+ // should not be removed until the ComponentLocator is disposed.
All
+ // other components have an entry for each instance which should
be
+ // removed.
+ if( !( handler instanceof ThreadSafeComponentHandler ) ) {
+ // Remove the component before calling put. This is critical
to avoid the
+ // problem where another thread calls put on the same
component before
+ // remove can be called.
+ this.componentMapping.remove( component );
+ }
+
+ try {
+ handler.put( component );
+ } catch( Exception e ) {
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( "Error trying to release
component.", e );
+ }
+ }
+ }
+ else if( this.parentManager != null ) {
+ this.parentManager.release( component );
+ } else {
+ this.getLogger().warn( "Attempted to release a " +
component.getClass().getName() +
+ " but its handler could not be located." );
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
+ */
+ public void configure( final Configuration configuration )
+ throws ConfigurationException {
+ if( null == roleManager ) {
+ final RoleManager roleInfo = new RoleManager();
+ roleInfo.enableLogging( getLogger() );
+ roleInfo.configure( configuration );
+ this.roleManager = roleInfo;
+ this.getLogger().debug( "No RoleManager given, deriving one from
configuration" );
+ }
+
+ // Set components
+
+ final Configuration[] configurations = configuration.getChildren();
+
+ for( int i = 0; i < configurations.length; i++ ) {
+ String type = configurations[ i ].getName();
+
+ if( !type.equals( "role" ) ) {
+ String role = configurations[ i ].getAttribute( "role", "" );
+ String className = configurations[ i ].getAttribute( "class",
"" );
+
+ if( role.equals( "" ) ) {
+ role = roleManager.getRoleForName( type );
+ }
+
+ if( null != role && !role.equals( "" ) ) {
+ if( className.equals( "" ) ) {
+ className = roleManager.getDefaultClassNameForRole(
role );
+ }
+
+
+ if (
"org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) {
+ className = CocoonServiceSelector.class.getName();
+ } else if
("org.apache.cocoon.components.treeprocessor.sitemap.ComponentsSelector".equals(className)
) {
+ className = ComponentsSelector.class.getName();
+ }
+ try {
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( "Adding component (" +
role + " = "
+ + className + ")" );
+ }
+
+ final Class clazz = this.loader.loadClass( className );
+ addComponent( role, clazz, configurations[ i ] );
+ } catch( final ClassNotFoundException cnfe ) {
+ final String message = "Could not get class ";
+
+ if( this.getLogger().isErrorEnabled() ) {
+ this.getLogger().error( message + className + "
for role " + role
+ + " on configuration element " +
configurations[ i ].getName(),
+ cnfe );
+ }
+
+ throw new ConfigurationException( message, cnfe );
+ } catch( final ServiceException ce ) {
+ final String message = "Bad component ";
+
+ if( this.getLogger().isErrorEnabled() ) {
+ this.getLogger().error( message + className + "
for role " + role
+ + " on configuration element " +
configurations[ i ].getName(),
+ ce );
+ }
+
+ throw new ConfigurationException( message, ce );
+ } catch( final Exception e ) {
+ if( this.getLogger().isErrorEnabled() ) {
+ this.getLogger().error( "Unexpected exception for
hint: " + role, e );
+ }
+ throw new ConfigurationException( "Unexpected
exception", e );
+ }
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
+ */
+ public void contextualize( final Context context ) {
+ this.context = context;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.avalon.framework.activity.Initializable#initialize()
+ */
+ public void initialize()
+ throws Exception {
+ synchronized( this ) {
+ this.initialized = true;
+
+ for( int i = 0; i < this.newComponentHandlers.size(); i++ )
+ {
+ final AbstractComponentHandler handler =
+ (AbstractComponentHandler)this.newComponentHandlers.get( i
);
+ try {
+ handler.initialize();
+ } catch( Exception e ) {
+ if( this.getLogger().isErrorEnabled() )
+ {
+ this.getLogger().error( "Caught an exception trying to
initialize "
+ + "the component handler.", e );
+ }
+
+ // Rethrow the exception
+ throw e;
+ }
+ }
+
+ List keys = new ArrayList( this.componentHandlers.keySet() );
+
+ for( int i = 0; i < keys.size(); i++ ) {
+ final Object key = keys.get( i );
+ final AbstractComponentHandler handler =
+ (AbstractComponentHandler)this.componentHandlers.get( key
);
+
+ if( !this.newComponentHandlers.contains( handler ) ) {
+ try {
+ handler.initialize();
+
+ } catch( Exception e ) {
+ if( this.getLogger().isErrorEnabled() ) {
+ this.getLogger().error( "Caught an exception
trying to initialize "
+ + "the component handler.", e );
+
+ }
+ // Rethrow the exception
+ throw e;
+ }
+ }
+ }
+ this.newComponentHandlers.clear();
+
+ if (parentAwareComponents == null) {
+ throw new ServiceException(null, "CocoonServiceManager already
initialized");
+ }
+ // Set parents for parentAware components
+ Iterator iter = parentAwareComponents.iterator();
+ while (iter.hasNext()) {
+ String role = (String)iter.next();
+ getLogger().debug(".. "+role);
+ if ( parentManager != null && parentManager.hasService( role )
) {
+ // lookup new component
+ Object component = null;
+ try {
+ component = this.lookup( role );
+ ((CocoonServiceSelector)component).setParentLocator(
this.parentManager, role );
+ } catch (ServiceException ignore) {
+ // we don't set the parent then
+ } finally {
+ this.release( component );
+ }
+ }
+ }
+ parentAwareComponents = null; // null to save memory, and catch
logic bugs.
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.avalon.framework.activity.Disposable#dispose()
+ */
+ public void dispose() {
+ synchronized( this ) {
+ boolean forceDisposal = false;
+
+ final List disposed = new ArrayList();
+
+ while( componentHandlers.size() > 0 ) {
+ for( Iterator iterator = componentHandlers.keySet().iterator();
+ iterator.hasNext(); ) {
+ final Object role = iterator.next();
+
+ final AbstractComponentHandler handler =
+ (AbstractComponentHandler)componentHandlers.get( role
);
+
+ if( forceDisposal || handler.canBeDisposed() ) {
+ if( forceDisposal && getLogger().isWarnEnabled() ) {
+ this.getLogger().warn
+ ( "disposing of handler for unreleased
component."
+ + " role [" + role + "]" );
+ }
+
+ handler.dispose();
+ disposed.add( role );
+ }
+ }
+
+ if( disposed.size() > 0 ) {
+ final Iterator i = disposed.iterator();
+ while ( i.hasNext() ) {
+ this.componentHandlers.remove( i.next() );
+ }
+ disposed.clear();
+ } else {
+ // no more disposable handlers!
+ forceDisposal = true;
+ }
+ }
+
+ this.disposed = true;
+ }
+ }
+
+ /*---------------------------------------------------------------
+ * RoleManageable Methods
+ *-------------------------------------------------------------*/
+ /**
+ * Configure the RoleManager
+ */
+ public void setRoleManager( final RoleManager roles )
+ {
+ if( null == roleManager )
+ {
+ roleManager = roles;
+ }
+ }
+
+
+ /**
+ * Configure the LoggerManager.
+ */
+ public void setLoggerManager( final LoggerManager loggerManager ) {
+ this.loggerManager = loggerManager;
+ }
+
+ /**
+ * Add a new component to the manager.
+ *
+ * @param role the role name for the new component.
+ * @param component the class of this component.
+ * @param configuration the configuration for this component.
+ */
+ public void addComponent( final String role,
+ final Class component,
+ final Configuration configuration )
+ throws ServiceException {
+ if( this.initialized ) {
+ throw new ServiceException( role,
+ "Cannot add components to an initialized
CocoonServiceManager." );
+ }
+
+ try {
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( "Attempting to get handler for role ["
+ role + "]" );
+ }
+
+ final AbstractComponentHandler handler =
AbstractComponentHandler.getComponentHandler( role,
+ component,
+
configuration,
+ this,
+ context,
+
this.getLogger(),
+
this.loggerManager,
+
this.roleManager);
+
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( "Handler type = " +
handler.getClass().getName() );
+ }
+
+ this.componentHandlers.put( role, handler );
+ this.newComponentHandlers.add( handler );
+ } catch ( final ServiceException se ) {
+ throw se;
+ } catch( final Exception e ) {
+ throw new ServiceException( role, "Could not set up component
handler.", e );
+ }
+ // Note that at this point, we're not initialized and cannot do
+ // lookups, so defer parental introductions to initialize().
+ if ( CocoonServiceSelector.class.isAssignableFrom( component ) ) {
+ this.parentAwareComponents.add(role);
+ }
+ }
+
+}
Added:
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceSelector.java
==============================================================================
--- (empty file)
+++
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceSelector.java
Tue Oct 19 12:10:38 2004
@@ -0,0 +1,692 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.avalon.excalibur.logger.LoggerManager;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.activity.Initializable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.ServiceSelector;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.avalon.framework.thread.ThreadSafe;
+
+/**
+ * Default component selector for Cocoon's components.
+ *
+ * @version CVS $Revision: 1.6 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class CocoonServiceSelector
+ extends AbstractLogEnabled
+ implements Contextualizable,
+ ServiceSelector,
+ Serviceable,
+ Configurable,
+ Initializable,
+ ThreadSafe,
+ Disposable {
+
+ /** The classloader used for this system. */
+ protected final ClassLoader loader;
+
+ /** The role name for this instance
+ */
+ private String rolename;
+
+ /** The application context for components
+ */
+ protected Context context;
+
+ /** The application context for components
+ */
+ private ServiceManager serviceManager;
+
+ /** Static configuraiton object.
+ */
+ private Configuration configuration;
+
+ /** Static component handlers.
+ */
+ private Map componentHandlers = Collections.synchronizedMap(new HashMap());
+
+ /** Dynamic component handlers mapping.
+ */
+ private Map componentMapping = Collections.synchronizedMap(new HashMap());
+
+ /** Flag for if this is disposed or not.
+ */
+ private boolean disposed;
+
+ /** Flag for if this is initialized or not.
+ */
+ private boolean initialized;
+
+ /** The RoleManager to get hint shortcuts
+ */
+ protected RoleManager roleManager;
+
+ private LoggerManager loggerManager;
+
+ /** The parent selector, if any */
+ protected CocoonServiceSelector parentSelector;
+
+ /** The parent locator, if any */
+ protected ServiceManager parentLocator;
+
+ /** The role of this selector. Set in <code>configure()</code>. */
+ protected String roleName;
+
+ /** The default hint */
+ protected String defaultHint;
+
+ /** This selector's location (used for debugging purposes) */
+ private String location;
+
+
+ /** Create the ComponentSelector */
+ public CocoonServiceSelector() {
+ this.loader = Thread.currentThread().getContextClassLoader();
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
+ */
+ public void contextualize( final Context context ) {
+ this.context = context;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.service.ServiceSelector#select(java.lang.Object)
+ */
+ public Object select( Object hint )
+ throws ServiceException {
+ if( !this.initialized ) {
+ if( this.getLogger().isWarnEnabled() ) {
+ this.getLogger().warn( "Looking up component on an
uninitialized ComponentLocator "
+ + "with hint [" + hint + "]" );
+ }
+ }
+
+ if( this.disposed ) {
+ throw new IllegalStateException(
+ "You cannot select a Component from a disposed
ComponentSelector" );
+ }
+
+ if (hint == null) {
+ hint = this.defaultHint;
+ }
+
+ AbstractComponentHandler handler =
(AbstractComponentHandler)this.componentHandlers.get( hint );
+
+ // Retrieve the instance of the requested component
+ if( null == handler ) {
+ // Doesn't exist here : try in parent selector
+ if ( this.parentSelector != null ) {
+ return this.parentSelector.select(hint);
+ }
+ final String message = getName()
+ + ": ComponentSelector could not find the component for hint
[" + hint + "]";
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( message );
+ }
+ throw new ServiceException( hint.toString(), message );
+ }
+
+ Object component = null;
+
+ try {
+ component = handler.get();
+ } catch( final ServiceException ce ) {
+ //rethrow
+ throw ce;
+ } catch( final Exception e ) {
+ final String message = getName()
+ + ": ComponentSelector could not access the Component for hint
[" + hint + "]";
+
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( message, e );
+ }
+ throw new ServiceException( hint.toString(), message, e );
+ }
+
+ if( null == component ) {
+ // Doesn't exist here : try in parent selector
+ if ( this.parentSelector != null ) {
+ component = this.parentSelector.select(hint);
+ } else {
+ final String message = getName()
+ + ": ComponentSelector could not find the component for
hint [" + hint + "]";
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( message );
+ }
+ throw new ServiceException( hint.toString(), message );
+ }
+ }
+
+ this.componentMapping.put( component, handler );
+ return component;
+
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.service.ServiceSelector#isSelectable(java.lang.Object)
+ */
+ public boolean isSelectable( Object hint ) {
+ if( !this.initialized ) return false;
+ if( this.disposed ) return false;
+
+ if (hint == null) {
+ hint = this.defaultHint;
+ }
+
+ boolean exists = false;
+
+ try {
+ AbstractComponentHandler handler =
(AbstractComponentHandler)this.componentHandlers.get( hint );
+ exists = (handler != null);
+ } catch( Throwable t ) {
+ // We can safely ignore all exceptions
+ }
+
+ if ( !exists && this.parentSelector != null ) {
+ exists = this.parentSelector.isSelectable( hint );
+ }
+ return exists;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.service.ServiceSelector#release(java.lang.Object)
+ */
+ public void release( final Object component ) {
+ if( null == component ) {
+ return;
+ }
+
+ // Was it selected on the parent ?
+ if ( this.parentSelector != null &&
+ this.parentSelector.canRelease(component) ) {
+ this.parentSelector.release(component);
+
+ } else {
+ final AbstractComponentHandler handler =
+ (AbstractComponentHandler)this.componentMapping.get( component
);
+
+ if( null == handler ) {
+ this.getLogger().warn( "Attempted to release a " +
component.getClass().getName()
+ + " but its handler could not be located." );
+ return;
+ }
+
+ // ThreadSafe components will always be using a
ThreadSafeComponentHandler,
+ // they will only have a single entry in the m_componentMapping
map which
+ // should not be removed until the ComponentLocator is disposed.
All
+ // other components have an entry for each instance which should
be
+ // removed.
+ if( !( handler instanceof ThreadSafeComponentHandler ) ) {
+ // Remove the component before calling put. This is critical
to avoid the
+ // problem where another thread calls put on the same
component before
+ // remove can be called.
+ this.componentMapping.remove( component );
+ }
+
+ try {
+ handler.put( component );
+ } catch( Exception e ) {
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( "Error trying to release
component", e );
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
+ */
+ public void service( final ServiceManager componentManager )
+ throws ServiceException {
+ this.serviceManager = componentManager;
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
+ */
+ public void configure( final Configuration config )
+ throws ConfigurationException {
+ // Store location
+ this.location = config.getLocation();
+
+ this.roleName = getRoleName(config);
+
+ // Pass a copy of the top-level object to superclass so that
+ // our name is properly initialized
+ // FIXME : could be avoided if parent m_role was protected or had
protected accessors
+ DefaultConfiguration temp = new DefaultConfiguration(config.getName(),
this.location);
+ if (config.getAttribute("role", null) != null) {
+ temp.setAttribute("role", this.roleName);
+ }
+
+ this.configuration = temp;
+
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( "ComponentSelector setting up with root
element: " +
+ this.configuration.getName() );
+ }
+
+ final String name = configuration.getName();
+ if( name.equals( "component" ) ) {
+ this.rolename = this.configuration.getAttribute( "role" );
+ } else {
+ this.rolename = this.roleManager.getRoleForName( name );
+ }
+
+ Configuration[] instances = this.configuration.getChildren();
+
+ for( int i = 0; i < instances.length; i++ ) {
+ final Object hint = instances[ i ].getAttribute( "name" ).trim();
+ String className;
+
+ if( "component-instance".equals( instances[ i ].getName() ) ) {
+ className = (String)instances[ i ].getAttribute( "class"
).trim();
+ } else {
+ className = this.roleManager.getDefaultClassNameForHint(
this.rolename,
+ instances[ i
].getName() );
+ }
+
+ if (
"org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) {
+ className = CocoonServiceSelector.class.getName();
+ } else if
("org.apache.cocoon.components.treeprocessor.sitemap.ComponentsSelector".equals(className)
) {
+ className = ComponentsSelector.class.getName();
+ }
+
+ try {
+ final Class clazz = this.loader.loadClass( className );
+ this.addComponent( hint, clazz, instances[ i ] );
+ } catch( final ClassNotFoundException cnfe ) {
+ final String message =
+ "The component instance for hint [" + hint
+ + "] has an invalid class name (" + className + ").";
+ if( this.getLogger().isErrorEnabled() ) {
+ this.getLogger().error( message, cnfe );
+ }
+
+ throw new ConfigurationException( message, cnfe );
+ } catch( final Exception e ) {
+ if( this.getLogger().isErrorEnabled() ) {
+ this.getLogger().error( "Unexpected exception for hint ["
+ hint + "]", e );
+ }
+ throw new ConfigurationException( "Unexpected exception", e );
+ }
+ }
+ // Get default hint
+ this.defaultHint =
config.getAttribute(this.getDefaultHintAttributeName(), null);
+
+ // Add components
+ String compInstanceName = getComponentInstanceName();
+
+ instances = config.getChildren();
+
+ for (int i = 0; i < instances.length; i++) {
+
+ Configuration instance = instances[i];
+
+ Object hint = instance.getAttribute("name").trim();
+
+ String classAttr = instance.getAttribute(getClassAttributeName(),
null);
+ String className;
+
+ if (compInstanceName == null) {
+ // component-instance implicitly defined by the presence of
the 'class' attribute
+ if (classAttr == null) {
+ className =
this.roleManager.getDefaultClassNameForHint(roleName, instance.getName());
+ } else {
+ className = classAttr.trim();
+ }
+
+ } else {
+ // component-instances names explicitly defined
+ if (compInstanceName.equals(instance.getName())) {
+ className = (classAttr == null) ? null : classAttr.trim();
+ } else {
+ className =
this.roleManager.getDefaultClassNameForHint(roleName, instance.getName());
+ }
+ }
+
+ if (className == null) {
+ String message = "Unable to determine class name for component
named '" + hint +
+ "' at " + instance.getLocation();
+
+ getLogger().error(message);
+ throw new ConfigurationException(message);
+ }
+
+ if (
"org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) {
+ className = CocoonServiceSelector.class.getName();
+ } else if
("org.apache.cocoon.components.treeprocessor.sitemap.ComponentsSelector".equals(className)
) {
+ className = ComponentsSelector.class.getName();
+ }
+
+ try {
+ Class clazz = this.loader.loadClass(className);
+ addComponent(hint, clazz, instance);
+
+ } catch(Exception e) {
+
+ String message = "Could not load class " + className + " for
component named '" +
+ hint + "' at " + instance.getLocation();
+
+ getLogger().error(message, e);
+ throw new ConfigurationException(message, e);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.avalon.framework.activity.Initializable#initialize()
+ */
+ public void initialize() {
+ synchronized( this ) {
+ this.initialized = true;
+
+ List keys = new ArrayList( this.componentHandlers.keySet() );
+
+ for( int i = 0; i < keys.size(); i++ ) {
+ final Object key = keys.get( i );
+ final AbstractComponentHandler handler =
+ (AbstractComponentHandler)this.componentHandlers.get( key
);
+
+ try {
+ handler.initialize();
+ } catch( Exception e ) {
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( "Caught an exception trying to
initialize "
+ + "of the component handler.", e );
+ }
+ }
+
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.avalon.framework.activity.Disposable#dispose()
+ */
+ public void dispose() {
+ synchronized( this ) {
+ Iterator keys = this.componentHandlers.keySet().iterator();
+ List keyList = new ArrayList();
+
+ while( keys.hasNext() ) {
+ Object key = keys.next();
+ AbstractComponentHandler handler =
+ (AbstractComponentHandler)this.componentHandlers.get( key
);
+
+ handler.dispose();
+
+ keyList.add( key );
+ }
+
+ keys = keyList.iterator();
+
+ while( keys.hasNext() ) {
+ this.componentHandlers.remove( keys.next() );
+ }
+
+ keyList.clear();
+
+ if ( this.parentLocator != null ) {
+ this.parentLocator.release( this.parentSelector );
+ this.parentLocator = null;
+ this.parentSelector = null;
+ }
+
+ this.disposed = true;
+ }
+ }
+
+ public void setRoleManager( final RoleManager roles ) {
+ this.roleManager = roles;
+ }
+
+ /**
+ * Configure the LoggerManager.
+ */
+ public void setLoggerManager( final LoggerManager manager ) {
+ this.loggerManager = manager;
+ }
+
+ /**
+ * Obtain a new ComponentHandler for the specified component. This method
+ * allows classes which extend the ExcaliburComponentSelector to use their
+ * own ComponentHandlers.
+ *
+ * @param componentClass Class of the component for which the handle is
+ * being requested.
+ * @param configuration The configuration for this component.
+ * @param componentManager The ComponentLocator which will be managing
+ * the Component.
+ * @param context The current context object.
+ * @param roleManager The current RoleManager.
+ * @param logkitManager The current LogKitManager.
+ *
+ * @throws Exception If there were any problems obtaining a
ComponentHandler
+ */
+ protected AbstractComponentHandler getComponentHandler( final String role,
+ final Class componentClass,
+ final Configuration
configuration,
+ final ServiceManager
componentManager,
+ final Context context,
+ final LoggerManager
logkitManager )
+ throws Exception {
+ return AbstractComponentHandler.getComponentHandler( role,
+ componentClass,
+ configuration,
+ this.serviceManager,
+ context,
+ this.getLogger(),
+ this.loggerManager,
+ this.roleManager);
+ }
+
+ /**
+ * Makes the ComponentHandlers available to subclasses.
+ *
+ * @return A reference to the componentHandler Map.
+ */
+ protected Map getComponentHandlers() {
+ return this.componentHandlers;
+ }
+
+ /** Add a new component to the manager.
+ * @param hint the hint name for the new component.
+ * @param component the class of this component.
+ * @param configuration the configuration for this component.
+ */
+ public void addComponent( final Object hint,
+ final Class component,
+ final Configuration configuration )
+ throws ServiceException {
+ if( this.initialized ) {
+ throw new ServiceException( hint.toString(),
+ "Cannot add components to an initialized ComponentSelector",
null );
+ }
+
+ try {
+ final AbstractComponentHandler handler = getComponentHandler(
this.rolename,
+ component,
+
configuration,
+
this.serviceManager,
+ this.context,
+
this.loggerManager );
+
+ handler.initialize();
+ this.componentHandlers.put( hint, handler );
+
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug(
+ "Adding " + component.getName() + " for hint [" +
hint.toString() + "]" );
+ }
+ } catch( final Exception e ) {
+ final String message =
+ "Could not set up Component for hint [ " + hint + "]";
+ if( this.getLogger().isErrorEnabled() ) {
+ this.getLogger().error( message, e );
+ }
+
+ throw new ServiceException( hint.toString(), message, e );
+ }
+ }
+
+ /**
+ * Return this selector's configuration name or a default name if no such
+ * configuration was provided. This accounts for the case when a static
+ * component instance has been added through
+ * <code>addComponentInstance</code> with no associated configuration
+ */
+ private String getName() {
+ if( null != this.configuration &&
+ !this.configuration.getName().equals( "" ) ) {
+ return this.configuration.getName();
+ }
+
+ return "Unnamed service selector";
+ }
+
+ /**
+ * Get the name for component-instance elements (i.e. components not
defined
+ * by their role shortcut. If <code>null</code>, any element having a
'class'
+ * attribute will be considered as a component instance.
+ * <p>
+ * The default here is to return <code>null</code>, and subclasses can
redefine
+ * this method to return particular values.
+ *
+ * @return <code>null</code>, but can be changed by subclasses
+ */
+ protected String getComponentInstanceName() {
+ return null;
+ }
+
+ /**
+ * Get the name of the attribute giving the class name of a component.
+ * The default here is "class", but this can be overriden in subclasses.
+ *
+ * @return "<code>class</code>", but can be changed by subclasses
+ */
+ protected String getClassAttributeName() {
+ return "class";
+ }
+
+ /**
+ * Get the name of the attribute giving the default hint to use if
+ * none is given. The default here is "default", but this can be
+ * overriden in subclasses. If this method returns <code>null</code>,
+ * no default hint can be specified.
+ *
+ * @return "<code>default</code>", but can be changed by subclasses
+ */
+ protected String getDefaultHintAttributeName() {
+ return "default";
+ }
+
+ /**
+ * Get the role name for this selector. This is called by
<code>configure()</code>
+ * to set the value of <code>this.roleName</code>.
+ *
+ * @return the role name, or <code>null<code> if it couldn't be determined.
+ */
+ protected String getRoleName(Configuration config) {
+ // Get the role for this selector
+ String roleName = config.getAttribute("role", null);
+ if (roleName == null && this.roleManager != null) {
+ roleName = this.roleManager.getRoleForName(config.getName());
+ }
+
+ return roleName;
+ }
+
+ /**
+ * Get the default hint, if any for this selector.
+ */
+ public String getDefaultHint() {
+ return this.defaultHint;
+ }
+
+ /**
+ * Does this selector declare a given hint? Check is performed on the
components declared for this
+ * selector only, and <strong>not</strong> those potentially inherited
from the parent selector.
+ *
+ * @param hint the hint to check for
+ * @return <code>true</code> if this selector has the specified hint
+ */
+ protected boolean hasDeclaredComponent(Object hint) {
+ if (hint == null) {
+ hint = this.defaultHint;
+ }
+
+ return this.isSelectable(hint);
+ }
+
+ /**
+ * Set the ComponentLocatorImpl that allows access to a possible
+ * parent of this selector
+ * @param locator
+ * @throws ComponentException
+ */
+ public void setParentLocator(ServiceManager locator, String role)
+ throws ServiceException {
+ if (this.parentSelector != null) {
+ throw new ServiceException(null, "Parent selector is already set");
+ }
+ this.parentLocator = locator;
+
+ // Get the parent, unwrapping it as far as needed
+ Object parent = locator.lookup(role);
+
+ if (parent instanceof CocoonServiceSelector) {
+ this.parentSelector = (CocoonServiceSelector)parent;
+ } else {
+ throw new IllegalArgumentException("Parent selector is not an
extended component selector (" + parent + ")");
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see
org.apache.avalon.excalibur.component.ExcaliburComponentSelector#canRelease(org.apache.avalon.framework.component.Component)
+ */
+ protected boolean canRelease(Object component) {
+ if ( this.parentSelector != null &&
+ this.parentSelector.canRelease(component) ) {
+ return true;
+ }
+ return this.componentMapping.containsKey( component );
+ }
+
+
+
+}
Added:
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentFactory.java
==============================================================================
--- (empty file)
+++
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentFactory.java
Tue Oct 19 12:10:38 2004
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import org.apache.avalon.excalibur.logger.LoggerManager;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.container.ContainerUtil;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.LogEnabled;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.parameters.Parameterizable;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+
+/**
+ * Factory for Avalon based components.
+ *
+ * @version CVS $Revision: 1.6 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class ComponentFactory {
+
+ /** The class which this <code>ComponentFactory</code>
+ * should create.
+ */
+ private final Class componentClass;
+
+ /** The Context for the component
+ */
+ private final Context context;
+
+ /** The service manager for this component
+ */
+ private final ServiceManager serviceManager;
+
+ /** The configuration for this component.
+ */
+ private final Configuration configuration;
+
+ /** The parameters for this component
+ */
+ private Parameters parameters;
+
+ private final Logger logger;
+
+ private final String role;
+
+ private final LoggerManager loggerManager;
+
+ private final RoleManager roleManager;
+
+ /**
+ * Construct a new component factory for the specified component.
+ *
+ * @param componentClass the class to instantiate (must have a default
constructor).
+ * @param configuration the <code>Configuration</code> object to pass to
new instances.
+ * @param componentManager the component manager to pass to
<code>Composable</code>s.
+ * @param context the <code>Context</code> to pass to
<code>Contexutalizable</code>s.
+ *
+ */
+ public ComponentFactory( final String role,
+ final Class componentClass,
+ final Configuration configuration,
+ final ServiceManager serviceManager,
+ final Context context,
+ final Logger logger,
+ final LoggerManager loggerManager,
+ final RoleManager roleManager) {
+ this.role = role;
+ this.componentClass = componentClass;
+ this.configuration = configuration;
+ this.serviceManager = serviceManager;
+ this.context = context;
+ this.logger = logger;
+ this.loggerManager = loggerManager;
+ this.roleManager = roleManager;
+ }
+
+ /**
+ * Create a new instance
+ */
+ public Object newInstance()
+ throws Exception {
+ final Object component = this.componentClass.newInstance();
+
+ if( this.logger.isDebugEnabled() ) {
+ this.logger.debug( "ComponentFactory creating new instance of " +
+ this.componentClass.getName() + "." );
+ }
+
+ if ( component instanceof LogEnabled ) {
+ if( null == this.configuration ) {
+ ContainerUtil.enableLogging( component, this.logger );
+ } else {
+ final String logger = this.configuration.getAttribute(
"logger", null );
+ if( null == logger ) {
+ this.logger.debug( "no logger attribute available, using
standard logger" );
+ ContainerUtil.enableLogging( component, this.logger );
+ } else {
+ this.logger.debug( "logger attribute is " + this.logger );
+ ContainerUtil.enableLogging( component,
this.loggerManager.getLoggerForCategory( logger ) );
+ }
+ }
+ }
+
+
+ if( component instanceof Contextualizable ) {
+ ContainerUtil.contextualize( component, this.context );
+ }
+
+ if( component instanceof Serviceable ) {
+ ContainerUtil.service( component, this.serviceManager );
+ }
+
+ if ( component instanceof CocoonServiceSelector ) {
+
((CocoonServiceSelector)component).setLoggerManager(this.loggerManager);
+
((CocoonServiceSelector)component).setRoleManager(this.roleManager);
+ }
+
+ ContainerUtil.configure( component, this.configuration );
+
+ if( component instanceof Parameterizable ) {
+ if ( this.parameters == null ) {
+ this.parameters = Parameters.fromConfiguration(
this.configuration );
+ }
+ ContainerUtil.parameterize( component, this.parameters );
+ }
+
+ ContainerUtil.initialize( component );
+
+ ContainerUtil.start( component );
+
+ return component;
+ }
+
+ public Class getCreatedClass() {
+ return this.componentClass;
+ }
+
+ /**
+ * Destroy an instance
+ */
+ public void decommission( final Object component )
+ throws Exception {
+ if( this.logger.isDebugEnabled() ) {
+ this.logger.debug( "ComponentFactory decommissioning instance of "
+
+ this.componentClass.getName() + "." );
+ }
+
+ ContainerUtil.stop( component );
+ ContainerUtil.dispose( component );
+ }
+
+}
Added:
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentsSelector.java
==============================================================================
--- (empty file)
+++
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentsSelector.java
Tue Oct 19 12:10:38 2004
@@ -0,0 +1,257 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.avalon.framework.CascadingRuntimeException;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.cocoon.acting.Action;
+import org.apache.cocoon.components.pipeline.ProcessingPipeline;
+import org.apache.cocoon.generation.Generator;
+import org.apache.cocoon.generation.GeneratorFactory;
+import org.apache.cocoon.matching.Matcher;
+import org.apache.cocoon.reading.Reader;
+import org.apache.cocoon.selection.Selector;
+import org.apache.cocoon.serialization.Serializer;
+import org.apache.cocoon.serialization.SerializerFactory;
+import org.apache.cocoon.transformation.Transformer;
+import org.apache.cocoon.transformation.TransformerFactory;
+
+/**
+ * Component selector for sitemap components.
+ *
+ * @version CVS $Id: ComponentsSelector.java 47303 2004-09-27 16:21:30Z
vgritsenko $
+ */
+public class ComponentsSelector extends CocoonServiceSelector {
+
+ public static final int UNKNOWN = -1;
+ public static final int GENERATOR = 0;
+ public static final int TRANSFORMER = 1;
+ public static final int SERIALIZER = 2;
+ public static final int READER = 3;
+ public static final int MATCHER = 4;
+ public static final int SELECTOR = 5;
+ public static final int ACTION = 6;
+ public static final int PIPELINE = 7;
+
+ public static final String[] SELECTOR_ROLES = {
+ Generator.ROLE + "Selector",
+ Transformer.ROLE + "Selector",
+ Serializer.ROLE + "Selector",
+ Reader.ROLE + "Selector",
+ Matcher.ROLE + "Selector",
+ Selector.ROLE + "Selector",
+ Action.ROLE + "Selector",
+ ProcessingPipeline.ROLE + "Selector"
+ };
+
+ public static final String[] COMPONENT_NAMES = {
+ "generator",
+ "transformer",
+ "serializer",
+ "reader",
+ "matcher",
+ "selector",
+ "action",
+ "pipe"
+ };
+
+ /** The role as an integer */
+ private int roleId;
+
+ /** The set of known hints, used to add standard components (see
ensureExists) */
+ private Set knownHints = new HashSet();
+
+
+ /**
+ * Return the component instance name according to the selector role
+ * (e.g. "action" for "org.apache.cocoon.acting.Action").
+ */
+ protected String getComponentInstanceName() {
+ return (this.roleId == UNKNOWN) ? null : COMPONENT_NAMES[this.roleId];
+ }
+
+ /**
+ * Get the attribute for class names. This is "src" for known roles, and
+ * "class" (the default) for other roles.
+ */
+ protected String getClassAttributeName() {
+ return (this.roleId == UNKNOWN) ? "class" : "src";
+ }
+
+ public void configure(Configuration config) throws ConfigurationException {
+ // Who are we ?
+ String role = getRoleName(config);
+ this.roleId = UNKNOWN; // unknown
+ for (int i = 0; i < SELECTOR_ROLES.length; i++) {
+ if (SELECTOR_ROLES[i].equals(role)) {
+ this.roleId = i;
+ break;
+ }
+ }
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Setting up sitemap component selector for " +
+ role + " (role id = " + this.roleId + ")");
+ }
+
+ super.configure(config);
+ }
+
+ /**
+ * Add a component in this selector.
+ */
+ public void addComponent(Object hint, Class clazz, Configuration config)
throws ServiceException {
+ super.addComponent(hint, clazz, config);
+
+ // Add to known hints. This is needed as we cannot call isSelectable()
if initialize()
+ // has not been called, and we cannot add components once it has been
called...
+ this.knownHints.add(hint);
+ }
+
+ /**
+ * Ensure system-defined components exist (e.g. <aggregator>) and
initialize
+ * the selector.
+ */
+ public void initialize() /* throws Exception */ {
+ // FIXME : need to catch exceptions since ECS doesn't propagate the
throws clause of Initializable
+ try {
+ DefaultConfiguration config = null;
+
+ // Ensure all system-defined hints exist.
+ // NOTE : checking this here means they can be user-defined in the
sitemap
+ switch(this.roleId) {
+ case GENERATOR :
+ config = new
DefaultConfiguration(COMPONENT_NAMES[GENERATOR], "autogenerated");
+ config.setAttribute("name", "<notifier>");
+ ensureExists("<notifier>",
+
org.apache.cocoon.sitemap.NotifyingGenerator.class, config);
+
+ config = new
DefaultConfiguration(COMPONENT_NAMES[GENERATOR], "autogenerated");
+ config.setAttribute("name", "<aggregator>");
+ ensureExists("<aggregator>",
+
org.apache.cocoon.sitemap.ContentAggregator.class, config);
+ break;
+
+ case TRANSFORMER :
+ config = new
DefaultConfiguration(COMPONENT_NAMES[TRANSFORMER], "autogenerated");
+ config.setAttribute("name", "<translator>");
+ ensureExists("<translator>",
+
org.apache.cocoon.sitemap.LinkTranslator.class, config);
+
+ config = new
DefaultConfiguration(COMPONENT_NAMES[TRANSFORMER], "autogenerated");
+ config.setAttribute("name", "<gatherer>");
+ ensureExists("<gatherer>",
+ org.apache.cocoon.sitemap.LinkGatherer.class,
config);
+ break;
+ }
+
+ super.initialize();
+
+ // Don't keep known hints (they're no more needed)
+ this.knownHints = null;
+ } catch (Exception e) {
+ throw new CascadingRuntimeException("Cannot setup default
components", e);
+ }
+ }
+
+ /**
+ * Ensure a component exists or add it otherwhise. We cannot simply call
hasComponent()
+ * since it requires to be initialized, and we want to add components, and
this must
+ * be done before initialization.
+ */
+ private void ensureExists(Object hint, Class clazz, Configuration config)
throws ServiceException {
+ if (!this.knownHints.contains(hint)) {
+ if (this.parentSelector == null ||
!this.parentSelector.isSelectable(hint)) {
+ addComponent(hint, clazz, config);
+ }
+ }
+ }
+
+ /**
+ * Override parent to implement support for [EMAIL PROTECTED]
GeneratorFactory},
+ * [EMAIL PROTECTED] TransformerFactory}, and [EMAIL PROTECTED]
SerializerFactory}.
+ */
+ public Object select(Object hint) throws ServiceException {
+ final Object component = super.select(hint);
+
+ switch (this.roleId) {
+ case GENERATOR:
+ if (component instanceof GeneratorFactory) {
+ return ((GeneratorFactory)component).getInstance();
+ }
+ break;
+ case TRANSFORMER:
+ if (component instanceof TransformerFactory) {
+ return ((TransformerFactory)component).getInstance();
+ }
+ break;
+ case SERIALIZER:
+ if (component instanceof SerializerFactory) {
+ return ((SerializerFactory)component).getInstance();
+ }
+ break;
+ }
+
+ return component;
+ }
+
+ /**
+ * Override parent to implement support for [EMAIL PROTECTED]
GeneratorFactory},
+ * [EMAIL PROTECTED] TransformerFactory}, and [EMAIL PROTECTED]
SerializerFactory}.
+ */
+ public void release(Object component) {
+
+ // If component is an Instance returned by Factory, get the Factory.
+ switch (this.roleId) {
+ case GENERATOR:
+ if (component instanceof GeneratorFactory.Instance) {
+ // Dispose component, if needed
+ if (component instanceof Disposable) {
+ ((Disposable)component).dispose();
+ }
+ component =
((GeneratorFactory.Instance)component).getFactory();
+ }
+ break;
+ case TRANSFORMER:
+ if (component instanceof TransformerFactory.Instance) {
+ // Dispose component, if needed
+ if (component instanceof Disposable) {
+ ((Disposable)component).dispose();
+ }
+ component =
((TransformerFactory.Instance)component).getFactory();
+ }
+ break;
+ case SERIALIZER:
+ if (component instanceof SerializerFactory.Instance) {
+ // Dispose component, if needed
+ if (component instanceof Disposable) {
+ ((Disposable)component).dispose();
+ }
+ component =
((SerializerFactory.Instance)component).getFactory();
+ }
+ break;
+ }
+
+ super.release(component);
+ }
+}
Added:
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/PoolableComponentHandler.java
==============================================================================
--- (empty file)
+++
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/PoolableComponentHandler.java
Tue Oct 19 12:10:38 2004
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.apache.avalon.excalibur.pool.Poolable;
+import org.apache.avalon.excalibur.pool.Recyclable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.logger.Logger;
+
+/**
+ * The PoolableComponentHandler to make sure that poolable components are
initialized
+ * destroyed and pooled correctly.
+ * <p>
+ * Components which implement Poolable may be configured to be pooled using
the following
+ * example configuration. This example assumes that the user component class
MyComp
+ * implements Poolable.
+ * <p>
+ * Configuration Example:
+ * <pre>
+ * <my-comp pool-max="8"/>
+ * </pre>
+ * <p>
+ * Roles Example:
+ * <pre>
+ * <role name="com.mypkg.MyComponent"
+ * shorthand="my-comp"
+ * default-class="com.mypkg.DefaultMyComponent"/>
+ * </pre>
+ * <p>
+ * Configuration Attributes:
+ * <ul>
+ * <li>The <code>pool-max</code> attribute is used to set the maximum number
of components which
+ * will be pooled. (Defaults to "8") If additional instances are required,
they're created,
+ * but not pooled.</li>
+ * </ul>
+ *
+ * @version CVS $Revision: 1.5 $ $Date: 2004/03/30 14:15:23 $
+ */
+public class PoolableComponentHandler
+extends AbstractComponentHandler {
+
+ /** The default max size of the pool */
+ public static final int DEFAULT_MAX_POOL_SIZE = 8;
+
+ /**
+ * Object used to synchronize access to the get and put methods
+ */
+ protected final Object semaphore = new Object();
+
+ /**
+ * The maximum size of the pool.
+ */
+ private final int max;
+
+ /**
+ * List of the Poolable instances which are available for use.
+ */
+ private LinkedList ready;
+
+ /**
+ * Store the size of the ready list to optimize operations which require
this value.
+ */
+ private int readySize;
+
+ /**
+ * Total number of Poolable instances in the pool
+ */
+ private int size;
+
+ /**
+ * Create a PoolableComponentHandler which manages a pool of Components
+ * created by the specified factory object.
+ *
+ * @param factory The factory object which is responsible for creating the
components
+ * managed by the ComponentHandler.
+ * @param config The configuration to use to configure the pool.
+ */
+ public PoolableComponentHandler( final Logger logger,
+ final ComponentFactory factory,
+ final Configuration config )
+ throws Exception {
+ super(logger, factory);
+
+ final int poolMax = config.getAttributeAsInteger( "pool-max",
DEFAULT_MAX_POOL_SIZE );
+ this.max = ( poolMax <= 0 ? Integer.MAX_VALUE : poolMax );
+
+ // Create the pool lists.
+ this.ready = new LinkedList();
+ }
+
+ /**
+ * Dispose of the ComponentHandler and any associated Pools and Factories.
+ */
+ public void dispose() {
+ super.dispose();
+
+ // Any Poolables in the m_ready list need to be disposed of
+ synchronized( this.semaphore ) {
+ // Remove objects in the ready list.
+ for( Iterator iter = this.ready.iterator(); iter.hasNext(); ) {
+ Poolable poolable = (Poolable)iter.next();
+ iter.remove();
+ this.readySize--;
+ permanentlyRemovePoolable( poolable );
+ }
+
+ if( ( this.size > 0 ) && this.logger.isDebugEnabled() ) {
+ this.logger.debug( "There were " + this.size
+ + " outstanding objects when the pool was
disposed." );
+ }
+ }
+ }
+
+ /**
+ * Permanently removes a poolable from the pool's active list and
+ * destroys it so that it will not ever be reused.
+ * <p>
+ * This method is only called by threads that have m_semaphore locked.
+ */
+ protected void permanentlyRemovePoolable( Object poolable ) {
+ this.size--;
+ this.removePoolable( poolable );
+ }
+
+ /**
+ * Called when an object is being removed permanently from the pool.
+ * This is the method to override when you need to enforce destructional
+ * policies.
+ * <p>
+ * This method is only called by threads that have m_semaphore locked.
+ *
+ * @param poolable Poolable to be completely removed from the pool.
+ */
+ protected void removePoolable( Object poolable ) {
+ try {
+ this.factory.decommission( poolable );
+ } catch( Exception e ) {
+ if( this.logger.isDebugEnabled() ) {
+ this.logger.debug( "Error decommissioning object", e );
+ }
+ }
+ }
+
+ /**
+ * Gets a Poolable from the pool. If there is room in the pool, a new
Poolable will be
+ * created. Depending on the parameters to the constructor, the method
may block or throw
+ * an exception if a Poolable is not available on the pool.
+ *
+ * @return Always returns a Poolable. Contract requires that put must
always be called with
+ * the Poolable returned.
+ * @throws Exception An exception may be thrown as described above or if
there is an exception
+ * thrown by the ObjectFactory's newInstance() method.
+ */
+ protected Object doGet() throws Exception {
+ Object poolable;
+ synchronized( this.semaphore ) {
+ // Look for a Poolable at the end of the m_ready list
+ if( this.readySize > 0 ){
+ // A poolable is ready and waiting in the pool
+ poolable = (Poolable)this.ready.removeLast();
+ this.readySize--;
+ } else {
+ // Create a new poolable. May throw an exception if the
poolable can not be
+ // instantiated.
+ poolable = this.factory.newInstance();
+ this.size++;
+
+ if( this.logger.isDebugEnabled() ) {
+ this.logger.debug( "Created a new " +
poolable.getClass().getName()
+ + " from the object factory." );
+ }
+ }
+ }
+
+ if( this.logger.isDebugEnabled() ) {
+ this.logger.debug( "Got a " + poolable.getClass().getName() + "
from the pool." );
+ }
+
+ return poolable;
+ }
+
+ /**
+ * Returns a poolable to the pool
+ *
+ * @param poolable Poolable to return to the pool.
+ */
+ protected void doPut( final Object poolable ) {
+ // Handle Recyclable objects
+ if( poolable instanceof Recyclable ) {
+ ( (Recyclable)poolable ).recycle();
+ }
+
+ synchronized( this.semaphore ) {
+ if( this.size <= this.max )
+ {
+ if( this.disposed ) {
+ // The pool has already been disposed.
+ if( this.logger.isDebugEnabled() ) {
+ this.logger.debug( "Put called for a " +
poolable.getClass().getName()
+ + " after the pool was disposed." );
+ }
+
+ this.permanentlyRemovePoolable( poolable );
+ } else {
+ // There is room in the pool to keep this poolable.
+ if( this.logger.isDebugEnabled() ) {
+ this.logger.debug( "Put a " +
poolable.getClass().getName()
+ + " back into the pool." );
+ }
+
+ this.ready.addLast( poolable );
+ this.readySize++;
+
+ }
+ } else {
+ // More Poolables were created than can be held in the pool,
so remove.
+ if( this.logger.isDebugEnabled() ) {
+ this.logger.debug( "No room to put a " +
poolable.getClass().getName()
+ + " back into the pool, so remove it."
);
+ }
+
+ this.permanentlyRemovePoolable( poolable );
+ }
+ }
+ }
+
+
+}
Added:
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/RoleManager.java
==============================================================================
--- (empty file)
+++
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/RoleManager.java
Tue Oct 19 12:10:38 2004
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+
+/**
+ * Default RoleManager implementation. It populates the RoleManager
+ * from a configuration file.
+ *
+ * @version CVS $Revision: 1.4 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class RoleManager
+extends AbstractLogEnabled
+implements Configurable {
+
+ /** Map for shorthand to role mapping */
+ private Map shorthands;
+
+ /** Map for role to default classname mapping */
+ private Map classNames;
+
+ /** Map for role->hint to classname mapping */
+ private Map hintClassNames;
+
+ /** Parent <code>RoleManager</code> for nested resolution */
+ private final RoleManager parent;
+
+ /**
+ * Default constructor--this RoleManager has no parent.
+ */
+ public RoleManager() {
+ this.parent = null;
+ }
+
+ /**
+ * Alternate constructor--this RoleManager has the specified
+ * parent.
+ *
+ * @param parent The parent <code>RoleManager</code>.
+ */
+ public RoleManager( RoleManager parent ) {
+ this.parent = parent;
+ }
+
+ /**
+ * Retrieves the real role name from a shorthand name. Usually
+ * the shorthand name refers to a configuration element name. If
+ * this RoleManager does not have the match, and there is a parent
+ * RoleManager, the parent will be asked to resolve the role.
+ *
+ * @param shorthandName The shortname that is an alias for the role.
+ * @return the official role name.
+ */
+ public final String getRoleForName( final String shorthandName ) {
+ final String role = (String)this.shorthands.get( shorthandName );
+
+ if( null == role && null != this.parent )
+ {
+ return this.parent.getRoleForName( shorthandName );
+ }
+
+ if( this.getLogger().isDebugEnabled() ) {
+ this.getLogger().debug( "looking up shorthand " + shorthandName +
+ ", returning " + role );
+ }
+
+ return role;
+ }
+
+ /**
+ * Retrieves the default class name for the specified role. This
+ * is only called when the configuration does not specify the
+ * class explicitly. If this RoleManager does not have the match,
+ * and there is a parent RoleManager, the parent will be asked
+ * to resolve the class name.
+ *
+ * @param role The role that has a default implementation.
+ * @return the Fully Qualified Class Name (FQCN) for the role.
+ */
+ public final String getDefaultClassNameForRole( final String role ) {
+ final String className = (String)this.classNames.get( role );
+
+ if( null == className && null != this.parent )
+ {
+ return this.parent.getDefaultClassNameForRole( role );
+ }
+
+ return className;
+ }
+
+ /**
+ * Retrieves a default class name for a role/hint combination.
+ * This is only called when a role is mapped to a
+ * DefaultComponentSelector, and the configuration elements use
+ * shorthand names for the type of component. If this RoleManager
+ * does not have the match, and there is a parent RoleManager, the
+ * parent will be asked to resolve the class name.
+ *
+ * @param role The role that this shorthand refers to.
+ * @param shorthand The shorthand name for the type of Component
+ * @return the FQCN for the role/hint combination.
+ */
+ public final String getDefaultClassNameForHint( final String role,
+ final String shorthand ) {
+ if( getLogger().isDebugEnabled() ) {
+ getLogger().debug( "looking up hintmap for role " + role );
+ }
+
+ final Map hintMap = (Map)this.hintClassNames.get( role );
+
+ if( null == hintMap ) {
+ if( null != this.parent ) {
+ return this.parent.getDefaultClassNameForHint( role, shorthand
);
+ } else {
+ return "";
+ }
+ }
+
+ if( getLogger().isDebugEnabled() ) {
+ getLogger().debug( "looking up classname for hint " + shorthand );
+ }
+
+ final String s = ( String ) hintMap.get( shorthand );
+
+ if( s == null && null != this.parent ) {
+ return this.parent.getDefaultClassNameForHint( role, shorthand );
+ } else {
+ return s;
+ }
+ }
+
+ /**
+ * Reads a configuration object and creates the role, shorthand,
+ * and class name mapping.
+ *
+ * @param configuration The configuration object.
+ * @throws ConfigurationException if the configuration is malformed
+ */
+ public final void configure( final Configuration configuration )
+ throws ConfigurationException {
+ final Map shorts = new HashMap();
+ final Map classes = new HashMap();
+ final Map hintclasses = new HashMap();
+
+ final Configuration[] roles = configuration.getChildren( "role" );
+
+ for( int i = 0; i < roles.length; i++ ) {
+ final String name = roles[ i ].getAttribute( "name" );
+ final String shorthand = roles[ i ].getAttribute( "shorthand" );
+ final String defaultClassName =
+ roles[ i ].getAttribute( "default-class", null );
+
+ shorts.put( shorthand, name );
+
+ if( null != defaultClassName ) {
+ classes.put( name, defaultClassName );
+ }
+
+ final Configuration[] hints = roles[ i ].getChildren( "hint" );
+ if( hints.length > 0 ) {
+ HashMap hintMap = new HashMap();
+
+ for( int j = 0; j < hints.length; j++ ) {
+ final String shortHand = hints[ j ].getAttribute(
"shorthand" ).trim();
+ String className = hints[ j ].getAttribute( "class"
).trim();
+
+ if (
"org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) {
+ className = CocoonServiceSelector.class.getName();
+ } else if
("org.apache.cocoon.components.treeprocessor.sitemap.ComponentsSelector".equals(className)
) {
+ className = ComponentsSelector.class.getName();
+ }
+ hintMap.put( shortHand, className );
+ if( getLogger().isDebugEnabled() ) {
+ getLogger().debug( "Adding hint type " + shortHand +
+ " associated with role " + name +
+ " and class " + className );
+ }
+ }
+
+ hintclasses.put( name, Collections.unmodifiableMap( hintMap )
);
+ }
+
+ if( getLogger().isDebugEnabled() ) {
+ getLogger().debug( "added Role " + name + " with shorthand " +
+ shorthand + " for " + defaultClassName );
+ }
+ }
+
+ this.shorthands = Collections.unmodifiableMap( shorts );
+ this.classNames = Collections.unmodifiableMap( classes );
+ this.hintClassNames = Collections.unmodifiableMap( hintclasses );
+ }
+}
Added:
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/SingleThreadedComponentHandler.java
==============================================================================
--- (empty file)
+++
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/SingleThreadedComponentHandler.java
Tue Oct 19 12:10:38 2004
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import org.apache.avalon.framework.logger.Logger;
+
+/**
+ * The DefaultComponentHandler to make sure components are initialized
+ * and destroyed correctly.
+ *
+ * @version CVS $Revision: 1.4 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class SingleThreadedComponentHandler
+extends AbstractComponentHandler {
+
+ /**
+ * Create a DefaultComponentHandler which manages a pool of Components
+ * created by the specified factory object.
+ *
+ * @param factory The factory object which is responsible for creating the
components
+ * managed by the ComponentHandler.
+ * @param config The configuration to use to configure the pool.
+ *
+ * @throws Exception If there are any problems creating the handler.
+ */
+ public SingleThreadedComponentHandler( final Logger logger,
+ final ComponentFactory factory ) {
+ super(logger, factory);
+ }
+
+ /**
+ * Get a reference of the desired Component
+ *
+ * @return A component instance.
+ *
+ * @throws Exception If there are any problems encountered acquiring a
+ * component instance.
+ */
+ protected Object doGet()
+ throws Exception {
+ return this.factory.newInstance();
+ }
+
+ /**
+ * Return a reference of the desired Component
+ *
+ * @param component Component to be be put/released back to the handler.
+ */
+ protected void doPut( final Object component ) {
+ try {
+ this.factory.decommission( component );
+ } catch( final Exception e ) {
+ if( this.logger.isWarnEnabled() )
+ {
+ this.logger.warn( "Error decommissioning component: "
+ + this.factory.getCreatedClass().getName(), e );
+ }
+ }
+ }
+
+}
Added:
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ThreadSafeComponentHandler.java
==============================================================================
--- (empty file)
+++
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ThreadSafeComponentHandler.java
Tue Oct 19 12:10:38 2004
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import org.apache.avalon.framework.logger.Logger;
+
+/**
+ * The ThreadSafeComponentHandler to make sure components are initialized
+ * and destroyed correctly.
+ *
+ * @version CVS $Revision: 1.4 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class ThreadSafeComponentHandler
+extends AbstractComponentHandler {
+
+ private Object instance;
+
+ /**
+ * Create a ThreadSafeComponentHandler which manages a single instance
+ * of an object return by the component factory.
+ * @param logger The logger to use
+ * @param factory The factory object which is responsible for creating the
components
+ * managed by the handler.
+ */
+ public ThreadSafeComponentHandler( final Logger logger,
+ final ComponentFactory factory ) {
+ super(logger, factory);
+ }
+
+ public void initialize()
+ throws Exception {
+ if( this.initialized ) {
+ return;
+ }
+ if( this.instance == null ) {
+ this.instance = this.factory.newInstance();
+ }
+ super.initialize();
+ }
+
+ /**
+ * Get a reference of the desired Component
+ */
+ protected Object doGet()
+ throws Exception {
+ return this.instance;
+ }
+
+ /**
+ * Return a reference of the desired Component
+ */
+ protected void doPut( final Object component ) {
+ // nothing to do
+ }
+
+ /**
+ * Dispose of the ComponentHandler and any associated Pools and Factories.
+ */
+ public void dispose() {
+ try {
+ this.factory.decommission( this.instance );
+ this.instance = null;
+ } catch( final Exception e ) {
+ if( this.logger.isWarnEnabled() ) {
+ this.logger.warn( "Error decommissioning component: " +
+ this.factory.getCreatedClass().getName(), e
);
+ }
+ }
+ super.dispose();
+ }
+}