package org.codehaus.plexus.component.manager; /* * 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. */ import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.ExecutionException; import org.codehaus.plexus.component.factory.ComponentInstantiationException; import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException; import org.codehaus.plexus.component.repository.ComponentDescriptor; import org.codehaus.plexus.MutablePlexusContainer; import org.codehaus.plexus.lifecycle.LifecycleHandler; /** * This ensures a component is only used as a singleton, and is only shutdown when the container * shuts down. * * @author Jason van Zyl */ public class SingletonComponentManager<T> extends AbstractComponentManager<T> { private boolean disposed; private Future<T> singletonFuture; public SingletonComponentManager( MutablePlexusContainer container, LifecycleHandler lifecycleHandler, ComponentDescriptor<T> componentDescriptor ) { super( container, lifecycleHandler, componentDescriptor ); } public synchronized void dispose() throws ComponentLifecycleException { T singleton; synchronized ( this ) { disposed = true; singleton = getExistingInstance(true); } // do not call destroyInstance inside of a synchronized block because // destroyInstance results in several callbacks to user code which // could result in a dead lock if ( singleton != null ) { destroyInstance( singleton ); } } public T getComponent( ) throws ComponentInstantiationException, ComponentLifecycleException { FutureTask<T> singletonFuture; synchronized (this) { if (disposed) { throw new ComponentLifecycleException("This ComponentManager has already been destroyed"); } // if singleton already created, simply return the existing singleton T singleton = getExistingInstance( false ); if (singleton != null) { return singleton; } // no existing singleton, create a new one singletonFuture = new FutureTask<T>(new CreateInstance()); this.singletonFuture = singletonFuture; } // do not call CreateInstance.get() inside of a synchronized block because createInstance results in // several callbacks to user code which could result in a dead lock if ( singletonFuture != null ) { singletonFuture.run(); } // try to get the future instance try { return singletonFuture.get(); } catch ( Exception e ) { // creation failed... clear future reference synchronized ( this ) { // only clear if still refering to this method's future if ( this.singletonFuture == singletonFuture ) { this.singletonFuture = null; } } // future.get() normally throws an execution execption which contains the real cause Throwable cause = e; if ( e instanceof ExecutionException && e.getCause() != null ) { cause = e.getCause(); } // rethrow ComponentInstantiationException if ( cause instanceof ComponentInstantiationException ) { throw (ComponentInstantiationException) cause; } // rethrow ComponentLifecycleException if ( cause instanceof ComponentLifecycleException ) { throw (ComponentLifecycleException) cause; } // nothing else was expected throw new ComponentLifecycleException( "Unexpected error obtaining singleton instance", cause ); } } public void release( Object component ) throws ComponentLifecycleException { T singleton = getExistingInstance(true); // do not call destroyInstance inside of a synchronized block because // destroyInstance results in several callbacks to user code which // could result in a dead lock if ( singleton != null ) { destroyInstance( singleton ); } } public synchronized String toString() { T singleton = getExistingInstance(false); return "SingletonComponentManager[" + singleton == null ? getComponentDescriptor().getImplementationClass().getName() : singleton + "]"; } private T getExistingInstance(boolean clearFuture) { synchronized (this) { try { return singletonFuture.get(); } catch (Exception e) { // ignored - exception will have been reported in the createInstance method } finally { if (clearFuture) { singletonFuture = null; } } } return null; } private class CreateInstance implements Callable<T> { public T call() throws Exception { return createInstance(); } } }