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 * 009 *****************************************************************************/ 010package org.picocontainer; 011 012import org.picocontainer.behaviors.Automating; 013import org.picocontainer.behaviors.Locking; 014import org.picocontainer.behaviors.PropertyApplying; 015import org.picocontainer.behaviors.Synchronizing; 016import org.picocontainer.containers.EmptyPicoContainer; 017import org.picocontainer.containers.TransientPicoContainer; 018import org.picocontainer.injectors.CompositeInjection; 019import org.picocontainer.injectors.MethodInjection; 020import org.picocontainer.lifecycle.JavaEE5LifecycleStrategy; 021import org.picocontainer.lifecycle.NullLifecycleStrategy; 022import org.picocontainer.lifecycle.ReflectionLifecycleStrategy; 023import org.picocontainer.lifecycle.StartableLifecycleStrategy; 024import org.picocontainer.monitors.ConsoleComponentMonitor; 025import org.picocontainer.monitors.NullComponentMonitor; 026 027import java.lang.annotation.Annotation; 028import java.util.ArrayList; 029import java.util.List; 030import java.util.Stack; 031 032import static org.picocontainer.behaviors.Behaviors.caching; 033import static org.picocontainer.behaviors.Behaviors.implementationHiding; 034import static org.picocontainer.injectors.Injectors.CDI; 035import static org.picocontainer.injectors.Injectors.SDI; 036import static org.picocontainer.injectors.Injectors.adaptiveDI; 037import static org.picocontainer.injectors.Injectors.annotatedFieldDI; 038import static org.picocontainer.injectors.Injectors.annotatedMethodDI; 039import static org.picocontainer.injectors.Injectors.namedField; 040import static org.picocontainer.injectors.Injectors.namedMethod; 041import static org.picocontainer.injectors.Injectors.typedFieldDI; 042 043/** 044 * Helps assembles the myriad items available to a picocontainer. 045 * <p>Simple Example:</p> 046 * <pre> 047 * MutablePicoContainer mpc = new PicoBuilder() 048 * .withCaching() 049 * .withLifecycle() 050 * .build(); 051 * </pre> 052 * @author Paul Hammant 053 */ 054public class PicoBuilder { 055 056 private PicoContainer parentContainer; 057 private Class<? extends MutablePicoContainer> mpcClass = DefaultPicoContainer.class; 058 private ComponentMonitor componentMonitor; 059 private List<Object> containerComps = new ArrayList<Object>(); 060 private boolean addChildToParent; 061 private LifecycleStrategy lifecycleStrategy; 062 private final Stack<Object> behaviors = new Stack<Object>(); 063 private final List<InjectionFactory> injectors = new ArrayList<InjectionFactory>(); 064 private Class<? extends ComponentMonitor> componentMonitorClass = NullComponentMonitor.class; 065 private Class<? extends LifecycleStrategy> lifecycleStrategyClass = NullLifecycleStrategy.class; 066 067 068 public PicoBuilder(PicoContainer parentContainer, InjectionFactory injectionType) { 069 this(parentContainer); 070 addInjector(injectionType); 071 } 072 073 /** 074 * Constructs a PicoBuilder using the specified PicoContainer as an argument. Note 075 * that this only creates child -> parent references. You must use parentContainer.addChildContainer() 076 * to the instance built here if you require child <-> parent references. 077 * @param parentContainer 078 */ 079 public PicoBuilder(PicoContainer parentContainer) { 080 if (parentContainer != null) { 081 this.parentContainer = parentContainer; 082 } else { 083 this.parentContainer = new EmptyPicoContainer(); 084 } 085 } 086 087 public PicoBuilder(InjectionFactory injectionType) { 088 this(new EmptyPicoContainer(), injectionType); 089 } 090 091 /** 092 * Will be used to build a PicoContainer not bound to any parent container. 093 */ 094 public PicoBuilder() { 095 this(new EmptyPicoContainer()); 096 } 097 098 public PicoBuilder withLifecycle() { 099 lifecycleStrategyClass = StartableLifecycleStrategy.class; 100 lifecycleStrategy = null; 101 return this; 102 } 103 104 /** 105 * Constructed PicoContainer will use {@linkplain org.picocontainer.lifecycle.ReflectionLifecycleStrategy ReflectionLifecycle}. 106 * @return <em>this</em> to allow for method chaining. 107 */ 108 public PicoBuilder withReflectionLifecycle() { 109 lifecycleStrategyClass = ReflectionLifecycleStrategy.class; 110 lifecycleStrategy = null; 111 return this; 112 } 113 114 /** 115 * Allows you to specify your own lifecycle strategy class. 116 * @param specifiedLifecycleStrategyType lifecycle strategy type. 117 * @return <em>this</em> to allow for method chaining. 118 */ 119 public PicoBuilder withLifecycle(Class<? extends LifecycleStrategy> specifiedLifecycleStrategyType) { 120 this.lifecycleStrategyClass = specifiedLifecycleStrategyType; 121 lifecycleStrategy = null; 122 return this; 123 } 124 125 /** 126 * Constructed PicoContainer will use {@linkplain org.picocontainer.lifecycle.JavaEE5LifecycleStrategy JavaEE5LifecycleStrategy}. 127 * @return <em>this</em> to allow for method chaining. 128 */ 129 public PicoBuilder withJavaEE5Lifecycle() { 130 this.lifecycleStrategyClass = JavaEE5LifecycleStrategy.class; 131 lifecycleStrategy = null; 132 return this; 133 } 134 135 /** 136 * Allows you to fully specify your lifecycle strategy by passing in a built instance 137 * @param specifiedLifecycleStrategy 138 * @return <em>this</em> to allow for method chaining. 139 */ 140 public PicoBuilder withLifecycle(LifecycleStrategy specifiedLifecycleStrategy) { 141 this.lifecycleStrategy = specifiedLifecycleStrategy; 142 lifecycleStrategyClass = null; 143 return this; 144 } 145 146 147 public PicoBuilder withConsoleMonitor() { 148 componentMonitorClass = ConsoleComponentMonitor.class; 149 return this; 150 } 151 152 public PicoBuilder withMonitor(Class<? extends ComponentMonitor> cmClass) { 153 if (cmClass == null) { 154 throw new NullPointerException("monitor class cannot be null"); 155 } 156 if (!ComponentMonitor.class.isAssignableFrom(cmClass)) { 157 throw new ClassCastException(cmClass.getName() + " is not a " + ComponentMonitor.class.getName()); 158 159 } 160 componentMonitorClass = cmClass; 161 componentMonitor = null; 162 return this; 163 } 164 165 public MutablePicoContainer build() { 166 167 DefaultPicoContainer tempContainer = new TransientPicoContainer(); 168 tempContainer.addComponent(PicoContainer.class, parentContainer); 169 170 addContainerComponents(tempContainer); 171 172 ComponentFactory componentFactory; 173 if (injectors.size() == 1) { 174 componentFactory = injectors.get(0); 175 } else if (injectors.size() == 0) { 176 componentFactory = adaptiveDI(); 177 } else { 178 componentFactory = new CompositeInjection(injectors.toArray(new InjectionFactory[injectors.size()])); 179 } 180 181 Stack<Object> clonedBehaviors = (Stack< Object >) behaviors.clone(); 182 while (!clonedBehaviors.empty()) { 183 componentFactory = buildComponentFactory(tempContainer, componentFactory, clonedBehaviors); 184 } 185 186 tempContainer.addComponent(ComponentFactory.class, componentFactory); 187 188 buildComponentMonitor(tempContainer); 189 190 if (lifecycleStrategy == null) { 191 tempContainer.addComponent(LifecycleStrategy.class, lifecycleStrategyClass); 192 } else { 193 tempContainer.addComponent(LifecycleStrategy.class, lifecycleStrategy); 194 195 } 196 tempContainer.addComponent("mpc", mpcClass); 197 198 MutablePicoContainer newContainer = (MutablePicoContainer) tempContainer.getComponent("mpc"); 199 200 addChildToParent(newContainer); 201 return newContainer; 202 } 203 204 private void buildComponentMonitor(DefaultPicoContainer tempContainer) { 205 if (componentMonitorClass == null) { 206 tempContainer.addComponent(ComponentMonitor.class, componentMonitor); 207 } else { 208 tempContainer.addComponent(ComponentMonitor.class, componentMonitorClass); 209 } 210 } 211 212 private void addChildToParent(MutablePicoContainer newContainer) { 213 if (addChildToParent) { 214 if (parentContainer instanceof MutablePicoContainer) { 215 ((MutablePicoContainer)parentContainer).addChildContainer(newContainer); 216 } else { 217 throw new PicoCompositionException("If using addChildContainer() the parent must be a MutablePicoContainer"); 218 } 219 } 220 } 221 222 private void addContainerComponents(DefaultPicoContainer temp) { 223 for (Object containerComp : containerComps) { 224 temp.addComponent(containerComp); 225 } 226 } 227 228 private ComponentFactory buildComponentFactory(DefaultPicoContainer container, final ComponentFactory lastCaf, final Stack<Object> clonedBehaviors) { 229 230 Object componentFactory = clonedBehaviors.pop(); 231 DefaultPicoContainer tmpContainer = new TransientPicoContainer(container); 232 tmpContainer.addComponent("componentFactory", componentFactory); 233 if (lastCaf != null) { 234 tmpContainer.addComponent(ComponentFactory.class, lastCaf); 235 } 236 ComponentFactory newlastCaf = (ComponentFactory) tmpContainer.getComponent("componentFactory"); 237 if (newlastCaf instanceof BehaviorFactory) { 238 ((BehaviorFactory) newlastCaf).wrap(lastCaf); 239 } 240 return newlastCaf; 241 } 242 243 public PicoBuilder withHiddenImplementations() { 244 behaviors.push(implementationHiding()); 245 return this; 246 } 247 248 public PicoBuilder withSetterInjection() { 249 addInjector(SDI()); 250 return this; 251 } 252 253 public PicoBuilder withAnnotatedMethodInjection(Class<? extends Annotation> injectionAnnotation) { 254 addInjector(annotatedMethodDI(injectionAnnotation)); 255 return this; 256 } 257 258 public PicoBuilder withAnnotatedMethodInjection() { 259 addInjector(annotatedMethodDI()); 260 return this; 261 } 262 263 public PicoBuilder withAnnotatedFieldInjection(Class<? extends Annotation> injectionAnnotation) { 264 addInjector(annotatedFieldDI(injectionAnnotation)); 265 return this; 266 } 267 268 public PicoBuilder withAnnotatedFieldInjection() { 269 addInjector(annotatedFieldDI()); 270 return this; 271 } 272 273 public PicoBuilder withTypedFieldInjection() { 274 addInjector(typedFieldDI()); 275 return this; 276 } 277 278 public PicoBuilder withConstructorInjection() { 279 addInjector(CDI()); 280 return this; 281 } 282 283 public PicoBuilder withNamedMethodInjection() { 284 addInjector(namedMethod()); 285 return this; 286 } 287 288 public PicoBuilder withNamedFieldInjection() { 289 addInjector(namedField()); 290 return this; 291 } 292 293 public PicoBuilder withCaching() { 294 behaviors.push(caching()); 295 return this; 296 } 297 298 public PicoBuilder withComponentFactory(ComponentFactory componentFactory) { 299 if (componentFactory == null) { 300 throw new NullPointerException("CAF cannot be null"); 301 } 302 behaviors.push(componentFactory); 303 return this; 304 } 305 306 public PicoBuilder withSynchronizing() { 307 behaviors.push(new Synchronizing()); 308 return this; 309 } 310 311 public PicoBuilder withLocking() { 312 behaviors.push(new Locking()); 313 return this; 314 } 315 316 public PicoBuilder withBehaviors(BehaviorFactory... factories) { 317 for (BehaviorFactory componentFactory : factories) { 318 behaviors.push(componentFactory); 319 } 320 return this; 321 } 322 323 public PicoBuilder implementedBy(Class<? extends MutablePicoContainer> containerClass) { 324 mpcClass = containerClass; 325 return this; 326 } 327 328 /** 329 * Allows you to specify your very own component monitor to be used by the created 330 * picocontainer 331 * @param specifiedComponentMonitor 332 * @return <em>this</em> to allow for method chaining. 333 */ 334 public PicoBuilder withMonitor(ComponentMonitor specifiedComponentMonitor) { 335 this.componentMonitor = specifiedComponentMonitor; 336 componentMonitorClass = null; 337 return this; 338 } 339 340 public PicoBuilder withComponentFactory(Class<? extends ComponentFactory> componentFactoryClass) { 341 behaviors.push(componentFactoryClass); 342 return this; 343 } 344 345 public PicoBuilder withCustomContainerComponent(Object containerDependency) { 346 containerComps.add(containerDependency); 347 return this; 348 } 349 350 public PicoBuilder withPropertyApplier() { 351 behaviors.push(new PropertyApplying()); 352 return this; 353 } 354 355 public PicoBuilder withAutomatic() { 356 behaviors.push(new Automating()); 357 return this; 358 } 359 360 public PicoBuilder withMethodInjection() { 361 addInjector(new MethodInjection()); 362 return this; 363 } 364 365 public PicoBuilder addChildToParent() { 366 addChildToParent = true; 367 return this; 368 } 369 370 protected void addInjector(InjectionFactory injectionType) { 371 injectors.add(injectionType); 372 } 373}