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 import org.picocontainer.DefaultPicoContainer;
017 import org.picocontainer.PicoCompositionException;
018 import org.picocontainer.annotations.Nullable;
019
020 public class ProviderTestCase {
021
022 @Test
023 public void provideMethodCanParticipateInInjection() {
024 DefaultPicoContainer dpc = new DefaultPicoContainer();
025 dpc.addAdapter(new Chocolatier(true));
026 dpc.addComponent(NeedsChocolate.class);
027 dpc.addComponent(CocaoBeans.class);
028 dpc.addComponent(String.class, "Cadbury's"); // the only string in the set of components
029 NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
030 assertNotNull(needsChocolate);
031 assertNotNull(needsChocolate.choc);
032 assertEquals(true, needsChocolate.choc.milky);
033 assertNotNull(needsChocolate.choc.cocaoBeans);
034 assertEquals("Cadbury's", needsChocolate.choc.name);
035 }
036
037 @Test
038 public void provideMethodCanDisambiguateUsingParameterNames() {
039 DefaultPicoContainer dpc = new DefaultPicoContainer();
040 dpc.addAdapter(new Chocolatier(true));
041 dpc.addComponent(NeedsChocolate.class);
042 dpc.addComponent(CocaoBeans.class);
043 dpc.addComponent("color", "Red"); // not used by virtue of key
044 dpc.addComponent("name", "Cadbury's");
045 dpc.addComponent("band", "Abba"); // not used by virtue of key
046 NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
047 assertNotNull(needsChocolate);
048 assertNotNull(needsChocolate.choc);
049 assertEquals(true, needsChocolate.choc.milky);
050 assertNotNull(needsChocolate.choc.cocaoBeans);
051 assertEquals("Cadbury's", needsChocolate.choc.name);
052 }
053
054 @Test
055 public void providerBarfsIfProvideMethodsParamsCanNotBeSatisfied() {
056 DefaultPicoContainer dpc = new DefaultPicoContainer();
057 dpc.addAdapter(new Chocolatier(true));
058 dpc.addComponent(NeedsChocolate.class);
059 try {
060 dpc.getComponent(NeedsChocolate.class);
061 } catch (PicoCompositionException e) {
062 assertTrue(e.getMessage().contains("Parameter 0 "));
063 assertTrue(e.getMessage().contains("cannot be null"));
064 }
065 }
066
067 @Test
068 public void providerDoesNotBarfIfProvideMethodsParamsCanNotBeSatisfiedButNullbleAnnotationUsed() {
069 DefaultPicoContainer dpc = new DefaultPicoContainer();
070 dpc.addAdapter(new NullableChocolatier());
071 dpc.addComponent(NeedsChocolate.class);
072 NeedsChocolate nc = dpc.getComponent(NeedsChocolate.class);
073 assertNotNull(nc);
074 assertNotNull(nc.choc);
075 assertTrue(nc.choc.cocaoBeans == null);
076 }
077
078 public static class CocaoBeans {
079 }
080
081 public static class Chocolate {
082 private boolean milky;
083 private final CocaoBeans cocaoBeans;
084 private final String name;
085
086 public Chocolate(String name) {
087 this(true, new CocaoBeans(), name);
088 }
089
090 public Chocolate(boolean milky, CocaoBeans cocaoBeans, String name) {
091 this.milky = milky;
092 this.cocaoBeans = cocaoBeans;
093 this.name = name;
094 }
095 }
096
097 public static class Chocolatier extends ProviderAdapter {
098 private final boolean milky;
099 public Chocolatier(boolean milky) {
100 this.milky = milky;
101 }
102 public Chocolate provide(CocaoBeans cocaoBeans, String name) {
103 return new Chocolate(milky, cocaoBeans, name);
104 }
105 @Override
106 protected boolean useNames() {
107 return true;
108 }
109 }
110
111 public static class NullableChocolatier extends Chocolatier {
112 public NullableChocolatier() {
113 super(true);
114 }
115
116 public Chocolate provide(@Nullable CocaoBeans cocaoBeans, @Nullable String name) {
117 return super.provide(cocaoBeans, name);
118 }
119 }
120
121 public static class NeedsChocolate {
122 private Chocolate choc;
123 public NeedsChocolate(Chocolate choc) {
124 this.choc = choc;
125 }
126 }
127
128 @Test
129 public void providerBarfsIfNoProvideMethod() {
130 DefaultPicoContainer dpc = new DefaultPicoContainer();
131 try {
132 dpc.addAdapter(new ProviderWithoutProvideMethod());
133 fail("should have barfed");
134 } catch (PicoCompositionException e) {
135 assertEquals("There must be a method named 'provide' in the AbstractProvider implementation", e.getMessage());
136 }
137 }
138
139 @Test
140 public void providerBarfsIfBadProvideMethod() {
141 DefaultPicoContainer dpc = new DefaultPicoContainer();
142 try {
143 dpc.addAdapter(new ProviderWithBadProvideMethod());
144 fail("should have barfed");
145 } catch (PicoCompositionException e) {
146 assertEquals("There must be a non void returning method named 'provide' in the AbstractProvider implementation", e.getMessage());
147 }
148 }
149
150 @Test
151 public void providerBarfsIfTooManyProvideMethod() {
152 DefaultPicoContainer dpc = new DefaultPicoContainer();
153 try {
154 dpc.addAdapter(new ProviderWithTooManyProvideMethods());
155 fail("should have barfed");
156 } catch (PicoCompositionException e) {
157 assertEquals("There must be only one method named 'provide' in the AbstractProvider implementation", e.getMessage());
158 }
159 }
160
161 public static class ProviderWithoutProvideMethod extends ProviderAdapter {
162 }
163 public static class ProviderWithBadProvideMethod extends ProviderAdapter {
164 public void provide() {
165
166 }
167 }
168 public static class ProviderWithTooManyProvideMethods extends ProviderAdapter {
169 public String provide(String str) {
170 return null;
171 }
172 public Integer provide() {
173 return null;
174 }
175 }
176
177 @Test
178 public void provideMethodCanParticipateInInjectionWhenNotExtendingProviderAdapter() {
179 DefaultPicoContainer dpc = new DefaultPicoContainer();
180 dpc.addAdapter(new ProviderAdapter(new Chocolatier2(true)));
181 dpc.addComponent(NeedsChocolate.class);
182 dpc.addComponent(CocaoBeans.class);
183 dpc.addComponent(String.class, "Cadbury's"); // the only string in the set of components
184 NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
185 assertNotNull(needsChocolate);
186 assertNotNull(needsChocolate.choc);
187 assertEquals(true, needsChocolate.choc.milky);
188 assertNotNull(needsChocolate.choc.cocaoBeans);
189 assertEquals("Cadbury's", needsChocolate.choc.name);
190 }
191
192 public static class Chocolatier2 implements Provider {
193 private final boolean milky;
194 public Chocolatier2(boolean milky) {
195 this.milky = milky;
196 }
197 public Chocolate provide(CocaoBeans cocaoBeans, String name) {
198 return new Chocolate(milky, cocaoBeans, name);
199 }
200 }
201
202 @Test
203 public void providedTypeCanBeDyanamicallyDeterminedFromInstanceRatherThanType() {
204 DefaultPicoContainer dpc = new DefaultPicoContainer();
205
206 // a simlation of what a web framework would essentially do in a thread-local way
207 dpc.addComponent(new StubHttpRequest("chocolate", "Lindt"));
208
209 // this is the style being recomended for automatic request-params -> beans
210 dpc.addAdapter(new ExampleRequestReader(Chocolate.class, "chocolate"));
211
212 dpc.addComponent(NeedsChocolate.class);
213 NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
214 assertNotNull(needsChocolate);
215 assertNotNull(needsChocolate.choc);
216 assertEquals(true, needsChocolate.choc.milky);
217 assertNotNull(needsChocolate.choc.cocaoBeans);
218 assertEquals("Lindt", needsChocolate.choc.name);
219 }
220
221
222 public static class StubHttpRequest {
223 private final String key;
224 private final String value;
225
226 public StubHttpRequest(String key, String value) {
227 this.key = key;
228 this.value = value;
229 }
230
231 public String getParameter(String name) {
232 return name.equals(key) ? value : null;
233 }
234 }
235
236 public static class ExampleRequestReader extends ProviderAdapter {
237 private final Class clazz;
238 private final String paramName;
239
240 public ExampleRequestReader(Class clazz, String paramName) {
241 this.clazz = clazz;
242 this.paramName = paramName;
243 }
244
245 public Class getComponentImplementation() {
246 return clazz;
247 }
248
249 public Object provide(StubHttpRequest req) {
250 try {
251 return clazz.getConstructor(String.class).newInstance(req.getParameter(paramName));
252 } catch (Exception e) {
253 throw new RuntimeException(e);
254 }
255 }
256 }
257
258
259 }