blob: cef5e5b00627926476a7560c84efcec7f5eaf824 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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;
import org.apache.avalon.excalibur.component.ExcaliburComponentSelector;
import org.apache.avalon.excalibur.component.RoleManager;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
/**
* An extension of <code>ExcaliburComponentSelector</code> that can have a parent
* and accepts a wider variety of configurations.
*
* @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
* @version CVS $Id$
*/
public class ExtendedComponentSelector extends ExcaliburComponentSelector
implements ParentAware {
/** The role manager */
protected RoleManager roles;
/** The parent selector, if any */
protected ExtendedComponentSelector parentSelector;
/** The parent locator, if any */
protected ComponentLocator parentLocator;
/** The class loader to use */
protected ClassLoader classLoader;
/** 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 with the Thread context ClassLoader */
public ExtendedComponentSelector() {
this.classLoader = Thread.currentThread().getContextClassLoader();
}
/** Create the ComponentSelector with a ClassLoader */
public ExtendedComponentSelector(ClassLoader loader) {
super(loader);
if (loader == null) {
this.classLoader = Thread.currentThread().getContextClassLoader();
} else {
this.classLoader = loader;
}
}
/**
* 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";
}
/**
* Configure the RoleManager. Redeclared only because parent member is private.
*/
public void setRoleManager(RoleManager roles) {
super.setRoleManager(roles);
this.roles = roles;
}
/**
* Set the parent of this selector. This can be done after the selector is
* initialized, but <em>only once</em>. This allows this selector to be
* created by a component manager while still being able to have a parent.
*
* @param parent the parent selector
* @throws IllegalStateException if parent is already set
*/
/* public void setParentSelector(ComponentSelector parent) {
if (this.parentSelector != null) {
throw new IllegalStateException("Parent selector is already set");
}
this.parentSelector = parent;
this.parentComponents = new HashSet();
}
*/
/**
* 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.roles != null) {
roleName = this.roles.getRoleForName(config.getName());
}
return roleName;
}
/**
* Configure this selector. This is the main difference with the parent class :
* <ul>
* <li>if {@link #getComponentInstanceName()} returns <code>null</code>,
* any child configurations having a attribute named as the result of
* {@link #getClassAttributeName()}, is considered as a component instance.
* </li>
* <li>if {@link #getComponentInstanceName()} returns a non-null value,
* only child configurations having this name are considered as a
* component instance.
* </li>
* <li>if other cases, it's name is considered to be a hint in the role manager.
* The behaviour is then the same as <code>ExcaliburComponentSelector</code>.
* </li>
*
* @param config the configuration
* @throws ConfigurationException if some hints aren't defined
*/
public void configure(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);
}
super.configure(temp);
// Get default hint
this.defaultHint = config.getAttribute(this.getDefaultHintAttributeName(), null);
// Add components
String compInstanceName = getComponentInstanceName();
Configuration[] 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.roles.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.roles.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);
}
try {
Class clazz = this.classLoader.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);
}
}
}
/**
* Get the default hint, if any for this selector.
*/
public String getDefaultHint() {
// Inherit parent default hint if have no own
if (this.defaultHint == null && this.parentSelector != null) {
return this.parentSelector.getDefaultHint();
}
return this.defaultHint;
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.component.ComponentSelector#select(java.lang.Object)
*/
public Component select(Object hint) throws ComponentException {
if (hint == null) {
hint = this.defaultHint;
}
if (parentSelector == null) {
// No parent: default behaviour
return super.select(hint);
}
try {
// Try in this selector first
final Component component = super.select(hint);
return component;
} catch (ComponentException original) {
try {
// Doesn't exist here: try in parent selector
final Component component = this.parentSelector.select(hint);
return component;
} catch (ComponentException nested) {
// Doesn't exist in parent too: throw exception.
if (nested.getCause() != null) {
// Nested exception has a cause; let's throw it instead of original.
throw nested;
}
// Throw original exception
throw original;
}
}
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.component.ComponentSelector#release(org.apache.avalon.framework.component.Component)
*/
public void release(Component component) {
// Was it selected on the parent ?
if (this.parentSelector != null && this.parentSelector.canRelease(component)) {
// Yes
this.parentSelector.release(component);
} else {
// No
super.release(component);
}
}
/**
* Does this selector or its parent have the given hint ?
*/
public boolean hasComponent(Object hint) {
boolean exists = super.hasComponent(hint);
if (!exists && this.parentSelector != null) {
exists = this.parentSelector.hasComponent(hint);
}
return exists;
}
/**
* 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) {
return super.hasComponent(hint);
}
/* (non-Javadoc)
* @see org.apache.cocoon.components.ParentAware#setParentInformation(org.apache.avalon.framework.component.ComponentManager, java.lang.String)
*/
public void setParentLocator(ComponentLocator locator)
throws ComponentException {
if (this.parentSelector != null) {
throw new ComponentException(null, "Parent selector is already set");
}
this.parentLocator = locator;
this.parentSelector = (ExtendedComponentSelector) locator.lookup();
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
public void dispose() {
super.dispose();
if (this.parentLocator != null) {
this.parentLocator.release(this.parentSelector);
this.parentLocator = null;
this.parentSelector = null;
}
}
/* (non-Javadoc)
* @see org.apache.avalon.excalibur.component.ExcaliburComponentSelector#canRelease(org.apache.avalon.framework.component.Component)
*/
protected boolean canRelease(Component component) {
if (this.parentSelector != null && this.parentSelector.canRelease(component)) {
return true;
}
return super.canRelease(component);
}
}