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.injectors; 011 012 import static org.junit.Assert.assertEquals; 013 import static org.junit.Assert.assertNotNull; 014 import static org.junit.Assert.assertTrue; 015 import static org.junit.Assert.fail; 016 import static org.picocontainer.tck.MockFactory.mockeryWithCountingNamingScheme; 017 018 import java.awt.event.ActionEvent; 019 import java.awt.event.ActionListener; 020 import java.lang.reflect.Constructor; 021 import java.lang.reflect.InvocationTargetException; 022 import java.util.HashMap; 023 import java.util.Map; 024 025 import javax.swing.AbstractButton; 026 027 import org.hamcrest.BaseMatcher; 028 import org.hamcrest.Description; 029 import org.hamcrest.Matcher; 030 import org.jmock.Expectations; 031 import org.jmock.Mockery; 032 import org.junit.Test; 033 import org.picocontainer.ComponentAdapter; 034 import org.picocontainer.ComponentMonitor; 035 import org.picocontainer.DefaultPicoContainer; 036 import org.picocontainer.MutablePicoContainer; 037 import org.picocontainer.Parameter; 038 import org.picocontainer.PicoCompositionException; 039 import org.picocontainer.PicoContainer; 040 import org.picocontainer.InjectionFactory; 041 import org.picocontainer.lifecycle.NullLifecycleStrategy; 042 import org.picocontainer.monitors.AbstractComponentMonitor; 043 import org.picocontainer.monitors.NullComponentMonitor; 044 import org.picocontainer.parameters.ComponentParameter; 045 import org.picocontainer.parameters.ConstantParameter; 046 import org.picocontainer.tck.AbstractComponentAdapterTest; 047 import org.picocontainer.testmodel.DependsOnTouchable; 048 import org.picocontainer.testmodel.NullLifecycle; 049 import org.picocontainer.testmodel.SimpleTouchable; 050 import org.picocontainer.testmodel.Touchable; 051 052 053 @SuppressWarnings("serial") 054 public class ConstructorInjectorTestCase extends AbstractComponentAdapterTest { 055 056 private Mockery mockery = mockeryWithCountingNamingScheme(); 057 058 protected Class getComponentAdapterType() { 059 return ConstructorInjector.class; 060 } 061 062 protected ComponentAdapter prepDEF_verifyWithoutDependencyWorks(MutablePicoContainer picoContainer) { 063 return new ConstructorInjector("foo", A.class, null, new NullComponentMonitor(), false); 064 } 065 066 public static class A { 067 public A() { 068 fail("verification should not instantiate"); 069 } 070 } 071 072 public static class B { 073 public B(A a) { 074 fail("verification should not instantiate"); 075 } 076 } 077 078 protected ComponentAdapter prepDEF_verifyDoesNotInstantiate(MutablePicoContainer picoContainer) { 079 picoContainer.addComponent(A.class); 080 return new ConstructorInjector(B.class, B.class, null, new NullComponentMonitor(), false); 081 } 082 083 protected ComponentAdapter prepDEF_visitable() { 084 return new ConstructorInjector("bar", B.class, new Parameter[] {ComponentParameter.DEFAULT} , new NullComponentMonitor(), false); 085 } 086 087 protected ComponentAdapter prepDEF_isAbleToTakeParameters(MutablePicoContainer picoContainer) { 088 picoContainer.addComponent(SimpleTouchable.class); 089 return new ConstructorInjector( 090 NamedDependsOnTouchable.class, NamedDependsOnTouchable.class, 091 new Parameter[] {ComponentParameter.DEFAULT, new ConstantParameter("Name")} , new NullComponentMonitor(), false); 092 } 093 094 protected ComponentAdapter prepSER_isSerializable(MutablePicoContainer picoContainer) { 095 return new ConstructorInjector(SimpleTouchable.class, SimpleTouchable.class, null, new NullComponentMonitor(), false); 096 } 097 098 protected ComponentAdapter prepSER_isXStreamSerializable(final MutablePicoContainer picoContainer) { 099 return prepSER_isSerializable(picoContainer); 100 } 101 102 public static class NamedDependsOnTouchable extends DependsOnTouchable { 103 public NamedDependsOnTouchable(Touchable t, String name) { 104 super(t); 105 } 106 } 107 108 protected ComponentAdapter prepVER_verificationFails(MutablePicoContainer picoContainer) { 109 return new ConstructorInjector(DependsOnTouchable.class, DependsOnTouchable.class, null, new NullComponentMonitor(), false); 110 } 111 112 protected ComponentAdapter prepINS_createsNewInstances(MutablePicoContainer picoContainer) { 113 return new ConstructorInjector(SimpleTouchable.class, SimpleTouchable.class, null, new NullComponentMonitor(), false); 114 } 115 116 public static class Erroneous { 117 public Erroneous() { 118 throw new VerifyError("test"); 119 } 120 } 121 122 protected ComponentAdapter prepINS_errorIsRethrown(MutablePicoContainer picoContainer) { 123 return new ConstructorInjector(Erroneous.class, Erroneous.class, null, new NullComponentMonitor(), false); 124 } 125 126 public static class RuntimeThrowing { 127 public RuntimeThrowing() { 128 throw new RuntimeException("test"); 129 } 130 } 131 132 protected ComponentAdapter prepINS_runtimeExceptionIsRethrown(MutablePicoContainer picoContainer) { 133 return new ConstructorInjector(RuntimeThrowing.class, RuntimeThrowing.class, null, new NullComponentMonitor(), false); 134 } 135 136 public static class NormalExceptionThrowing { 137 public NormalExceptionThrowing() throws Exception { 138 throw new Exception("test"); 139 } 140 } 141 142 protected ComponentAdapter prepINS_normalExceptionIsRethrownInsidePicoInitializationException( 143 MutablePicoContainer picoContainer) { 144 return new ConstructorInjector(NormalExceptionThrowing.class, NormalExceptionThrowing.class, null, new NullComponentMonitor(), false); 145 } 146 147 protected ComponentAdapter prepRES_dependenciesAreResolved(MutablePicoContainer picoContainer) { 148 picoContainer.addComponent(SimpleTouchable.class); 149 return new ConstructorInjector(DependsOnTouchable.class, DependsOnTouchable.class, null, new NullComponentMonitor(), false); 150 } 151 152 public static class C1 { 153 public C1(C2 c2) { 154 fail("verification should not instantiate"); 155 } 156 } 157 158 public static class C2 { 159 public C2(C1 c1) { 160 fail("verification should not instantiate"); 161 } 162 } 163 164 protected ComponentAdapter prepRES_failingVerificationWithCyclicDependencyException(MutablePicoContainer picoContainer) { 165 final ComponentAdapter componentAdapter = new ConstructorInjector(C1.class, C1.class, null, new NullComponentMonitor(), false); 166 picoContainer.addAdapter(componentAdapter); 167 picoContainer.addComponent(C2.class, C2.class); 168 return componentAdapter; 169 } 170 171 protected ComponentAdapter prepRES_failingInstantiationWithCyclicDependencyException(MutablePicoContainer picoContainer) { 172 final ComponentAdapter componentAdapter = new ConstructorInjector(C1.class, C1.class, null, new NullComponentMonitor(), false); 173 picoContainer.addAdapter(componentAdapter); 174 picoContainer.addComponent(C2.class, C2.class); 175 return componentAdapter; 176 } 177 178 @Test public void testNormalExceptionThrownInCtorIsRethrownInsideInvocationTargetExeption() { 179 DefaultPicoContainer picoContainer = new DefaultPicoContainer(); 180 picoContainer.addComponent(NormalExceptionThrowing.class); 181 try { 182 picoContainer.getComponent(NormalExceptionThrowing.class); 183 fail(); 184 } catch (PicoCompositionException e) { 185 assertEquals("test", e.getCause().getMessage()); 186 } 187 } 188 189 public static class InstantiationExceptionThrowing { 190 public InstantiationExceptionThrowing() { 191 throw new RuntimeException("Barf"); 192 } 193 } 194 195 @Test public void testInstantiationExceptionThrownInCtorIsRethrownInsideInvocationTargetExeption() { 196 DefaultPicoContainer picoContainer = new DefaultPicoContainer(); 197 try { 198 picoContainer.addComponent(InstantiationExceptionThrowing.class); 199 picoContainer.getComponent(InstantiationExceptionThrowing.class); 200 fail(); 201 } catch (RuntimeException e) { 202 assertEquals("Barf", e.getMessage()); 203 } 204 } 205 206 public static class AllConstructorsArePrivate { 207 private AllConstructorsArePrivate() { 208 } 209 } 210 211 @Test public void testPicoInitializationExceptionThrownBecauseOfFilteredConstructors() { 212 DefaultPicoContainer picoContainer = new DefaultPicoContainer(); 213 try { 214 picoContainer.addComponent(AllConstructorsArePrivate.class); 215 picoContainer.getComponent(AllConstructorsArePrivate.class); 216 fail(); 217 } catch (PicoCompositionException e) { 218 String s = e.getMessage(); 219 assertTrue(s.indexOf("constructors were not accessible") > 0); 220 assertTrue(s.indexOf(AllConstructorsArePrivate.class.getName()) > 0); 221 } 222 } 223 224 @Test public void testRegisterInterfaceShouldFail() throws PicoCompositionException { 225 MutablePicoContainer pico = new DefaultPicoContainer(); 226 227 try { 228 pico.addComponent(Runnable.class); 229 fail("Shouldn't be allowed to register abstract classes or interfaces."); 230 } catch (AbstractInjector.NotConcreteRegistrationException e) { 231 assertEquals(Runnable.class, e.getComponentImplementation()); 232 assertTrue(e.getMessage().indexOf(Runnable.class.getName()) > 0); 233 } 234 } 235 236 @Test public void testRegisterAbstractShouldFail() throws PicoCompositionException { 237 MutablePicoContainer pico = new DefaultPicoContainer(); 238 239 try { 240 pico.addComponent(AbstractButton.class); 241 fail("Shouldn't be allowed to register abstract classes or interfaces."); 242 } catch (AbstractInjector.NotConcreteRegistrationException e) { 243 assertEquals(AbstractButton.class, e.getComponentImplementation()); 244 assertTrue(e.getMessage().indexOf(AbstractButton.class.getName()) > 0); 245 } 246 } 247 248 private static class Private { 249 private Private() { 250 } 251 } 252 253 private static class NotYourBusiness { 254 private NotYourBusiness(Private aPrivate) { 255 assertNotNull(aPrivate); 256 } 257 } 258 259 static public class Component201 { 260 public Component201(final String s) { 261 } 262 263 protected Component201(final Integer i, final Boolean b) { 264 fail("Wrong constructor taken."); 265 } 266 } 267 268 // http://jira.codehaus.org/browse/PICO-201 269 @Test public void testShouldNotConsiderNonPublicConstructors() { 270 DefaultPicoContainer pico = new DefaultPicoContainer(); 271 pico.addComponent(Component201.class); 272 pico.addComponent(new Integer(2)); 273 pico.addComponent(Boolean.TRUE); 274 pico.addComponent("Hello"); 275 assertNotNull(pico.getComponent(Component201.class)); 276 } 277 278 @Test public void testMonitoringHappensBeforeAndAfterInstantiation() throws NoSuchMethodException { 279 final ComponentMonitor monitor = mockery.mock(ComponentMonitor.class); 280 final Constructor emptyHashMapCtor = HashMap.class.getConstructor(); 281 final Matcher<Long> durationIsGreaterThanOrEqualToZero = new BaseMatcher<Long>() { 282 public boolean matches(Object item) { 283 Long duration = (Long)item; 284 return 0 <= duration; 285 } 286 287 public void describeTo(Description description) { 288 description.appendText("The endTime wasn't after the startTime"); 289 } 290 }; 291 292 final Matcher<Object> isAHashMapThatWozCreated = new BaseMatcher<Object>() { 293 public boolean matches(Object item) { 294 return item instanceof HashMap; 295 } 296 297 public void describeTo(Description description) { 298 description.appendText("Should have been a hashmap"); 299 } 300 }; 301 302 final Matcher<Object[]> injectedIsEmptyArray = new BaseMatcher<Object[]>() { 303 public boolean matches(Object item) { 304 Object[] injected = (Object[])item; 305 return 0 == injected.length; 306 } 307 public void describeTo(Description description) { 308 description.appendText("Should have had nothing injected into it"); 309 } 310 }; 311 312 mockery.checking(new Expectations(){{ 313 one(monitor).instantiating(with(any(PicoContainer.class)), (ComponentAdapter)with(a(ConstructorInjector.class)), with(equal(emptyHashMapCtor))); 314 will(returnValue(emptyHashMapCtor)); 315 one(monitor).instantiated(with(any(PicoContainer.class)), (ComponentAdapter)with(a(ConstructorInjector.class)), with(equal(emptyHashMapCtor)), 316 with(isAHashMapThatWozCreated), with(injectedIsEmptyArray), 317 with(durationIsGreaterThanOrEqualToZero)); 318 }}); 319 320 ConstructorInjector cica = new ConstructorInjector( 321 Map.class, HashMap.class, new Parameter[0], monitor, false); 322 cica.getComponentInstance(null, ComponentAdapter.NOTHING.class); 323 } 324 325 @Test public void testMonitoringHappensBeforeAndOnFailOfImpossibleComponentsInstantiation() throws NoSuchMethodException { 326 final ComponentMonitor monitor = mockery.mock(ComponentMonitor.class); 327 final Constructor barfingActionListenerCtor = BarfingActionListener.class.getConstructor(); 328 329 final Matcher<Exception> isITE = new BaseMatcher<Exception>() { 330 public boolean matches(Object item) { 331 Exception ex = (Exception)item; 332 return ex instanceof InvocationTargetException; 333 } 334 335 public void describeTo(Description description) { 336 description.appendText("Should have been unable to instantiate"); 337 } 338 }; 339 340 mockery.checking(new Expectations(){{ 341 one(monitor).instantiating(with(any(PicoContainer.class)), (ComponentAdapter)with(a(ConstructorInjector.class)), with(equal(barfingActionListenerCtor))); 342 will(returnValue(barfingActionListenerCtor)); 343 one(monitor).instantiationFailed(with(any(PicoContainer.class)), (ComponentAdapter)with(a(ConstructorInjector.class)), with(equal(barfingActionListenerCtor)), 344 with(isITE)); 345 }}); 346 347 348 ConstructorInjector cica = new ConstructorInjector( 349 ActionListener.class, BarfingActionListener.class, new Parameter[0], monitor, false); 350 try { 351 cica.getComponentInstance(null, ComponentAdapter.NOTHING.class); 352 fail("Should barf"); 353 } catch (RuntimeException e) { 354 assertEquals("Barf!", e.getMessage()); 355 } 356 } 357 358 private static class BarfingActionListener implements ActionListener { 359 public BarfingActionListener() { 360 throw new RuntimeException("Barf!"); 361 } 362 363 public void actionPerformed(ActionEvent e) { 364 } 365 } 366 367 public static class One { 368 public One(Two two) { 369 two.inc(); 370 } 371 } 372 public static class Two { 373 private int inc; 374 public void inc() { 375 inc++; 376 } 377 378 public long howMany() { 379 return inc; 380 } 381 } 382 383 /* 384 * (TODO: On some machines, the number of iterations aren't enough.) 385 */ 386 @Test public void testSpeedOfRememberedConstructor() { 387 long with, without; 388 injectionFactory = new ForgetfulConstructorInjection(); 389 timeIt(); // discard 390 timeIt(); // discard 391 timeIt(); // discard 392 without = timeIt(); 393 injectionFactory = new ConstructorInjection(); 394 with = timeIt(); 395 assertTrue("'with' should be less than 'without' but they were in fact: " + with + ", and " + without, with < without); 396 } 397 398 InjectionFactory injectionFactory; 399 private long timeIt() { 400 int iterations = 20000; 401 DefaultPicoContainer dpc = new DefaultPicoContainer(injectionFactory); 402 Two two = new Two(); 403 dpc.addComponent(two); 404 dpc.addComponent(One.class); 405 long start = System.currentTimeMillis(); 406 for (int x = 0; x < iterations; x++) { 407 dpc.getComponent(One.class); 408 } 409 long end = System.currentTimeMillis(); 410 assertEquals(iterations, two.howMany()); 411 return end-start; 412 } 413 414 }