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