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 * Idea by Rachel Davies, Original code by Aslak Hellesoy and Paul Hammant * 009 *****************************************************************************/ 010 package org.picocontainer; 011 012 import static org.junit.Assert.assertEquals; 013 import static org.junit.Assert.assertNotNull; 014 import static org.junit.Assert.assertNotSame; 015 import static org.junit.Assert.assertSame; 016 import static org.junit.Assert.assertTrue; 017 import static org.junit.Assert.fail; 018 import static org.picocontainer.Characteristics.CDI; 019 import static org.picocontainer.Characteristics.SDI; 020 021 import java.io.Serializable; 022 import java.io.StringWriter; 023 import java.lang.reflect.Field; 024 import java.lang.reflect.Member; 025 import java.lang.reflect.Type; 026 import java.util.ArrayList; 027 import java.util.Collection; 028 import java.util.HashMap; 029 import java.util.HashSet; 030 import java.util.LinkedList; 031 import java.util.List; 032 import java.util.Map; 033 import java.util.Properties; 034 035 import org.junit.Test; 036 import org.picocontainer.annotations.Inject; 037 import org.picocontainer.behaviors.Caching; 038 import org.picocontainer.behaviors.Decorating; 039 import org.picocontainer.behaviors.FieldDecorating; 040 import org.picocontainer.containers.EmptyPicoContainer; 041 import org.picocontainer.injectors.AbstractInjector; 042 import org.picocontainer.injectors.ConstructorInjection; 043 import org.picocontainer.injectors.ConstructorInjector; 044 import org.picocontainer.injectors.FactoryInjector; 045 import org.picocontainer.injectors.MultiInjection; 046 import org.picocontainer.lifecycle.NullLifecycleStrategy; 047 import org.picocontainer.monitors.NullComponentMonitor; 048 import org.picocontainer.monitors.WriterComponentMonitor; 049 import org.picocontainer.tck.AbstractPicoContainerTest; 050 import org.picocontainer.testmodel.DecoratedTouchable; 051 import org.picocontainer.testmodel.DependsOnTouchable; 052 import org.picocontainer.testmodel.SimpleTouchable; 053 import org.picocontainer.testmodel.Touchable; 054 055 /** 056 * @author Aslak Hellesøp;y 057 * @author Paul Hammant 058 * @author Ward Cunningham 059 * @author Mauro Talevi 060 */ 061 @SuppressWarnings("serial") 062 public final class DefaultPicoContainerTestCase extends AbstractPicoContainerTest { 063 064 protected MutablePicoContainer createPicoContainer(PicoContainer parent) { 065 return new DefaultPicoContainer(parent); 066 } 067 068 protected Properties[] getProperties() { 069 return new Properties[0]; 070 } 071 072 @Test public void testInstantiationWithNullComponentFactory() { 073 try { 074 new DefaultPicoContainer((ComponentFactory) null, null); 075 fail("NPE expected"); 076 } catch (NullPointerException e) { 077 // expected 078 } 079 } 080 081 @Test public void testUpDownDependenciesCannotBeFollowed() { 082 MutablePicoContainer parent = createPicoContainer(null); 083 MutablePicoContainer child = createPicoContainer(parent); 084 085 // ComponentF -> ComponentA -> ComponentB+C 086 child.addComponent(ComponentF.class); 087 parent.addComponent(ComponentA.class); 088 child.addComponent(ComponentB.class); 089 child.addComponent(ComponentC.class); 090 091 try { 092 child.getComponent(ComponentF.class); 093 fail("Thrown " 094 + AbstractInjector.UnsatisfiableDependenciesException.class 095 .getName() + " expected"); 096 } catch (final AbstractInjector.UnsatisfiableDependenciesException e) { 097 assertEquals(ComponentB.class, e.getUnsatisfiedDependencyType()); 098 } 099 100 } 101 102 @Test public void testComponentsCanBeRemovedByInstance() { 103 MutablePicoContainer pico = createPicoContainer(null); 104 pico.addComponent(HashMap.class); 105 pico.addComponent(ArrayList.class); 106 List list = pico.getComponent(List.class); 107 pico.removeComponentByInstance(list); 108 assertEquals(1, pico.getComponentAdapters().size()); 109 assertEquals(1, pico.getComponents().size()); 110 assertEquals(HashMap.class, pico.getComponent(Serializable.class) 111 .getClass()); 112 } 113 114 @Test public void testComponentInstancesListIsReturnedForNullType() { 115 MutablePicoContainer pico = createPicoContainer(null); 116 List componentInstances = pico.getComponents(null); 117 assertNotNull(componentInstances); 118 assertEquals(0, componentInstances.size()); 119 } 120 121 @Test public void testComponentsWithCommonSupertypeWhichIsAConstructorArgumentCanBeLookedUpByConcreteType() { 122 MutablePicoContainer pico = createPicoContainer(null); 123 pico.addComponent(LinkedList.class, LinkedList.class, Parameter.ZERO); 124 pico.addComponent(ArrayList.class); 125 assertEquals(ArrayList.class, pico 126 .getComponent((Class) ArrayList.class).getClass()); 127 } 128 129 /* 130 * When pico tries to resolve DecoratedTouchable it find as dependency 131 * itself and SimpleTouchable. Problem is basically the same as above. Pico 132 * should not consider self as solution. 133 * 134 * JS fixed it ( PICO-222 ) KP 135 */ 136 @Test public void testUnambiguouSelfDependency() { 137 MutablePicoContainer pico = createPicoContainer(null); 138 pico.addComponent(SimpleTouchable.class); 139 pico.addComponent(DecoratedTouchable.class); 140 Touchable t = (Touchable) pico 141 .getComponent((Object) DecoratedTouchable.class); 142 assertNotNull(t); 143 } 144 145 @Test public void testPicoUsedInBuilderStyle() { 146 MutablePicoContainer pico = createPicoContainer(null); 147 Touchable t = pico.change(Characteristics.CACHE).addComponent( 148 SimpleTouchable.class).addComponent(DecoratedTouchable.class) 149 .getComponent(DecoratedTouchable.class); 150 SimpleTouchable t2 = pico.getComponent(SimpleTouchable.class); 151 assertNotNull(t); 152 assertNotNull(t2); 153 t.touch(); 154 assertTrue(t2.wasTouched); 155 } 156 157 public static class Thingie { 158 public Thingie(List c) { 159 assertNotNull(c); 160 } 161 } 162 163 @Test public void testThangCanBeInstantiatedWithArrayList() { 164 MutablePicoContainer pico = new DefaultPicoContainer(); 165 pico.addComponent(Thingie.class); 166 pico.addComponent(ArrayList.class); 167 assertNotNull(pico.getComponent(Thingie.class)); 168 } 169 170 @Test public void testGetComponentAdaptersOfTypeNullReturnsEmptyList() { 171 DefaultPicoContainer pico = new DefaultPicoContainer(); 172 List adapters = pico.getComponentAdapters(null); 173 assertNotNull(adapters); 174 assertEquals(0, adapters.size()); 175 } 176 177 public static class Service { 178 } 179 180 public static final class TransientComponent { 181 private final Service service; 182 183 public TransientComponent(Service service) { 184 this.service = service; 185 } 186 } 187 188 @Test public void testDefaultPicoContainerReturnsNewInstanceForEachCallWhenUsingTransientComponentAdapter() { 189 190 DefaultPicoContainer picoContainer = new DefaultPicoContainer( 191 new Caching().wrap(new ConstructorInjection())); 192 193 picoContainer.addComponent(Service.class); 194 picoContainer.as(Characteristics.NO_CACHE).addAdapter( 195 new ConstructorInjector(TransientComponent.class, 196 TransientComponent.class, null, 197 new NullComponentMonitor(), 198 new NullLifecycleStrategy(), false)); 199 TransientComponent c1 = picoContainer 200 .getComponent(TransientComponent.class); 201 TransientComponent c2 = picoContainer 202 .getComponent(TransientComponent.class); 203 assertNotSame(c1, c2); 204 assertSame(c1.service, c2.service); 205 } 206 207 public static class DependsOnCollection { 208 public DependsOnCollection(Collection c) { 209 } 210 } 211 212 @Test public void testShouldProvideInfoAboutDependingWhenAmbiguityHappens() { 213 MutablePicoContainer pico = this.createPicoContainer(null); 214 pico.addComponent(new ArrayList()); 215 pico.addComponent(new LinkedList()); 216 pico.addComponent(DependsOnCollection.class); 217 try { 218 pico.getComponent(DependsOnCollection.class); 219 fail(); 220 } catch (AbstractInjector.AmbiguousComponentResolutionException expected) { 221 String doc = DependsOnCollection.class.getName(); 222 assertEquals( 223 "class " 224 + doc 225 + " needs a 'java.util.Collection' injected, but there are too many choices to inject. These:[class java.util.ArrayList, class java.util.LinkedList], refer http://picocontainer.org/ambiguous-injectable-help.html", 226 expected.getMessage()); 227 } 228 } 229 230 @Test public void testInstantiationWithMonitorAndParent() { 231 StringWriter writer = new StringWriter(); 232 ComponentMonitor monitor = new WriterComponentMonitor(writer); 233 DefaultPicoContainer parent = new DefaultPicoContainer(); 234 DefaultPicoContainer child = new DefaultPicoContainer(monitor, parent); 235 parent.addComponent("st", SimpleTouchable.class); 236 child.addComponent("dot", DependsOnTouchable.class); 237 DependsOnTouchable dot = (DependsOnTouchable) child.getComponent("dot"); 238 assertNotNull(dot); 239 assertTrue("writer not empty", writer.toString().length() > 0); 240 241 } 242 243 @Test 244 public void testRepresentationOfContainerTree() { 245 StringWriter writer = new StringWriter(); 246 DefaultPicoContainer parent = new DefaultPicoContainer(); 247 parent.setName("parent"); 248 DefaultPicoContainer child = new DefaultPicoContainer(parent); 249 child.setName("child"); 250 parent.addComponent("st", SimpleTouchable.class); 251 child.addComponent("dot", DependsOnTouchable.class); 252 assertEquals("child:1<I<parent:1<|", child.toString()); 253 } 254 255 @SuppressWarnings("serial") 256 @Test public void testStartCapturedByMonitor() { 257 final StringBuffer sb = new StringBuffer(); 258 DefaultPicoContainer dpc = new DefaultPicoContainer( 259 new NullComponentMonitor() { 260 public void invoking(PicoContainer container, 261 ComponentAdapter componentAdapter, Member member, 262 Object instance) { 263 sb.append(member.toString()); 264 } 265 }); 266 dpc.as(Characteristics.CACHE).addComponent(DefaultPicoContainer.class); 267 dpc.start(); 268 assertEquals( 269 "ComponentMonitor should have been notified that the component had been started", 270 "public abstract void org.picocontainer.Startable.start()", sb 271 .toString()); 272 } 273 274 public static class StartableClazz implements Startable { 275 private MutablePicoContainer _pico; 276 277 public void start() { 278 List<SimpleTouchable> cps = _pico 279 .getComponents(SimpleTouchable.class); 280 assertNotNull(cps); 281 } 282 283 public void stop() { 284 } 285 286 } 287 288 @Test public void testListComponentsOnStart() { 289 290 // This is really discouraged. Breaks basic principals of IoC - 291 // components should not refer 292 // to their containers 293 // 294 // Might be deleted in due coure, along with adaptersClone stuff in DPC 295 296 DefaultPicoContainer dpc = new DefaultPicoContainer(); 297 dpc.addComponent(SimpleTouchable.class); 298 StartableClazz cl = new StartableClazz(); 299 cl._pico = dpc; 300 dpc.addComponent(cl); 301 dpc.start(); 302 } 303 304 @Test public void testCanChangeMonitor() { 305 StringWriter writer1 = new StringWriter(); 306 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1); 307 DefaultPicoContainer pico = new DefaultPicoContainer(monitor1); 308 pico.addComponent("t1", SimpleTouchable.class); 309 pico.addComponent("t3", SimpleTouchable.class); 310 Touchable t1 = (Touchable) pico.getComponent("t1"); 311 assertNotNull(t1); 312 final String s = writer1.toString(); 313 assertTrue("writer not empty", s.length() > 0); 314 StringWriter writer2 = new StringWriter(); 315 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2); 316 pico.changeMonitor(monitor2); 317 pico.addComponent("t2", SimpleTouchable.class); 318 Touchable t2 = (Touchable) pico.getComponent("t2"); 319 assertNotNull(t2); 320 final String s2 = writer2.toString(); 321 assertTrue("writer not empty", s2.length() > 0); 322 assertTrue("writers of same length", 323 writer1.toString().length() == writer2.toString().length()); 324 Touchable t3 = (Touchable) pico.getComponent("t3"); 325 assertNotNull(t3); 326 assertTrue("old writer was used", writer1.toString().length() < writer2 327 .toString().length()); 328 } 329 330 @Test public void testCanChangeMonitorOfChildContainers() { 331 StringWriter writer1 = new StringWriter(); 332 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1); 333 DefaultPicoContainer parent = new DefaultPicoContainer(); 334 DefaultPicoContainer child = new DefaultPicoContainer(monitor1); 335 parent.addChildContainer(child); 336 child.addComponent("t1", SimpleTouchable.class); 337 child.addComponent("t3", SimpleTouchable.class); 338 Touchable t1 = (Touchable) child.getComponent("t1"); 339 assertNotNull(t1); 340 assertTrue("writer not empty", writer1.toString().length() > 0); 341 StringWriter writer2 = new StringWriter(); 342 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2); 343 parent.changeMonitor(monitor2); 344 child.addComponent("t2", SimpleTouchable.class); 345 Touchable t2 = (Touchable) child.getComponent("t2"); 346 assertNotNull(t2); 347 assertTrue("writer not empty", writer2.toString().length() > 0); 348 String s1 = writer1.toString(); 349 String s2 = writer2.toString(); 350 assertTrue("writers of same length", s1.length() == s2.length()); 351 Touchable t3 = (Touchable) child.getComponent("t3"); 352 assertNotNull(t3); 353 assertTrue("old writer was used", writer1.toString().length() < writer2 354 .toString().length()); 355 } 356 357 @Test public void testChangeMonitorIsIgnoredIfNotSupportingStrategy() { 358 StringWriter writer = new StringWriter(); 359 ComponentMonitor monitor = new WriterComponentMonitor(writer); 360 DefaultPicoContainer parent = new DefaultPicoContainer( 361 new ComponentFactoryWithNoMonitor( 362 new ComponentAdapterWithNoMonitor(new SimpleTouchable()))); 363 parent.addChildContainer(new EmptyPicoContainer()); 364 parent.addComponent("t1", SimpleTouchable.class); 365 parent.changeMonitor(monitor); 366 assertTrue("writer empty", writer.toString().length() == 0); 367 } 368 369 @Test public void testCanReturnCurrentMonitorFromComponentFactory() { 370 StringWriter writer1 = new StringWriter(); 371 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1); 372 DefaultPicoContainer pico = new DefaultPicoContainer(monitor1); 373 assertEquals(monitor1, pico.currentMonitor()); 374 StringWriter writer2 = new StringWriter(); 375 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2); 376 pico.changeMonitor(monitor2); 377 assertEquals(monitor2, pico.currentMonitor()); 378 } 379 380 private static final class ComponentFactoryWithNoMonitor implements ComponentFactory { 381 private final ComponentAdapter adapter; 382 383 public ComponentFactoryWithNoMonitor(ComponentAdapter adapter) { 384 this.adapter = adapter; 385 } 386 387 public ComponentAdapter createComponentAdapter( 388 ComponentMonitor componentMonitor, 389 LifecycleStrategy lifecycleStrategy, 390 Properties componentProperties, Object componentKey, 391 Class componentImplementation, Parameter... parameters) 392 throws PicoCompositionException { 393 return adapter; 394 } 395 396 public void verify(PicoContainer container) { 397 } 398 399 public void accept(PicoVisitor visitor) { 400 visitor.visitComponentFactory(this); 401 } 402 } 403 404 private static final class ComponentAdapterWithNoMonitor implements 405 ComponentAdapter { 406 private final Object instance; 407 408 public ComponentAdapterWithNoMonitor(Object instance) { 409 this.instance = instance; 410 } 411 412 public Object getComponentKey() { 413 return instance.getClass(); 414 } 415 416 public Class getComponentImplementation() { 417 return instance.getClass(); 418 } 419 420 public Object getComponentInstance(PicoContainer container) throws PicoCompositionException { 421 return getComponentInstance(container, null); 422 } 423 424 public Object getComponentInstance(PicoContainer container, Type into) 425 throws PicoCompositionException { 426 return instance; 427 } 428 429 public void verify(PicoContainer container) 430 throws PicoCompositionException { 431 } 432 433 public void accept(PicoVisitor visitor) { 434 } 435 436 public ComponentAdapter getDelegate() { 437 return null; 438 } 439 440 public ComponentAdapter findAdapterOfType(Class componentAdapterType) { 441 return null; 442 } 443 444 public String getDescriptor() { 445 return null; 446 } 447 448 } 449 450 @Test public void testMakeChildContainer() { 451 MutablePicoContainer parent = new DefaultPicoContainer(); 452 parent.addComponent("t1", SimpleTouchable.class); 453 MutablePicoContainer child = parent.makeChildContainer(); 454 Object t1 = child.getParent().getComponent("t1"); 455 assertNotNull(t1); 456 assertTrue(t1 instanceof SimpleTouchable); 457 } 458 459 @Test public void testCanUseCustomLifecycleStrategyForClassRegistrations() { 460 DefaultPicoContainer dpc = new DefaultPicoContainer( 461 new FailingLifecycleStrategy(), null); 462 dpc.as(Characteristics.CACHE).addComponent(Startable.class, 463 MyStartable.class); 464 try { 465 dpc.start(); 466 fail("should have barfed"); 467 } catch (RuntimeException e) { 468 assertEquals("foo", e.getMessage()); 469 } 470 } 471 472 @Test public void testCanUseCustomLifecycleStrategyForInstanceRegistrations() { 473 DefaultPicoContainer dpc = new DefaultPicoContainer( 474 new FailingLifecycleStrategy(), null); 475 Startable myStartable = new MyStartable(); 476 dpc.addComponent(Startable.class, myStartable); 477 try { 478 dpc.start(); 479 fail("should have barfed"); 480 } catch (RuntimeException e) { 481 assertEquals("foo", e.getMessage()); 482 } 483 } 484 485 public static class FailingLifecycleStrategy implements LifecycleStrategy { 486 public void start(Object component) { 487 throw new RuntimeException("foo"); 488 } 489 490 public void stop(Object component) { 491 } 492 493 public void dispose(Object component) { 494 } 495 496 public boolean hasLifecycle(Class type) { 497 return true; 498 } 499 500 } 501 502 public static class MyStartable implements Startable { 503 public MyStartable() { 504 } 505 506 public void start() { 507 } 508 509 public void stop() { 510 } 511 } 512 513 public static interface A { 514 515 } 516 517 public static class SimpleA implements A { 518 519 } 520 521 public static class WrappingA implements A { 522 private final A wrapped; 523 524 public WrappingA(A wrapped) { 525 this.wrapped = wrapped; 526 } 527 } 528 529 @Test public void testCanRegisterTwoComponentsImplementingSameInterfaceOneWithInterfaceAsKey() 530 throws Exception { 531 MutablePicoContainer container = createPicoContainer(null); 532 533 container.addComponent(SimpleA.class); 534 container.addComponent(A.class, WrappingA.class); 535 536 container.start(); 537 538 assertEquals(WrappingA.class, container.getComponent(A.class) 539 .getClass()); 540 } 541 542 @Test public void testCanRegisterTwoComponentsWithSameImplementionAndDifferentKey() 543 throws Exception { 544 MutablePicoContainer container = createPicoContainer(null); 545 546 container.addComponent(SimpleA.class); 547 container.addComponent("A", SimpleA.class); 548 549 container.start(); 550 551 assertNotNull(container.getComponent("A")); 552 assertNotNull(container.getComponent(SimpleA.class)); 553 assertNotSame(container.getComponent("A"), container 554 .getComponent(SimpleA.class)); 555 } 556 557 @Test public void testPicoCanDifferentiateBetweenNamedStringsThatWouldOtherwiseBeAmbiguous() { 558 MutablePicoContainer mpc = createPicoContainer(null); 559 mpc.addComponent("greeting", "1"); 560 mpc.addComponent("message", "2"); 561 mpc.as(Characteristics.USE_NAMES).addComponent( 562 PicoCompositionException.class, PicoCompositionException.class); 563 assertEquals("2", mpc.getComponent(PicoCompositionException.class) 564 .getMessage()); 565 } 566 567 @Test public void testPicoCanDifferentiateBetweenNamedObjectsThatWouldOtherwiseBeAmbiguous() { 568 MutablePicoContainer mpc = createPicoContainer(null); 569 Horse dobbin = new Horse(); 570 Horse redRum = new Horse(); 571 mpc.addComponent("dobbin", dobbin); 572 mpc.addComponent("horse", redRum); 573 mpc.as(Characteristics.USE_NAMES).addComponent(CdiTurtle.class); 574 assertEquals(redRum, mpc.getComponent(CdiTurtle.class).horse); 575 } 576 577 @Test public void testPicoCanDifferentiateBetweenNamedIntsThatWouldOtherwiseBeAmbiguous() { 578 MutablePicoContainer mpc = createPicoContainer(null); 579 mpc.addComponent("one", 1); 580 mpc.addComponent("two", 2); 581 mpc.as(Characteristics.USE_NAMES).addComponent(NeedsTwo.class); 582 assertEquals(2, mpc.getComponent(NeedsTwo.class).two); 583 } 584 585 public static class ListComponentsInStartClass implements Startable { 586 private MutablePicoContainer _pico; 587 588 public void start() { 589 List<SimpleTouchable> cps = _pico 590 .getComponents(SimpleTouchable.class); 591 assertNotNull(cps); 592 } 593 594 public void stop() { 595 } 596 597 } 598 599 /** 600 * JIRA: PICO-295 reported by Erik Putrycz 601 */ 602 @Test public void testListComponentsInStart() { 603 DefaultPicoContainer dpc = new DefaultPicoContainer(); 604 dpc.addComponent(SimpleTouchable.class); 605 ListComponentsInStartClass cl = new ListComponentsInStartClass(); 606 cl._pico = dpc; 607 dpc.addComponent(cl); 608 dpc.start(); 609 } 610 611 public static class NeedsTwo { 612 private final int two; 613 614 public NeedsTwo(Integer two) { 615 this.two = two; 616 } 617 } 618 619 public static class Horse { 620 } 621 622 public static class CdiTurtle { 623 public final Horse horse; 624 625 public CdiTurtle(Horse horse) { 626 this.horse = horse; 627 } 628 } 629 630 public static class SdiDonkey { 631 public Horse horse; 632 633 public void setHorse(Horse horse) { 634 this.horse = horse; 635 } 636 } 637 638 public static class SdiRabbit { 639 public Horse horse; 640 641 public void setHorse(Horse horse) { 642 this.horse = horse; 643 } 644 } 645 646 @Test public void testMixingOfSDIandCDI() { 647 648 MutablePicoContainer container = createPicoContainer(null).change( 649 Characteristics.CACHE); 650 container.addComponent(Horse.class); 651 container.change(SDI); 652 container.addComponent(SdiDonkey.class); 653 container.addComponent(SdiRabbit.class); 654 container.change(CDI); 655 container.addComponent(CdiTurtle.class); 656 657 SdiDonkey donkey = container.getComponent(SdiDonkey.class); 658 SdiRabbit rabbit = container.getComponent(SdiRabbit.class); 659 CdiTurtle turtle = container.getComponent(CdiTurtle.class); 660 661 assertions(donkey, rabbit, turtle); 662 } 663 664 @Test public void testMixingOfSDIandCDIDifferently() { 665 666 MutablePicoContainer container = createPicoContainer(null).change( 667 Characteristics.CACHE); 668 container.addComponent(Horse.class); 669 container.addComponent(CdiTurtle.class); 670 container.change(SDI); 671 container.addComponent(SdiDonkey.class); 672 container.addComponent(SdiRabbit.class); 673 674 SdiDonkey donkey = container.getComponent(SdiDonkey.class); 675 SdiRabbit rabbit = container.getComponent(SdiRabbit.class); 676 CdiTurtle turtle = container.getComponent(CdiTurtle.class); 677 678 assertions(donkey, rabbit, turtle); 679 } 680 681 @Test public void testMixingOfSDIandCDIInBuilderStyle() { 682 683 MutablePicoContainer container = createPicoContainer(null).change( 684 Characteristics.CACHE); 685 container.addComponent(Horse.class).change(SDI).addComponent( 686 SdiDonkey.class).addComponent(SdiRabbit.class).change(CDI) 687 .addComponent(CdiTurtle.class); 688 689 SdiDonkey donkey = container.getComponent(SdiDonkey.class); 690 SdiRabbit rabbit = container.getComponent(SdiRabbit.class); 691 CdiTurtle turtle = container.getComponent(CdiTurtle.class); 692 693 assertions(donkey, rabbit, turtle); 694 } 695 696 private void assertions(SdiDonkey donkey, SdiRabbit rabbit, CdiTurtle turtle) { 697 assertNotNull(rabbit); 698 assertNotNull(donkey); 699 assertNotNull(turtle); 700 assertNotNull(turtle.horse); 701 assertNotNull(donkey.horse); 702 assertNotNull(rabbit.horse); 703 assertSame(donkey.horse, turtle.horse); 704 assertSame(rabbit.horse, turtle.horse); 705 } 706 707 @Test public void testMixingOfSDIandCDIWithTemporaryCharacterizations() { 708 709 MutablePicoContainer container = createPicoContainer(null).change( 710 Characteristics.CACHE); 711 container.addComponent(Horse.class); 712 container.addComponent(CdiTurtle.class); 713 container.as(SDI).addComponent(SdiDonkey.class); 714 container.as(SDI).addComponent(SdiRabbit.class); 715 716 SdiDonkey donkey = container.getComponent(SdiDonkey.class); 717 SdiRabbit rabbit = container.getComponent(SdiRabbit.class); 718 CdiTurtle turtle = container.getComponent(CdiTurtle.class); 719 720 assertions(donkey, rabbit, turtle); 721 } 722 723 @Test public void testMixingOfSDIandCDIWithTemporaryCharacterizationsDifferently() { 724 725 MutablePicoContainer container = createPicoContainer(null).change( 726 Characteristics.CACHE); 727 container.as(SDI).addComponent(SdiDonkey.class); 728 container.as(SDI).addComponent(SdiRabbit.class); 729 container.addComponent(Horse.class); 730 container.addComponent(CdiTurtle.class); 731 732 SdiDonkey donkey = container.getComponent(SdiDonkey.class); 733 SdiRabbit rabbit = container.getComponent(SdiRabbit.class); 734 CdiTurtle turtle = container.getComponent(CdiTurtle.class); 735 736 assertions(donkey, rabbit, turtle); 737 } 738 739 @SuppressWarnings("serial") 740 @Test public void testNoComponentIsMonitoredAndPotentiallyLateProvided() { 741 final String[] missingKey = new String[1]; 742 743 String foo = (String) new DefaultPicoContainer( 744 new NullComponentMonitor() { 745 public Object noComponentFound( 746 MutablePicoContainer container, Object componentKey) { 747 missingKey[0] = (String) componentKey; 748 return "foo"; 749 } 750 }).getComponent("missingKey"); 751 752 assertNotNull(missingKey[0]); 753 assertEquals("missingKey", missingKey[0]); 754 assertEquals("foo", foo); 755 756 } 757 758 @Test public void testThatComponentCannotBeRemovedFromStartedContainer() { 759 MutablePicoContainer container = createPicoContainer(null); 760 container.addComponent(Map.class, HashMap.class); 761 container.start(); 762 try { 763 container.removeComponent(Map.class); 764 fail("should have barfed"); 765 } catch (PicoCompositionException e) { 766 } 767 } 768 769 @Test public void testThatSimpleStringComponentIsAddedOnlyOnce() { 770 MutablePicoContainer container = createPicoContainer(null); 771 container.addComponent("foo bar"); 772 assertEquals(1, container.getComponentAdapters().size()); 773 } 774 775 @Test public void canInterceptImplementationViaNewInjectionFactoryMethodOnMonitor() { 776 DefaultPicoContainer dpc = new DefaultPicoContainer(new MyNullComponentMonitor()); 777 dpc.addComponent(Collection.class, HashSet.class); 778 dpc.addComponent(List.class, ArrayList.class); 779 assertNotNull(dpc.getComponent(List.class)); 780 assertEquals("doppleganger", dpc.getComponent(List.class).get(0)); 781 } 782 783 @SuppressWarnings("serial") 784 private static class MyNullComponentMonitor extends NullComponentMonitor { 785 public AbstractInjector newInjectionFactory(AbstractInjector abstractInjector) { 786 if (abstractInjector.getComponentKey() == List.class) { 787 return new AbstractInjector(List.class, ArrayList.class, Parameter.DEFAULT, MyNullComponentMonitor.this, null, false) { 788 public Object getComponentInstance(PicoContainer container) throws PicoCompositionException { 789 return getComponentInstance(container, null); 790 } 791 792 public Object getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException { 793 ArrayList list = new ArrayList(); 794 list.add("doppleganger"); 795 return list; 796 } 797 798 public void decorateComponentInstance(PicoContainer container, Type into, Object instance) { 799 } 800 }; 801 } else { 802 return abstractInjector; 803 } 804 } 805 } 806 807 public static interface Swede { 808 } 809 public static class Turnip2 extends Turnip { 810 public Turnip2(String foo, Swede swede) { 811 super(foo); 812 assertNotNull(swede); 813 super.swede = swede; 814 } 815 } 816 public static class Turnip { 817 @Inject 818 Swede swede; 819 private final String foo; 820 821 public Turnip(String foo) { 822 this.foo = foo; 823 } 824 825 public Swede getSwede() { 826 return swede; 827 } 828 829 public String getFoo() { 830 return foo; 831 } 832 } 833 834 @Test public void testThatComponentCanHaveAProvidedDependency() { 835 MutablePicoContainer container = new DefaultPicoContainer(new MultiInjection()); 836 container.addComponent(String.class, "foo"); 837 container.addComponent(Turnip.class); 838 container.addAdapter(new SwedeFactoryInjector()); 839 Turnip t = container.getComponent(Turnip.class); 840 assertNotNull(t); 841 assertEquals("Swede for " + Turnip.class.getName(), t.getSwede().toString()); 842 assertEquals("foo", t.getFoo()); 843 844 } 845 846 @Test public void testThatComponentCanHaveAProvidedDependencyViaConstructor() { 847 MutablePicoContainer container = new DefaultPicoContainer(); 848 container.addComponent(String.class, "foo"); 849 container.addComponent(Turnip2.class); 850 container.addAdapter(new SwedeFactoryInjector()); 851 Turnip2 t = container.getComponent(Turnip2.class); 852 assertNotNull(t); 853 assertEquals("Swede for " + Turnip2.class.getName(), t.getSwede().toString()); 854 assertEquals("foo", t.getFoo()); 855 856 } 857 858 @Test public void testThatComponentCanHaveAProvidedDependencyViaConstructorADifferentWay() { 859 MutablePicoContainer container = new DefaultPicoContainer(); 860 container.addComponent(String.class, "foo"); 861 container.addComponent(Turnip2.class); 862 container.addAdapter(new Swede2FactoryInjector()); // this injector defines Swede2 as key in its ctor 863 Turnip2 t = container.getComponent(Turnip2.class); 864 assertNotNull(t); 865 assertEquals("Swede for " + Turnip2.class.getName(), t.getSwede().toString()); 866 assertEquals("foo", t.getFoo()); 867 868 } 869 870 @Test 871 public void testThatComponentCanHaveAProvidedDependencyViaDecoratorBehavior() { 872 MutablePicoContainer container = new DefaultPicoContainer(new SwedeDecorating().wrap(new ConstructorInjection())); 873 container.addComponent(String.class, "foo"); 874 container.addComponent(Turnip.class); 875 Turnip t = container.getComponent(Turnip.class); 876 assertNotNull(t); 877 assertNotNull(t.getSwede()); 878 assertEquals("Swede:" + Turnip.class.getName(), t.getSwede().toString()); 879 assertEquals("foo", t.getFoo()); 880 881 } 882 883 @Test 884 public void testThatComponentCanHaveAProvidedDependencyViaFieldDecoratorBehavior() { 885 MutablePicoContainer container = new DefaultPicoContainer( 886 new FieldDecorating(Swede.class) { 887 public Object decorate(final Object instance) { 888 return new Swede() { 889 public String toString() { 890 return "Swede:" + instance.getClass().getName(); 891 } 892 }; 893 } 894 }.wrap(new ConstructorInjection())); 895 container.addComponent(String.class, "foo"); 896 container.addComponent(Turnip.class); 897 Turnip t = container.getComponent(Turnip.class); 898 assertNotNull(t); 899 assertNotNull(t.getSwede()); 900 assertEquals("Swede:" + Turnip.class.getName(), t.getSwede().toString()); 901 assertEquals("foo", t.getFoo()); 902 903 } 904 905 private static class SwedeDecorating extends Decorating { 906 public void decorate(final Object instance) { 907 Field[] fields = instance.getClass().getDeclaredFields(); 908 for (int i = 0; i < fields.length; i++) { 909 Field field = fields[i]; 910 if (field.getType() == Swede.class) { 911 Swede value = new Swede() { 912 public String toString() { 913 return "Swede:" + instance.getClass().getName(); 914 } 915 }; 916 field.setAccessible(true); 917 try { 918 field.set(instance, value); 919 } catch (IllegalAccessException e) { 920 throw new RuntimeException(e); 921 } 922 } 923 924 } 925 } 926 } 927 928 private static class SwedeFactoryInjector extends FactoryInjector<Swede> { 929 public Swede getComponentInstance(PicoContainer container, final Type into) throws PicoCompositionException { 930 // Mauro: you can do anything in here by way of startegy for injecting a specific logger :-) 931 return new Swede() { 932 public String toString() { 933 return "Swede for " + ((Class) into).getName(); 934 } 935 }; 936 } 937 } 938 939 private static class Swede2FactoryInjector extends FactoryInjector { 940 private Swede2FactoryInjector() { 941 super(Swede.class); 942 } 943 public Swede getComponentInstance(PicoContainer container, final Type into) throws PicoCompositionException { 944 // Mauro: you can do anything in here by way of startegy for injecting a specific logger :-) 945 return new Swede() { 946 public String toString() { 947 return "Swede for " + ((Class) into).getName(); 948 } 949 }; 950 } 951 } 952 953 private abstract class Footle<T> { 954 private class ServiceConnectionInjector extends FactoryInjector<T> { 955 public T getComponentInstance(PicoContainer container, Type into) { 956 System.out.println("**** injector called for " + into); 957 return null; 958 } 959 } 960 private void addAdapter(MutablePicoContainer mpc) { 961 mpc.addAdapter(new ServiceConnectionInjector()); 962 } 963 } 964 965 public static interface Tree { 966 String leafColor(); 967 } 968 public static class OakTree implements Tree { 969 private String leafColor; 970 971 public OakTree(String leafColor) { 972 this.leafColor = leafColor; 973 } 974 975 public String leafColor() { 976 return leafColor; 977 } 978 } 979 980 @Test public void ensureSophistcatedFactorInjectorCaseIsPossible() { 981 982 DefaultPicoContainer pico = new DefaultPicoContainer(); 983 pico.addConfig("leafColor", "green"); 984 pico.addComponent(Tree.class, OakTree.class); 985 986 Footle<Map> ft = new Footle<Map>(){}; 987 988 ft.addAdapter(pico); 989 990 Tree tree = pico.getComponent(Tree.class); 991 } 992 993 }