import java.util.List;\r
import java.util.Map;\r
\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
import org.codehaus.plexus.PlexusContainer;\r
import org.codehaus.plexus.context.Context;\r
+import org.codehaus.plexus.context.ContextException;\r
import org.codehaus.plexus.logging.LogEnabled;\r
import org.codehaus.plexus.logging.LoggerManager;\r
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;\r
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;\r
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;\r
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;\r
import org.springframework.beans.BeansException;\r
import org.springframework.beans.SimpleTypeConverter;\r
import org.springframework.beans.TypeConverter;\r
import org.springframework.beans.factory.BeanInitializationException;\r
import org.springframework.beans.factory.DisposableBean;\r
import org.springframework.beans.factory.FactoryBean;\r
+import org.springframework.beans.factory.InitializingBean;\r
import org.springframework.beans.factory.ListableBeanFactory;\r
+import org.springframework.beans.factory.config.AbstractFactoryBean;\r
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;\r
import org.springframework.beans.factory.config.RuntimeBeanReference;\r
import org.springframework.util.ReflectionUtils;\r
\r
* </ul>\r
* If not set, the beanFActory will auto-detect the loggerManager to use by\r
* searching for the adequate bean in the spring context.\r
+ * <p>\r
*\r
* @author <a href="mailto:nicolas@apache.org">Nicolas De Loof</a>\r
*/\r
public class PlexusComponentFactoryBean\r
implements FactoryBean, BeanFactoryAware, DisposableBean\r
{\r
+ /** Logger available to subclasses */\r
+ protected final Log logger = LogFactory.getLog( getClass() );\r
+\r
+ /** The beanFactory */\r
+ private BeanFactory beanFactory;\r
+\r
/**\r
* @todo isn't there a constant for this in plexus ?\r
*/\r
private static final String SINGLETON = "singleton";\r
\r
+ /** The plexus component role */\r
private Class role;\r
\r
+ /** The plexus component implementation class */\r
private Class implementation;\r
\r
- private String instanciationStrategy = SINGLETON;\r
+ /** The plexus component instantiation strategy */\r
+ private String instantiationStrategy = SINGLETON;\r
\r
+ /** The plexus component requirements and configurations */\r
private Map requirements;\r
\r
- private ListableBeanFactory beanFactory;\r
-\r
- private LoggerManager loggerManager;\r
-\r
- private TypeConverter typeConverter = new SimpleTypeConverter();\r
-\r
+ /** The plexus component created by this FactoryBean */\r
private List instances = new LinkedList();\r
\r
- private Context context;\r
+ /** Optional plexus loggerManager */\r
+ private static LoggerManager loggerManager;\r
+\r
+ /** Optional plexus context */\r
+ private static Context context;\r
\r
+ /**\r
+ * {@inheritDoc}\r
+ *\r
+ * @see org.springframework.beans.factory.config.AbstractFactoryBean#destroy()\r
+ */\r
public void destroy()\r
throws Exception\r
{\r
{\r
for ( Iterator iterator = instances.iterator(); iterator.hasNext(); )\r
{\r
- Object component = (Object) iterator.next();\r
- if ( component instanceof Disposable )\r
+ Object isntance = iterator.next();\r
+ if ( isntance instanceof Disposable )\r
{\r
- ( (Disposable) component ).dispose();\r
-\r
+ ( (Disposable) isntance ).dispose();\r
}\r
}\r
}\r
}\r
\r
+ /**\r
+ * {@inheritDoc}\r
+ * @see org.springframework.beans.factory.FactoryBean#getObject()\r
+ */\r
public Object getObject()\r
throws Exception\r
{\r
- // Spring MAY cache the object built by this factory if getSingleton()\r
- // returns true, but can also requires us to ensure unicity.\r
- if ( SINGLETON.equals( instanciationStrategy ) && !instances.isEmpty() )\r
+ if ( isSingleton() && !instances.isEmpty())\r
{\r
return instances.get( 0 );\r
}\r
+ return createInstance();\r
+ }\r
\r
+ /**\r
+ * Create the plexus component instance. Inject dependencies declared as\r
+ * requirements using direct field injection\r
+ */\r
+ public Object createInstance()\r
+ throws Exception\r
+ {\r
+ logger.debug( "Creating plexus component " + implementation );\r
final Object component = implementation.newInstance();\r
synchronized ( instances )\r
{\r
}\r
if ( requirements != null )\r
{\r
- ReflectionUtils.doWithFields( implementation, new ReflectionUtils.FieldCallback()\r
+ for ( Iterator iterator = requirements.entrySet().iterator(); iterator.hasNext(); )\r
{\r
- public void doWith( Field field )\r
- throws IllegalArgumentException, IllegalAccessException\r
+ Map.Entry requirement = (Map.Entry) iterator.next();\r
+ String fieldName = (String) requirement.getKey();\r
+\r
+ if ( fieldName.startsWith( "#" ) )\r
{\r
- Object dependency = resolveRequirement( field );\r
- if ( dependency != null )\r
+ // implicit field injection : the field name was no\r
+ // specified in the plexus descriptor as only one filed\r
+ // matches Dependency type\r
+\r
+ RuntimeBeanReference ref = (RuntimeBeanReference) requirement.getValue();\r
+ Object dependency = beanFactory.getBean( ref.getBeanName() );\r
+\r
+ Field[] fields = implementation.getDeclaredFields();\r
+ for ( int i = 0; i < fields.length; i++ )\r
{\r
- ReflectionUtils.makeAccessible( field );\r
- ReflectionUtils.setField( field, component, dependency );\r
+ Field field = fields[i];\r
+ if ( ReflectionUtils.COPYABLE_FIELDS.matches( field )\r
+ && field.getType().isAssignableFrom( dependency.getClass() ) )\r
+ {\r
+ if ( logger.isTraceEnabled() )\r
+ {\r
+ logger.trace( "Injecting dependency " + dependency + " into field " + field.getName() );\r
+ }\r
+ ReflectionUtils.makeAccessible( field );\r
+ ReflectionUtils.setField( field, component, dependency );\r
+ }\r
}\r
}\r
- }, ReflectionUtils.COPYABLE_FIELDS );\r
+ else\r
+ {\r
+ // explicit field injection\r
+ Field field;\r
+ try\r
+ {\r
+ fieldName = PlexusToSpringUtils.toCamelCase( fieldName );\r
+ field = implementation.getDeclaredField( fieldName );\r
+ }\r
+ catch ( NoSuchFieldException e )\r
+ {\r
+ logger.warn( "No field " + fieldName + " on implementation class " + implementation );\r
+ continue;\r
+ }\r
+ Object dependency = resolveRequirement( field, requirement.getValue() );\r
+ if ( logger.isTraceEnabled() )\r
+ {\r
+ logger.trace( "Injecting dependency " + dependency + " into field " + field.getName() );\r
+ }\r
+ ReflectionUtils.makeAccessible( field );\r
+ ReflectionUtils.setField( field, component, dependency );\r
+ }\r
+ }\r
}\r
\r
+ handlePlexusLifecycle( component );\r
+\r
+ return component;\r
+ }\r
+\r
+ private void handlePlexusLifecycle( final Object component )\r
+ throws ContextException, InitializationException\r
+ {\r
if ( component instanceof LogEnabled )\r
{\r
( (LogEnabled) component ).enableLogging( getLoggerManager().getLoggerForComponent( role.getName() ) );\r
}\r
\r
- if (component instanceof Contextualizable )\r
+ if ( component instanceof Contextualizable )\r
{\r
- // VERRY limiter support for Contextualizable\r
- ((Contextualizable) component).contextualize( getContext() );\r
+ // VERRY limited support for Contextualizable\r
+ ( (Contextualizable) component ).contextualize( getContext() );\r
}\r
\r
+ // TODO add support for Startable, Stopable -> LifeCycle ?\r
+\r
if ( component instanceof Initializable )\r
{\r
( (Initializable) component ).initialize();\r
}\r
+ }\r
\r
- // TODO add support for Startable, Stopable -> LifeCycle ?\r
+ /**\r
+ * Resolve the requirement that this field exposes in the component\r
+ *\r
+ * @param field\r
+ * @return\r
+ */\r
+ protected Object resolveRequirement( Field field, Object requirement )\r
+ {\r
+ if ( requirement instanceof RuntimeBeanReference )\r
+ {\r
+ String beanName = ( (RuntimeBeanReference) requirement ).getBeanName();\r
+ if ( Map.class.isAssignableFrom( field.getType() ) )\r
+ {\r
+ // component ask plexus for a Map of all available\r
+ // components for the role\r
+ requirement = PlexusToSpringUtils.lookupMap( beanName, getListableBeanFactory() );\r
+ }\r
+ else if ( Collection.class.isAssignableFrom( field.getType() ) )\r
+ {\r
+ requirement = PlexusToSpringUtils.LookupList( beanName, getListableBeanFactory() );\r
+ }\r
+ else\r
+ {\r
+ requirement = beanFactory.getBean( beanName );\r
+ }\r
+ }\r
+ if ( requirement != null )\r
+ {\r
+ requirement = getBeanTypeConverter().convertIfNecessary( requirement, field.getType() );\r
+ }\r
+ return requirement;\r
\r
- return component;\r
}\r
\r
public Class getObjectType()\r
\r
public boolean isSingleton()\r
{\r
- return SINGLETON.equals( instanciationStrategy );\r
+ return SINGLETON.equals( instantiationStrategy );\r
}\r
\r
/**\r
*/\r
protected Context getContext()\r
{\r
- if (context == null)\r
+ if ( context == null )\r
{\r
PlexusContainer container = (PlexusContainer) beanFactory.getBean( "plexusContainer" );\r
context = container.getContext();\r
return context;\r
}\r
\r
+ protected TypeConverter getBeanTypeConverter()\r
+ {\r
+ if ( beanFactory instanceof ConfigurableBeanFactory )\r
+ {\r
+ return ( (ConfigurableBeanFactory) beanFactory ).getTypeConverter();\r
+ }\r
+ else\r
+ {\r
+ return new SimpleTypeConverter();\r
+ }\r
+ }\r
+\r
/**\r
* Retrieve the loggerManager instance to be used for LogEnabled components\r
+ *\r
* @return\r
*/\r
protected LoggerManager getLoggerManager()\r
{\r
loggerManager = (LoggerManager) beanFactory.getBean( "loggerManager" );\r
}\r
- Map loggers = beanFactory.getBeansOfType( LoggerManager.class );\r
- if ( loggers.size() == 1 )\r
- {\r
- loggerManager = (LoggerManager) loggers.values().iterator().next();\r
- }\r
else\r
{\r
- throw new BeanInitializationException(\r
- "You must explicitly set a LoggerManager or define a unique one in bean context" );\r
+ Map loggers = getListableBeanFactory().getBeansOfType( LoggerManager.class );\r
+ if ( loggers.size() == 1 )\r
+ {\r
+ loggerManager = (LoggerManager) loggers.values().iterator().next();\r
+ }\r
}\r
}\r
+ if ( loggerManager == null )\r
+ {\r
+ throw new BeanCreationException( "A LoggerManager instance must be set in the applicationContext" );\r
+ }\r
return loggerManager;\r
}\r
\r
- public void setBeanFactory( BeanFactory beanFactory )\r
- throws BeansException\r
+ private ListableBeanFactory getListableBeanFactory()\r
{\r
- this.beanFactory = (ListableBeanFactory) beanFactory;\r
+ if ( beanFactory instanceof ListableBeanFactory )\r
+ {\r
+ return (ListableBeanFactory) beanFactory;\r
+ }\r
+ throw new BeanInitializationException( "A ListableBeanFactory is required by the PlexusComponentFactoryBean" );\r
}\r
\r
/**\r
*/\r
public void setLoggerManager( LoggerManager loggerManager )\r
{\r
- this.loggerManager = loggerManager;\r
+ PlexusComponentFactoryBean.loggerManager = loggerManager;\r
}\r
\r
/**\r
{\r
throw new BeanCreationException( "Plexus poolable instanciation-strategy is not supported" );\r
}\r
- this.instanciationStrategy = instanciationStrategy;\r
+ this.instantiationStrategy = instanciationStrategy;\r
}\r
\r
/**\r
this.requirements = requirements;\r
}\r
\r
- protected void setTypeConverter( TypeConverter typeConverter )\r
+ public void setContext( Context context )\r
{\r
- this.typeConverter = typeConverter;\r
+ PlexusComponentFactoryBean.context = context;\r
}\r
\r
-\r
- /**\r
- * Resolve the requirement that this field exposes in the component\r
- * @param field\r
- * @return\r
- */\r
- protected Object resolveRequirement( Field field )\r
- {\r
- Object dependency = requirements.get( field.getName() );\r
- if ( dependency instanceof RuntimeBeanReference )\r
- {\r
- String beanName = ( (RuntimeBeanReference) dependency ).getBeanName();\r
- if ( Map.class.isAssignableFrom( field.getType() ) )\r
- {\r
- // component ask plexus for a Map of all available\r
- // components for the role\r
- dependency = PlexusToSpringUtils.lookupMap( beanName, beanFactory );\r
- }\r
- else if ( Collection.class.isAssignableFrom( field.getType() ) )\r
- {\r
- dependency = PlexusToSpringUtils.LookupList( beanName, beanFactory );\r
- }\r
- else\r
- {\r
- dependency = beanFactory.getBean( beanName );\r
- }\r
- }\r
- if (dependency != null)\r
- {\r
- dependency = typeConverter.convertIfNecessary( dependency, field.getType() );\r
- }\r
- return dependency;\r
-\r
- }\r
-\r
- protected void setContext( Context context )\r
+ public void setBeanFactory( BeanFactory beanFactory )\r
{\r
- this.context = context;\r
+ this.beanFactory = beanFactory;\r
}\r
\r
}\r