    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&ouml;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, (Exception) e.getCause(), component);
    116                }
    117            }
    118        }
    119
    120        protected void monitorAndThrowReflectionLifecycleException(final Method method,
    121                                                                 final Exception e,
    122                                                                 final Object component) {
    123            RuntimeException re;
    124            if (e.getCause() instanceof RuntimeException) {
    125                re = (RuntimeException) e.getCause();
    126            } else {
    127                re = new RuntimeException("wrapper", e);
    128            }
    129            currentMonitor().lifecycleInvocationFailed(null, null, method, component, re);
    130        }
    131
    132        /**
    133         * {@inheritDoc} The component has a lifecycle if at least one of the three methods is present.
    134         */
    135        public boolean hasLifecycle(final Class<?> type) {
    136            Method[] methods = init(type);
    137            for (Method method : methods) {
    138                if (method != null) {
    139                    return true;
    140                }
    141            }
    142            return false;
    143        }
    144
    145        /**
    146         * Initializes the method array with the given type.
    147         * @param type the type to examine for reflection lifecycle methods.
    148         * @return Method array containing start/stop/dispose methods.
    149         */
    150        private Method[] init(final Class<?> type) {
    151            Method[] methods;
    152            synchronized (methodMap) {
    153                methods = methodMap.get(type);
    154                if (methods == null) {
    155                    methods = new Method[methodNames.length];
    156                    for (int i = 0; i < methods.length; i++) {
    157                        try {
    158                            methods[i] = type.getMethod(methodNames[i]);
    159                        } catch (NoSuchMethodException e) {
    160                            continue;
    161                        }
    162                    }
    163                    methodMap.put(type, methods);
    164                }
    165            }
    166            return methods;
    167        }
    168    }