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