/******************************************************************************* * 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 } } } }