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