001    package org.picocontainer.containers;
002    
003    import java.lang.annotation.Annotation;
004    
005    import org.picocontainer.ComponentAdapter;
006    import org.picocontainer.ComponentFactory;
007    import org.picocontainer.ComponentMonitor;
008    import org.picocontainer.DefaultPicoContainer;
009    import org.picocontainer.LifecycleStrategy;
010    import org.picocontainer.MutablePicoContainer;
011    import org.picocontainer.NameBinding;
012    import org.picocontainer.PicoContainer;
013    import org.picocontainer.behaviors.AdaptingBehavior;
014    import org.picocontainer.injectors.AdaptingInjection;
015    
016    @SuppressWarnings("serial")
017    public 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    }