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.tck;
011
012 import static org.junit.Assert.assertEquals;
013 import static org.junit.Assert.assertFalse;
014 import static org.junit.Assert.assertNotNull;
015 import static org.junit.Assert.assertNotSame;
016 import static org.junit.Assert.assertNull;
017 import static org.junit.Assert.assertSame;
018 import static org.junit.Assert.assertTrue;
019 import static org.junit.Assert.fail;
020
021 import org.picocontainer.parameters.ComponentParameter;
022
023 import org.picocontainer.Converting;
024
025 import java.io.ByteArrayInputStream;
026 import java.io.ByteArrayOutputStream;
027 import java.io.IOException;
028 import java.io.ObjectInputStream;
029 import java.io.ObjectOutputStream;
030 import java.io.Serializable;
031 import java.lang.reflect.Type;
032 import java.util.ArrayList;
033 import java.util.Arrays;
034 import java.util.Collection;
035 import java.util.HashMap;
036 import java.util.HashSet;
037 import java.util.LinkedList;
038 import java.util.List;
039 import java.util.Map;
040 import java.util.Properties;
041 import java.util.Set;
042 import org.junit.Test;
043 import org.picocontainer.Behavior;
044 import org.picocontainer.Characteristics;
045 import org.picocontainer.ComponentAdapter;
046 import org.picocontainer.ComponentFactory;
047 import org.picocontainer.DefaultPicoContainer;
048 import org.picocontainer.Disposable;
049 import org.picocontainer.MutablePicoContainer;
050 import org.picocontainer.NameBinding;
051 import org.picocontainer.Parameter;
052 import org.picocontainer.PicoCompositionException;
053 import org.picocontainer.PicoContainer;
054 import org.picocontainer.PicoException;
055 import org.picocontainer.PicoVerificationException;
056 import org.picocontainer.PicoVisitor;
057 import org.picocontainer.Startable;
058 import org.picocontainer.adapters.InstanceAdapter;
059 import org.picocontainer.behaviors.AbstractBehavior;
060 import org.picocontainer.behaviors.AdaptingBehavior;
061 import org.picocontainer.injectors.AbstractInjector;
062 import org.picocontainer.injectors.ConstructorInjector;
063 import org.picocontainer.injectors.AbstractInjector.UnsatisfiableDependenciesException;
064 import org.picocontainer.injectors.SingleMemberInjector.ParameterCannotBeNullException;
065 import org.picocontainer.lifecycle.NullLifecycleStrategy;
066 import org.picocontainer.monitors.NullComponentMonitor;
067 import org.picocontainer.parameters.BasicComponentParameter;
068 import org.picocontainer.parameters.ConstantParameter;
069 import org.picocontainer.parameters.NullParameter;
070 import org.picocontainer.testmodel.DependsOnTouchable;
071 import org.picocontainer.testmodel.SimpleTouchable;
072 import org.picocontainer.testmodel.Touchable;
073 import org.picocontainer.testmodel.Washable;
074 import org.picocontainer.testmodel.WashableTouchable;
075 import org.picocontainer.visitors.AbstractPicoVisitor;
076 import org.picocontainer.visitors.TraversalCheckingVisitor;
077 import org.picocontainer.visitors.VerifyingVisitor;
078
079 /** This test tests (at least it should) all the methods in MutablePicoContainer. */
080 @SuppressWarnings("serial")
081 public abstract class AbstractPicoContainerTest {
082
083 protected abstract MutablePicoContainer createPicoContainer(PicoContainer parent);
084
085 protected final MutablePicoContainer createPicoContainerWithDependsOnTouchableOnly() throws PicoCompositionException {
086 MutablePicoContainer pico = createPicoContainer(null);
087 pico.addComponent(DependsOnTouchable.class);
088 return pico;
089 }
090
091 protected final MutablePicoContainer createPicoContainerWithTouchableAndDependsOnTouchable() throws PicoCompositionException {
092 MutablePicoContainer pico = createPicoContainerWithDependsOnTouchableOnly();
093 pico.as(Characteristics.CACHE).addComponent(Touchable.class, SimpleTouchable.class);
094 return pico;
095 }
096
097 @Test public void testBasicInstantiationAndContainment() throws PicoException {
098 PicoContainer pico = createPicoContainerWithTouchableAndDependsOnTouchable();
099 assertTrue("Component should be instance of Touchable",
100 Touchable.class.isAssignableFrom(pico.getComponentAdapter(Touchable.class, (NameBinding) null).getComponentImplementation()));
101 }
102
103 @Test public void testRegisteredComponentsExistAndAreTheCorrectTypes() throws PicoException {
104 PicoContainer pico = createPicoContainerWithTouchableAndDependsOnTouchable();
105 assertNotNull("Container should have Touchable addComponent",
106 pico.getComponentAdapter(Touchable.class, (NameBinding) null));
107 assertNotNull("Container should have DependsOnTouchable addComponent",
108 pico.getComponentAdapter(DependsOnTouchable.class, (NameBinding) null));
109 assertTrue("Component should be instance of Touchable",
110 pico.getComponent(Touchable.class) != null);
111 assertTrue("Component should be instance of DependsOnTouchable",
112 pico.getComponent(DependsOnTouchable.class) != null);
113 assertNull("should not have non existent addComponent", pico.getComponentAdapter(Map.class, (NameBinding) null));
114 }
115
116 @Test public void testRegistersSingleInstance() throws PicoException {
117 MutablePicoContainer pico = createPicoContainer(null);
118 StringBuffer sb = new StringBuffer();
119 pico.addComponent(sb);
120 assertSame(sb, pico.getComponent(StringBuffer.class));
121 }
122
123 @Test public void testContainerIsSerializable() throws PicoException,
124 IOException, ClassNotFoundException
125 {
126
127 getTouchableFromSerializedContainer();
128
129 }
130
131 private Touchable getTouchableFromSerializedContainer() throws IOException, ClassNotFoundException {
132 MutablePicoContainer pico = createPicoContainerWithTouchableAndDependsOnTouchable();
133 // Add a list too, using a constant parameter
134 pico.addComponent("list", ArrayList.class, new ConstantParameter(10));
135
136 ByteArrayOutputStream baos = new ByteArrayOutputStream();
137 ObjectOutputStream oos = new ObjectOutputStream(baos);
138
139 oos.writeObject(pico);
140 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
141
142 pico = (MutablePicoContainer)ois.readObject();
143
144 DependsOnTouchable dependsOnTouchable = pico.getComponent(DependsOnTouchable.class);
145 assertNotNull(dependsOnTouchable);
146 return pico.getComponent(Touchable.class);
147 }
148
149 @Test public void testSerializedContainerCanRetrieveImplementation() throws PicoException,
150 IOException, ClassNotFoundException
151 {
152
153 Touchable touchable = getTouchableFromSerializedContainer();
154
155 SimpleTouchable simpleTouchable = (SimpleTouchable)touchable;
156
157 assertTrue(simpleTouchable.wasTouched);
158 }
159
160
161 @Test public void testGettingComponentWithMissingDependencyFails() throws PicoException {
162 PicoContainer picoContainer = createPicoContainerWithDependsOnTouchableOnly();
163 try {
164 picoContainer.getComponent(DependsOnTouchable.class);
165 fail("should need a Touchable");
166 } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
167 assertSame(picoContainer.getComponentAdapter(DependsOnTouchable.class, (NameBinding) null).getComponentImplementation(),
168 e.getUnsatisfiableComponentAdapter().getComponentImplementation());
169 final Set unsatisfiableDependencies = e.getUnsatisfiableDependencies();
170 assertEquals(1, unsatisfiableDependencies.size());
171
172 // Touchable.class is now inside a List (the list of unsatisfied parameters) -- mparaz
173 List unsatisfied = (List)unsatisfiableDependencies.iterator().next();
174 assertEquals(1, unsatisfied.size());
175 assertEquals(Touchable.class, unsatisfied.get(0));
176 }
177 }
178
179 @Test public void testDuplicateRegistration() {
180 try {
181 MutablePicoContainer pico = createPicoContainer(null);
182 pico.addComponent(Object.class);
183 pico.addComponent(Object.class);
184 fail("Should have failed with duplicate registration");
185 } catch (PicoCompositionException e) {
186 assertTrue("Wrong key", e.getMessage().indexOf(Object.class.toString()) > -1);
187 }
188 }
189
190 @Test public void testExternallyInstantiatedObjectsCanBeRegisteredAndLookedUp() throws PicoException {
191 MutablePicoContainer pico = createPicoContainer(null);
192 final HashMap map = new HashMap();
193 pico.as(getProperties()).addComponent(Map.class, map);
194 assertSame(map, pico.getComponent(Map.class));
195 }
196
197 @Test public void testAmbiguousResolution() throws PicoCompositionException {
198 MutablePicoContainer pico = createPicoContainer(null);
199 pico.addComponent("ping", String.class);
200 pico.addComponent("pong", "pang");
201 try {
202 pico.getComponent(String.class);
203 } catch (AbstractInjector.AmbiguousComponentResolutionException e) {
204 assertTrue(e.getMessage().indexOf("java.lang.String") != -1);
205 assertTrue(e.getMessage().indexOf("<no-component>") != -1);
206 }
207 }
208
209 @Test public void testLookupWithUnregisteredKeyReturnsNull() throws PicoCompositionException {
210 MutablePicoContainer pico = createPicoContainer(null);
211 assertNull(pico.getComponent(String.class));
212 }
213
214 @Test public void testLookupWithUnregisteredTypeReturnsNull() throws PicoCompositionException {
215 MutablePicoContainer pico = createPicoContainer(null);
216 assertNull(pico.getComponent(String.class));
217 }
218
219 public static class ListAdder {
220 public ListAdder(Collection<String> list) {
221 list.add("something");
222 }
223 }
224
225 @Test public void testUnsatisfiableDependenciesExceptionGivesVerboseEnoughErrorMessage() {
226 MutablePicoContainer pico = createPicoContainer(null);
227 pico.addComponent(ComponentD.class);
228
229 try {
230 pico.getComponent(ComponentD.class);
231 } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
232 Set unsatisfiableDependencies = e.getUnsatisfiableDependencies();
233 assertEquals(1, unsatisfiableDependencies.size());
234
235 List list = (List)unsatisfiableDependencies.iterator().next();
236
237 final List<Class> expectedList = new ArrayList<Class>(2);
238 expectedList.add(ComponentE.class);
239 expectedList.add(ComponentB.class);
240
241 assertEquals(expectedList, list);
242 }
243 }
244
245 @Test public void testUnsatisfiableDependenciesExceptionGivesUnsatisfiedDependencyTypes() {
246 MutablePicoContainer pico = createPicoContainer(null);
247 // D depends on E and B
248 pico.addComponent(ComponentD.class);
249
250 // first - do not register any dependency
251 // should yield first unsatisfied dependency
252 try {
253 pico.getComponent(ComponentD.class);
254 } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
255 Set unsatisfiableDependencies = e.getUnsatisfiableDependencies();
256 assertEquals(1, unsatisfiableDependencies.size());
257 List list = (List)unsatisfiableDependencies.iterator().next();
258 final List<Class> expectedList = new ArrayList<Class>(2);
259 expectedList.add(ComponentE.class);
260 expectedList.add(ComponentB.class);
261 assertEquals(expectedList, list);
262
263 Type unsatisfiedDependencyType = e.getUnsatisfiedDependencyType();
264 assertNotNull(unsatisfiedDependencyType);
265 assertEquals(ComponentE.class, unsatisfiedDependencyType);
266 }
267
268 // now register only first dependency
269 // should yield second unsatisfied dependency
270 pico.addComponent(ComponentE.class);
271 try {
272 pico.getComponent(ComponentD.class);
273 } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
274 Set unsatisfiableDependencies = e.getUnsatisfiableDependencies();
275 assertEquals(1, unsatisfiableDependencies.size());
276 List list = (List)unsatisfiableDependencies.iterator().next();
277 final List<Class> expectedList = new ArrayList<Class>(2);
278 expectedList.add(ComponentE.class);
279 expectedList.add(ComponentB.class);
280 assertEquals(expectedList, list);
281
282 Type unsatisfiedDependencyType = e.getUnsatisfiedDependencyType();
283 assertNotNull(unsatisfiedDependencyType);
284 assertEquals(ComponentB.class, unsatisfiedDependencyType);
285 }
286 }
287
288 @Test public void testCyclicDependencyThrowsCyclicDependencyException() {
289 assertCyclicDependencyThrowsCyclicDependencyException(createPicoContainer(null));
290 }
291
292 private static void assertCyclicDependencyThrowsCyclicDependencyException(MutablePicoContainer pico) {
293 pico.addComponent(ComponentB.class);
294 pico.addComponent(ComponentD.class);
295 pico.addComponent(ComponentE.class);
296
297 try {
298 pico.getComponent(ComponentD.class);
299 fail("CyclicDependencyException expected");
300 } catch (AbstractInjector.CyclicDependencyException e) {
301 // CyclicDependencyException reports now the stack.
302 //final List dependencies = Arrays.asList(ComponentD.class.getConstructors()[0].getParameterTypes());
303 final List<Class> dependencies = Arrays.<Class>asList(ComponentD.class, ComponentE.class, ComponentD.class);
304 final List<Class> reportedDependencies = Arrays.asList(e.getDependencies());
305 assertEquals(dependencies, reportedDependencies);
306 } catch (StackOverflowError e) {
307 fail();
308 }
309 }
310
311 @Test public void testCyclicDependencyThrowsCyclicDependencyExceptionWithParentContainer() {
312 MutablePicoContainer pico = createPicoContainer(createPicoContainer(null));
313 assertCyclicDependencyThrowsCyclicDependencyException(pico);
314 }
315
316 @Test public void testRemovalNonRegisteredComponentAdapterWorksAndReturnsNull() {
317 final MutablePicoContainer picoContainer = createPicoContainer(null);
318 assertNull(picoContainer.removeComponent("COMPONENT DOES NOT EXIST"));
319 }
320
321 /** Important! Nanning really, really depends on this! */
322 @Test public void testComponentAdapterRegistrationOrderIsMaintained() throws NoSuchMethodException {
323
324 ConstructorInjector c1 = new ConstructorInjector("1", Object.class, null, new NullComponentMonitor(), false);
325 ConstructorInjector c2 = new ConstructorInjector("2", String.class, null, new NullComponentMonitor(), false);
326
327 MutablePicoContainer picoContainer = createPicoContainer(null);
328 picoContainer.addAdapter(c1).addAdapter(c2);
329 Collection<ComponentAdapter<?>> list2 = picoContainer.getComponentAdapters();
330 //registration order should be maintained
331 assertEquals(2, list2.size());
332 assertEquals(c1.getComponentKey(), ((ComponentAdapter)list2.toArray()[0]).getComponentKey());
333 assertEquals(c2.getComponentKey(), ((ComponentAdapter)list2.toArray()[1]).getComponentKey());
334
335 picoContainer.getComponents(); // create all the instances at once
336 assertFalse("instances should be created in same order as adapters are created",
337 picoContainer.getComponents().get(0) instanceof String);
338 assertTrue("instances should be created in same order as adapters are created",
339 picoContainer.getComponents().get(1) instanceof String);
340
341 MutablePicoContainer reversedPicoContainer = createPicoContainer(null);
342 reversedPicoContainer.addAdapter(c2);
343 reversedPicoContainer.addAdapter(c1);
344 //registration order should be maintained
345 list2 = reversedPicoContainer.getComponentAdapters();
346 assertEquals(2, list2.size());
347 assertEquals(c2.getComponentKey(), ((ComponentAdapter)list2.toArray()[0]).getComponentKey());
348 assertEquals(c1.getComponentKey(), ((ComponentAdapter)list2.toArray()[1]).getComponentKey());
349
350 reversedPicoContainer.getComponents(); // create all the instances at once
351 assertTrue("instances should be created in same order as adapters are created",
352 reversedPicoContainer.getComponents().get(0) instanceof String);
353 assertFalse("instances should be created in same order as adapters are created",
354 reversedPicoContainer.getComponents().get(1) instanceof String);
355 }
356
357 public static final class NeedsTouchable {
358 public final Touchable touchable;
359
360 public NeedsTouchable(Touchable touchable) {
361 this.touchable = touchable;
362 }
363 }
364
365 public static final class NeedsWashable {
366 public final Washable washable;
367
368 public NeedsWashable(Washable washable) {
369 this.washable = washable;
370 }
371 }
372
373 @Test public void testSameInstanceCanBeUsedAsDifferentTypeWhenCaching() {
374 MutablePicoContainer pico = createPicoContainer(null);
375 pico.as(Characteristics.CACHE).addComponent("wt", WashableTouchable.class);
376 pico.addComponent("nw", NeedsWashable.class);
377 pico.as(Characteristics.CACHE).addComponent("nt", NeedsTouchable.class);
378
379 NeedsWashable nw = (NeedsWashable)pico.getComponent("nw");
380 NeedsTouchable nt = (NeedsTouchable)pico.getComponent("nt");
381 assertSame(nw.washable, nt.touchable);
382 }
383
384 @Test public void testRegisterComponentWithObjectBadType() throws PicoCompositionException {
385 MutablePicoContainer pico = createPicoContainer(null);
386
387 try {
388 pico.addComponent(Serializable.class, new Object());
389 fail("Shouldn't be able to register an Object.class as Serializable because it is not, " +
390 "it does not implement it, Object.class does not implement much.");
391 } catch (ClassCastException e) {
392 assertNotNull(e.getMessage());
393 }
394
395 }
396
397 public static class JMSService {
398 public final String serverid;
399 public final String path;
400
401 public JMSService(String serverid, String path) {
402 this.serverid = serverid;
403 this.path = path;
404 }
405 }
406
407 // http://jira.codehaus.org/secure/ViewIssue.jspa?key=PICO-52
408 @Test public void testPico52() {
409 MutablePicoContainer pico = createPicoContainer(null);
410
411 pico.addComponent("foo", JMSService.class, new ConstantParameter("0"), new ConstantParameter("something"));
412 JMSService jms = (JMSService)pico.getComponent("foo");
413 assertEquals("0", jms.serverid);
414 assertEquals("something", jms.path);
415 }
416
417 public static class ComponentA {
418 public final ComponentC c;
419
420 public ComponentA(ComponentB b, ComponentC c) {
421 this.c = c;
422 assertNotNull(b);
423 assertNotNull(c);
424 }
425 }
426
427 public static class ComponentB {
428 //Does nothing.
429 }
430
431 public static class ComponentC {
432 //Does nothing.
433 }
434
435 public static class ComponentD {
436 public ComponentD(ComponentE e, ComponentB b) {
437 assertNotNull(e);
438 assertNotNull(b);
439 }
440 }
441
442 public static class ComponentE {
443 public ComponentE(ComponentD d) {
444 assertNotNull(d);
445 }
446 }
447
448 public static class ComponentF {
449 public ComponentF(ComponentA a) {
450 assertNotNull(a);
451 }
452 }
453
454 @Test public void testAggregatedVerificationException() {
455 MutablePicoContainer pico = createPicoContainer(null);
456 pico.addComponent(ComponentA.class);
457 pico.addComponent(ComponentE.class);
458 try {
459 new VerifyingVisitor().traverse(pico);
460 fail("we expect a PicoVerificationException");
461 } catch (PicoVerificationException e) {
462 List nested = e.getNestedExceptions();
463 assertEquals(2, nested.size());
464 assertTrue(-1 != e.getMessage().indexOf(ComponentA.class.getName()));
465 assertTrue(-1 != e.getMessage().indexOf(ComponentE.class.getName()));
466 }
467 }
468
469 // An adapter has no longer a hosting container.
470
471 // @Test public void testRegistrationOfAdapterSetsHostingContainerAsSelf() {
472 // final InstanceAdapter componentAdapter = new InstanceAdapter("", new Object());
473 // final MutablePicoContainer picoContainer = createPicoContainer(null);
474 // picoContainer.addAdapter(componentAdapter);
475 // assertSame(picoContainer, componentAdapter.getContainer());
476 // }
477
478 public static class ContainerDependency {
479 public ContainerDependency(PicoContainer container) {
480 assertNotNull(container);
481 }
482 }
483
484 // ImplicitPicoContainer injection is bad. It is an open door for hackers. Developers with
485 // special PicoContainer needs should specifically register() a comtainer they want components to
486 // be able to pick up on.
487
488 // @Test public void testImplicitPicoContainerInjection() {
489 // MutablePicoContainer pico = createPicoContainer(null);
490 // pico.addAdapter(ContainerDependency.class);
491 // ContainerDependency dep = (ContainerDependency) pico.getComponent(ContainerDependency.class);
492 // assertSame(pico, dep.pico);
493 // }
494
495 @Test public void testShouldReturnNullWhenUnregistereingUnmanagedComponent() {
496 final MutablePicoContainer pico = createPicoContainer(null);
497 assertNull(pico.removeComponentByInstance("yo"));
498 }
499
500 @Test public void testShouldReturnNullForComponentAdapterOfUnregisteredType() {
501 final MutablePicoContainer pico = createPicoContainer(null);
502 assertNull(pico.getComponent(List.class));
503 }
504
505 @Test public void testShouldReturnNonMutableParent() {
506 DefaultPicoContainer parent = new DefaultPicoContainer();
507 final MutablePicoContainer picoContainer = createPicoContainer(parent);
508 assertNotSame(parent, picoContainer.getParent());
509 assertFalse(picoContainer.getParent() instanceof MutablePicoContainer);
510 }
511
512 class Foo implements Startable, Disposable {
513 public boolean started;
514 public boolean stopped;
515 public boolean disposed;
516
517 public void start() {
518 started = true;
519 }
520
521 public void stop() {
522 stopped = true;
523 }
524
525 public void dispose() {
526 disposed = true;
527 }
528
529 }
530
531 @Test public void testContainerCascadesDefaultLifecycle() {
532 final MutablePicoContainer picoContainer = createPicoContainer(null);
533 Foo foo = new Foo();
534 picoContainer.addComponent(foo);
535 picoContainer.start();
536 assertEquals(true, foo.started);
537 picoContainer.stop();
538 assertEquals(true, foo.stopped);
539 picoContainer.dispose();
540 assertEquals(true, foo.disposed);
541 }
542
543 @Test public void testComponentInstancesFromParentsAreNotDirectlyAccessible2() {
544 final MutablePicoContainer a = createPicoContainer(null);
545 final MutablePicoContainer b = createPicoContainer(a);
546 final MutablePicoContainer c = createPicoContainer(b);
547
548 Object ao = new Object();
549 Object bo = new Object();
550 Object co = new Object();
551
552 a.addComponent("a", ao);
553 b.addComponent("b", bo);
554 c.addComponent("c", co);
555
556 assertEquals(1, a.getComponents().size());
557 assertEquals(1, b.getComponents().size());
558 assertEquals(1, c.getComponents().size());
559 }
560
561 @Test public void testStartStopAndDisposeCascadedtoChildren() {
562 final MutablePicoContainer parent = createPicoContainer(null);
563 parent.addComponent(new StringBuffer());
564 final MutablePicoContainer child = createPicoContainer(parent);
565 parent.addChildContainer(child);
566 child.addComponent(LifeCycleMonitoring.class);
567 parent.start();
568 try {
569 child.start();
570 fail("IllegalStateException expected");
571 } catch (IllegalStateException e) {
572 assertEquals("child already started", "Cannot start. Current container state was: STARTED", e.getMessage());
573 }
574 parent.stop();
575 try {
576 child.stop();
577 fail("IllegalStateException expected");
578 } catch (IllegalStateException e) {
579 assertEquals("child not started", "Cannot stop. Current container state was: STOPPED", e.getMessage());
580 }
581 parent.dispose();
582 try {
583 child.dispose();
584 fail("IllegalStateException expected");
585 } catch (IllegalStateException e) {
586 assertEquals("child already disposed", "Cannot dispose. Current lifecycle state is: DISPOSED", e.getMessage());
587 }
588
589 }
590
591 @Test public void testMakingOfChildContainer() {
592 final MutablePicoContainer parent = createPicoContainer(null);
593 MutablePicoContainer child = parent.makeChildContainer();
594 assertNotNull(child);
595 }
596
597 @Test public void testMakingOfChildContainerPercolatesLifecycleManager() {
598 final MutablePicoContainer parent = createPicoContainer(null);
599 parent.addComponent("one", TestLifecycleComponent.class);
600 MutablePicoContainer child = parent.makeChildContainer();
601 assertNotNull(child);
602 child.addComponent("two", TestLifecycleComponent.class);
603 parent.start();
604 try {
605 child.start();
606 } catch (IllegalStateException e) {
607 assertEquals("child already started", "Cannot start. Current container state was: STARTED", e.getMessage());
608 }
609 //TODO - The Behavior reference in child containers is not used. Thus is is almost pointless
610 // The reason is because DefaultPicoContainer's accept() method visits child containers' on its own.
611 // This may be file for visiting components in a tree for general cases, but for lifecycle, we
612 // should hand to each Behavior's start(..) at each appropriate node. See mail-list discussion.
613 }
614
615 @SuppressWarnings("unused")
616 public static final class TestBehavior extends AbstractBehavior implements Behavior {
617
618 public final ArrayList<PicoContainer> started = new ArrayList<PicoContainer>();
619
620 public TestBehavior(ComponentAdapter delegate) {
621 super(delegate);
622 }
623
624 @Override
625 public void start(PicoContainer node) {
626 started.add(node);
627 }
628
629 @Override
630 public void stop(PicoContainer node) {
631 //Does nothing.
632 }
633
634 @Override
635 public void dispose(PicoContainer node) {
636 //Does nothing.
637 }
638
639 @Override
640 public boolean componentHasLifecycle() {
641 return true;
642 }
643
644 public String getDescriptor() {
645 return null;
646 }
647 }
648
649 public static class TestLifecycleComponent implements Startable {
650 public boolean started;
651
652 public void start() {
653 started = true;
654 }
655
656 public void stop() {
657 //Does nothing.
658 }
659 }
660
661 @Test public void testStartStopAndDisposeNotCascadedtoRemovedChildren() {
662 final MutablePicoContainer parent = createPicoContainer(null);
663 parent.addComponent(new StringBuffer());
664 StringBuffer sb = parent.getComponents(StringBuffer.class).get(0);
665
666 final MutablePicoContainer child = createPicoContainer(parent);
667 assertEquals(parent, parent.addChildContainer(child));
668 child.addComponent(LifeCycleMonitoring.class);
669 assertTrue(parent.removeChildContainer(child));
670 parent.start();
671 assertTrue(sb.toString().indexOf("-started") == -1);
672 parent.stop();
673 assertTrue(sb.toString().indexOf("-stopped") == -1);
674 parent.dispose();
675 assertTrue(sb.toString().indexOf("-disposed") == -1);
676 }
677
678 @Test public void testShouldCascadeStartStopAndDisposeToChild() {
679
680 StringBuffer sb = new StringBuffer();
681 final MutablePicoContainer parent = createPicoContainer(null);
682 parent.addComponent(sb);
683 parent.addComponent(Map.class, HashMap.class);
684
685 final MutablePicoContainer child = parent.makeChildContainer();
686 child.addComponent(LifeCycleMonitoring.class);
687
688 Map map = parent.getComponent(Map.class);
689 assertNotNull(map);
690 parent.start();
691 try {
692 child.start();
693 fail("IllegalStateException expected");
694 } catch (IllegalStateException e) {
695 assertEquals("child already started", "Cannot start. Current container state was: STARTED", e.getMessage());
696 }
697 parent.stop();
698 try {
699 child.stop();
700 fail("IllegalStateException expected");
701 } catch (IllegalStateException e) {
702 assertEquals("child not started", "Cannot stop. Current container state was: STOPPED", e.getMessage());
703 }
704 parent.dispose();
705 try {
706 child.dispose();
707 fail("IllegalStateException expected");
708 } catch (IllegalStateException e) {
709 assertEquals("child already disposed", "Cannot dispose. Current lifecycle state is: DISPOSED", e.getMessage());
710 }
711 }
712
713 public static final class LifeCycleMonitoring implements Startable, Disposable {
714 final StringBuffer sb;
715
716 public LifeCycleMonitoring(StringBuffer sb) {
717 this.sb = sb;
718 sb.append("-instantiated");
719 }
720
721 public void start() {
722 sb.append("-started");
723 }
724
725 public void stop() {
726 sb.append("-stopped");
727 }
728
729 public void dispose() {
730 sb.append("-disposed");
731 }
732 }
733
734 public static class RecordingStrategyVisitor extends AbstractPicoVisitor {
735
736 private final List<Object> list;
737
738 public RecordingStrategyVisitor(List<Object> list) {
739 this.list = list;
740 }
741
742 public boolean visitContainer(PicoContainer pico) {
743 list.add(pico.getClass());
744 return CONTINUE_TRAVERSAL;
745 }
746
747 public void visitComponentAdapter(ComponentAdapter componentAdapter) {
748 list.add(componentAdapter.getClass());
749 }
750
751 public void visitComponentFactory(ComponentFactory componentFactory) {
752 list.add(componentFactory.getClass());
753 }
754
755 public void visitParameter(Parameter parameter) {
756 list.add(parameter.getClass());
757 }
758
759 }
760
761 protected abstract Properties[] getProperties();
762
763 @Test public void testAcceptImplementsBreadthFirstStrategy() {
764 final MutablePicoContainer parent = createPicoContainer(null);
765 final MutablePicoContainer child = parent.makeChildContainer();
766 ComponentAdapter hashMapAdapter =
767 parent.as(getProperties()).addAdapter(new ConstructorInjector(HashMap.class, HashMap.class, null, new NullComponentMonitor(), false))
768 .getComponentAdapter(HashMap.class, (NameBinding) null);
769 ComponentAdapter hashSetAdapter =
770 parent.as(getProperties()).addAdapter(new ConstructorInjector(HashSet.class, HashSet.class, null, new NullComponentMonitor(), false))
771 .getComponentAdapter(HashSet.class, (NameBinding) null);
772 InstanceAdapter instanceAdapter = new InstanceAdapter(String.class, "foo",
773 new NullLifecycleStrategy(),
774 new NullComponentMonitor());
775 ComponentAdapter stringAdapter = parent.as(getProperties()).addAdapter(instanceAdapter).getComponentAdapter(instanceAdapter.getComponentKey());
776 ComponentAdapter arrayListAdapter =
777 child.as(getProperties()).addAdapter(new ConstructorInjector(ArrayList.class, ArrayList.class, null, new NullComponentMonitor(), false))
778 .getComponentAdapter(ArrayList.class, (NameBinding) null);
779 Parameter componentParameter = BasicComponentParameter.BASIC_DEFAULT;
780 Parameter throwableParameter = new ConstantParameter(new Throwable("bar"));
781 ConstructorInjector ci = new ConstructorInjector(Exception.class, Exception.class, new Parameter[] {componentParameter,
782 throwableParameter}, new NullComponentMonitor(), false);
783 ComponentAdapter exceptionAdapter = child.as(getProperties()).addAdapter(ci).getComponentAdapter(Exception.class, (NameBinding) null);
784
785 List<Class> expectedList = new ArrayList<Class>();
786
787 addContainers(expectedList);
788 addDefaultComponentFactories(expectedList);
789 expectedList.add(hashMapAdapter.getClass());
790 expectedList.add(hashSetAdapter.getClass());
791 expectedList.add(stringAdapter.getClass());
792 addContainers(expectedList);
793 addDefaultComponentFactories(expectedList);
794 expectedList.add(arrayListAdapter.getClass());
795 expectedList.add(exceptionAdapter.getClass());
796 expectedList.add(componentParameter.getClass());
797 expectedList.add(throwableParameter.getClass());
798 List<Object> visitedList = new LinkedList<Object>();
799 PicoVisitor visitor = new RecordingStrategyVisitor(visitedList);
800 visitor.traverse(parent);
801 assertEquals(expectedList.size(), visitedList.size());
802 for (Class c : expectedList) {
803 assertTrue(visitedList.remove(c));
804 }
805 assertEquals(0, visitedList.size());
806 }
807
808 /**
809 * Verifies that you can halt a container traversal.
810 */
811 @Test
812 public void testAcceptIsAbortable() {
813 final MutablePicoContainer parent = createPicoContainer(null);
814 final MutablePicoContainer child = parent.makeChildContainer();
815 child.addComponent("This is a test");
816
817 TraversalCheckingVisitor parentComponentCountingVisitor = new TraversalCheckingVisitor() {
818 private int containerCount = 0;
819
820 @Override
821 @SuppressWarnings("unused")
822 public void visitComponentAdapter(ComponentAdapter<?> componentAdapter) {
823 if (containerCount == 0) {
824 fail("Should have visited a container first");
825 }
826 fail("Should never have visited an adapter.");
827 }
828
829 @Override
830 @SuppressWarnings("unused")
831 public boolean visitContainer(PicoContainer pico) {
832 containerCount++;
833 if (containerCount > 1) {
834 return ABORT_TRAVERSAL;
835 }
836
837 return CONTINUE_TRAVERSAL;
838 }
839
840 };
841
842 parentComponentCountingVisitor.traverse(parent);
843 }
844
845 protected void addContainers(List expectedList) {
846 expectedList.add(DefaultPicoContainer.class);
847 }
848
849 protected void addDefaultComponentFactories(List expectedList) {
850 expectedList.add(AdaptingBehavior.class);
851 }
852
853 @Test public void testAmbiguousDependencies() throws PicoCompositionException {
854
855 MutablePicoContainer pico = this.createPicoContainer(null);
856
857 // Register two Touchables that Fred will be confused about
858 pico.addComponent(SimpleTouchable.class);
859 pico.addComponent(DerivedTouchable.class);
860
861 // Register a confused DependsOnTouchable
862 pico.addComponent(DependsOnTouchable.class);
863
864 try {
865 pico.getComponent(DependsOnTouchable.class);
866 fail("DependsOnTouchable should have been confused about the two Touchables");
867 } catch (AbstractInjector.AmbiguousComponentResolutionException e) {
868 List componentImplementations = Arrays.asList(e.getAmbiguousComponentKeys());
869 assertTrue(componentImplementations.contains(DerivedTouchable.class));
870 assertTrue(componentImplementations.contains(SimpleTouchable.class));
871
872 assertTrue(e.getMessage().indexOf(DerivedTouchable.class.getName()) != -1);
873 }
874 }
875
876
877 public static class DerivedTouchable extends SimpleTouchable {
878 public DerivedTouchable() {
879 //Does nothing.
880 }
881 }
882
883
884 public static final class NonGreedyClass {
885
886 public final int value = 0;
887
888 public NonGreedyClass() {
889 //Do nothing.
890 }
891
892 public NonGreedyClass(ComponentA component) {
893 fail("Greedy Constructor should never have been called. Instead got: " + component);
894 }
895
896
897 }
898
899 @Test public void testNoArgConstructorToBeSelected() {
900 MutablePicoContainer pico = this.createPicoContainer(null);
901 pico.addComponent(ComponentA.class);
902 pico.addComponent(NonGreedyClass.class, NonGreedyClass.class, Parameter.ZERO);
903
904
905 NonGreedyClass instance = pico.getComponent(NonGreedyClass.class);
906 assertNotNull(instance);
907 }
908
909 public static class ConstantParameterTestService {
910 private final String arg;
911
912 public ConstantParameterTestService(String arg) {
913 this.arg = arg;
914 }
915
916 public String getArg() {
917 return arg;
918 }
919 }
920
921
922 /**
923 * Currently failing
924 */
925 @Test
926 public void testNullConstantParameter() {
927 MutablePicoContainer pico = createPicoContainer(null);
928 pico.addComponent(ConstantParameterTestService.class, ConstantParameterTestService.class, NullParameter.INSTANCE);
929 ConstantParameterTestService service = (ConstantParameterTestService) pico.getComponent(ConstantParameterTestService.class);
930 assertNotNull(service);
931 assertNull(service.getArg());
932 }
933
934
935 public static class PrimitiveConstructor {
936 @SuppressWarnings("unused")
937 public PrimitiveConstructor(int number) {
938 //does nothing.
939 }
940 }
941
942 @Test(expected=ParameterCannotBeNullException.class)
943 public void testNullConstantParametersDoNotInjectOnPrimitives() {
944 MutablePicoContainer pico = createPicoContainer(null);
945 pico.addComponent(PrimitiveConstructor.class, PrimitiveConstructor.class, NullParameter.INSTANCE);
946
947 //Should throw exception here.
948 pico.getComponent(PrimitiveConstructor.class);
949 }
950
951
952 @Test
953 public void testNullValuesDoNotInject() {
954 MutablePicoContainer pico = createPicoContainer(null);
955 pico.addComponent(ConstantParameterTestService.class, ConstantParameterTestService.class, new ConstantParameter(null));
956 try {
957 ConstantParameterTestService service = (ConstantParameterTestService) pico.getComponent(ConstantParameterTestService.class);
958 fail("Should have thrown unsatisfiable dependencies exception. Instead got " + service + " as a return value");
959 } catch (UnsatisfiableDependenciesException e) {
960 assertNotNull(e.getMessage());
961 }
962 }
963
964 @Test
965 public void testNullComponentsDoNotInject() {
966 MutablePicoContainer pico = createPicoContainer(null)
967 .addComponent(ComponentA.class)
968 .addComponent(ComponentB.class);
969
970
971 try {
972 pico.addComponent(ComponentC.class, null);
973 fail("Pico should not have been able to register null component instance");
974 } catch (NullPointerException e) {
975 assertNotNull(e.getMessage());
976 }
977 }
978
979
980 public static class ConverterSample {
981 public final int value;
982
983 public ConverterSample(Integer value) {
984 this.value = value;
985 }
986 }
987
988 @Test
989 public void testIntegrationWithConverters() {
990 MutablePicoContainer mpc = new DefaultPicoContainer();
991 if ( !(mpc instanceof Converting)) {
992 System.out.println("Skipping 'testIntegrationWithConverters' " +
993 "because pico implementation is not Converting");
994 return;
995 }
996
997 mpc.addComponent("converterParameter", "42")
998 .addComponent(ConverterSample.class, ConverterSample.class,
999 new ComponentParameter("converterParameter"));
1000 ConverterSample result = mpc.getComponent(ConverterSample.class);
1001 assertEquals(42, result.value);
1002 }
1003
1004 }