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 * Original code by *
009 *****************************************************************************/
010 package org.picocontainer.injectors;
011
012 import static org.junit.Assert.assertEquals;
013 import static org.junit.Assert.assertNotNull;
014 import static org.junit.Assert.assertTrue;
015 import static org.junit.Assert.fail;
016 import static org.picocontainer.tck.MockFactory.mockeryWithCountingNamingScheme;
017
018 import java.awt.event.ActionEvent;
019 import java.awt.event.ActionListener;
020 import java.lang.reflect.Constructor;
021 import java.lang.reflect.InvocationTargetException;
022 import java.util.HashMap;
023 import java.util.Map;
024
025 import javax.swing.AbstractButton;
026
027 import org.hamcrest.BaseMatcher;
028 import org.hamcrest.Description;
029 import org.hamcrest.Matcher;
030 import org.jmock.Expectations;
031 import org.jmock.Mockery;
032 import org.junit.Test;
033 import org.picocontainer.ComponentAdapter;
034 import org.picocontainer.ComponentMonitor;
035 import org.picocontainer.DefaultPicoContainer;
036 import org.picocontainer.MutablePicoContainer;
037 import org.picocontainer.Parameter;
038 import org.picocontainer.PicoCompositionException;
039 import org.picocontainer.PicoContainer;
040 import org.picocontainer.lifecycle.NullLifecycleStrategy;
041 import org.picocontainer.monitors.AbstractComponentMonitor;
042 import org.picocontainer.monitors.NullComponentMonitor;
043 import org.picocontainer.parameters.ComponentParameter;
044 import org.picocontainer.parameters.ConstantParameter;
045 import org.picocontainer.tck.AbstractComponentAdapterTest;
046 import org.picocontainer.testmodel.DependsOnTouchable;
047 import org.picocontainer.testmodel.NullLifecycle;
048 import org.picocontainer.testmodel.SimpleTouchable;
049 import org.picocontainer.testmodel.Touchable;
050
051
052 public class ConstructorInjectorTestCase extends AbstractComponentAdapterTest {
053
054 private Mockery mockery = mockeryWithCountingNamingScheme();
055
056 protected Class getComponentAdapterType() {
057 return ConstructorInjector.class;
058 }
059
060 protected ComponentAdapter prepDEF_verifyWithoutDependencyWorks(MutablePicoContainer picoContainer) {
061 return new ConstructorInjector("foo", A.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
062 }
063
064 public static class A {
065 public A() {
066 fail("verification should not instantiate");
067 }
068 }
069
070 public static class B {
071 public B(A a) {
072 fail("verification should not instantiate");
073 }
074 }
075
076 protected ComponentAdapter prepDEF_verifyDoesNotInstantiate(MutablePicoContainer picoContainer) {
077 picoContainer.addComponent(A.class);
078 return new ConstructorInjector(B.class, B.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
079 }
080
081 protected ComponentAdapter prepDEF_visitable() {
082 return new ConstructorInjector("bar", B.class, new Parameter[] {ComponentParameter.DEFAULT} , new NullComponentMonitor(), new NullLifecycleStrategy(), false);
083 }
084
085 protected ComponentAdapter prepDEF_isAbleToTakeParameters(MutablePicoContainer picoContainer) {
086 picoContainer.addComponent(SimpleTouchable.class);
087 return new ConstructorInjector(
088 NamedDependsOnTouchable.class, NamedDependsOnTouchable.class,
089 new Parameter[] {ComponentParameter.DEFAULT, new ConstantParameter("Name")} , new NullComponentMonitor(), new NullLifecycleStrategy(), false);
090 }
091
092 protected ComponentAdapter prepSER_isSerializable(MutablePicoContainer picoContainer) {
093 return new ConstructorInjector(SimpleTouchable.class, SimpleTouchable.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
094 }
095
096 protected ComponentAdapter prepSER_isXStreamSerializable(final MutablePicoContainer picoContainer) {
097 return prepSER_isSerializable(picoContainer);
098 }
099
100 public static class NamedDependsOnTouchable extends DependsOnTouchable {
101 public NamedDependsOnTouchable(Touchable t, String name) {
102 super(t);
103 }
104 }
105
106 protected ComponentAdapter prepVER_verificationFails(MutablePicoContainer picoContainer) {
107 return new ConstructorInjector(DependsOnTouchable.class, DependsOnTouchable.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
108 }
109
110 protected ComponentAdapter prepINS_createsNewInstances(MutablePicoContainer picoContainer) {
111 return new ConstructorInjector(SimpleTouchable.class, SimpleTouchable.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
112 }
113
114 public static class Erroneous {
115 public Erroneous() {
116 throw new VerifyError("test");
117 }
118 }
119
120 protected ComponentAdapter prepINS_errorIsRethrown(MutablePicoContainer picoContainer) {
121 return new ConstructorInjector(Erroneous.class, Erroneous.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
122 }
123
124 public static class RuntimeThrowing {
125 public RuntimeThrowing() {
126 throw new RuntimeException("test");
127 }
128 }
129
130 protected ComponentAdapter prepINS_runtimeExceptionIsRethrown(MutablePicoContainer picoContainer) {
131 return new ConstructorInjector(RuntimeThrowing.class, RuntimeThrowing.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
132 }
133
134 public static class NormalExceptionThrowing {
135 public NormalExceptionThrowing() throws Exception {
136 throw new Exception("test");
137 }
138 }
139
140 protected ComponentAdapter prepINS_normalExceptionIsRethrownInsidePicoInitializationException(
141 MutablePicoContainer picoContainer) {
142 return new ConstructorInjector(NormalExceptionThrowing.class, NormalExceptionThrowing.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
143 }
144
145 protected ComponentAdapter prepRES_dependenciesAreResolved(MutablePicoContainer picoContainer) {
146 picoContainer.addComponent(SimpleTouchable.class);
147 return new ConstructorInjector(DependsOnTouchable.class, DependsOnTouchable.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
148 }
149
150 public static class C1 {
151 public C1(C2 c2) {
152 fail("verification should not instantiate");
153 }
154 }
155
156 public static class C2 {
157 public C2(C1 c1) {
158 fail("verification should not instantiate");
159 }
160 }
161
162 protected ComponentAdapter prepRES_failingVerificationWithCyclicDependencyException(MutablePicoContainer picoContainer) {
163 final ComponentAdapter componentAdapter = new ConstructorInjector(C1.class, C1.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
164 picoContainer.addAdapter(componentAdapter);
165 picoContainer.addComponent(C2.class, C2.class);
166 return componentAdapter;
167 }
168
169 protected ComponentAdapter prepRES_failingInstantiationWithCyclicDependencyException(MutablePicoContainer picoContainer) {
170 final ComponentAdapter componentAdapter = new ConstructorInjector(C1.class, C1.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
171 picoContainer.addAdapter(componentAdapter);
172 picoContainer.addComponent(C2.class, C2.class);
173 return componentAdapter;
174 }
175
176 @Test public void testNormalExceptionThrownInCtorIsRethrownInsideInvocationTargetExeption() {
177 DefaultPicoContainer picoContainer = new DefaultPicoContainer();
178 picoContainer.addComponent(NormalExceptionThrowing.class);
179 try {
180 picoContainer.getComponent(NormalExceptionThrowing.class);
181 fail();
182 } catch (PicoCompositionException e) {
183 assertEquals("test", e.getCause().getMessage());
184 }
185 }
186
187 public static class InstantiationExceptionThrowing {
188 public InstantiationExceptionThrowing() {
189 throw new RuntimeException("Barf");
190 }
191 }
192
193 @Test public void testInstantiationExceptionThrownInCtorIsRethrownInsideInvocationTargetExeption() {
194 DefaultPicoContainer picoContainer = new DefaultPicoContainer();
195 try {
196 picoContainer.addComponent(InstantiationExceptionThrowing.class);
197 picoContainer.getComponent(InstantiationExceptionThrowing.class);
198 fail();
199 } catch (RuntimeException e) {
200 assertEquals("Barf", e.getMessage());
201 }
202 }
203
204 public static class AllConstructorsArePrivate {
205 private AllConstructorsArePrivate() {
206 }
207 }
208
209 @Test public void testPicoInitializationExceptionThrownBecauseOfFilteredConstructors() {
210 DefaultPicoContainer picoContainer = new DefaultPicoContainer();
211 try {
212 picoContainer.addComponent(AllConstructorsArePrivate.class);
213 picoContainer.getComponent(AllConstructorsArePrivate.class);
214 fail();
215 } catch (PicoCompositionException e) {
216 String s = e.getMessage();
217 assertTrue(s.indexOf("constructors were not accessible") > 0);
218 assertTrue(s.indexOf(AllConstructorsArePrivate.class.getName()) > 0);
219 }
220 }
221
222 @Test public void testRegisterInterfaceShouldFail() throws PicoCompositionException {
223 MutablePicoContainer pico = new DefaultPicoContainer();
224
225 try {
226 pico.addComponent(Runnable.class);
227 fail("Shouldn't be allowed to register abstract classes or interfaces.");
228 } catch (AbstractInjector.NotConcreteRegistrationException e) {
229 assertEquals(Runnable.class, e.getComponentImplementation());
230 assertTrue(e.getMessage().indexOf(Runnable.class.getName()) > 0);
231 }
232 }
233
234 @Test public void testRegisterAbstractShouldFail() throws PicoCompositionException {
235 MutablePicoContainer pico = new DefaultPicoContainer();
236
237 try {
238 pico.addComponent(AbstractButton.class);
239 fail("Shouldn't be allowed to register abstract classes or interfaces.");
240 } catch (AbstractInjector.NotConcreteRegistrationException e) {
241 assertEquals(AbstractButton.class, e.getComponentImplementation());
242 assertTrue(e.getMessage().indexOf(AbstractButton.class.getName()) > 0);
243 }
244 }
245
246 private static class Private {
247 private Private() {
248 }
249 }
250
251 private static class NotYourBusiness {
252 private NotYourBusiness(Private aPrivate) {
253 assertNotNull(aPrivate);
254 }
255 }
256
257 static public class Component201 {
258 public Component201(final String s) {
259 }
260
261 protected Component201(final Integer i, final Boolean b) {
262 fail("Wrong constructor taken.");
263 }
264 }
265
266 // http://jira.codehaus.org/browse/PICO-201
267 @Test public void testShouldNotConsiderNonPublicConstructors() {
268 DefaultPicoContainer pico = new DefaultPicoContainer();
269 pico.addComponent(Component201.class);
270 pico.addComponent(new Integer(2));
271 pico.addComponent(Boolean.TRUE);
272 pico.addComponent("Hello");
273 assertNotNull(pico.getComponent(Component201.class));
274 }
275
276 @Test public void testMonitoringHappensBeforeAndAfterInstantiation() throws NoSuchMethodException {
277 final ComponentMonitor monitor = mockery.mock(ComponentMonitor.class);
278 final Constructor emptyHashMapCtor = HashMap.class.getConstructor();
279 final Matcher<Long> durationIsGreaterThanOrEqualToZero = new BaseMatcher<Long>() {
280 public boolean matches(Object item) {
281 Long duration = (Long)item;
282 return 0 <= duration;
283 }
284
285 public void describeTo(Description description) {
286 description.appendText("The endTime wasn't after the startTime");
287 }
288 };
289
290 final Matcher<Object> isAHashMapThatWozCreated = new BaseMatcher<Object>() {
291 public boolean matches(Object item) {
292 return item instanceof HashMap;
293 }
294
295 public void describeTo(Description description) {
296 description.appendText("Should have been a hashmap");
297 }
298 };
299
300 final Matcher<Object[]> injectedIsEmptyArray = new BaseMatcher<Object[]>() {
301 public boolean matches(Object item) {
302 Object[] injected = (Object[])item;
303 return 0 == injected.length;
304 }
305 public void describeTo(Description description) {
306 description.appendText("Should have had nothing injected into it");
307 }
308 };
309
310 mockery.checking(new Expectations(){{
311 one(monitor).instantiating(with(any(PicoContainer.class)), (ComponentAdapter)with(a(ConstructorInjector.class)), with(equal(emptyHashMapCtor)));
312 will(returnValue(emptyHashMapCtor));
313 one(monitor).instantiated(with(any(PicoContainer.class)), (ComponentAdapter)with(a(ConstructorInjector.class)), with(equal(emptyHashMapCtor)),
314 with(isAHashMapThatWozCreated), with(injectedIsEmptyArray),
315 with(durationIsGreaterThanOrEqualToZero));
316 }});
317
318 ConstructorInjector cica = new ConstructorInjector(
319 Map.class, HashMap.class, new Parameter[0], monitor, new NullLifecycleStrategy(), false);
320 cica.getComponentInstance(null);
321 }
322
323 @Test public void testMonitoringHappensBeforeAndOnFailOfImpossibleComponentsInstantiation() throws NoSuchMethodException {
324 final ComponentMonitor monitor = mockery.mock(ComponentMonitor.class);
325 final Constructor barfingActionListenerCtor = BarfingActionListener.class.getConstructor();
326
327 final Matcher<Exception> isITE = new BaseMatcher<Exception>() {
328 public boolean matches(Object item) {
329 Exception ex = (Exception)item;
330 return ex instanceof InvocationTargetException;
331 }
332
333 public void describeTo(Description description) {
334 description.appendText("Should have been unable to instantiate");
335 }
336 };
337
338 mockery.checking(new Expectations(){{
339 one(monitor).instantiating(with(any(PicoContainer.class)), (ComponentAdapter)with(a(ConstructorInjector.class)), with(equal(barfingActionListenerCtor)));
340 will(returnValue(barfingActionListenerCtor));
341 one(monitor).instantiationFailed(with(any(PicoContainer.class)), (ComponentAdapter)with(a(ConstructorInjector.class)), with(equal(barfingActionListenerCtor)),
342 with(isITE));
343 }});
344
345
346 ConstructorInjector cica = new ConstructorInjector(
347 ActionListener.class, BarfingActionListener.class, new Parameter[0], monitor, new NullLifecycleStrategy(), false);
348 try {
349 cica.getComponentInstance(null);
350 fail("Should barf");
351 } catch (RuntimeException e) {
352 assertEquals("Barf!", e.getMessage());
353 }
354 }
355
356 private static class BarfingActionListener implements ActionListener {
357 public BarfingActionListener() {
358 throw new RuntimeException("Barf!");
359 }
360
361 public void actionPerformed(ActionEvent e) {
362 }
363 }
364
365 @Test public void testCustomLifecycleCanBeInjected() throws NoSuchMethodException {
366 RecordingLifecycleStrategy strategy = new RecordingLifecycleStrategy(new StringBuffer());
367 ConstructorInjector cica = new ConstructorInjector(
368 NullLifecycle.class, NullLifecycle.class, new Parameter[0],
369 new AbstractComponentMonitor(), strategy, false);
370 Touchable touchable = new SimpleTouchable();
371 cica.start(touchable);
372 cica.stop(touchable);
373 cica.dispose(touchable);
374 assertEquals("<start<stop<dispose", strategy.recording());
375 }
376 }