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