001    /*****************************************************************************
002     * Copyright (C) PicoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     * Original code by Mike Rimov                                               *
009     *****************************************************************************/
010    package org.picocontainer.gems.containers;
011    
012    import java.util.ArrayList;
013    import java.util.HashMap;
014    import java.util.List;
015    import java.util.Map;
016    import java.util.Properties;
017    
018    import org.picocontainer.ComponentAdapter;
019    import org.picocontainer.ComponentFactory;
020    import org.picocontainer.ComponentMonitor;
021    import org.picocontainer.DefaultPicoContainer;
022    import org.picocontainer.LifecycleStrategy;
023    import org.picocontainer.MutablePicoContainer;
024    import org.picocontainer.Parameter;
025    import org.picocontainer.PicoCompositionException;
026    import org.picocontainer.PicoContainer;
027    import org.picocontainer.adapters.InstanceAdapter;
028    import org.picocontainer.behaviors.Stored;
029    
030    /**
031     * Normal PicoContainers are meant to be created, started, stopped, disposed and
032     * garbage collected. The goal of this container is to reduce the number of
033     * registration calls (and therefore objects created) in areas where performance
034     * is key (for example, this might be used in NanoContainer request containers).
035     * <p>
036     * It accomplishes its goal in two ways: <br/> (1) Once a container is disposed
037     * of, start() may be called again, allowing for recycling of the container. (This is default
038     * behavior with a picocontainer)
039     * </p>
040     * <p>
041     * (2) All instance registrations will be unregistered when stop is called. (For example,
042     * HttpServletRequest would be removed), and all component adapter instance values
043     * are flushed.
044     * </p>
045     * <h4>Container Storage</h4>
046     * <p>It is still up to the builder of this container to decide where to store its reference.  Since
047     * it is reusable, it needs to be stored someplace that doesn't easily expire.  Probably the most
048     * common storage location would be ThreadLocal storage.  HttpSession might be another valid
049     * storage location.</p>
050     * @author Michael Rimov
051     */
052    @SuppressWarnings("serial")
053    public class ReusablePicoContainer extends DefaultPicoContainer {
054    
055            private final List<ComponentAdapter<?>> instanceRegistrations = new ArrayList<ComponentAdapter<?>>();
056    
057            private final Map<ComponentAdapter<?>, Stored<?>> storedReferences = new HashMap<ComponentAdapter<?>, Stored<?>>();
058            
059            public ReusablePicoContainer() {
060                    super();
061            }
062    
063            public ReusablePicoContainer(final ComponentFactory componentFactory,
064                            final LifecycleStrategy lifecycleStrategy, final PicoContainer parent,
065                            final ComponentMonitor componentMonitor) {
066                    super(componentFactory, lifecycleStrategy, parent, componentMonitor);
067            }
068    
069            public ReusablePicoContainer(final ComponentFactory componentFactory,
070                            final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) {
071                    super(componentFactory, lifecycleStrategy, parent);
072            }
073    
074            public ReusablePicoContainer(final ComponentFactory componentFactory,
075                            final PicoContainer parent) {
076                    super(componentFactory, parent);
077            }
078    
079            public ReusablePicoContainer(final ComponentFactory componentFactory) {
080                    super(componentFactory);
081            }
082    
083            public ReusablePicoContainer(final ComponentMonitor monitor,
084                            final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) {
085                    super(monitor, lifecycleStrategy, parent);
086            }
087    
088            public ReusablePicoContainer(final ComponentMonitor monitor, final PicoContainer parent) {
089                    super(monitor, parent);
090            }
091    
092            public ReusablePicoContainer(final ComponentMonitor monitor) {
093                    super(monitor);
094            }
095    
096            public ReusablePicoContainer(final LifecycleStrategy lifecycleStrategy,
097                            final PicoContainer parent) {
098                    super(lifecycleStrategy, parent);
099            }
100    
101            public ReusablePicoContainer(final PicoContainer parent) {
102                    super(parent);
103            }
104    
105            @Override
106            public MutablePicoContainer addComponent(final Object componentKey,
107                            final Object componentImplementationOrInstance,
108                            final Parameter... parameters) throws PicoCompositionException {
109                    
110                    if (componentKey == null) {
111                            throw new NullPointerException("componentKey");
112                    }
113                    
114                    if (componentImplementationOrInstance == null) {
115                            throw new NullPointerException("componentImplementationOrInstance");                    
116                    }
117                    
118                    super.addComponent(componentKey, 
119                                    componentImplementationOrInstance, 
120                                    parameters);
121                    
122                    if (! (componentImplementationOrInstance instanceof Class)) {
123                            instanceRegistrations.add(super.getComponentAdapter(componentKey));
124                    } else {
125                            addStoredReference(componentKey);                       
126                    }
127                    
128                    return this;
129            }
130    
131            @Override
132            public  MutablePicoContainer addComponent(final Object implOrInstance)
133                            throws PicoCompositionException {
134                    if ((implOrInstance instanceof Class)) {
135                            super.addComponent(implOrInstance);
136                            addStoredReference(implOrInstance);
137                            return this;
138                    } else {
139                            return this.addComponent(implOrInstance.getClass(), implOrInstance);
140                    }
141            }
142    
143            /**
144             * Precalculates all references to Stored behaviors.
145             * @param key the object key.
146             */
147            private void addStoredReference(final Object key) {
148                    ComponentAdapter<?> ca = this.getComponentAdapter(key);
149                    Stored<?> stored =  ca.findAdapterOfType(Stored.class);
150                if (stored != null) {
151                    storedReferences.put(ca, stored);
152                }
153        }
154    
155            @Override
156            public synchronized void stop() {
157                    super.stop();
158                    
159                    //Remove all instance registrations.
160                    for (ComponentAdapter<?> eachAdapter : this.instanceRegistrations) {
161                            this.removeComponent(eachAdapter.getComponentKey());
162                    }
163                    
164                    instanceRegistrations.clear();
165                    
166                    //Flush all remaining objects.
167                    for (Stored<?> eachStoredBehavior : this.storedReferences.values()) {
168                            eachStoredBehavior.flush();                     
169                    }
170            }
171    
172            @Override
173        public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter, final Properties properties) {
174                    super.addAdapter(componentAdapter, properties);
175                    if (componentAdapter.findAdapterOfType(InstanceAdapter.class) != null) {
176                            this.instanceRegistrations.add(componentAdapter);
177                    } else {
178                            this.addStoredReference(componentAdapter.getComponentKey());
179                    }
180                    
181                    return this;
182        }
183    
184            @Override
185        public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter) {
186                    super.addAdapter(componentAdapter);
187                    if (componentAdapter.findAdapterOfType(InstanceAdapter.class) != null) {
188                            this.instanceRegistrations.add(componentAdapter);
189                    } else {
190                            this.addStoredReference(componentAdapter.getComponentKey());
191                    }
192                    
193                    return this;
194        }
195            
196            private void removeLocalReferences(final ComponentAdapter<?> ca) {
197                    this.storedReferences.remove(ca);
198            }
199    
200            @Override
201        public <T> ComponentAdapter<T> removeComponent(final Object componentKey) {
202                    ComponentAdapter<T> result =  super.removeComponent(componentKey);
203                    if (result != null) {
204                            removeLocalReferences(result);
205                    }
206                    
207                    return result;
208        }
209    
210            @Override
211        public <T> ComponentAdapter<T> removeComponentByInstance(final T componentInstance) {
212                    ComponentAdapter<T> result =  super.removeComponentByInstance(componentInstance);
213                    if (result != null) {
214                            removeLocalReferences(result);
215                    }
216                    
217                    return result;
218        }
219    
220        @Override
221            public MutablePicoContainer makeChildContainer() {
222            ReusablePicoContainer pc = new ReusablePicoContainer(componentFactory, lifecycleStrategy, this);
223            addChildContainer(pc);
224            return pc;
225        }
226    
227    }