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 *****************************************************************************/
010package org.picocontainer.gems.containers;
011
012import java.util.ArrayList;
013import java.util.HashMap;
014import java.util.List;
015import java.util.Map;
016import java.util.Properties;
017
018import org.picocontainer.ComponentAdapter;
019import org.picocontainer.ComponentFactory;
020import org.picocontainer.ComponentMonitor;
021import org.picocontainer.DefaultPicoContainer;
022import org.picocontainer.LifecycleStrategy;
023import org.picocontainer.MutablePicoContainer;
024import org.picocontainer.Parameter;
025import org.picocontainer.PicoCompositionException;
026import org.picocontainer.PicoContainer;
027import org.picocontainer.adapters.InstanceAdapter;
028import 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 and adapter 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 * @todo Convert to composition.
052 */
053@SuppressWarnings("serial")
054public class ReusablePicoContainer extends DefaultPicoContainer {
055
056    /**
057     * A list of all addComponent(key, instance) calls that were made.  These will be removed from the container
058     * each time the container is stopped.
059     */
060        private final List<ComponentAdapter<?>> instanceRegistrations = new ArrayList<ComponentAdapter<?>>();
061
062        /**
063         * A list of all addFlushableAdapter() calls that were made.  These instances will be removed from
064         * the container each time it is stopped.
065         */
066    private final List<ComponentAdapter<?>> adapterRegistrations = new ArrayList<ComponentAdapter<?>>();
067        
068        private final Map<ComponentAdapter<?>, Stored<?>> storedReferences = new HashMap<ComponentAdapter<?>, Stored<?>>();
069
070        /**
071         * Default constructor.
072         */
073        public ReusablePicoContainer() {
074                super();
075        }
076
077        public ReusablePicoContainer(final ComponentFactory componentFactory,
078                        final LifecycleStrategy lifecycleStrategy, final PicoContainer parent,
079                        final ComponentMonitor componentMonitor) {
080                super(componentFactory, lifecycleStrategy, parent, componentMonitor);
081        }
082
083        public ReusablePicoContainer(final ComponentFactory componentFactory,
084                        final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) {
085                super(componentFactory, lifecycleStrategy, parent);
086        }
087
088        public ReusablePicoContainer(final ComponentFactory componentFactory,
089                        final PicoContainer parent) {
090                super(componentFactory, parent);
091        }
092
093        public ReusablePicoContainer(final ComponentFactory componentFactory) {
094                super(componentFactory);
095        }
096
097        public ReusablePicoContainer(final ComponentMonitor monitor,
098                        final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) {
099                super(monitor, lifecycleStrategy, parent);
100        }
101
102        public ReusablePicoContainer(final ComponentMonitor monitor, final PicoContainer parent) {
103                super(monitor, parent);
104        }
105
106        public ReusablePicoContainer(final ComponentMonitor monitor) {
107                super(monitor);
108        }
109
110        public ReusablePicoContainer(final LifecycleStrategy lifecycleStrategy,
111                        final PicoContainer parent) {
112                super(lifecycleStrategy, parent);
113        }
114
115        public ReusablePicoContainer(final PicoContainer parent) {
116                super(parent);
117        }
118
119        @Override
120        public MutablePicoContainer addComponent(final Object componentKey,
121                        final Object componentImplementationOrInstance,
122                        final Parameter... parameters) throws PicoCompositionException {
123                
124                if (componentKey == null) {
125                        throw new NullPointerException("componentKey");
126                }
127                
128                if (componentImplementationOrInstance == null) {
129                        throw new NullPointerException("componentImplementationOrInstance");                    
130                }
131                
132                super.addComponent(componentKey, 
133                                componentImplementationOrInstance, 
134                                parameters);
135                
136                if (! (componentImplementationOrInstance instanceof Class)) {
137                        instanceRegistrations.add(super.getComponentAdapter(componentKey));
138                } else {
139                        addStoredReference(componentKey);                       
140                }
141                
142                return this;
143        }
144
145        @Override
146        public  MutablePicoContainer addComponent(final Object implOrInstance)
147                        throws PicoCompositionException {
148                if ((implOrInstance instanceof Class)) {
149                        super.addComponent(implOrInstance);
150                        addStoredReference(implOrInstance);
151                        return this;
152                } else {
153                        return this.addComponent(implOrInstance.getClass(), implOrInstance);
154                }
155        }
156
157        /**
158         * Precalculates all references to Stored behaviors.
159         * @param key the object key.
160         */
161        private void addStoredReference(final Object key) {
162                ComponentAdapter<?> ca = this.getComponentAdapter(key);
163                Stored<?> stored =  ca.findAdapterOfType(Stored.class);
164            if (stored != null) {
165                storedReferences.put(ca, stored);
166            }
167    }
168
169        @Override
170        public synchronized void stop() {           
171                super.stop();
172                flushInstances();
173        }
174
175    /**
176     * Automatically called by {@link #stop() stop()}, this method clears all instantiated
177     * instances and readies the picocontainer for reuse. 
178     */
179    public void flushInstances() {
180        //Remove all instance registrations.
181                for (ComponentAdapter<?> eachAdapter : this.instanceRegistrations) {
182                        this.removeComponent(eachAdapter.getComponentKey());
183                }               
184                instanceRegistrations.clear();
185                
186                for (ComponentAdapter<?> eachAdapter : this.adapterRegistrations) {
187                    this.removeComponent(eachAdapter.getComponentKey());
188                }
189                adapterRegistrations.clear();
190                
191                //Flush all remaining objects.
192                for (Stored<?> eachStoredBehavior : this.storedReferences.values()) {
193                        eachStoredBehavior.flush();                     
194                }
195    }
196
197        @Override
198    public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter, final Properties properties) {
199                super.addAdapter(componentAdapter, properties);
200                if (componentAdapter.findAdapterOfType(InstanceAdapter.class) != null) {
201                        this.instanceRegistrations.add(componentAdapter);
202                } else {
203                        this.addStoredReference(componentAdapter.getComponentKey());
204                }
205                
206                return this;
207    }
208
209        /**
210         * Use this instead of addAdapter in cases where you have custom adapters that the container
211         * cannot figure out whether it should be flushed after each request or not. Anything
212         * added through this method will be flushed after each stop() of the container.
213         * @param componentAdapter
214         * @return <em>this</em> to allow for method chaining.
215         */
216        public MutablePicoContainer addFlushableAdapter(final ComponentAdapter<?> componentAdapter) {
217            adapterRegistrations.add(componentAdapter);
218            addAdapter(componentAdapter);
219            return this;
220        }
221        
222        @Override
223    public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter) {
224                super.addAdapter(componentAdapter);
225                if (componentAdapter.findAdapterOfType(InstanceAdapter.class) != null) {
226                        this.instanceRegistrations.add(componentAdapter);
227                } else {
228                        this.addStoredReference(componentAdapter.getComponentKey());
229                }
230                
231                return this;
232    }
233        
234        private void removeLocalReferences(final ComponentAdapter<?> ca) {
235                this.storedReferences.remove(ca);
236        }
237
238        @Override
239    public <T> ComponentAdapter<T> removeComponent(final Object componentKey) {
240                ComponentAdapter< T > result= null;
241        try {
242            result = super.removeComponent(componentKey);
243        } catch (PicoCompositionException e) {
244            //Help with debugging any lifecycle errors.
245            throw new PicoCompositionException("There was an error removing component by key: '" + componentKey + "'",e);
246        }
247
248        if (result != null) {
249                        removeLocalReferences(result);
250                }
251                
252                return result;
253    }
254
255        @Override
256    public <T> ComponentAdapter<T> removeComponentByInstance(final T componentInstance) {
257                ComponentAdapter<T> result =  super.removeComponentByInstance(componentInstance);
258                if (result != null) {
259                        removeLocalReferences(result);
260                }
261                
262                return result;
263    }
264
265    @Override
266        public MutablePicoContainer makeChildContainer() {
267        ReusablePicoContainer pc = new ReusablePicoContainer(componentFactory, lifecycleStrategy, this);
268        addChildContainer(pc);
269        return pc;
270    }
271
272}