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