001package org.picocontainer.containers;
002
003import java.lang.annotation.Annotation;
004
005import org.picocontainer.ComponentAdapter;
006import org.picocontainer.ComponentFactory;
007import org.picocontainer.ComponentMonitor;
008import org.picocontainer.DefaultPicoContainer;
009import org.picocontainer.LifecycleStrategy;
010import org.picocontainer.MutablePicoContainer;
011import org.picocontainer.NameBinding;
012import org.picocontainer.PicoContainer;
013import org.picocontainer.behaviors.AdaptingBehavior;
014import org.picocontainer.injectors.AdaptingInjection;
015
016@SuppressWarnings("serial")
017public class TieringPicoContainer extends DefaultPicoContainer {
018
019    /**
020     * Creates a new container with a custom ComponentFactory, LifecycleStrategy for instance registration,
021     * and a parent container.
022     * <em>
023     * Important note about caching: If you intend the components to be cached, you should pass
024     * in a factory that creates {@link org.picocontainer.behaviors.Cached} instances, such as for example
025     * {@link org.picocontainer.behaviors.Caching}. Caching can delegate to other ComponentAdapterFactories.
026     * </em>
027     *
028     * @param componentFactory the factory to use for creation of ComponentAdapters.
029     * @param lifecycleStrategy
030     *                                the lifecycle strategy chosen for registered
031     *                                instance (not implementations!)
032     * @param parent                  the parent container (used for component dependency lookups).
033     */
034    public TieringPicoContainer(final ComponentFactory componentFactory, final LifecycleStrategy lifecycleStrategy,
035                                final PicoContainer parent) {
036        super(componentFactory, lifecycleStrategy, parent);
037    }
038
039    public TieringPicoContainer(final ComponentFactory componentFactory, final LifecycleStrategy lifecycleStrategy,
040                                final PicoContainer parent, final ComponentMonitor componentMonitor) {
041        super(componentFactory, lifecycleStrategy, parent, componentMonitor);
042    }
043
044    /**
045     * Creates a new container with the AdaptingInjection using a
046     * custom ComponentMonitor
047     *
048     * @param monitor the ComponentMonitor to use
049     * @param parent  the parent container (used for component dependency lookups).
050     */
051    public TieringPicoContainer(final ComponentMonitor monitor, final PicoContainer parent) {
052        super(monitor, parent);
053    }
054
055    /**
056     * Creates a new container with the AdaptingInjection using a
057     * custom ComponentMonitor and lifecycle strategy
058     *
059     * @param monitor           the ComponentMonitor to use
060     * @param lifecycleStrategy the lifecycle strategy to use.
061     * @param parent            the parent container (used for component dependency lookups).
062     */
063    public TieringPicoContainer(final ComponentMonitor monitor, final LifecycleStrategy lifecycleStrategy,
064                                final PicoContainer parent) {
065        super(monitor, lifecycleStrategy, parent);
066    }
067
068    /**
069     * Creates a new container with the AdaptingInjection using a
070     * custom lifecycle strategy
071     *
072     * @param lifecycleStrategy the lifecycle strategy to use.
073     * @param parent            the parent container (used for component dependency lookups).
074     */
075    public TieringPicoContainer(final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) {
076        super(lifecycleStrategy, parent);
077    }
078
079
080    /**
081     * Creates a new container with a custom ComponentFactory and no parent container.
082     *
083     * @param componentFactory the ComponentFactory to use.
084     */
085    public TieringPicoContainer(final ComponentFactory componentFactory) {
086        super(componentFactory);
087    }
088
089    /**
090     * Creates a new container with the AdaptingInjection using a
091     * custom ComponentMonitor
092     *
093     * @param monitor the ComponentMonitor to use
094     */
095    public TieringPicoContainer(final ComponentMonitor monitor) {
096        super(monitor);
097    }
098
099    /**
100     * Creates a new container with a (caching) {@link AdaptingInjection}
101     * and a parent container.
102     *
103     * @param parent the parent container (used for component dependency lookups).
104     */
105    public TieringPicoContainer(final PicoContainer parent) {
106        super(parent);
107    }
108
109    /** Creates a new container with a {@link AdaptingBehavior} and no parent container. */
110    public TieringPicoContainer() {
111        super();
112    }
113
114    public PicoContainer getParent() {
115        return new TieringGuard(super.getParent());
116    }
117
118    public MutablePicoContainer makeChildContainer() {
119        return new TieringPicoContainer(super.componentFactory, super.lifecycleStrategy, this, super.componentMonitor);
120    }
121
122    private static class TieringGuard extends AbstractDelegatingPicoContainer {
123
124        private static final AskingParentForComponent askingParentForComponent = new AskingParentForComponent();
125
126        public TieringGuard(PicoContainer parent) {
127            super(parent);
128        }
129
130        public <T> ComponentAdapter<T> getComponentAdapter(Class<T> componentType, NameBinding componentNameBinding) {
131            boolean iDidIt = false;
132            try {
133                if (notYetAskingParentForComponent()) {
134                    nowAskingParentForComponent();
135                    iDidIt = true;
136                    return super.getComponentAdapter(componentType, componentNameBinding);
137                } else {
138                    return null;
139                }
140            } finally {
141                if (iDidIt) {
142                    doneAskingParentForComponent();
143                }
144            }
145        }
146
147        private <T> void nowAskingParentForComponent() {
148            askingParentForComponent.set(Boolean.TRUE);
149        }
150
151        public <T> ComponentAdapter<T> getComponentAdapter(Class<T> componentType, Class<? extends Annotation> binding) {
152            boolean iDidIt = false;
153            try {
154                if (notYetAskingParentForComponent()) {
155                    nowAskingParentForComponent();
156                    iDidIt = true;
157                    return super.getComponentAdapter(componentType, binding);
158                } else {
159                    return null;
160                }
161            } finally {
162                if (iDidIt) {
163                    doneAskingParentForComponent();
164                }
165            }
166        }
167
168        private <T> void doneAskingParentForComponent() {
169            askingParentForComponent.set(Boolean.FALSE);
170        }
171
172        private <T> boolean notYetAskingParentForComponent() {
173            return askingParentForComponent.get() == Boolean.FALSE;
174        }
175
176        public ComponentAdapter<?> getComponentAdapter(Object componentKey) {
177            boolean iDidIt = false;
178            try {
179                if (notYetAskingParentForComponent()) {
180                    nowAskingParentForComponent();
181                    iDidIt = true;
182                    return super.getComponentAdapter(componentKey);
183                } else {
184                    return null;
185                }
186            } finally {
187                if (iDidIt) {
188                    doneAskingParentForComponent();
189                }
190            }
191        }
192    }
193    private static class AskingParentForComponent extends ThreadLocal {
194        protected Object initialValue() {
195            return Boolean.FALSE;
196        }
197    }
198}