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