| /* |
| * Copyright 2002-2005 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.core.container.handler; |
| |
| 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; |
| import org.apache.cocoon.components.ComponentInfo; |
| import org.apache.cocoon.core.container.ComponentEnvironment; |
| import org.apache.cocoon.core.container.ComponentFactory; |
| import org.apache.cocoon.core.container.DefaultServiceSelector; |
| import org.apache.cocoon.core.container.StandaloneServiceSelector; |
| import org.apache.cocoon.util.JMXUtils; |
| |
| /** |
| * This class acts like a Factory to instantiate the correct version |
| * of the component handler that you need. |
| * |
| * @version $Id$ |
| * @since 2.2 |
| */ |
| public abstract class AbstractComponentHandler |
| implements ComponentHandler { |
| |
| private final Object referenceSemaphore = new Object(); |
| private int references = 0; |
| |
| protected final Logger logger; |
| |
| /** 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 */ |
| private boolean initialized = false; |
| |
| /** Information about the component */ |
| private ComponentInfo info; |
| |
| /** |
| * Looks up and returns a component handler for a given component class. |
| * |
| * @param role the component's role. Can be <code>null</code> if the role isn't known. |
| * @param componentEnv The component's creation environment. |
| * @param info The description of the component (configuration, lifecycle etc.) |
| * |
| * @throws Exception If there were any problems obtaining a ComponentHandler |
| */ |
| public static ComponentHandler getComponentHandler(String role, |
| ComponentEnvironment componentEnv, |
| ComponentInfo info) |
| throws Exception { |
| |
| // Load the class |
| Class componentClass; |
| |
| try { |
| componentClass = componentEnv.loadClass(info.getServiceClassName()); |
| } catch (ClassNotFoundException cnfe) { |
| throw new Exception("Cannot find class " + info.getServiceClassName() + " for component at " + |
| info.getConfiguration().getLocation(), cnfe); |
| } |
| |
| int numInterfaces = 0; |
| |
| // 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."); |
| } |
| |
| if( SingleThreaded.class.isAssignableFrom( componentClass ) ) { |
| numInterfaces++; |
| info.setModel(ComponentInfo.MODEL_PRIMITIVE); |
| } |
| |
| if( ThreadSafe.class.isAssignableFrom( componentClass ) ) { |
| numInterfaces++; |
| info.setModel(ComponentInfo.MODEL_SINGLETON); |
| } |
| |
| if( Poolable.class.isAssignableFrom( componentClass ) ) { |
| numInterfaces++; |
| if ( info.getModel() != ComponentInfo.MODEL_NON_THREAD_SAFE_POOLED ) { |
| info.setModel(ComponentInfo.MODEL_POOLED); |
| if ( ComponentInfo.TYPE_NON_THREAD_SAFE_POOLED.equals(info.getConfiguration().getAttribute("model", null))) { |
| info.setModel(ComponentInfo.MODEL_NON_THREAD_SAFE_POOLED); |
| } |
| } |
| } |
| |
| 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" ); |
| } |
| |
| if ( numInterfaces == 0 ) { |
| // this component does not use avalon interfaces, so get the info from the configuration |
| info.fill(info.getConfiguration()); |
| } |
| info.setRole(role); |
| |
| // Create the factory to use to create the instances of the Component. |
| ComponentFactory factory; |
| ComponentHandler handler; |
| |
| if (DefaultServiceSelector.class.isAssignableFrom(componentClass)) { |
| // Special factory for DefaultServiceSelector |
| factory = new DefaultServiceSelector.Factory(componentEnv, info, role); |
| handler = new ThreadSafeComponentHandler(info, componentEnv.logger, factory); |
| handler.initialize(); |
| return handler; |
| |
| } else if (StandaloneServiceSelector.class.isAssignableFrom(componentClass)) { |
| // Special factory for StandaloneServiceSelector |
| factory = new StandaloneServiceSelector.Factory(componentEnv, info, role); |
| |
| } else { |
| factory = new ComponentFactory(componentEnv, info); |
| } |
| |
| if( info.getModel() == ComponentInfo.MODEL_NON_THREAD_SAFE_POOLED) { |
| handler = new NonThreadSafePoolableComponentHandler( info, componentEnv.logger, factory, info.getConfiguration() ); |
| } else if( info.getModel() == ComponentInfo.MODEL_POOLED ) { |
| handler = new PoolableComponentHandler( info, componentEnv.logger, factory, info.getConfiguration() ); |
| } else if( info.getModel() == ComponentInfo.MODEL_SINGLETON ) { |
| handler = new ThreadSafeComponentHandler( info, componentEnv.logger, factory ); |
| } else { |
| // This is a SingleThreaded component |
| handler = new SingleThreadedComponentHandler( info, componentEnv.logger, factory ); |
| } |
| |
| return handler; |
| } |
| |
| /** |
| * Creates a new ComponentHandler. |
| */ |
| public AbstractComponentHandler(ComponentInfo info, Logger logger) { |
| this.logger = logger; |
| this.info = info; |
| } |
| |
| public ComponentInfo getInfo() { |
| return this.info; |
| } |
| |
| /** |
| * 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 { |
| initialize(); |
| 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; |
| |
| /** |
| * Default here is to return <code>false</code> |
| */ |
| public boolean isSingleton() { |
| return false; |
| } |
| |
| /** |
| * 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 ); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.core.container.ComponentHandler#dispose() |
| */ |
| public void dispose() { |
| this.disposed = true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.core.container.ComponentHandler#initialize() |
| */ |
| public final void initialize() throws Exception { |
| if( this.initialized ) { |
| return; |
| } |
| |
| doInitialize(); |
| this.initialized = true; |
| } |
| |
| protected abstract void doInitialize() throws Exception; |
| |
| /** |
| * Create a component handler (version used by XSP) |
| * TODO - perhaps we can remove this later? |
| */ |
| public static ComponentHandler getComponentHandler(Class clazz, Logger logger, Context context, ServiceManager manager, Configuration config) throws Exception { |
| ComponentEnvironment env = new ComponentEnvironment(clazz.getClassLoader(), logger, null, null, context, manager); |
| ComponentInfo info = new ComponentInfo(); |
| info.setServiceClassName(clazz.getName()); |
| info.setConfiguration(config); |
| info.setJmxDomain(JMXUtils.findJmxDomain(info.getJmxDomain(), manager)); |
| info.setJmxName(JMXUtils.findJmxName(info.getJmxName(), clazz.getName())); |
| info.setRole("XSP"); |
| return getComponentHandler(null, env, info); |
| |
| } |
| } |