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