summaryrefslogtreecommitdiff
path: root/israfil-foundation-container/src/main/java/net/israfil/foundation/container/DefaultContainer.java
diff options
context:
space:
mode:
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.java216
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;
+ }
+
+}