diff options
Diffstat (limited to 'israfil-foundation-container/src/main/java/net/israfil/foundation/container/DefaultContainer.java')
| -rw-r--r-- | israfil-foundation-container/src/main/java/net/israfil/foundation/container/DefaultContainer.java | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/israfil-foundation-container/src/main/java/net/israfil/foundation/container/DefaultContainer.java b/israfil-foundation-container/src/main/java/net/israfil/foundation/container/DefaultContainer.java new file mode 100644 index 0000000..f11dc47 --- /dev/null +++ b/israfil-foundation-container/src/main/java/net/israfil/foundation/container/DefaultContainer.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2008 Israfil Consulting Services Corporation + * Copyright (c) 2008 Christian Edward Gruber + * All Rights Reserved + * + * This software is licensed under the Berkeley Standard Distribution license, + * (BSD license), as defined below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Israfil Consulting Services nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $Id: Copyright.java 618 2008-04-14 14:03:03Z christianedwardgruber $ + */ +package net.israfil.foundation.container; + +import java.util.HashMap; +import java.util.Map; + +import net.israfil.foundation.container.adapters.IndependentAutoWiringAdapter; +import net.israfil.foundation.container.error.ComponentAlreadyRegisteredError; +import net.israfil.foundation.container.error.CouldNotCreateComponentError; +import net.israfil.foundation.container.error.UnsatisfiedDependencyError; +import net.israfil.foundation.container.util.CyclicalReferenceDetectionUtil; +import net.israfil.foundation.container.util.NonDuplicateStack; + + +/** + * A default implementation of AutoWiringAdaptableContainer, a Container + * that uses an AutoWiringAdapter to ensure that components can be + * created appropriately with their dependencies satisfied automatically. + * + * This adapter is intended for constructor injection, but relies on the + * adapter to perform such. No reflection is used by this class itself. + * + */ +public class DefaultContainer extends AbstractContainer implements AutoWiringAdaptableContainer { + + private Map<Object,Object> registry = new HashMap<Object,Object>(); + + private final boolean failEarly; + + private boolean starting = false; + + private final Object CREATION_MUTEX = new Object(); + + /** + * A default constructor that will throw errors regarding circular + * dependencies at registration time, throw missing dependency errors + * at wire-up (getComponent()) time, and has no parent. + * + * @param failEarly A boolean to indicate that this container should detect missing or circular dependencies at start() time. + */ + public DefaultContainer() { + super(); + this.failEarly = false; + } + + /** + * Construct this container such that it detects missing dependencies + * upon invocation of start(). Otherwise, it will fail at wire time, + * rather than at registration time. Circular references will give errors + * at registration time. + * + * @param failEarly A boolean to indicate that this container should detect missing or circular dependencies at start() time. + */ + public DefaultContainer(boolean failEarly) { + super(); + this.failEarly = failEarly; + } + + /** + * This method constructs a DefaultAutoWiringAdaptableContainer that + * has a parent container for backup resolution. The parent container + * can be of any type of Container. + * + * @param parent The parent container (optional) + */ + public DefaultContainer(Container parent) { + super(parent); + this.failEarly = false; + } + + /** + * Construct this container such that it detects missing dependencies + * upon invocation of start(). Otherwise, it will fail at wire time, + * rather than at registration time. Circular references will give errors + * at registration time. This method also provides for a parent container. + * The parent container can be of any type of Container. + * + * @param failEarly A boolean to indicate that this container should detect missing or circular dependencies at start() time. + * @param parent The parent Container (optional) + */ + public DefaultContainer(Container parent, boolean failEarly) { + super(parent); + this.failEarly = failEarly; + } + + public synchronized void start() { + if (isRunning() || + starting) return; + if (getParent() != null && !getParent().isRunning()) throw new RuntimeException("Parent container is not started."); + this.starting = true; + if (failEarly) { + for (Object key : registry.keySet()) { + // force get each component, forcing all wiring. + Object ignore = getComponent(key); + if (false) ignore.hashCode(); // cover warning - compiler should remove + } + } + super.start(); + starting = false; + } + + public void registerType(Object key, Class<?> componentType) { + registerType(key,componentType, 0); + } + + protected void registerType(Object key, Class<?> componentType, long timeout) { + registerType(key,new IndependentAutoWiringAdapter(componentType), timeout); + } + + public void registerType(Object key, AutoWiringAdapter componentAdapter) { + registerType(key,componentAdapter,0); + } + + /** + * Registers a component for later instantiation and (optionally) startup. + * + * @param key the key by which this component will be identified in the system + * @param componentAdapter an AutoWiringAdapter for creating this component and listing its dependencies + * @param timeout an (optional) timeout applied to any startup lifecycle + */ + protected void registerType(Object key, AutoWiringAdapter componentAdapter, long timeout) { + if (this.isRunning()) throw new RuntimeException("Cannot register when container is started."); + // FIXME: Figure out whether to support parent registry checking. Probably can't do it. + + if (registry.containsKey(key)) throw new ComponentAlreadyRegisteredError("Component already registered for " + key); + detectCircularDependencies(key,componentAdapter); + registry.put(key, componentAdapter); + } + + private void detectCircularDependencies(Object key,AutoWiringAdapter componentAdapter) { + NonDuplicateStack nds = new NonDuplicateStack(); + nds.push(key); + CyclicalReferenceDetectionUtil.detectCircularDependencies(this.registry, nds, componentAdapter); + } + + /** + * This method wires the object up with its dependencies, providing said + * dependencies to the object's adapter. It also starts the object if its + * startable + * @param originalKey + * @param adapter + * @return + */ + private Object wireObject(Object originalKey, AutoWiringAdapter adapter) { + Object[] dependencies = adapter.dependencies(); + Object[] parameters = new Object[dependencies.length]; + for (int depn = 0; depn < dependencies.length; depn++ ) { + Object key = adapter.dependencies()[depn]; + parameters[depn] = getComponent(key); + if (parameters[depn] == null) throw new UnsatisfiedDependencyError("Could not materialize dependency for key " + key); + } + try { + Object o = adapter.create(parameters); + if (o == null) throw new InstantiationException("Could not create a non-null object. Adapter " + adapter.getClass().getName() + " returned null."); + else { + try { + Startable s = ((Startable)o); + if (!s.isRunning()) s.start(); + } catch (ClassCastException e) {} + return o; + } + } catch (IllegalAccessException e) { + throw new CouldNotCreateComponentError("Could not create component " + originalKey + " of type " + adapter.getType(),e); + } catch (InstantiationException e) { + throw new CouldNotCreateComponentError("Could not create component " + originalKey + " of type " + adapter.getType(),e); + } catch (Throwable e) { + throw new CouldNotCreateComponentError("Could not create component " + originalKey + " of type " + adapter.getType(),e); + } + } + + public Object getComponent(Object key) { + Object component = super.getStoredComponent(key); + if (component == null && registry.containsKey(key)) { + synchronized (CREATION_MUTEX) { + if (component == null) { // in case following thread gets in just after storage. + component = wireObject(key,(AutoWiringAdapter)registry.get(key)); + store(key, component); + } + } + } + return component; + } + +} |
