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 *****************************************************************************/ 009package org.picocontainer.injectors; 010 011import org.junit.Test; 012import org.picocontainer.ComponentMonitor; 013import org.picocontainer.DefaultPicoContainer; 014import org.picocontainer.LifecycleStrategy; 015import org.picocontainer.MutablePicoContainer; 016import org.picocontainer.PicoCompositionException; 017import org.picocontainer.annotations.Nullable; 018import org.picocontainer.behaviors.Caching; 019import org.picocontainer.behaviors.ThreadCaching; 020import org.picocontainer.lifecycle.ReflectionLifecycleStrategy; 021import org.picocontainer.monitors.LifecycleComponentMonitor; 022 023import static org.junit.Assert.assertEquals; 024import static org.junit.Assert.assertNotNull; 025import static org.junit.Assert.assertTrue; 026import static org.junit.Assert.fail; 027 028public class ProviderTestCase { 029 030 @Test 031 public void provideMethodCanParticipateInInjection() { 032 DefaultPicoContainer dpc = new DefaultPicoContainer(); 033 dpc.addAdapter(new Chocolatier(true)); 034 dpc.addComponent(NeedsChocolate.class); 035 dpc.addComponent(CocaoBeans.class); 036 dpc.addComponent(String.class, "Cadbury's"); // the only string in the set of components 037 NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class); 038 assertNotNull(needsChocolate); 039 assertNotNull(needsChocolate.choc); 040 assertEquals(true, needsChocolate.choc.milky); 041 assertNotNull(needsChocolate.choc.cocaoBeans); 042 assertEquals("Cadbury's", needsChocolate.choc.name); 043 } 044 045 @Test 046 public void provideMethodCanDisambiguateUsingParameterNames() { 047 DefaultPicoContainer dpc = new DefaultPicoContainer(); 048 dpc.addAdapter(new Chocolatier(true)); 049 dpc.addComponent(NeedsChocolate.class); 050 dpc.addComponent(CocaoBeans.class); 051 dpc.addComponent("color", "Red"); // not used by virtue of key 052 dpc.addComponent("name", "Cadbury's"); 053 dpc.addComponent("band", "Abba"); // not used by virtue of key 054 NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class); 055 assertNotNull(needsChocolate); 056 assertNotNull(needsChocolate.choc); 057 assertEquals(true, needsChocolate.choc.milky); 058 assertNotNull(needsChocolate.choc.cocaoBeans); 059 assertEquals("Cadbury's", needsChocolate.choc.name); 060 } 061 062 @Test 063 public void providerBarfsIfProvideMethodsParamsCanNotBeSatisfied() { 064 DefaultPicoContainer dpc = new DefaultPicoContainer(); 065 dpc.addAdapter(new Chocolatier(true)); 066 dpc.addComponent(NeedsChocolate.class); 067 try { 068 dpc.getComponent(NeedsChocolate.class); 069 } catch (PicoCompositionException e) { 070 String message = e.getMessage(); 071 assertTrue(message.contains("Parameter 0 ")); 072 assertTrue(message.contains("cannot be null")); 073 } 074 } 075 076 @Test 077 public void providerDoesNotBarfIfProvideMethodsParamsCanNotBeSatisfiedButNullbleAnnotationUsed() { 078 DefaultPicoContainer dpc = new DefaultPicoContainer(); 079 dpc.addAdapter(new NullableChocolatier()); 080 dpc.addComponent(NeedsChocolate.class); 081 NeedsChocolate nc = dpc.getComponent(NeedsChocolate.class); 082 assertNotNull(nc); 083 assertNotNull(nc.choc); 084 assertTrue(nc.choc.cocaoBeans == null); 085 } 086 087 @Test 088 public void testHasLifecycle() { 089 DefaultPicoContainer dpc = new DefaultPicoContainer(new Caching()); 090 dpc.addAdapter(new NullableChocolatier()); 091 dpc.addComponent(NeedsChocolate.class); 092 NeedsChocolate nc = dpc.getComponent(NeedsChocolate.class); 093 dpc.start(); 094 dpc.stop(); 095 dpc.dispose(); 096 } 097 098 public static class CocaoBeans { 099 } 100 101 public static class Chocolate { 102 private boolean milky; 103 private final CocaoBeans cocaoBeans; 104 private final String name; 105 106 public Chocolate(String name) { 107 this(true, new CocaoBeans(), name); 108 } 109 110 public Chocolate(boolean milky, CocaoBeans cocaoBeans, String name) { 111 this.milky = milky; 112 this.cocaoBeans = cocaoBeans; 113 this.name = name; 114 } 115 } 116 117 public static class Chocolatier extends ProviderAdapter { 118 private final boolean milky; 119 public Chocolatier(boolean milky) { 120 this.milky = milky; 121 } 122 public Chocolate provide(CocaoBeans cocaoBeans, String name) { 123 return new Chocolate(milky, cocaoBeans, name); 124 } 125 @Override 126 protected boolean useNames() { 127 return true; 128 } 129 } 130 131 public static class NullableChocolatier extends Chocolatier { 132 public NullableChocolatier() { 133 super(true); 134 } 135 136 public Chocolate provide(@Nullable CocaoBeans cocaoBeans, @Nullable String name) { 137 return super.provide(cocaoBeans, name); 138 } 139 } 140 141 public static class NeedsChocolate { 142 private Chocolate choc; 143 public NeedsChocolate(Chocolate choc) { 144 this.choc = choc; 145 } 146 } 147 148 @Test 149 public void providerBarfsIfNoProvideMethod() { 150 DefaultPicoContainer dpc = new DefaultPicoContainer(); 151 try { 152 dpc.addAdapter(new ProviderWithoutProvideMethod()); 153 fail("should have barfed"); 154 } catch (PicoCompositionException e) { 155 assertEquals("There must be a method named 'provide' in the AbstractProvider implementation", e.getMessage()); 156 } 157 } 158 159 @Test 160 public void providerBarfsIfBadProvideMethod() { 161 DefaultPicoContainer dpc = new DefaultPicoContainer(); 162 try { 163 dpc.addAdapter(new ProviderWithBadProvideMethod()); 164 fail("should have barfed"); 165 } catch (PicoCompositionException e) { 166 assertEquals("There must be a non void returning method named 'provide' in the AbstractProvider implementation", e.getMessage()); 167 } 168 } 169 170 @Test 171 public void providerBarfsIfTooManyProvideMethod() { 172 DefaultPicoContainer dpc = new DefaultPicoContainer(); 173 try { 174 dpc.addAdapter(new ProviderWithTooManyProvideMethods()); 175 fail("should have barfed"); 176 } catch (PicoCompositionException e) { 177 assertEquals("There must be only one method named 'provide' in the AbstractProvider implementation", e.getMessage()); 178 } 179 } 180 181 public static class ProviderWithoutProvideMethod extends ProviderAdapter { 182 } 183 public static class ProviderWithBadProvideMethod extends ProviderAdapter { 184 public void provide() { 185 186 } 187 } 188 public static class ProviderWithTooManyProvideMethods extends ProviderAdapter { 189 public String provide(String str) { 190 return null; 191 } 192 public Integer provide() { 193 return null; 194 } 195 } 196 197 @Test 198 public void provideMethodCanParticipateInInjectionWhenNotExtendingProviderAdapter() { 199 DefaultPicoContainer dpc = new DefaultPicoContainer(); 200 dpc.addAdapter(new ProviderAdapter(new Chocolatier2(true))); 201 dpc.addComponent(NeedsChocolate.class); 202 dpc.addComponent(CocaoBeans.class); 203 dpc.addComponent(String.class, "Cadbury's"); // the only string in the set of components 204 NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class); 205 assertNotNull(needsChocolate); 206 assertNotNull(needsChocolate.choc); 207 assertEquals(true, needsChocolate.choc.milky); 208 assertNotNull(needsChocolate.choc.cocaoBeans); 209 assertEquals("Cadbury's", needsChocolate.choc.name); 210 } 211 212 public static class Chocolatier2 implements Provider { 213 private final boolean milky; 214 public Chocolatier2(boolean milky) { 215 this.milky = milky; 216 } 217 public Chocolate provide(CocaoBeans cocaoBeans, String name) { 218 return new Chocolate(milky, cocaoBeans, name); 219 } 220 } 221 222 @Test 223 public void providedTypeCanBeDyanamicallyDeterminedFromInstanceRatherThanType() { 224 DefaultPicoContainer dpc = new DefaultPicoContainer(); 225 226 // a simlation of what a web framework would essentially do in a thread-local way 227 dpc.addComponent(new StubHttpRequest("chocolate", "Lindt")); 228 229 // this is the style being recomended for automatic request-params -> beans 230 dpc.addAdapter(new ExampleRequestReader(Chocolate.class, "chocolate")); 231 232 dpc.addComponent(NeedsChocolate.class); 233 NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class); 234 assertNotNull(needsChocolate); 235 assertNotNull(needsChocolate.choc); 236 assertEquals(true, needsChocolate.choc.milky); 237 assertNotNull(needsChocolate.choc.cocaoBeans); 238 assertEquals("Lindt", needsChocolate.choc.name); 239 } 240 241 242 public static class StubHttpRequest { 243 private final String key; 244 private final String value; 245 246 public StubHttpRequest(String key, String value) { 247 this.key = key; 248 this.value = value; 249 } 250 251 public String getParameter(String name) { 252 return name.equals(key) ? value : null; 253 } 254 } 255 256 public static class ExampleRequestReader extends ProviderAdapter { 257 private final Class clazz; 258 private final String paramName; 259 260 public ExampleRequestReader(Class clazz, String paramName) { 261 this.clazz = clazz; 262 this.paramName = paramName; 263 } 264 265 public Class getComponentImplementation() { 266 return clazz; 267 } 268 269 public Object provide(StubHttpRequest req) { 270 try { 271 return clazz.getConstructor(String.class).newInstance(req.getParameter(paramName)); 272 } catch (Exception e) { 273 throw new RuntimeException(e); 274 } 275 } 276 } 277 278 @Test 279 public void providersCanHaveLifecyclesToo() { 280 ComponentMonitor componentMonitor = new LifecycleComponentMonitor(); 281 LifecycleStrategy lifecycleStrategy = new 282 ReflectionLifecycleStrategy(componentMonitor); 283 284 MutablePicoContainer pico = new DefaultPicoContainer(new 285 ThreadCaching(), lifecycleStrategy, null); 286 287 StringBuilder sb = new StringBuilder(); 288 pico.addComponent(Configuration.class); 289 pico.addAdapter(new ProviderAdapter(lifecycleStrategy, new ComponentProvider(sb))); 290 Object foo = pico.getComponent(Component.class); 291 pico.start(); 292 pico.stop(); 293 assertEquals("@<>", sb.toString()); 294 295 } 296 297 public class ComponentProvider implements Provider { 298 private StringBuilder sb; 299 300 public ComponentProvider(StringBuilder sb) { 301 this.sb = sb; 302 } 303 304 public Component provide(Configuration config) { 305 return new ComponentImpl(sb, config.getHost(), config.getPort()); 306 } 307 } 308 309 public static class Configuration { 310 311 public String getHost() { 312 return "hello"; 313 } 314 315 public int getPort() { 316 return 99; 317 } 318 319 public void start() { 320 } 321 322 public void stop() { 323 } 324 325 } 326 327 public static interface Component { 328 329 public void start(); 330 331 public void stop(); 332 333 } 334 335 public static class ComponentImpl implements Component { 336 337 private StringBuilder sb; 338 339 public ComponentImpl(StringBuilder sb, String host, int port) { 340 this.sb = sb.append("@"); 341 } 342 343 public void start() { 344 sb.append("<"); 345 } 346 public void stop() { 347 sb.append(">"); 348 } 349 350 } 351 352 353 /** 354 * Reference Johann Burkard's 355 * http://jira.codehaus.org/browse/PICO-375 356 */ 357 @Test 358 public void providerTest() { 359 DefaultPicoContainer container = new DefaultPicoContainer(); 360 ProviderAdapter adapter = new ProviderAdapter(new BlorbProvider()); 361 container.addAdapter(adapter); 362 assertNotNull(container.getComponent(Blorb.class)); 363 } 364 // Differs from Johann's by the "implements Provider" only. 365 public static class BlorbProvider implements Provider { 366 public Blorb provide() { 367 return new Blorb(); 368 } 369 } 370 public static class Blorb {} 371 372// @Test 373// public void providerAdapterMustBeHandedAnImplementationOfProvider() { 374// DefaultPicoContainer container = new DefaultPicoContainer(); 375// try { 376// ProviderAdapter adapter = new ProviderAdapter(new Blorb2Provider()); 377// } catch (Exception e) { 378// 379// } 380// } 381// public static class Blorb2Provider { 382// public Blorb2 provide() { 383// return new Blorb2(); 384// } 385// } 386// public static class Blorb2 {} 387 388 389 390}