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