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    package org.picocontainer.lifecycle;
009    
010    import java.lang.reflect.InvocationTargetException;
011    import java.lang.reflect.Method;
012    import java.util.HashMap;
013    import java.util.Map;
014    
015    import org.picocontainer.ComponentMonitor;
016    
017    /**
018     * Reflection lifecycle strategy. Starts, stops, disposes of component if appropriate methods are
019     * present. The component may implement only one of the three methods.
020     *
021     * @author Paul Hammant
022     * @author Mauro Talevi
023     * @author Jörg Schaible
024     * @see org.picocontainer.Startable
025     * @see org.picocontainer.Disposable
026     * @see org.picocontainer.lifecycle.StartableLifecycleStrategy
027     */
028    @SuppressWarnings("serial")
029    public class ReflectionLifecycleStrategy extends AbstractMonitoringLifecycleStrategy {
030    
031            /**
032             * Index in the methodnames array that contains the name of the 'start'
033             * method.
034             */
035            private final static int START = 0;
036    
037            /**
038             * Index in the methodNames array that contains the name of the 'stop'
039             * method.
040             */
041            private final static int STOP = 1;
042    
043            /**
044             * Index in the methodNames array that contains the name of the 'dispose'
045             * method.
046             */
047            private final static int DISPOSE = 2;
048    
049            /**
050             * An array of method names that are part of the lifecycle functions.
051             */
052        private final String[] methodNames;
053    
054        /**
055         * Map of classes mapped to method arrays that are cached for reflection.
056         */
057        private final transient Map<Class<?>, Method[]> methodMap = new HashMap<Class<?>, Method[]>();
058    
059        /**
060         * Construct a ReflectionLifecycleStrategy.
061         *
062         * @param monitor the monitor to use
063         * @throws NullPointerException if the monitor is <code>null</code>
064         */
065        public ReflectionLifecycleStrategy(final ComponentMonitor monitor) {
066            this(monitor, "start", "stop", "dispose");
067        }
068    
069        /**
070         * Construct a ReflectionLifecycleStrategy with individual method names. Note, that a lifecycle
071         * method does not have any arguments.
072         *
073         * @param monitor the monitor to use
074         * @param startMethodName the name of the start method
075         * @param stopMethodName the name of the stop method
076         * @param disposeMethodName the name of the dispose method
077         * @throws NullPointerException if the monitor is <code>null</code>
078         */
079        public ReflectionLifecycleStrategy(
080                final ComponentMonitor monitor, final String startMethodName, final String stopMethodName,
081                final String disposeMethodName) {
082            super(monitor);
083            methodNames = new String[]{startMethodName, stopMethodName, disposeMethodName};
084        }
085    
086        /** {@inheritDoc} **/
087        public void start(final Object component) {
088            Method[] methods = init(component.getClass());
089            invokeMethod(component, methods[START]);
090            
091        }
092    
093            /** {@inheritDoc} **/
094        public void stop(final Object component) {
095            Method[] methods = init(component.getClass());
096            invokeMethod(component, methods[STOP]);
097        }
098    
099        /** {@inheritDoc} **/
100        public void dispose(final Object component) {
101            Method[] methods = init(component.getClass());
102            invokeMethod(component, methods[DISPOSE]);
103        }
104    
105        private void invokeMethod(final Object component, final Method method) {
106            if (component != null && method != null) {
107                try {
108                    long str = System.currentTimeMillis();
109                    currentMonitor().invoking(null, null, method, component, new Object[0]);
110                    method.invoke(component);
111                    currentMonitor().invoked(null, null, method, component, System.currentTimeMillis() - str, new Object[0], null);
112                } catch (IllegalAccessException e) {
113                    monitorAndThrowReflectionLifecycleException(method, e, component);
114                } catch (InvocationTargetException e) {
115                    monitorAndThrowReflectionLifecycleException(method, e.getCause(), component);
116                }
117            }
118        }
119    
120        protected void monitorAndThrowReflectionLifecycleException(final Method method,
121                                                                 final Throwable e,
122                                                                 final Object component) {
123            RuntimeException re;
124            if (e.getCause() instanceof RuntimeException) {
125                re = (RuntimeException) e.getCause();
126            // TODO - change lifecycleInvocationFailed to take a throwable in future version
127    //        } else if (e.getCause() instanceof Error) {
128    //            re = (Error) e.getCause();
129            } else {
130                re = new RuntimeException("wrapper", e);
131            }
132            currentMonitor().lifecycleInvocationFailed(null, null, method, component, re);
133        }
134    
135        /**
136         * {@inheritDoc} The component has a lifecycle if at least one of the three methods is present.
137         */
138        public boolean hasLifecycle(final Class<?> type) {
139            Method[] methods = init(type);
140            for (Method method : methods) {
141                if (method != null) {
142                    return true;
143                }
144            }
145            return false;
146        }
147    
148        /**
149         * Initializes the method array with the given type.
150         * @param type the type to examine for reflection lifecycle methods.
151         * @return Method array containing start/stop/dispose methods.
152         */
153        private Method[] init(final Class<?> type) {
154            Method[] methods;
155            synchronized (methodMap) {
156                methods = methodMap.get(type);
157                if (methods == null) {
158                    methods = new Method[methodNames.length];
159                    for (int i = 0; i < methods.length; i++) {
160                        try {
161                            methods[i] = type.getMethod(methodNames[i]);
162                        } catch (NoSuchMethodException e) {
163                            continue;
164                        }
165                    }
166                    methodMap.put(type, methods);
167                }
168            }
169            return methods;
170        }
171    }