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 *****************************************************************************/
010 package org.picocontainer;
011
012 import org.picocontainer.behaviors.Automating;
013 import org.picocontainer.behaviors.Locking;
014 import org.picocontainer.behaviors.PropertyApplying;
015 import org.picocontainer.behaviors.Synchronizing;
016 import org.picocontainer.containers.EmptyPicoContainer;
017 import org.picocontainer.containers.TransientPicoContainer;
018 import org.picocontainer.injectors.CompositeInjection;
019 import org.picocontainer.injectors.MethodInjection;
020 import org.picocontainer.lifecycle.JavaEE5LifecycleStrategy;
021 import org.picocontainer.lifecycle.NullLifecycleStrategy;
022 import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
023 import org.picocontainer.lifecycle.StartableLifecycleStrategy;
024 import org.picocontainer.monitors.ConsoleComponentMonitor;
025 import org.picocontainer.monitors.NullComponentMonitor;
026
027 import java.lang.annotation.Annotation;
028 import java.util.ArrayList;
029 import java.util.List;
030 import java.util.Stack;
031
032 import static org.picocontainer.behaviors.Behaviors.caching;
033 import static org.picocontainer.behaviors.Behaviors.implementationHiding;
034 import static org.picocontainer.injectors.Injectors.CDI;
035 import static org.picocontainer.injectors.Injectors.SDI;
036 import static org.picocontainer.injectors.Injectors.adaptiveDI;
037 import static org.picocontainer.injectors.Injectors.annotatedFieldDI;
038 import static org.picocontainer.injectors.Injectors.annotatedMethodDI;
039 import static org.picocontainer.injectors.Injectors.namedField;
040 import static org.picocontainer.injectors.Injectors.namedMethod;
041 import 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 */
054 public 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 }