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 *****************************************************************************/
008package org.picocontainer.lifecycle;
009
010import org.picocontainer.ComponentMonitor;
011
012import java.lang.reflect.InvocationTargetException;
013import java.lang.reflect.Method;
014import java.util.HashMap;
015import java.util.Map;
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")
029public 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                        final String methodName = methodNames[i];
162                                if (methodName == null) {
163                                // skipping, we're not interested in this lifecycle method.
164                                        continue;
165                         }
166                         methods[i] = type.getMethod(methodName);
167                    } catch (NoSuchMethodException e) {
168                        continue;
169                    }
170                }
171                methodMap.put(type, methods);
172            }
173        }
174        return methods;
175    }
176}