org.codehaus.plexus.component.builder.XBeanComponentBuilder Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2001-2006 Codehaus 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.codehaus.plexus.component.builder;
import static org.apache.xbean.recipe.RecipeHelper.toClass;
import org.apache.xbean.recipe.AbstractRecipe;
import org.apache.xbean.recipe.ConstructionException;
import org.apache.xbean.recipe.ObjectRecipe;
import org.apache.xbean.recipe.Option;
import org.apache.xbean.recipe.RecipeHelper;
import org.codehaus.plexus.MutablePlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.ComponentRegistry;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.MapOrientedComponent;
import org.codehaus.plexus.component.collections.ComponentList;
import org.codehaus.plexus.component.collections.ComponentMap;
import org.codehaus.plexus.component.configurator.BasicComponentConfigurator;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.codehaus.plexus.component.configurator.ComponentConfigurator;
import org.codehaus.plexus.component.configurator.converters.ConfigurationConverter;
import org.codehaus.plexus.component.configurator.converters.composite.MapConverter;
import org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup;
import org.codehaus.plexus.component.configurator.converters.lookup.DefaultConverterLookup;
import org.codehaus.plexus.component.configurator.converters.special.ClassRealmConverter;
import org.codehaus.plexus.component.configurator.expression.DefaultExpressionEvaluator;
import org.codehaus.plexus.component.factory.ComponentFactory;
import org.codehaus.plexus.component.factory.ComponentInstantiationException;
import org.codehaus.plexus.component.factory.java.JavaComponentFactory;
import org.codehaus.plexus.component.manager.ComponentManager;
import org.codehaus.plexus.component.repository.ComponentDescriptor;
import org.codehaus.plexus.component.repository.ComponentRequirement;
import org.codehaus.plexus.component.repository.ComponentRequirementList;
import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.PlexusConfigurationException;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.PhaseExecutionException;
import org.codehaus.plexus.util.StringUtils;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.LinkedHashSet;
public class XBeanComponentBuilder implements ComponentBuilder {
private static final ThreadLocal>> STACK =
new ThreadLocal>>()
{
protected LinkedHashSet> initialValue()
{
return new LinkedHashSet>();
}
};
private ComponentManager componentManager;
public XBeanComponentBuilder() {
}
public XBeanComponentBuilder(ComponentManager componentManager) {
setComponentManager(componentManager);
}
public ComponentManager getComponentManager() {
return componentManager;
}
public void setComponentManager(ComponentManager componentManager) {
this.componentManager = componentManager;
}
protected MutablePlexusContainer getContainer() {
return componentManager.getContainer();
}
public T build( ComponentDescriptor descriptor, ClassRealm realm, ComponentBuildListener listener )
throws ComponentInstantiationException, ComponentLifecycleException
{
LinkedHashSet> stack = STACK.get();
if ( stack.contains( descriptor ) )
{
// create list of circularity
List> circularity = new ArrayList>( stack );
circularity.subList( circularity.indexOf( descriptor ), circularity.size() );
circularity.add( descriptor );
// nice circularity message
String message = "Creation circularity: ";
for ( ComponentDescriptor> componentDescriptor : circularity )
{
message += "\n\t[" + componentDescriptor.getRole() + ", " + componentDescriptor.getRoleHint() + "]";
}
throw new ComponentInstantiationException( message );
}
stack.add( descriptor );
try
{
if (listener != null) {
listener.beforeComponentCreate(descriptor, realm);
}
T component = createComponentInstance(descriptor, realm);
if (listener != null) {
listener.componentCreated(descriptor, component, realm);
}
startComponentLifecycle(component, realm);
if (listener != null) {
listener.componentConfigured(descriptor, component, realm);
}
return component;
}
finally
{
stack.remove( descriptor );
}
}
protected T createComponentInstance(ComponentDescriptor descriptor, ClassRealm realm) throws ComponentInstantiationException, ComponentLifecycleException {
MutablePlexusContainer container = getContainer();
if (realm == null) {
realm = descriptor.getRealm();
}
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(realm);
try {
ObjectRecipe recipe;
T instance;
ComponentFactory componentFactory = container.getComponentFactoryManager().findComponentFactory(descriptor.getComponentFactory());
if (JavaComponentFactory.class.equals(componentFactory.getClass())) {
// xbean-reflect will create object and do injection
recipe = createObjectRecipe( null, descriptor, realm );
instance = (T) recipe.create();
} else {
// todo figure out how to easily let xbean use the factory to construct the component
// use object factory to construct component and then inject into that object
instance = (T) componentFactory.newInstance(descriptor, realm, container);
recipe = createObjectRecipe( instance, descriptor, realm );
recipe.setProperties( instance );
}
// todo figure out how to easily let xbean do this map oriented stuff (if it is actually used in plexus)
if ( instance instanceof MapOrientedComponent) {
MapOrientedComponent mapOrientedComponent = (MapOrientedComponent) instance;
processMapOrientedComponent(descriptor, mapOrientedComponent, realm);
}
return instance;
} catch (Exception e) {
throw new ComponentLifecycleException("Error constructing component " + descriptor.getHumanReadableKey(), e);
} catch (LinkageError e) {
throw new ComponentLifecycleException("Error constructing component " + descriptor.getHumanReadableKey(), e);
} finally {
Thread.currentThread().setContextClassLoader(oldClassLoader);
}
}
public ObjectRecipe createObjectRecipe(T instance, ComponentDescriptor descriptor, ClassRealm realm) throws ComponentInstantiationException, PlexusConfigurationException {
String factoryMethod = null;
String[] constructorArgNames = null;
Class[] constructorArgTypes = null;
Class> implClass = ( instance != null ) ? instance.getClass() : descriptor.getImplementationClass();
if ( implClass == null || implClass == Object.class )
{
// if the descriptor could not load the class, it's time to report this up to the caller now
try
{
realm.loadClass( descriptor.getImplementation() );
}
catch ( ClassNotFoundException e )
{
throw new ComponentInstantiationException( "Could not load implementation class for component "
+ descriptor.getHumanReadableKey() + " from class realm " + realm, e );
}
catch ( LinkageError e )
{
throw new ComponentInstantiationException( "Could not load implementation class for component "
+ descriptor.getHumanReadableKey() + " from class realm " + realm, e );
}
}
ObjectRecipe recipe = new ObjectRecipe( implClass,
factoryMethod,
constructorArgNames,
constructorArgTypes);
recipe.allow(Option.FIELD_INJECTION);
recipe.allow(Option.PRIVATE_PROPERTIES);
// MapOrientedComponents don't get normal injection
if (!MapOrientedComponent.class.isAssignableFrom( implClass )) {
for (ComponentRequirement requirement : descriptor.getRequirements() ) {
String name = requirement.getFieldName();
RequirementRecipe requirementRecipe = new RequirementRecipe(descriptor, requirement, getContainer(), name == null);
if (name != null) {
recipe.setProperty(name, requirementRecipe);
} else {
recipe.setAutoMatchProperty(requirement.getRole(), requirementRecipe);
}
}
// add configuration data
if (shouldConfigure(descriptor )) {
PlexusConfiguration configuration = descriptor.getConfiguration();
if (configuration != null) {
for (String name : configuration.getAttributeNames()) {
String value;
try {
value = configuration.getAttribute(name);
} catch (PlexusConfigurationException e) {
throw new ComponentInstantiationException("Error getting value for attribute " + name, e);
}
name = fromXML(name);
recipe.setProperty(name, value);
}
for (PlexusConfiguration child : configuration.getChildren()) {
String name = child.getName();
name = fromXML(name);
if ( StringUtils.isNotEmpty( child.getValue( null ) ) )
{
recipe.setProperty( name, child.getValue() );
}
else
{
recipe.setProperty( name, new PlexusConfigurationRecipe( child ) );
}
}
}
}
}
return recipe;
}
protected boolean shouldConfigure( ComponentDescriptor descriptor ) {
String configuratorId = descriptor.getComponentConfigurator();
if (StringUtils.isEmpty(configuratorId)) {
return true;
}
try {
ComponentConfigurator componentConfigurator = getContainer().lookup(ComponentConfigurator.class, configuratorId);
return componentConfigurator == null || componentConfigurator.getClass().equals(BasicComponentConfigurator.class);
} catch (ComponentLookupException e) {
}
return true;
}
protected String fromXML(String elementName) {
return StringUtils.lowercaseFirstLetter(StringUtils.removeAndHump(elementName, "-"));
}
protected void startComponentLifecycle(Object component, ClassRealm realm) throws ComponentLifecycleException {
try {
componentManager.start(component);
} catch (PhaseExecutionException e) {
throw new ComponentLifecycleException("Error starting component", e);
}
}
public static class RequirementRecipe extends AbstractRecipe {
private ComponentDescriptor componentDescriptor;
private ComponentRequirement requirement;
private MutablePlexusContainer container;
private boolean autoMatch;
public RequirementRecipe(ComponentDescriptor componentDescriptor, ComponentRequirement requirement, MutablePlexusContainer container, boolean autoMatch) {
this.componentDescriptor = componentDescriptor;
this.requirement = requirement;
this.container = container;
this.autoMatch = autoMatch;
}
public boolean canCreate(Type expectedType) {
if (!autoMatch)
{
return true;
}
Class> propertyType = toClass(expectedType);
// Never auto match array, map or collection
if (propertyType.isArray() || Map.class.isAssignableFrom(propertyType) || Collection.class.isAssignableFrom(propertyType) || requirement instanceof ComponentRequirementList) {
return false;
}
// if the type to be created is an instance of the expected type, return true
try {
ComponentRegistry componentRegistry = container.getComponentRegistry();
return componentRegistry.getComponentDescriptor(propertyType, requirement.getRole(), requirement.getRoleHint()) != null;
} catch (Exception e) {
}
return false;
}
@Override
protected Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException {
Class> propertyType = toClass(expectedType);
try {
String role = requirement.getRole();
List roleHints = null;
if (requirement instanceof ComponentRequirementList) {
roleHints = ((ComponentRequirementList) requirement).getRoleHints();
}
Object assignment;
if (propertyType.isArray()) {
assignment = new ArrayList