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}