blob: 2105c5faa183c895b3f75ef26a61d9ce6337d09d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-present Sonatype, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stuart McCulloch (Sonatype, Inc.) - initial API and implementation
*******************************************************************************/
package org.eclipse.sisu.plexus;
import java.security.SecureClassLoader;
import java.util.ArrayDeque;
import java.util.Deque;
import javax.inject.Provider;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.LoggerManager;
import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable;
import org.eclipse.sisu.bean.BeanManager;
import org.eclipse.sisu.bean.BeanProperty;
import org.eclipse.sisu.bean.BeanScheduler;
import org.eclipse.sisu.bean.PropertyBinding;
import org.eclipse.sisu.inject.Logs;
import com.google.inject.Binder;
import com.google.inject.Module;
/**
* {@link BeanManager} that manages Plexus components requiring lifecycle management.
*/
public final class PlexusLifecycleManager
extends BeanScheduler
implements BeanManager, Module
{
// ----------------------------------------------------------------------
// Constants
// ----------------------------------------------------------------------
private static final Class<?>[] LIFECYCLE_TYPES =
{ LogEnabled.class, Contextualizable.class, Initializable.class, Startable.class, Disposable.class };
// ----------------------------------------------------------------------
// Implementation fields
// ----------------------------------------------------------------------
private final Deque<Startable> startableBeans = new ArrayDeque<Startable>();
private final Deque<Disposable> disposableBeans = new ArrayDeque<Disposable>();
private final Logger consoleLogger = new ConsoleLogger();
private final Provider<Context> plexusContextProvider;
private final Provider<LoggerManager> plexusLoggerManagerProvider;
private final Provider<?> slf4jLoggerFactoryProvider;
private final BeanManager delegate;
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
public PlexusLifecycleManager( final Provider<Context> plexusContextProvider,
final Provider<LoggerManager> plexusLoggerManagerProvider,
final Provider<?> slf4jLoggerFactoryProvider, //
final BeanManager delegate )
{
this.plexusContextProvider = plexusContextProvider;
this.plexusLoggerManagerProvider = plexusLoggerManagerProvider;
this.slf4jLoggerFactoryProvider = slf4jLoggerFactoryProvider;
this.delegate = delegate;
}
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
public void configure( final Binder binder )
{
BeanScheduler.MODULE.configure( binder );
}
public boolean manage( final Class<?> clazz )
{
for ( final Class<?> lifecycleType : LIFECYCLE_TYPES )
{
if ( lifecycleType.isAssignableFrom( clazz ) )
{
return true;
}
}
return null != delegate ? delegate.manage( clazz ) : false;
}
@SuppressWarnings( "rawtypes" )
public PropertyBinding manage( final BeanProperty property )
{
final Class clazz = property.getType().getRawType();
if ( "org.slf4j.Logger".equals( clazz.getName() ) )
{
return new PropertyBinding()
{
@SuppressWarnings( "unchecked" )
public <B> void injectProperty( final B bean )
{
property.set( bean, getSLF4JLogger( bean ) );
}
};
}
if ( Logger.class.equals( clazz ) )
{
return new PropertyBinding()
{
@SuppressWarnings( "unchecked" )
public <B> void injectProperty( final B bean )
{
property.set( bean, getPlexusLogger( bean ) );
}
};
}
return null != delegate ? delegate.manage( property ) : null;
}
public boolean manage( final Object bean )
{
if ( bean instanceof Disposable )
{
synchronizedPush( disposableBeans, (Disposable) bean );
}
if ( bean instanceof LogEnabled )
{
( (LogEnabled) bean ).enableLogging( getPlexusLogger( bean ) );
}
if ( bean instanceof Contextualizable || bean instanceof Initializable || bean instanceof Startable )
{
schedule( bean );
}
return null != delegate ? delegate.manage( bean ) : true;
}
public boolean unmanage( final Object bean )
{
if ( synchronizedRemove( startableBeans, bean ) )
{
stop( (Startable) bean );
}
if ( synchronizedRemove( disposableBeans, bean ) )
{
dispose( (Disposable) bean );
}
return null != delegate ? delegate.unmanage( bean ) : true;
}
public boolean unmanage()
{
for ( Startable bean; ( bean = synchronizedPop( startableBeans ) ) != null; )
{
stop( bean );
}
for ( Disposable bean; ( bean = synchronizedPop( disposableBeans ) ) != null; )
{
dispose( bean );
}
return null != delegate ? delegate.unmanage() : true;
}
// ----------------------------------------------------------------------
// Customized methods
// ----------------------------------------------------------------------
@Override
protected void activate( final Object bean )
{
final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try
{
for ( Class<?> clazz = bean.getClass(); clazz != null; clazz = clazz.getSuperclass() )
{
// need to check hierarchy in case bean is proxied
final ClassLoader loader = clazz.getClassLoader();
if ( loader instanceof SecureClassLoader )
{
Thread.currentThread().setContextClassLoader( loader );
break;
}
}
/*
* Run through the startup phase of the standard plexus "personality"
*/
if ( bean instanceof Contextualizable )
{
contextualize( (Contextualizable) bean );
}
if ( bean instanceof Initializable )
{
initialize( (Initializable) bean );
}
if ( bean instanceof Startable )
{
// register before calling start in case it fails
final Startable startableBean = (Startable) bean;
synchronizedPush( startableBeans, startableBean );
start( startableBean );
}
}
finally
{
Thread.currentThread().setContextClassLoader( tccl );
}
}
// ----------------------------------------------------------------------
// Implementation methods
// ----------------------------------------------------------------------
Logger getPlexusLogger( final Object bean )
{
final String name = bean.getClass().getName();
try
{
return plexusLoggerManagerProvider.get().getLoggerForComponent( name, null );
}
catch ( final RuntimeException e )
{
return consoleLogger;
}
}
Object getSLF4JLogger( final Object bean )
{
final String name = bean.getClass().getName();
try
{
return ( (org.slf4j.ILoggerFactory) slf4jLoggerFactoryProvider.get() ).getLogger( name );
}
catch ( final RuntimeException e )
{
return org.slf4j.LoggerFactory.getLogger( name );
}
}
private static <T> void synchronizedPush( final Deque<T> deque, final T element )
{
synchronized ( deque )
{
deque.addLast( element );
}
}
private static boolean synchronizedRemove( final Deque<?> deque, final Object element )
{
synchronized ( deque )
{
return deque.remove( element );
}
}
private static <T> T synchronizedPop( final Deque<T> deque )
{
synchronized ( deque )
{
return deque.pollLast();
}
}
private void contextualize( final Contextualizable bean )
{
Logs.trace( "Contextualize: <>", bean, null );
try
{
bean.contextualize( plexusContextProvider.get() );
}
catch ( final Throwable e )
{
Logs.catchThrowable( e );
try
{
getPlexusLogger( this ).warn( "Error contextualizing: " + Logs.identityToString( bean ), e );
}
finally
{
Logs.throwUnchecked( e );
}
}
}
private void initialize( final Initializable bean )
{
Logs.trace( "Initialize: <>", bean, null );
try
{
bean.initialize();
}
catch ( final Throwable e )
{
Logs.catchThrowable( e );
try
{
getPlexusLogger( this ).warn( "Error initializing: " + Logs.identityToString( bean ), e );
}
finally
{
Logs.throwUnchecked( e );
}
}
}
private void start( final Startable bean )
{
Logs.trace( "Start: <>", bean, null );
try
{
bean.start();
}
catch ( final Throwable e )
{
Logs.catchThrowable( e );
try
{
getPlexusLogger( this ).warn( "Error starting: " + Logs.identityToString( bean ), e );
}
finally
{
Logs.throwUnchecked( e );
}
}
}
@SuppressWarnings( "finally" )
private void stop( final Startable bean )
{
Logs.trace( "Stop: <>", bean, null );
try
{
bean.stop();
}
catch ( final Throwable e )
{
Logs.catchThrowable( e );
try
{
getPlexusLogger( this ).warn( "Problem stopping: " + Logs.identityToString( bean ), e );
}
finally
{
return; // ignore any logging exceptions and continue with shutdown
}
}
}
@SuppressWarnings( "finally" )
private void dispose( final Disposable bean )
{
Logs.trace( "Dispose: <>", bean, null );
try
{
bean.dispose();
}
catch ( final Throwable e )
{
Logs.catchThrowable( e );
try
{
getPlexusLogger( this ).warn( "Problem disposing: " + Logs.identityToString( bean ), e );
}
finally
{
return; // ignore any logging exceptions and continue with shutdown
}
}
}
}