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 }