blob: 7b25fd50b8219db2b86d8c0933c6779f082e3487 [file] [log] [blame]
/*
* 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$
* @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 <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 key 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 key can be specified.
*
* @return "<code>default</code>", but can be changed by subclasses
*/
protected String getDefaultKeyAttributeName() {
return "default";
}
/**
* A special factory for <code>DefaultServiceSelector</code>, that passes it the
* <code>RoleManager</code> 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);
}
}
}