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 *****************************************************************************/ 009 010 package org.picocontainer.injectors; 011 012 import static org.junit.Assert.assertNotNull; 013 import static org.junit.Assert.assertTrue;import static org.junit.Assert.assertSame; 014 import static org.junit.Assert.assertEquals; 015 import static org.junit.Assert.fail; 016 import org.junit.Test; 017 import org.junit.runner.RunWith; 018 import org.picocontainer.*; 019 import org.picocontainer.Injector; 020 import org.picocontainer.monitors.NullComponentMonitor; 021 import org.picocontainer.behaviors.Caching; 022 import org.picocontainer.containers.EmptyPicoContainer; 023 import org.picocontainer.containers.TransientPicoContainer; 024 import org.picocontainer.tck.AbstractComponentFactoryTest; 025 import static org.picocontainer.tck.MockFactory.mockeryWithCountingNamingScheme; 026 import org.jmock.integration.junit4.JMock; 027 import org.jmock.Mockery; 028 import org.jmock.Expectations; 029 import org.jmock.api.Action; 030 import org.jmock.api.Invocation; 031 import org.hamcrest.Description; 032 033 import java.lang.reflect.Method; 034 import java.lang.reflect.Constructor; 035 import java.lang.reflect.Member; 036 import java.lang.annotation.Retention; 037 import java.lang.annotation.RetentionPolicy; 038 import java.lang.annotation.Target; 039 import java.lang.annotation.ElementType; 040 041 @RunWith(JMock.class) 042 public class ReinjectionTestCase extends AbstractComponentFactoryTest { 043 044 private Mockery mockery = mockeryWithCountingNamingScheme(); 045 046 @Retention(RetentionPolicy.RUNTIME) 047 @Target(value={ElementType.METHOD, ElementType.FIELD}) 048 public @interface Hurrah { 049 } 050 051 public static interface INeedsShoe { 052 int doIt(String s); 053 054 Object getBar(); 055 056 Object getString(); 057 } 058 059 public static class NeedsShoe implements INeedsShoe { 060 private Shoe bar; 061 private String string; 062 063 public NeedsShoe(Shoe bar) { 064 this.bar = bar; 065 } 066 067 @Hurrah 068 public int doIt(String s) { 069 this.string = s; 070 return Integer.parseInt(s) / 2; 071 } 072 073 public int doInt(int s) { 074 this.string = "i="+ s; 075 return s/2; 076 } 077 078 public Object getBar() { 079 return bar; 080 } 081 082 public Object getString() { 083 return string; 084 } 085 public static enum M { 086 doIt("doIt", String.class); 087 private Method method; 088 089 M(String s, Class... paramTypes) { 090 try { 091 method = NeedsShoe.class.getMethod(s, paramTypes); 092 } catch (NoSuchMethodException e) { 093 throw new UnsupportedOperationException(e); 094 } 095 } 096 public Method toMethod() { 097 return method; 098 } 099 } 100 } 101 102 public static class Shoe { 103 } 104 105 private static Method DOIT_METHOD; 106 107 static { 108 try { 109 DOIT_METHOD = NeedsShoe.class.getMethod("doIt", String.class); 110 } catch (NoSuchMethodException e) { 111 e.printStackTrace(); 112 } 113 } 114 115 @Test public void testCachedComponentCanBeReflectionMethodReinjectedByATransientChildContainer() { 116 cachedComponentCanBeReinjectedByATransientChildContainer(new MethodInjection(DOIT_METHOD)); 117 } 118 119 @Test public void testCachedComponentCanBeMethodNameReinjectedByATransientChildContainer() { 120 cachedComponentCanBeReinjectedByATransientChildContainer(new MethodInjection("doIt")); 121 } 122 123 @Test public void testCachedComponentCanBeAnnotatedMethodReinjectedByATransientChildContainer() { 124 cachedComponentCanBeReinjectedByATransientChildContainer(new AnnotatedMethodInjection(Hurrah.class, false)); 125 } 126 127 private void cachedComponentCanBeReinjectedByATransientChildContainer(AbstractInjectionFactory methodInjection) { 128 DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection())); 129 parent.addComponent(INeedsShoe.class, NeedsShoe.class); 130 parent.addComponent(Shoe.class); 131 parent.addComponent("12"); 132 133 INeedsShoe needsShoe = parent.getComponent(INeedsShoe.class); 134 assertNotNull(needsShoe.getBar()); 135 assertTrue(needsShoe.getString() == null); 136 137 TransientPicoContainer tpc = new TransientPicoContainer(new Reinjection(methodInjection, parent), parent); 138 tpc.addComponent(INeedsShoe.class, NeedsShoe.class); 139 140 INeedsShoe needsShoe2 = tpc.getComponent(INeedsShoe.class); 141 assertSame(needsShoe, needsShoe2); 142 assertNotNull(needsShoe2.getBar()); 143 assertNotNull(needsShoe2.getString()); 144 145 INeedsShoe needsShoe3 = parent.getComponent(INeedsShoe.class); 146 assertSame(needsShoe, needsShoe3); 147 assertNotNull(needsShoe3.getBar()); 148 assertNotNull(needsShoe3.getString()); 149 } 150 151 @Test 152 public void confirmThatReinjectionCanLeverageParameterNamesForDisambiguation() { 153 MethodInjection methodInjection = new MethodInjection(DOIT_METHOD); 154 DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection())); 155 156 // parameter name leverage can't work on interfaces if using bytecode retrieval technique 157 158 parent.addComponent(NeedsShoe.class); 159 parent.addComponent(Shoe.class); 160 parent.addComponent("a", "1333"); 161 parent.addComponent("s", "12"); 162 parent.addComponent("tjklhjkjhkjh", "44"); 163 164 NeedsShoe needsShoe = parent.getComponent(NeedsShoe.class); 165 assertNotNull(needsShoe.bar); 166 assertTrue(needsShoe.string == null); 167 168 Reinjection reinjection = new Reinjection(methodInjection, parent); 169 TransientPicoContainer tpc = new TransientPicoContainer(reinjection, parent); 170 tpc.as(Characteristics.USE_NAMES).addComponent(NeedsShoe.class); 171 172 NeedsShoe needsShoe2 = tpc.getComponent(NeedsShoe.class); 173 assertSame(needsShoe, needsShoe2); 174 assertNotNull(needsShoe2.bar); 175 assertNotNull(needsShoe2.string); 176 assertEquals("12", needsShoe2.string); 177 178 } 179 180 @Test 181 public void confirmThatReinjectionCanLeverageParameterNamesForDisambiguationWithTypeConversion() throws NoSuchMethodException { 182 MethodInjection methodInjection = new MethodInjection(NeedsShoe.class.getMethod("doInt", int.class)); 183 DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection())); 184 185 // parameter name leverage can't work on interfaces if using bytecode retrieval technique 186 187 parent.addComponent(NeedsShoe.class); 188 parent.addComponent(Shoe.class); 189 parent.addComponent("a", "1333"); 190 parent.addComponent("s", "12"); 191 parent.addComponent("tjklhjkjhkjh", "44"); 192 193 NeedsShoe needsShoe = parent.getComponent(NeedsShoe.class); 194 assertNotNull(needsShoe.bar); 195 assertTrue(needsShoe.string == null); 196 197 Reinjection reinjection = new Reinjection(methodInjection, parent); 198 TransientPicoContainer tpc = new TransientPicoContainer(reinjection, parent); 199 tpc.as(Characteristics.USE_NAMES).addComponent(NeedsShoe.class); 200 201 NeedsShoe needsShoe2 = tpc.getComponent(NeedsShoe.class); 202 assertSame(needsShoe, needsShoe2); 203 assertNotNull(needsShoe2.bar); 204 assertNotNull(needsShoe2.string); 205 assertEquals("i=12", needsShoe2.string); 206 207 } 208 209 @Test public void testCachedComponentCanBeReinjectedByATransientReflectionMethodReinjector() { 210 cachedComponentCanBeReinjectedByATransientReinjector(new MethodInjection(DOIT_METHOD)); 211 } 212 213 @Test public void testCachedComponentCanBeReinjectedByATransientMethodNameReinjector() { 214 cachedComponentCanBeReinjectedByATransientReinjector(new MethodInjection("doIt")); 215 } 216 217 @Test public void testCachedComponentCanBeReinjectedByATransientAnnotatedMethodReinjector() { 218 cachedComponentCanBeReinjectedByATransientReinjector(new AnnotatedMethodInjection(Hurrah.class, false)); 219 } 220 221 public static class ReturnParameterAction implements Action { 222 private final int parameter; 223 224 public ReturnParameterAction(int parameter) { 225 this.parameter = parameter; 226 } 227 228 public void describeTo(Description description) { 229 // describe it 230 } 231 232 public Object invoke(Invocation invocation) { 233 return invocation.getParameter(parameter); 234 } 235 } 236 237 private void cachedComponentCanBeReinjectedByATransientReinjector(AbstractInjectionFactory methodInjection) { 238 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection())); 239 parent.setName("parent"); 240 parent.addComponent(INeedsShoe.class, NeedsShoe.class); 241 parent.addComponent(Shoe.class); 242 parent.addComponent("12"); 243 244 final INeedsShoe foo = parent.getComponent(INeedsShoe.class); 245 assertNotNull(foo.getBar()); 246 assertTrue(foo.getString() == null); 247 248 final ComponentMonitor cm = mockery.mock(ComponentMonitor.class); 249 Reinjector reinjector = new Reinjector(parent, cm); 250 mockery.checking(new Expectations() {{ 251 atLeast(1).of(cm).newInjector(with(any(org.picocontainer.Injector.class))); 252 will(new ReturnParameterAction(0)); 253 one(cm).invoking(with(any(PicoContainer.class)), with(any(ComponentAdapter.class)), 254 with(any(Method.class)), with(any(Object.class)), with(any(Object[].class))); 255 will(returnValue(ComponentMonitor.KEEP)); 256 one(cm).invoked(with(any(PicoContainer.class)), with(any(ComponentAdapter.class)), 257 with(any(Method.class)), with(any(Object.class)), with(any(Long.class)), with(any(Object[].class)), with(any(Integer.class))); 258 }}); 259 260 Object o = reinjector.reinject(NeedsShoe.class, methodInjection); 261 int result = (Integer) o; 262 assertEquals(6, result); 263 264 INeedsShoe foo3 = parent.getComponent(INeedsShoe.class); 265 assertSame(foo, foo3); 266 assertNotNull(foo3.getBar()); 267 assertNotNull(foo3.getString()); 268 assertEquals("12", foo3.getString()); 269 } 270 271 @Test public void testOverloadedReinjectMethodsAreIdentical() { 272 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection())); 273 parent.addComponent(INeedsShoe.class, NeedsShoe.class); 274 parent.addComponent(Shoe.class); 275 parent.addComponent("12"); 276 277 final ComponentMonitor cm = new NullComponentMonitor(); 278 Reinjector reinjector = new Reinjector(parent, cm); 279 280 //int result = (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD); 281 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD)); 282 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, new MethodInjection(DOIT_METHOD))); 283 284 } 285 286 @Test public void testOverloadedReinjectMethodsAreIdentical2() { 287 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection())); 288 parent.addComponent(INeedsShoe.class, NeedsShoe.class); 289 parent.addComponent(Shoe.class); 290 parent.addComponent("12"); 291 292 final ComponentMonitor cm = new NullComponentMonitor(); 293 Reinjector reinjector = new Reinjector(parent, cm); 294 295 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, NeedsShoe.M.doIt)); 296 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, new MethodInjection(DOIT_METHOD))); 297 298 } 299 300 @Test public void testReinjectorCanBeOverridenByComponentMonitor() { 301 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection())); 302 parent.addComponent(INeedsShoe.class, NeedsShoe.class); 303 parent.addComponent(Shoe.class); 304 parent.addComponent("12"); 305 306 final ComponentMonitor cm = new NullComponentMonitor() { 307 public Object invoking(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member, Object instance, Object[] args) { 308 return 4444; 309 } 310 }; 311 Reinjector reinjector = new Reinjector(parent, cm); 312 313 assertEquals(4444, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD)); 314 315 } 316 317 @Test public void testReinjectorCanBeHonoredByComponentMonitor() { 318 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection())); 319 parent.addComponent(INeedsShoe.class, NeedsShoe.class); 320 parent.addComponent(Shoe.class); 321 parent.addComponent("12"); 322 323 final ComponentMonitor cm = new NullComponentMonitor() { 324 public Object invoking(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member, Object instance, Object[] args) { 325 return ComponentMonitor.KEEP; 326 } 327 }; 328 Reinjector reinjector = new Reinjector(parent, cm); 329 330 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD)); 331 332 } 333 334 @Test public void testReinjectorCanBeNullifiedByComponentMonitor() { 335 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection())); 336 parent.addComponent(INeedsShoe.class, NeedsShoe.class); 337 parent.addComponent(Shoe.class); 338 parent.addComponent("12"); 339 340 final ComponentMonitor cm = new NullComponentMonitor() { 341 public Object invoking(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member, Object instance, Object[] args) { 342 return null; 343 } 344 }; 345 Reinjector reinjector = new Reinjector(parent, cm); 346 347 Object retval = reinjector.reinject(NeedsShoe.class, DOIT_METHOD); 348 assertTrue(retval == null); 349 350 } 351 352 protected ComponentFactory createComponentFactory() { 353 return new Reinjection(new MethodInjection(DOIT_METHOD), new EmptyPicoContainer()); 354 } 355 356 @Test 357 public void testRegisterComponent() throws PicoCompositionException { 358 try { 359 super.testRegisterComponent(); 360 fail(); 361 } catch (PicoCompositionException e) { 362 assertTrue(e.getMessage().contains("] not on impl ")); 363 } 364 } 365 366 @Test 367 public void testUnregisterComponent() throws PicoCompositionException { 368 try { 369 super.testUnregisterComponent(); 370 fail(); 371 } catch (PicoCompositionException e) { 372 assertTrue(e.getMessage().contains("] not on impl ")); 373 } 374 } 375 376 @Test 377 public void testEquals() throws PicoCompositionException { 378 try { 379 super.testEquals(); 380 fail(); 381 } catch (PicoCompositionException e) { 382 assertTrue(e.getMessage().contains("] not on impl ")); 383 } 384 } 385 }