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 org.picocontainer.ComponentMonitor;
013 import org.picocontainer.DefaultPicoContainer;
014 import org.picocontainer.LifecycleStrategy;
015 import org.picocontainer.MutablePicoContainer;
016 import org.picocontainer.PicoCompositionException;
017 import org.picocontainer.annotations.Nullable;
018 import org.picocontainer.behaviors.Caching;
019 import org.picocontainer.behaviors.ThreadCaching;
020 import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
021 import org.picocontainer.monitors.LifecycleComponentMonitor;
022
023 import static org.junit.Assert.assertEquals;
024 import static org.junit.Assert.assertNotNull;
025 import static org.junit.Assert.assertTrue;
026 import static org.junit.Assert.fail;
027
028 public 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 }