Return-Path: Delivered-To: apmail-cocoon-cvs-archive@www.apache.org Received: (qmail 42524 invoked from network); 3 Nov 2005 15:00:12 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 3 Nov 2005 15:00:12 -0000 Received: (qmail 30911 invoked by uid 500); 3 Nov 2005 14:38:48 -0000 Delivered-To: apmail-cocoon-cvs-archive@cocoon.apache.org Received: (qmail 28102 invoked by uid 500); 3 Nov 2005 14:37:10 -0000 Mailing-List: contact cvs-help@cocoon.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@cocoon.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@cocoon.apache.org Received: (qmail 26310 invoked by uid 99); 3 Nov 2005 14:36:06 -0000 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Thu, 03 Nov 2005 06:35:21 -0800 Received: (qmail 12261 invoked by uid 65534); 3 Nov 2005 14:34:40 -0000 Message-ID: <20051103143440.12251.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r330548 [38/132] - in /cocoon/whiteboard/maven2/cocoon-flat-layout: ./ cocoon-ajax-block/ cocoon-ajax-block/api/ cocoon-ajax-block/api/src/ cocoon-ajax-block/api/src/main/ cocoon-ajax-block/api/src/main/java/ cocoon-ajax-block/api/src/main/... Date: Thu, 03 Nov 2005 14:00:48 -0000 To: cvs@cocoon.apache.org From: jheymans@apache.org X-Mailer: svnmailer-1.0.5 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/WiringNotFoundException.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/WiringNotFoundException.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/WiringNotFoundException.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/WiringNotFoundException.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,31 @@ +/* + * Copyright 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; + +/** + * Throw this exception in the case that the wiring.xml is not found. + * + * @version $Id: WiringNotFoundException.java 312637 2005-10-10 13:00:42Z cziegeler $ + * @since 2.2 + */ +public class WiringNotFoundException extends RuntimeException { + + public WiringNotFoundException(String message) { + super(message); + } + +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/WiringNotFoundException.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/AbstractServiceManager.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/AbstractServiceManager.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/AbstractServiceManager.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/AbstractServiceManager.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,192 @@ +/* + * 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; + +import java.util.Collections; +import java.util.HashMap; +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.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +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.thread.ThreadSafe; +import org.apache.cocoon.components.ComponentInfo; +import org.apache.cocoon.core.container.handler.AbstractComponentHandler; +import org.apache.cocoon.core.container.handler.ComponentHandler; + +/** + * Base class for all service managers: ServiceManager and ServiceSelector + * + * @version $Id: AbstractServiceManager.java 312637 2005-10-10 13:00:42Z cziegeler $ + * @since 2.2 + */ +public abstract class AbstractServiceManager +extends AbstractLogEnabled +implements Contextualizable, ThreadSafe, Disposable, Initializable { + + /** The application context for components */ + protected Context context; + + /** Static component mapping handlers. */ + protected final Map componentMapping = Collections.synchronizedMap(new HashMap()); + + /** Used to map roles to ComponentHandlers. */ + protected final Map componentHandlers = Collections.synchronizedMap(new HashMap()); + + /** Is the Manager disposed or not? */ + protected boolean disposed; + + /** Is the Manager initialized? */ + protected boolean initialized; + + /** RoleInfos. */ + protected RoleManager roleManager; + + /** LoggerManager. */ + protected LoggerManager loggerManager; + + protected ComponentEnvironment componentEnv; + + + /* (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; + } + + 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. + * + * @param role the component's role. + * @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 Component. + * + * @throws Exception If there were any problems obtaining a ComponentHandler + */ + protected ComponentHandler getComponentHandler( final String role, + final Class componentClass, + final Configuration configuration, + final ServiceManager serviceManager, + final ComponentInfo baseInfo) + throws Exception { + if (this.componentEnv == null) { + this.componentEnv = new ComponentEnvironment(null, getLogger(), this.roleManager, + this.loggerManager, this.context, serviceManager); + } + // FIXME - we should always get an info here + ComponentInfo info; + if ( baseInfo != null ) { + info = baseInfo.duplicate(); + } else { + info = new ComponentInfo(); + info.fill(configuration); + } + info.setConfiguration(configuration); + info.setServiceClassName(componentClass.getName()); + + return AbstractComponentHandler.getComponentHandler(role, + this.componentEnv, + info); + } + + protected void addComponent(String className, + String role, + Configuration configuration, + ComponentInfo info) + throws ConfigurationException { + // check for old excalibur class names - we only test against the selector + // implementation + if ( "org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) { + className = DefaultServiceSelector.class.getName(); + } + + try { + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( "Adding component (" + role + " = " + className + ")" ); + } + // FIXME - use different classloader + final Class clazz = this.getClass().getClassLoader().loadClass( className ); + this.addComponent( role, clazz, configuration, info ); + } catch( final ClassNotFoundException cnfe ) { + final String message = "Could not get class (" + className + ") for role " + + role + " at " + configuration.getLocation(); + + if( this.getLogger().isErrorEnabled() ) { + this.getLogger().error( message, cnfe ); + } + + throw new ConfigurationException( message, cnfe ); + } catch( final ServiceException ce ) { + final String message = "Cannot setup class "+ className + " for role " + role + + " at " + configuration.getLocation(); + + if( this.getLogger().isErrorEnabled() ) { + this.getLogger().error( message, ce ); + } + + throw new ConfigurationException( message, ce ); + } catch( final Exception e ) { + final String message = "Unexpected exception when setting up role " + role + " at " + configuration.getLocation(); + if( this.getLogger().isErrorEnabled() ) { + this.getLogger().error( message, e ); + } + throw new ConfigurationException( message, e ); + } + } + + protected abstract void addComponent(String role, + Class clazz, + Configuration config, + ComponentInfo info) + throws ServiceException; + + + /* (non-Javadoc) + * @see org.apache.avalon.framework.activity.Disposable#dispose() + */ + public void dispose() { + this.disposed = true; + } + + /* (non-Javadoc) + * @see org.apache.avalon.framework.activity.Initializable#initialize() + */ + public void initialize() throws Exception { + this.initialized = true; + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/AbstractServiceManager.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentEnvironment.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentEnvironment.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentEnvironment.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentEnvironment.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,95 @@ +/* + * 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; + +import java.io.InputStream; + +import org.apache.avalon.excalibur.logger.LoggerManager; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.context.Context; +import org.apache.avalon.framework.context.ContextException; +import org.apache.avalon.framework.logger.Logger; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.cocoon.components.ComponentInfo; +import org.apache.cocoon.configuration.ConfigurationBuilder; +import org.apache.cocoon.core.Core; +import org.apache.cocoon.core.Settings; + +/** + * The component enviromnent contains all objects necessary to create + * a new component; it's just a "container" of objects. + * + * @version $Id: ComponentEnvironment.java 312637 2005-10-10 13:00:42Z cziegeler $ + * @since 2.2 + */ +public class ComponentEnvironment { + + public final ServiceManager serviceManager; + public final Context context; + public final Logger logger; + public final RoleManager roleManager; + public final LoggerManager loggerManager; + private final ClassLoader classLoader; + private Core core; + + public ComponentEnvironment(ClassLoader classLoader, Logger logger, RoleManager roleManager, LoggerManager loggerManager, + Context context, ServiceManager serviceManager) { + + // Find a class loader + if (classLoader == null) { + classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = this.getClass().getClassLoader(); + } + } + + this.classLoader = classLoader; + this.logger = logger; + this.roleManager = roleManager; + this.loggerManager = loggerManager; + this.context = context; + this.serviceManager = serviceManager; + // FIXME - we should ensure that the context is never null! + if ( this.context != null ) { + try { + this.core = (Core)this.context.get(Core.ROLE); + } catch (ContextException ignore) { + // this can never happen + } + } + } + + public Class loadClass(String name) throws ClassNotFoundException { + return this.classLoader.loadClass(name); + } + + public ComponentInfo loadComponentInfo(String name) + throws Exception { + final StringBuffer bu = new StringBuffer(name); + bu.append(".xconf"); + ComponentInfo ci = null; + final InputStream is = this.classLoader.getResourceAsStream(bu.toString()); + if ( is != null ) { + final Settings settings = (this.core == null ? null : this.core.getSettings()); + final ConfigurationBuilder cb = new ConfigurationBuilder(settings); + final Configuration conf = cb.build(is); + ci = new ComponentInfo(); + ci.fill(conf); + } + return ci; + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentEnvironment.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentFactory.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentFactory.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentFactory.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentFactory.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,214 @@ +/* + * 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; + +import java.lang.reflect.Method; + +import org.apache.avalon.excalibur.pool.Recyclable; +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.avalon.framework.context.ContextException; +import org.apache.avalon.framework.logger.Logger; +import org.apache.avalon.framework.parameters.Parameterizable; +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.cocoon.components.ComponentInfo; +import org.apache.cocoon.core.Core; + +/** + * Factory for Avalon based components. + * + * @version $Id: ComponentFactory.java 312861 2005-10-11 11:46:08Z cziegeler $ + * @since 2.2 + */ +public class ComponentFactory { + + protected final ComponentInfo serviceInfo; + + protected final ComponentEnvironment environment; + + /** + * The component's logger, which may be different from the environment's logger + */ + protected final Logger componentLogger; + + /** The parameters for this component + */ + protected final Parameters parameters; + + protected final Class serviceClass; + protected final Method initMethod; + protected final Method destroyMethod; + protected final Method poolInMethod; + protected final Method poolOutMethod; + protected Method configureSettingsMethod; + protected Core core; + + /** + * Construct a new component factory for the specified component. + * + * @param environment Describes the environment for the component. + * @param info Describes the configuration/settings for the component. + * + */ + public ComponentFactory( final ComponentEnvironment environment, + final ComponentInfo info) + throws Exception { + // FIXME - we should ensure that the context is never null! + if ( environment.context != null ) { + try { + this.core = (Core)environment.context.get(Core.ROLE); + } catch (ContextException ignore) { + // this can never happen + } + } + this.environment = environment; + this.serviceInfo = info; + + // this is our default logger: + Logger actualLogger = this.environment.logger; + final String category = this.serviceInfo.getLoggerCategory(); + if ( category != null ) { + // If the handler is created "manually" (e.g. XSP engine), loggerManager can be null + if( this.environment.loggerManager != null ) { + actualLogger = this.environment.loggerManager.getLoggerForCategory(category); + } + } + this.componentLogger = actualLogger; + + this.serviceClass = this.environment.loadClass(this.serviceInfo.getServiceClassName()); + if ( Parameterizable.class.isAssignableFrom(this.serviceClass) ) { + this.parameters = Parameters.fromConfiguration( this.serviceInfo.getConfiguration() ); + } else { + this.parameters = null; + } + if ( this.serviceInfo.getDestroyMethodName() != null ) { + this.destroyMethod = this.serviceClass.getMethod(this.serviceInfo.getDestroyMethodName(), null); + } else { + this.destroyMethod = null; + } + if ( this.serviceInfo.getInitMethodName() != null ) { + this.initMethod = this.serviceClass.getMethod(this.serviceInfo.getInitMethodName(), null); + } else { + this.initMethod = null; + } + if ( this.serviceInfo.getPoolInMethodName() != null ) { + this.poolInMethod = this.serviceClass.getMethod(this.serviceInfo.getPoolInMethodName(), null); + } else { + this.poolInMethod = null; + } + if ( this.serviceInfo.getPoolOutMethodName() != null ) { + this.poolOutMethod = this.serviceClass.getMethod(this.serviceInfo.getPoolOutMethodName(), null); + } else { + this.poolOutMethod = null; + } + try { + this.configureSettingsMethod = this.serviceClass.getMethod("configure", new Class[] {Core.class}); + } catch (Throwable ignore) { + // we have to catch throwable here, as the above test can + // result in NoClassDefFound exceptions etc. + this.configureSettingsMethod = null; + } + } + + /** + * Create a new instance + */ + public final Object newInstance() + throws Exception { + final Object component = this.serviceClass.newInstance(); + + setupInstance(component); + return component; + } + + /** + * Invoke the various lifecycle interfaces to setup a newly created component + * @param component + * @throws Exception + */ + protected void setupInstance(Object component) throws Exception { + if( this.environment.logger.isDebugEnabled() ) { + this.environment.logger.debug( "ComponentFactory creating new instance of " + + this.serviceClass.getName() + "." ); + } + + ContainerUtil.enableLogging(component, this.componentLogger); + ContainerUtil.contextualize( component, this.environment.context ); + ContainerUtil.service( component, this.environment.serviceManager ); + if ( this.configureSettingsMethod != null && this.core != null) { + this.configureSettingsMethod.invoke( component, new Object[] {this.core}); + } + ContainerUtil.configure( component, this.serviceInfo.getConfiguration() ); + + if( component instanceof Parameterizable ) { + ContainerUtil.parameterize( component, this.parameters ); + } + + ContainerUtil.initialize( component ); + + if ( this.initMethod != null ) { + this.initMethod.invoke(component, null); + } + + ContainerUtil.start( component ); + } + + public Class getCreatedClass() { + return this.serviceClass; + } + + /** + * Destroy an instance + */ + public void decommission( final Object component ) + throws Exception { + if( this.environment.logger.isDebugEnabled() ) { + this.environment.logger.debug( "ComponentFactory decommissioning instance of " + + this.serviceClass.getName() + "." ); + } + + ContainerUtil.stop( component ); + ContainerUtil.dispose( component ); + + if ( this.destroyMethod != null ) { + this.destroyMethod.invoke(component, null); + } + } + + /** + * Handle service specific methods for getting it out of the pool + */ + public void exitingPool( final Object component ) + throws Exception { + if ( this.poolOutMethod != null ) { + this.poolOutMethod.invoke(component, null); + } + } + + /** + * Handle service specific methods for putting it into the pool + */ + public void enteringPool( final Object component ) + throws Exception { + // Handle Recyclable objects + if( component instanceof Recyclable ) { + ( (Recyclable)component ).recycle(); + } + if ( this.poolInMethod != null ) { + this.poolInMethod.invoke(component, null); + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentFactory.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentLocatorWrapper.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentLocatorWrapper.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentLocatorWrapper.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentLocatorWrapper.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,65 @@ +/* + * Copyright 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; + +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.cocoon.ProcessingException; +import org.apache.cocoon.sitemap.ComponentLocator; + +/** + * Wrapper for a service manager. + * + * @version Id: ComponentLocatorWrapper.java 179038 2005-05-30 08:19:24Z cziegeler $ + * @since 2.2 + */ +final public class ComponentLocatorWrapper +implements ComponentLocator { + + protected final ServiceManager manager; + + public ComponentLocatorWrapper(ServiceManager m) { + this.manager = m; + } + + /** + * @see org.apache.cocoon.sitemap.ComponentLocator#hasComponent(java.lang.String) + */ + public boolean hasComponent(String key) { + return this.manager.hasService(key); + } + + /** + * @see org.apache.cocoon.sitemap.ComponentLocator#getComponent(java.lang.String) + */ + public Object getComponent(String key) + throws ProcessingException { + try { + return this.manager.lookup(key); + } catch (ServiceException se) { + throw new ProcessingException("Unable to lookup component for key: " + key, se); + } + } + + /** + * @see org.apache.cocoon.sitemap.ComponentLocator#release(java.lang.Object) + */ + public void release(Object component) { + this.manager.release(component); + } + +} \ No newline at end of file Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/ComponentLocatorWrapper.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/CoreServiceManager.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/CoreServiceManager.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/CoreServiceManager.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/CoreServiceManager.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,885 @@ +/* + * 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; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.activity.Startable; +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.ContextException; +import org.apache.avalon.framework.context.Contextualizable; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.Logger; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.thread.ThreadSafe; +import org.apache.cocoon.components.ComponentInfo; +import org.apache.cocoon.components.Preloadable; +import org.apache.cocoon.configuration.ConfigurationBuilder; +import org.apache.cocoon.core.Core; +import org.apache.cocoon.core.CoreResourceNotFoundException; +import org.apache.cocoon.core.Settings; +import org.apache.cocoon.core.container.handler.AbstractComponentHandler; +import org.apache.cocoon.core.container.handler.AliasComponentHandler; +import org.apache.cocoon.core.container.handler.ComponentHandler; +import org.apache.cocoon.core.container.handler.InstanceComponentHandler; +import org.apache.cocoon.core.container.handler.LazyHandler; +import org.apache.cocoon.core.source.SimpleSourceResolver; +import org.apache.cocoon.matching.helpers.WildcardHelper; +import org.apache.cocoon.sitemap.impl.ComponentManager; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceResolver; +import org.apache.excalibur.source.TraversableSource; + +/** + * Default service manager for Cocoon's components. + * + * @version $Id: CoreServiceManager.java 328027 2005-10-24 10:38:36Z cziegeler $ + * @since 2.2 + */ +public class CoreServiceManager + extends AbstractLogEnabled + implements Contextualizable, ThreadSafe, Disposable, Initializable, ServiceManager, Configurable { + + /** + * An empty configuration object, that can be used when no configuration is known but one + * is needed. + */ + public static final Configuration EMPTY_CONFIGURATION = new DefaultConfiguration("-", "unknown location"); + + /** Parameter map for the context protocol */ + protected static final Map CONTEXT_PARAMETERS = Collections.singletonMap("force-traversable", Boolean.TRUE); + + /** The application context for components */ + protected Context context; + + /** Static component mapping handlers. */ + protected final Map componentMapping = Collections.synchronizedMap(new HashMap()); + + /** Used to map roles to ComponentHandlers. */ + protected final Map componentHandlers = Collections.synchronizedMap(new HashMap()); + + /** Is the Manager disposed or not? */ + protected boolean disposed; + + /** Is the Manager initialized? */ + protected boolean initialized; + + /** RoleInfos. */ + protected RoleManager roleManager; + + /** LoggerManager. */ + protected LoggerManager loggerManager; + + protected ComponentEnvironment componentEnv; + + /** The settings */ + private Settings settings; + + /** The location where this manager is defined */ + protected String location; + + /** The parent ServiceManager */ + protected ServiceManager parentManager; + + /** The classloader to get classes from */ + protected ClassLoader classloader; + + /** The resolver used to resolve includes. It is lazily loaded in {@link #setupSourceResolver()}. */ + private SourceResolver cachedSourceResolver; + + /** Create the ServiceManager with a parent ServiceManager */ + public CoreServiceManager( final ServiceManager parent ) { + this(parent, null); + } + + /** Create the ServiceManager with a parent ServiceManager and a ClassLoader */ + public CoreServiceManager( final ServiceManager parent, final ClassLoader classloader ) { + this.parentManager = parent; + this.classloader = classloader; + + RoleManager parentRoleManager = null; + // FIXME - We should change this to a cleaner way! + ServiceManager coreServicemanager = parent; + if ( parent instanceof ComponentManager ) { + coreServicemanager = ((ComponentManager)parent).getServiceManager(); + } + // get role manager and logger manager + if ( coreServicemanager instanceof CoreServiceManager ) { + parentRoleManager = ((CoreServiceManager)coreServicemanager).roleManager; + this.loggerManager = ((CoreServiceManager)coreServicemanager).loggerManager; + } + + // Always create a role manager, it can be filled several times either through + // the root "roles" attribute or through loading of includes + this.roleManager = new RoleManager(parentRoleManager); + } + + //============================================================================================= + // Avalon lifecycle + //============================================================================================= + + /* (non-Javadoc) + * @see org.apache.avalon.framework.logger.LogEnabled#enableLogging(org.apache.avalon.framework.logger.Logger) + */ + public void enableLogging(Logger logger) { + super.enableLogging(logger); + this.roleManager.enableLogging(logger); + } + + /* (non-Javadoc) + * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) + */ + public void contextualize( final Context context ) + throws ContextException { + this.context = context; + this.settings = ((Core)context.get(Core.ROLE)).getSettings(); + } + + /** + * Configure the LoggerManager. + */ + public void setLoggerManager( final LoggerManager manager ) { + this.loggerManager = manager; + } + + public void setRoleManager (RoleManager rm) { + if (rm != null) { + // Override the one eventually got in the parent (see constructor) + this.roleManager = new RoleManager(rm); + } + } + + /* (non-Javadoc) + * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) + */ + public void configure(Configuration configuration) throws ConfigurationException { + this.componentEnv = new ComponentEnvironment(this.classloader, getLogger(), this.roleManager, this.loggerManager, this.context, this); + + // Setup location + this.location = configuration.getLocation(); + + // Find the current URI + String currentURI; + int pos = this.location.lastIndexOf(':'); + if (pos == -1) { + // No available location: start at the context + currentURI = "context://"; + } else { + pos = this.location.lastIndexOf(':', pos); + currentURI = this.location.substring(0, pos-1); + } + + try { + // and load configuration with a empty list of loaded configurations + parseConfiguration(configuration, currentURI, new HashSet()); + } finally { + // Release any source resolver that may have been created to load includes + releaseCachedSourceResolver(); + } + } + + /* (non-Javadoc) + * @see org.apache.avalon.framework.activity.Initializable#initialize() + */ + public void initialize() + throws Exception { + this.initialized = true; + + // Initialize component handlers. This is done in no particular order, but initializing a + // handler may indirectly initialize another handler through a call to lookup(). + // This isn't a problem as a handler's initialize() method can be called several times. + + // We copy the list of handlers as the componentHandler Map may change if implicitely declared + // components are looked up. + ComponentHandler[] handlers = (ComponentHandler[])this.componentHandlers.values().toArray( + new ComponentHandler[this.componentHandlers.size()]); + + for( int i = 0; i < handlers.length; i++ ) { + try { + handlers[i].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; + } + } + +// Object[] keyArray = this.componentHandlers.keySet().toArray(); +// java.util.Arrays.sort(keyArray); +// for (int i = 0; i < keyArray.length; i++) { +// System.err.println("Component key = " + keyArray[i]); +// } + } + + /* (non-Javadoc) + * @see org.apache.avalon.framework.activity.Disposable#dispose() + */ + public void dispose() { + 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 ComponentHandler handler = + (ComponentHandler)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; + } + + //============================================================================================= + // ServiceManager implementation + //============================================================================================= + + /* (non-Javadoc) + * @see org.apache.avalon.framework.service.ServiceManager#hasService(java.lang.String) + */ + public boolean hasService( final String role ) { + if( !this.initialized || 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#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 CoreServiceManager [" + role + "]" ); + } + } + + if( this.disposed ) { + throw new IllegalStateException( + "You cannot lookup components on a disposed CoreServiceManager" ); + } + + if( role == null ) { + final String message = + "CoreServiceManager attempted to retrieve service with null role."; + + if( this.getLogger().isErrorEnabled() ) { + this.getLogger().error( message ); + } + throw new ServiceException( role, message ); + } + + ComponentHandler handler = (ComponentHandler)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 SM 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 ComponentInfo info = this.roleManager.getDefaultServiceInfoForRole( role ); + + if( info != null ) { + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( "Could not find ComponentHandler, attempting to create " + + "one for role [" + role + "]" ); + } + + try { + final Configuration configuration = new DefaultConfiguration( "", "-" ); + + handler = this.getComponentHandler(role, + info.getServiceClassName(), + configuration.getChild(role), + info); + + } catch (ServiceException se) { + throw se; + } catch( final Exception e ) { + final String message = "Could not find component for role [" + role + "]"; + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( message, e ); + } + throw new ServiceException( role, message, e ); + } + try { + handler.initialize(); + } catch (ServiceException se) { + throw se; + } catch( final Exception e ) { + final String message = "Could not create component for role [" + role + "]: " + handler.getClass(); + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( message, 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 for role: [" + role + "]"; + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( message ); + } + throw new ServiceException( role, message ); + } + + Object component = null; + + try { + component = handler.get(); + } catch ( ServiceException se) { + // Rethrow insteand of wrapping it again + throw se; + } catch( final Exception e ) { + final String message = "Could not access the component for role [" + role + "]"; + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( message, e ); + } + + throw new ServiceException( role, message, e ); + } + this.initialize( role, component ); + + // 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 ); + + return component; + } + + /* (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 ComponentHandler handler = + (ComponentHandler)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.isSingleton() ) { + // 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." ); + } + } + + //============================================================================================= + // Additional public & protected contract + //============================================================================================= + + /** + * Add a new component to the manager. + * + * @param role the role name for the new component. + * @param className the class of this component. + * @param configuration the configuration for this component. + */ + public void addComponent( String role, + String className, + Configuration configuration, + ComponentInfo info) + throws ConfigurationException { + if( this.initialized ) { + throw new IllegalStateException("Cannot add components to an initialized CoreServiceManager." ); + } + + // check for old excalibur class names - we only test against the selector + // implementation + if ( "org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) { + className = DefaultServiceSelector.class.getName(); + } + + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( "Adding component (" + role + " = " + className + ")" ); + } + + ComponentHandler handler = (ComponentHandler)this.componentHandlers.get(role); + if (handler != null) { + // Check that override is allowed. If yes, the handler will be redefined below, allowing + // the new definition to feed this manager with its components. + checkComponentOverride(role, className, configuration, handler); + } + + try { + handler = this.getComponentHandler(role, className, configuration, info); + + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( "Handler type = " + handler.getClass().getName() ); + } + + this.componentHandlers.put( role, handler ); + } catch ( final ConfigurationException ce ) { + throw ce; + } catch( final Exception e ) { + throw new ConfigurationException( "Could not add component defined at " + configuration.getLocation(), e ); + } + +// // Initialize shadow selector now, it will feed this service manager +// if ( DefaultServiceSelector.class.isAssignableFrom( component )) { +// try { +// handler.initialize(); +// } catch(ServiceException se) { +// throw se; +// } catch(Exception e) { +// throw new ServiceException(role, "Could not initialize selector", e); +// } +// } + } + + /** + * Add an existing object to the manager. The object should be fully configured as no + * setup lifecycle methods are called. On manager disposal, the Disposable + * method is considered. + * + * @param role the role under which the object will be known + * @param instance the component instance + * @throws ServiceException + */ + public void addInstance(String role, Object instance) throws ServiceException { + if( this.initialized ) { + throw new ServiceException(role, + "Cannot add components to an initialized CoreServiceManager."); + } + + ComponentHandler handler = (ComponentHandler)this.componentHandlers.get(role); + if (handler != null) { + ComponentInfo info = handler.getInfo(); + throw new ServiceException(role, "Component already defined at " + info.getLocation()); + } + + this.componentHandlers.put(role, new InstanceComponentHandler(getLogger(), instance)); + } + + /** + * Add an alias to a role, i.e. define a synonym for the role. + * + * @param existingRole the existing role that will be aliased + * @param newRole the new role + * @throws ServiceException if the existing role could not be found in the current + * manager and its ancestors + */ + public void addRoleAlias(String existingRole, String newRole) throws ServiceException { + ComponentHandler handler = (ComponentHandler)this.componentHandlers.get(existingRole); + if (handler == null) { + // Aliased component not found here, but can be defined by an ancestor + CoreServiceManager current = this; + while(handler == null && current.parentManager != null) { + if (!(current.parentManager instanceof CoreServiceManager)) { + throw new ServiceException(newRole, "Cannot alias to components not managed by CoreServiceManager"); + } + current = (CoreServiceManager)current.parentManager; + handler = (ComponentHandler)current.componentHandlers.get(existingRole); + } + } + + if (handler == null) { + throw new ServiceException(newRole, "Cannot alias non-existing role " + existingRole); + } + + this.componentHandlers.put(newRole, new AliasComponentHandler(this.getLogger(), handler)); + } + + /** + * Initialize the component + * @throws ServiceException + */ + protected void initialize(String role, Object component) + throws ServiceException { + // we do nothing here, can be used in subclasses + } + + //============================================================================================= + // Private methods + //============================================================================================= + + /** + * Obtain a new ComponentHandler for the specified component. + * + * @param role the component's role. + * @param className Class of the component for which the handle is + * being requested. + * @param configuration The configuration for this component. + * @param baseInfo The information for managing the component, like service manager etc. + * + * @throws Exception If there were any problems obtaining a ComponentHandler + */ + private ComponentHandler getComponentHandler( final String role, + final String className, + final Configuration configuration, + final ComponentInfo baseInfo) + throws Exception { + + boolean lazyLoad; + + if (configuration.getAttribute("preload", null) != null) { + // This one has precedence + lazyLoad = configuration.getAttributeAsBoolean("preload"); + + } else { + lazyLoad = this.settings.isLazyMode(); + + if (lazyLoad) { + // Check if the class implements Startable or Preloadable + Class componentClass; + try { + componentClass = componentEnv.loadClass(className); + } catch (ClassNotFoundException cnfe) { + throw new Exception("Cannot find class " + className + " for component at " + + configuration.getLocation(), cnfe); + } + if (Startable.class.isAssignableFrom(componentClass) || + Preloadable.class.isAssignableFrom(componentClass)) { + lazyLoad = false; + } + } + } + + if (lazyLoad) { + return new LazyHandler(role, className, configuration, componentEnv); + } + + // FIXME - we should ensure that we always get an info + ComponentInfo info; + if ( baseInfo != null ) { + info = baseInfo.duplicate(); + } else { + info = new ComponentInfo(); + info.fill(configuration); + } + info.setConfiguration(configuration); + info.setServiceClassName(className); + + return AbstractComponentHandler.getComponentHandler(role, this.componentEnv, info); + } + + private void parseConfiguration(final Configuration configuration, String contextURI, Set loadedURIs) + throws ConfigurationException { + + final Configuration[] configurations = configuration.getChildren(); + + for( int i = 0; i < configurations.length; i++ ) { + final Configuration componentConfig = configurations[i]; + + final String componentName = componentConfig.getName(); + + if ("include".equals(componentName)) { + handleInclude(contextURI, loadedURIs, componentConfig); + + } else { + // Component declaration + // Find the role + String role = componentConfig.getAttribute("role", null); + if (role == null) { + // Get the role from the role manager if not explicitely specified + role = roleManager.getRoleForName(componentName); + if (role == null) { + // Unknown role + throw new ConfigurationException("Unknown component type '" + componentName + + "' at " + componentConfig.getLocation()); + } + } + + // Find the className + String className = componentConfig.getAttribute("class", null); + if (className == null) { + // Get the default class name for this role + final ComponentInfo info = roleManager.getDefaultServiceInfoForRole(role); + if (info == null) { + throw new ConfigurationException("Cannot find a class for role " + role + " at " + componentConfig.getLocation()); + } + className = info.getServiceClassName(); + } + + // If it has a "name" attribute, add it to the role (similar to the + // declaration within a service selector) + // Note: this has to be done *after* finding the className above as we change the role + String name = componentConfig.getAttribute("name", null); + if (name != null) { + role = role + "/" + name; + } + + this.addComponent(role, className, componentConfig, null); + } + } + } + + private void handleInclude(String contextURI, Set loadedURIs, Configuration includeStatement) + throws ConfigurationException { + String includeURI = includeStatement.getAttribute("src", null); + String directoryURI = null; + if ( includeURI == null ) { + // check for directories + directoryURI = includeStatement.getAttribute("dir", null); + } + if ( includeURI == null && directoryURI == null ) { + throw new ConfigurationException("Include statement must either have a 'src' or 'dir' attribute, at " + + includeStatement.getLocation()); + } + + // Setup the source resolver if needed + setupSourceResolver(); + + if ( includeURI != null ) { + Source src; + try { + src = this.cachedSourceResolver.resolveURI(includeURI, contextURI, null); + } catch (Exception e) { + throw new ConfigurationException("Cannot load '" + includeURI + "' at " + includeStatement.getLocation(), e); + } + + loadURI(src, loadedURIs, includeStatement); + } else { + final String pattern = includeStatement.getAttribute("pattern", null); + int[] parsedPattern = null; + if ( pattern != null ) { + parsedPattern = WildcardHelper.compilePattern(pattern); + } + Source directory = null; + try { + directory = this.cachedSourceResolver.resolveURI(directoryURI, contextURI, CONTEXT_PARAMETERS); + if ( directory instanceof TraversableSource ) { + final Iterator children = ((TraversableSource)directory).getChildren().iterator(); + while ( children.hasNext() ) { + final Source s = (Source)children.next(); + if ( parsedPattern == null || this.match(s.getURI(), parsedPattern)) { + this.loadURI(s, loadedURIs, includeStatement); + } + } + } else { + throw new ConfigurationException("Include.dir must point to a directory, '" + directory.getURI() + "' is not a directory.'"); + } + } catch (IOException ioe) { + throw new ConfigurationException("Unable to read configurations from " + directoryURI); + } finally { + this.cachedSourceResolver.release(directory); + } + } + } + + private void loadURI(Source src, Set loadedURIs, Configuration includeStatement) + throws ConfigurationException { + // If already loaded: do nothing + try { + + String uri = src.getURI(); + + if (!loadedURIs.contains(uri)) { + if ( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug("Loading configuration from: " + uri); + } + // load it and store it in the read set + Configuration includeConfig = null; + try { + ConfigurationBuilder builder = new ConfigurationBuilder(this.settings); + includeConfig = builder.build(src.getInputStream(), uri); + } catch (Exception e) { + throw new ConfigurationException("Cannot load '" + uri + "' at " + includeStatement.getLocation(), e); + } + loadedURIs.add(uri); + + // what is it? + String includeKind = includeConfig.getName(); + if (includeKind.equals("components")) { + // more components + parseConfiguration(includeConfig, uri, loadedURIs); + } else if (includeKind.equals("role-list")) { + // more roles + this.roleManager.configure(includeConfig); + } else { + throw new ConfigurationException("Unknow document '" + includeKind + "' included at " + + includeStatement.getLocation()); + } + } + } finally { + this.cachedSourceResolver.release(src); + } + } + + /** + * If the parent manager does not exist or does not + * provide a source resolver, a simple one is created here to load the file. + */ + private void setupSourceResolver() { + if (this.cachedSourceResolver == null) { + + if (this.parentManager != null && this.parentManager.hasService(SourceResolver.ROLE)) { + try { + this.cachedSourceResolver = (SourceResolver)this.parentManager.lookup(SourceResolver.ROLE); + } catch(ServiceException se) { + // Unlikely to happen + throw new CoreResourceNotFoundException("Cannot get source resolver from parent, at " + location, se); + } + } else { + // Create our own + SimpleSourceResolver simpleSR = new SimpleSourceResolver(); + simpleSR.enableLogging(getLogger()); + try { + simpleSR.contextualize(this.context); + } catch (ContextException ce) { + throw new CoreResourceNotFoundException("Cannot setup source resolver, at " + location, ce); + } + this.cachedSourceResolver = simpleSR; + } + } + } + + private boolean match(String uri, int[] parsedPattern ) { + int pos = uri.lastIndexOf('/'); + if ( pos != -1 ) { + uri = uri.substring(pos+1); + } + return WildcardHelper.match(null, uri, parsedPattern); + } + + /** + * Release the source resolver that may have been created by the first call to + * loadConfiguration(). + */ + private void releaseCachedSourceResolver() { + if (this.cachedSourceResolver != null && + this.parentManager != null && this.parentManager.hasService(SourceResolver.ROLE)) { + this.parentManager.release(this.cachedSourceResolver); + } + this.cachedSourceResolver = null; + } + + /** + * Check if a component can be overriden. Only {@link DefaultServiceSelector} or its subclasses can be + * overriden, as they directly feed this manager with their component definitions and are empty + * shells delegating to this manager afterwards. + */ + private void checkComponentOverride(String role, String className, Configuration config, + ComponentHandler existingHandler) throws ConfigurationException { + + // We only allow selectors to be overloaded + ComponentInfo info = existingHandler.getInfo(); + if (!className.equals(info.getServiceClassName())) { + throw new ConfigurationException("Role " + role + " redefined with a different class name, at " + + config.getLocation()); + } + + Class clazz; + try { + clazz = this.componentEnv.loadClass(className); + } catch(ClassNotFoundException cnfe) { + throw new ConfigurationException("Cannot load class " + className + " for component at " + + config.getLocation(), cnfe); + } + + if (!DefaultServiceSelector.class.isAssignableFrom(clazz)) { + throw new ConfigurationException("Component declared at " + info.getLocation() + " is redefined at " + + config.getLocation()); + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/CoreServiceManager.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/DefaultServiceSelector.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/DefaultServiceSelector.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/DefaultServiceSelector.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/DefaultServiceSelector.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,220 @@ +/* + * 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; + +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.container.ContainerUtil; +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; +import org.apache.cocoon.components.ComponentInfo; +import org.apache.cocoon.components.Preloadable; + +/** + * Default component selector for Cocoon's components. This selector "flattens" its declaration + * by adding them as components of its containing ServiceManager. This allows a smooth transition towards + * a fully flat configuration by allowing the use of selectors in legacy components and also + * declaration of hinted components (i.e. with a role of type "rolename/hint") in the service manager. + * + * @version $Id: DefaultServiceSelector.java 326062 2005-10-18 09:31:53Z sylvain $ + * @since 2.2 + */ +public class DefaultServiceSelector extends AbstractLogEnabled implements ThreadSafe, Preloadable, Serviceable, Configurable, ServiceSelector { + + /** Synthetic hint to alias the default component */ + public static final String DEFAULT_HINT = "$default$"; + + private CoreServiceManager manager; + private RoleManager roleManager; + private String roleName; + private String rolePrefix; + + public void service(ServiceManager manager) throws ServiceException { + try { + this.manager = (CoreServiceManager)manager; + } catch (ClassCastException cce) { + throw new ServiceException ("DefaultServiceSelector", + "A DefaultServiceSelector can only be hosted by a CoreServiceManager"); + } + } + + public void setRole(String role) { + this.roleName = role; + } + + public void setRoleManager(RoleManager roles) { + this.roleManager = roles; + } + + public void configure(Configuration config) throws ConfigurationException { + + if (roleName == null) { + throw new ConfigurationException("No role given for DefaultServiceSelector at " + config.getLocation()); + } + + // Remove "Selector" suffix, if any and add a trailing "/" + if (roleName.endsWith("Selector")) { + this.rolePrefix = roleName.substring(0, roleName.length() - 8) + "/"; + } else { + this.rolePrefix = roleName + "/"; + } + + // Add components + String compInstanceName = getComponentInstanceName(); + + Configuration[] instances = config.getChildren(); + + for (int i = 0; i < instances.length; i++) { + + Configuration instance = instances[i]; + ComponentInfo info = null; + + String key = instance.getAttribute("name"); + + 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) { + info = this.roleManager.getDefaultServiceInfoForKey(roleName, instance.getName()); + className = info.getServiceClassName(); + } else { + className = classAttr; + } + + } else { + // component-instances names explicitly defined + if (compInstanceName.equals(instance.getName())) { + className = (classAttr == null) ? null : classAttr; + } else { + info = this.roleManager.getDefaultServiceInfoForKey(roleName, instance.getName()); + className = info.getServiceClassName(); + } + } + + if (className == null) { + String message = "Unable to determine class name for component named '" + key + + "' at " + instance.getLocation(); + + getLogger().error(message); + throw new ConfigurationException(message); + } + + // Add this component in the manager + this.manager.addComponent(this.rolePrefix + key, className, instance, info); + } + + // Register default key, if any + String defaultKey = config.getAttribute(this.getDefaultKeyAttributeName(), null); + if (defaultKey != null) { + try { + this.manager.addRoleAlias(this.rolePrefix + defaultKey, this.rolePrefix + DEFAULT_HINT); + } catch (ServiceException e) { + throw new ConfigurationException("Cannot set default to " + defaultKey + " at " + config.getLocation(), e); + } + } + } + + public Object select(Object hint) throws ServiceException { + String key = (hint == null) ? DEFAULT_HINT : hint.toString(); + + return this.manager.lookup(this.rolePrefix + key); + } + + public boolean isSelectable(Object hint) { + String key = hint == null ? DEFAULT_HINT : hint.toString(); + + return key != null && this.manager.hasService(this.rolePrefix + key); + } + + public void release(Object obj) { + this.manager.release(obj); + } + + // --------------------------------------------------------------- + /** + * Get the name for component-instance elements (i.e. components not defined + * by their role shortcut. If null, any element having a 'class' + * attribute will be considered as a component instance. + *

+ * The default here is to return null, and subclasses can redefine + * this method to return particular values. + * + * @return null, 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 "class", but can be changed by subclasses + */ + protected String getClassAttributeName() { + return "class"; + } + + /** + * Get the name of the attribute giving the default key to use if + * none is given. The default here is "default", but this can be + * overriden in subclasses. If this method returns null, + * no default key can be specified. + * + * @return "default", but can be changed by subclasses + */ + protected String getDefaultKeyAttributeName() { + return "default"; + } + + /** + * A special factory for DefaultServiceSelector, that passes it the + * RoleManager and its role name. + */ + public static class Factory extends ComponentFactory { + private final String role; + + public Factory(ComponentEnvironment env, ComponentInfo info, String role) + throws Exception { + super(env, info); + this.role = role; + } + + protected void setupInstance(Object object) + throws Exception { + DefaultServiceSelector component = (DefaultServiceSelector)object; + + ContainerUtil.enableLogging(component, this.environment.logger); + ContainerUtil.contextualize(component, this.environment.context); + ContainerUtil.service(component, this.environment.serviceManager); + + component.setRoleManager(this.environment.roleManager); + component.setRole(this.role); + + ContainerUtil.configure(component, this.serviceInfo.getConfiguration()); + ContainerUtil.initialize(component); + ContainerUtil.start(component); + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/DefaultServiceSelector.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/RoleManager.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/RoleManager.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/RoleManager.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/RoleManager.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,251 @@ +/* + * 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; + +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; +import org.apache.cocoon.components.ComponentInfo; + +/** + * Default RoleManager implementation. It populates the RoleManager + * from a configuration file. + * + * @version $Id: RoleManager.java 312637 2005-10-10 13:00:42Z cziegeler $ + * @since 2.2 + */ +public class RoleManager +extends AbstractLogEnabled +implements Configurable { + + /** Map for shorthand to role mapping */ + private final Map shorthands = new HashMap(); + + /** Map for role to default classname mapping */ + private final Map classNames = new HashMap(); + + /** Map for role->key to classname mapping */ + private final Map keyClassNames = new HashMap(); + + /** Parent role manager 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 RoleManager. + */ + 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 ComponentInfo getDefaultServiceInfoForRole( final String role ) { + final ComponentInfo info = (ComponentInfo)this.classNames.get( role ); + + if( info == null && this.parent != null ) { + return this.parent.getDefaultServiceInfoForRole( role ); + } + + return info; + } + + /** + * Retrieves a default class name for a role/key combination. + * This is only called when a role is mapped to a + * StandaloneServiceSelector, 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/key combination. + */ + public final ComponentInfo getDefaultServiceInfoForKey( final String role, + final String shorthand ) { + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( "looking up keymap for role " + role ); + } + + final Map keyMap = (Map)this.keyClassNames.get( role ); + + if( null == keyMap ) { + if( null != this.parent ) { + return this.parent.getDefaultServiceInfoForKey( role, shorthand ); + } + return null; + } + + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( "looking up classname for key " + shorthand ); + } + + final ComponentInfo s = ( ComponentInfo ) keyMap.get( shorthand ); + + if( s == null && this.parent != null ) { + return this.parent.getDefaultServiceInfoForKey( role, shorthand ); + } + 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 { + + // When reading a roles file, we only want "role" elements. + boolean strictMode = "roles-list".equals(configuration.getName()); + + final Configuration[] roles = configuration.getChildren(); + + for( int i = 0; i < roles.length; i++ ) { + Configuration role = roles[i]; + + if (!"role".equals(role.getName())) { + if (strictMode) { + throw new ConfigurationException("Unexpected '" + role.getName() + "' element at " + role.getLocation()); + } + // Skip to next one + continue; + } + + final String roleName = role.getAttribute("name"); + final String shorthand = role.getAttribute("shorthand", null); + final String defaultClassName = role.getAttribute("default-class", null); + + if (shorthand != null) { + // Store the shorthand and check that its consistent with any previous one + Object previous = this.shorthands.put( shorthand, roleName ); + if (previous != null && !previous.equals(roleName)) { + throw new ConfigurationException("Shorthand '" + shorthand + "' already used for role " + + previous + ": inconsistent declaration at " + role.getLocation()); + } + } + + if( defaultClassName != null ) { + ComponentInfo info = (ComponentInfo)this.classNames.get(roleName); + if (info == null) { + // Create a new info and store it + info = new ComponentInfo(); + info.setServiceClassName(defaultClassName); + info.fill(role); + this.classNames.put(roleName, info); + } else { + // Check that it's consistent with the existing info + if (!defaultClassName.equals(info.getServiceClassName())) { + throw new ConfigurationException("Invalid redeclaration: default class already set to " + info.getServiceClassName() + + " for role " + roleName + " at " + role.getLocation()); + } + //FIXME: should check also other ServiceInfo members + } + } + + final Configuration[] keys = role.getChildren( "hint" ); + if( keys.length > 0 ) { + Map keyMap = (Map)this.keyClassNames.get(roleName); + if (keyMap == null) { + keyMap = new HashMap(); + this.keyClassNames.put(roleName, keyMap); + } + + for( int j = 0; j < keys.length; j++ ) { + Configuration key = keys[j]; + + final String shortHand = key.getAttribute( "shorthand" ).trim(); + final String className = key.getAttribute( "class" ).trim(); + + ComponentInfo info = (ComponentInfo)keyMap.get(shortHand); + if (info == null) { + info = new ComponentInfo(); + info.setServiceClassName(className); + info.fill(key); + + keyMap.put( shortHand, info ); + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( "Adding key type " + shortHand + + " associated with role " + roleName + + " and class " + className ); + } + } else { + // Check that it's consistent with the existing info + if (!className.equals(info.getServiceClassName())) { + throw new ConfigurationException("Invalid redeclaration: class already set to " + info.getServiceClassName() + + " for hint " + shortHand + " at " + key.getLocation()); + } + //FIXME: should check also other ServiceInfo members + } + } + } + + if( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug( "added Role " + roleName + " with shorthand " + + shorthand + " for " + defaultClassName ); + } + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/core/container/RoleManager.java ------------------------------------------------------------------------------ svn:eol-style = native