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.defaults; 011 012 import org.picocontainer.ComponentAdapter; 013 import org.picocontainer.ComponentMonitor; 014 import org.picocontainer.MutablePicoContainer; 015 import org.picocontainer.Parameter; 016 import org.picocontainer.PicoContainer; 017 import org.picocontainer.PicoInitializationException; 018 import org.picocontainer.PicoIntrospectionException; 019 import org.picocontainer.PicoVisitor; 020 import org.picocontainer.Startable; 021 import org.picocontainer.alternatives.EmptyPicoContainer; 022 import org.picocontainer.monitors.DefaultComponentMonitor; 023 import org.picocontainer.monitors.WriterComponentMonitor; 024 import org.picocontainer.tck.AbstractPicoContainerTestCase; 025 import org.picocontainer.testmodel.DecoratedTouchable; 026 import org.picocontainer.testmodel.DependsOnTouchable; 027 import org.picocontainer.testmodel.SimpleTouchable; 028 import org.picocontainer.testmodel.Touchable; 029 030 import java.io.Serializable; 031 import java.io.StringWriter; 032 import java.lang.reflect.Method; 033 import java.util.ArrayList; 034 import java.util.Collection; 035 import java.util.HashMap; 036 import java.util.LinkedList; 037 import java.util.List; 038 039 /** 040 * @author Aslak Hellesøy 041 * @author Paul Hammant 042 * @author Ward Cunningham 043 * @author Mauro Talevi 044 * @version $Revision: 3116 $ 045 */ 046 public class DefaultPicoContainerTestCase extends AbstractPicoContainerTestCase { 047 protected MutablePicoContainer createPicoContainer(PicoContainer parent) { 048 return new DefaultPicoContainer(parent); 049 } 050 051 public void testInstantiationWithNullComponentAdapterFactory(){ 052 try { 053 new DefaultPicoContainer((ComponentAdapterFactory)null, (PicoContainer)null); 054 fail("NPE expected"); 055 } catch (NullPointerException e) { 056 // expected 057 } 058 } 059 public void testUpDownDependenciesCannotBeFollowed() { 060 MutablePicoContainer parent = createPicoContainer(null); 061 MutablePicoContainer child = createPicoContainer(parent); 062 063 // ComponentF -> ComponentA -> ComponentB+C 064 child.registerComponentImplementation(ComponentF.class); 065 parent.registerComponentImplementation(ComponentA.class); 066 child.registerComponentImplementation(ComponentB.class); 067 child.registerComponentImplementation(ComponentC.class); 068 069 try { 070 child.getComponentInstance(ComponentF.class); 071 fail("Thrown " + UnsatisfiableDependenciesException.class.getName() + " expected"); 072 } catch (final UnsatisfiableDependenciesException e) { 073 assertEquals(ComponentB.class, e.getUnsatisfiedDependencyType()); 074 } 075 } 076 077 public void testComponentsCanBeRemovedByInstance() { 078 MutablePicoContainer pico = createPicoContainer(null); 079 pico.registerComponentImplementation(HashMap.class); 080 pico.registerComponentImplementation(ArrayList.class); 081 List list = (List) pico.getComponentInstanceOfType(List.class); 082 pico.unregisterComponentByInstance(list); 083 assertEquals(1, pico.getComponentAdapters().size()); 084 assertEquals(1, pico.getComponentInstances().size()); 085 assertEquals(HashMap.class, pico.getComponentInstanceOfType(Serializable.class).getClass()); 086 } 087 088 public void testComponentInstancesListIsReturnedForNullType(){ 089 MutablePicoContainer pico = createPicoContainer(null); 090 List componentInstances = pico.getComponentInstancesOfType(null); 091 assertNotNull(componentInstances); 092 assertEquals(0, componentInstances.size()); 093 } 094 095 public void testComponentsWithCommonSupertypeWhichIsAConstructorArgumentCanBeLookedUpByConcreteType() { 096 MutablePicoContainer pico = createPicoContainer(null); 097 pico.registerComponentImplementation(LinkedList.class, LinkedList.class, new Parameter[0]); 098 pico.registerComponentImplementation(ArrayList.class); 099 assertEquals(ArrayList.class, pico.getComponentInstanceOfType(ArrayList.class).getClass()); 100 } 101 102 /* 103 When pico tries to resolve DecoratedTouchable it find as dependency itself and SimpleTouchable. 104 Problem is basically the same as above. Pico should not consider self as solution. 105 106 JS 107 fixed it ( PICO-222 ) 108 KP 109 */ 110 public void testUnambiguouSelfDependency() { 111 MutablePicoContainer pico = createPicoContainer(null); 112 pico.registerComponentImplementation(SimpleTouchable.class); 113 pico.registerComponentImplementation(DecoratedTouchable.class); 114 Touchable t = (Touchable) pico.getComponentInstance(DecoratedTouchable.class); 115 assertNotNull(t); 116 } 117 118 public static class Thingie { 119 public Thingie(List c) { 120 assertNotNull(c); 121 } 122 } 123 124 public void testThangCanBeInstantiatedWithArrayList() { 125 MutablePicoContainer pico = new DefaultPicoContainer(); 126 pico.registerComponentImplementation(Thingie.class); 127 pico.registerComponentImplementation(ArrayList.class); 128 assertNotNull(pico.getComponentInstance(Thingie.class)); 129 } 130 131 public void testGetComponentAdaptersOfTypeNullReturnsEmptyList() { 132 DefaultPicoContainer pico = new DefaultPicoContainer(); 133 List adapters = pico.getComponentAdaptersOfType(null); 134 assertNotNull(adapters); 135 assertEquals(0, adapters.size()); 136 } 137 138 139 public static class Service { 140 } 141 142 public static class TransientComponent { 143 private Service service; 144 145 public TransientComponent(Service service) { 146 this.service = service; 147 } 148 } 149 150 public void testDefaultPicoContainerReturnsNewInstanceForEachCallWhenUsingTransientComponentAdapter() { 151 DefaultPicoContainer picoContainer = new DefaultPicoContainer(); 152 picoContainer.registerComponentImplementation(Service.class); 153 picoContainer.registerComponent(new ConstructorInjectionComponentAdapter(TransientComponent.class, TransientComponent.class)); 154 TransientComponent c1 = (TransientComponent) picoContainer.getComponentInstance(TransientComponent.class); 155 TransientComponent c2 = (TransientComponent) picoContainer.getComponentInstance(TransientComponent.class); 156 assertNotSame(c1, c2); 157 assertSame(c1.service, c2.service); 158 } 159 160 public static class DependsOnCollection { 161 public DependsOnCollection(Collection c) { 162 } 163 } 164 165 public void testShouldProvideInfoAboutDependingWhenAmbiguityHappens() { 166 MutablePicoContainer pico = this.createPicoContainer(null); 167 pico.registerComponentInstance(new ArrayList()); 168 pico.registerComponentInstance(new LinkedList()); 169 pico.registerComponentImplementation(DependsOnCollection.class); 170 try { 171 pico.getComponentInstanceOfType(DependsOnCollection.class); 172 fail(); 173 } catch (AmbiguousComponentResolutionException expected) { 174 String doc = DependsOnCollection.class.getName(); 175 assertEquals("class " + doc + " has ambiguous dependency on interface java.util.Collection, resolves to multiple classes: [class java.util.ArrayList, class java.util.LinkedList]", expected.getMessage()); 176 } 177 } 178 179 public void testInstantiationWithMonitorAndParent() { 180 StringWriter writer = new StringWriter(); 181 ComponentMonitor monitor = new WriterComponentMonitor(writer); 182 DefaultPicoContainer parent = new DefaultPicoContainer(); 183 DefaultPicoContainer child = new DefaultPicoContainer(monitor, parent); 184 parent.registerComponentImplementation("st", SimpleTouchable.class); 185 child.registerComponentImplementation("dot", DependsOnTouchable.class); 186 DependsOnTouchable dot = (DependsOnTouchable) child.getComponentInstance("dot"); 187 assertNotNull(dot); 188 assertTrue("writer not empty", writer.toString().length() > 0); 189 } 190 191 public void testStartCapturedByMonitor() { 192 final StringBuffer sb = new StringBuffer(); 193 DefaultPicoContainer dpc = new DefaultPicoContainer(new DefaultComponentMonitor() { 194 public void invoking(Method method, Object instance) { 195 sb.append(method.toString()); 196 } 197 }); 198 dpc.registerComponentImplementation(DefaultPicoContainer.class); 199 dpc.start(); 200 assertEquals("ComponentMonitor should have been notified that the component had been started", 201 "public abstract void org.picocontainer.Startable.start()", sb.toString()); 202 } 203 204 public void testCanChangeMonitor() { 205 StringWriter writer1 = new StringWriter(); 206 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1); 207 DefaultPicoContainer pico = new DefaultPicoContainer(monitor1); 208 pico.registerComponentImplementation("t1", SimpleTouchable.class); 209 pico.registerComponentImplementation("t3", SimpleTouchable.class); 210 Touchable t1 = (Touchable) pico.getComponentInstance("t1"); 211 assertNotNull(t1); 212 assertTrue("writer not empty", writer1.toString().length() > 0); 213 StringWriter writer2 = new StringWriter(); 214 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2); 215 pico.changeMonitor(monitor2); 216 pico.registerComponentImplementation("t2", SimpleTouchable.class); 217 Touchable t2 = (Touchable) pico.getComponentInstance("t2"); 218 assertNotNull(t2); 219 assertTrue("writer not empty", writer2.toString().length() > 0); 220 assertTrue("writers of same length", writer1.toString().length() == writer2.toString().length()); 221 Touchable t3 = (Touchable) pico.getComponentInstance("t3"); 222 assertNotNull(t3); 223 assertTrue("old writer was used", writer1.toString().length() < writer2.toString().length()); 224 } 225 226 public void testCanChangeMonitorOfChildContainers() { 227 StringWriter writer1 = new StringWriter(); 228 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1); 229 DefaultPicoContainer parent = new DefaultPicoContainer(); 230 DefaultPicoContainer child = new DefaultPicoContainer(monitor1); 231 parent.addChildContainer(child); 232 child.registerComponentImplementation("t1", SimpleTouchable.class); 233 child.registerComponentImplementation("t3", SimpleTouchable.class); 234 Touchable t1 = (Touchable) child.getComponentInstance("t1"); 235 assertNotNull(t1); 236 assertTrue("writer not empty", writer1.toString().length() > 0); 237 StringWriter writer2 = new StringWriter(); 238 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2); 239 parent.changeMonitor(monitor2); 240 child.registerComponentImplementation("t2", SimpleTouchable.class); 241 Touchable t2 = (Touchable) child.getComponentInstance("t2"); 242 assertNotNull(t2); 243 assertTrue("writer not empty", writer2.toString().length() > 0); 244 String s1 = writer1.toString(); 245 String s2 = writer2.toString(); 246 assertTrue("writers of same length", s1.length() == s2.length()); 247 Touchable t3 = (Touchable) child.getComponentInstance("t3"); 248 assertNotNull(t3); 249 assertTrue("old writer was used", writer1.toString().length() < writer2.toString().length()); 250 } 251 252 public void testChangeMonitorIsIgnoredIfNotSupportingStrategy(){ 253 StringWriter writer = new StringWriter(); 254 ComponentMonitor monitor = new WriterComponentMonitor(writer); 255 DefaultPicoContainer parent = new DefaultPicoContainer(new ComponentAdapterFactoryWithNoMonitor(new ComponentAdapterWithNoMonitor(new SimpleTouchable()))); 256 parent.addChildContainer(new EmptyPicoContainer()); 257 parent.registerComponentImplementation("t1", SimpleTouchable.class); 258 parent.changeMonitor(monitor); 259 assertTrue("writer empty", writer.toString().length() == 0); 260 } 261 262 public void testCanReturnCurrentMonitorFromComponentAdapterFactory() { 263 StringWriter writer1 = new StringWriter(); 264 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1); 265 DefaultPicoContainer pico = new DefaultPicoContainer(monitor1); 266 assertEquals(monitor1, pico.currentMonitor()); 267 StringWriter writer2 = new StringWriter(); 268 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2); 269 pico.changeMonitor(monitor2); 270 assertEquals(monitor2, pico.currentMonitor()); 271 } 272 273 public void testCanReturnCurrentMonitorFromComponentAdapter() { 274 StringWriter writer1 = new StringWriter(); 275 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1); 276 InstanceComponentAdapter adapterWithMonitor = new InstanceComponentAdapter(SimpleTouchable.class.getName(), new SimpleTouchable()); 277 adapterWithMonitor.changeMonitor(monitor1); 278 DefaultPicoContainer pico = new DefaultPicoContainer(new ComponentAdapterFactoryWithNoMonitor(adapterWithMonitor)); 279 pico.registerComponentImplementation("t1", SimpleTouchable.class); 280 assertEquals(monitor1, pico.currentMonitor()); 281 StringWriter writer2 = new StringWriter(); 282 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2); 283 pico.changeMonitor(monitor2); 284 assertEquals(monitor2, pico.currentMonitor()); 285 } 286 287 public void testCanReturnCurrentMonitorFromChildContainer() { 288 StringWriter writer1 = new StringWriter(); 289 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1); 290 DefaultPicoContainer pico = new DefaultPicoContainer(new ComponentAdapterFactoryWithNoMonitor(new ComponentAdapterWithNoMonitor(new SimpleTouchable()))); 291 pico.registerComponentImplementation("t1", SimpleTouchable.class); 292 // first child does not support ComponentMonitorStrategy 293 pico.addChildContainer(new EmptyPicoContainer()); 294 // second child does support ComponentMonitorStrategy 295 pico.addChildContainer(new DefaultPicoContainer(monitor1)); 296 assertEquals(monitor1, pico.currentMonitor()); 297 StringWriter writer2 = new StringWriter(); 298 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2); 299 pico.changeMonitor(monitor2); 300 assertEquals(monitor2, pico.currentMonitor()); 301 } 302 303 public void testCannotReturnCurrentMonitor() { 304 DefaultPicoContainer pico = new DefaultPicoContainer(new ComponentAdapterFactoryWithNoMonitor(null)); 305 try { 306 pico.currentMonitor(); 307 fail("PicoIntrospectionException expected"); 308 } catch (PicoIntrospectionException e) { 309 assertEquals("No component monitor found in container or its children", e.getMessage()); 310 } 311 } 312 313 private static class ComponentAdapterFactoryWithNoMonitor implements ComponentAdapterFactory { 314 private ComponentAdapter adapter; 315 public ComponentAdapterFactoryWithNoMonitor(ComponentAdapter adapter){ 316 this.adapter = adapter; 317 } 318 public ComponentAdapter createComponentAdapter(Object componentKey, Class componentImplementation, Parameter[] parameters) throws PicoIntrospectionException, AssignabilityRegistrationException, NotConcreteRegistrationException { 319 return adapter; 320 } 321 } 322 323 private static class ComponentAdapterWithNoMonitor implements ComponentAdapter { 324 private Object instance; 325 public ComponentAdapterWithNoMonitor(Object instance){ 326 this.instance = instance; 327 } 328 public Object getComponentKey() { 329 return instance.getClass(); 330 } 331 public Class getComponentImplementation() { 332 return instance.getClass(); 333 } 334 public Object getComponentInstance(PicoContainer container) throws PicoInitializationException, PicoIntrospectionException { 335 return instance; 336 } 337 public void verify(PicoContainer container) throws PicoIntrospectionException { 338 } 339 public void accept(PicoVisitor visitor) { 340 } 341 } 342 343 public void testMakeChildContainer() { 344 MutablePicoContainer parent = new DefaultPicoContainer(); 345 parent.registerComponentImplementation("t1", SimpleTouchable.class); 346 MutablePicoContainer child = parent.makeChildContainer(); 347 Object t1 = child.getParent().getComponentInstance("t1"); 348 assertNotNull(t1); 349 assertTrue(t1 instanceof SimpleTouchable); 350 } 351 352 public void testCanUseCustomLifecycleStrategyForClassRegistrations() { 353 DefaultPicoContainer dpc = new DefaultPicoContainer(new FailingLifecycleStrategy(), null); 354 dpc.registerComponentImplementation(Startable.class, MyStartable.class); 355 try { 356 dpc.start(); 357 fail("should have barfed"); 358 } catch (RuntimeException e) { 359 assertEquals("foo", e.getMessage()); 360 } 361 } 362 363 public void testCanUseCustomLifecycleStrategyForInstanceRegistrations() { 364 DefaultPicoContainer dpc = new DefaultPicoContainer(new FailingLifecycleStrategy(), null); 365 Startable myStartable = new MyStartable(); 366 dpc.registerComponentInstance(Startable.class, myStartable); 367 try { 368 dpc.start(); 369 fail("should have barfed"); 370 } catch (RuntimeException e) { 371 assertEquals("foo", e.getMessage()); 372 } 373 } 374 375 public static class FailingLifecycleStrategy implements LifecycleStrategy { 376 public void start(Object component) { 377 throw new RuntimeException("foo"); 378 } 379 380 public void stop(Object component) { 381 } 382 383 public void dispose(Object component) { 384 } 385 386 public boolean hasLifecycle(Class type) { 387 return true; 388 } 389 390 } 391 public static class MyStartable implements Startable { 392 public MyStartable() { 393 } 394 395 public void start() { 396 } 397 398 public void stop() { 399 } 400 } 401 402 public static interface A { 403 404 } 405 406 public static class SimpleA implements A 407 { 408 409 } 410 411 public static class WrappingA implements A 412 { 413 private final A wrapped; 414 415 public WrappingA(A wrapped) { 416 this.wrapped = wrapped; 417 } 418 } 419 420 public void testCanRegisterTwoComponentsImplementingSameInterfaceOneWithInterfaceAsKey() throws Exception { 421 MutablePicoContainer container = createPicoContainer(null); 422 423 container.registerComponentImplementation(SimpleA.class); 424 container.registerComponentImplementation(A.class, WrappingA.class); 425 426 container.start(); 427 428 assertEquals(WrappingA.class, container.getComponentInstance(A.class).getClass()); 429 } 430 431 public void testCanRegisterTwoComponentsWithSameImplementionAndDifferentKey() throws Exception { 432 MutablePicoContainer container = createPicoContainer(null); 433 434 container.registerComponentImplementation(SimpleA.class); 435 container.registerComponentImplementation("A", SimpleA.class); 436 437 container.start(); 438 439 assertNotNull(container.getComponentInstance("A")); 440 assertNotNull(container.getComponentInstance(SimpleA.class)); 441 assertNotSame(container.getComponentInstance("A"), container.getComponentInstance(SimpleA.class)); 442 } 443 444 public static class MyPicoContainer extends DefaultPicoContainer { 445 446 public ComponentAdapter registerComponent(ComponentAdapter componentAdapter) { 447 return super.registerComponent(new SynchronizedComponentAdapter(componentAdapter)); 448 } 449 450 } 451 452 public void testDerivedPicoContainerCanOverloadRegisterComponentForAllCreatedComponentAdapters() { 453 MutablePicoContainer mpc = new MyPicoContainer(); 454 assertEquals(SynchronizedComponentAdapter.class, mpc.registerComponent(new InstanceComponentAdapter("foo", "bar")).getClass()); 455 assertEquals(SynchronizedComponentAdapter.class, mpc.registerComponentInstance("foobar").getClass()); 456 assertEquals(SynchronizedComponentAdapter.class, mpc.registerComponentImplementation(SimpleA.class).getClass()); 457 } 458 }