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