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.gems.lifecycle;
009    
010    import org.picocontainer.ComponentMonitor;
011    import org.picocontainer.defaults.AbstractMonitoringLifecycleStrategy;
012    
013    import java.lang.reflect.InvocationTargetException;
014    import java.lang.reflect.Method;
015    import java.util.HashMap;
016    import java.util.Map;
017    
018    
019    /**
020     * Reflection lifecycle strategy. Starts, stops, disposes of component if appropriate methods are
021     * present. The component may implement only one of the three methods.
022     * 
023     * @author Paul Hammant
024     * @author Mauro Talevi
025     * @author Jörg Schaible
026     * @see org.picocontainer.Startable
027     * @see org.picocontainer.Disposable
028     * @see org.picocontainer.defaults.DefaultLifecycleStrategy
029     * @since 1.2
030     */
031    public class ReflectionLifecycleStrategy extends AbstractMonitoringLifecycleStrategy {
032    
033        private final static int START = 0;
034        private final static int STOP = 1;
035        private final static int DISPOSE = 2;
036        private String[] methodNames;
037        private final transient Map methodMap = new HashMap();
038    
039        /**
040         * Construct a ReflectionLifecycleStrategy.
041         * 
042         * @param monitor the monitor to use
043         * @throws NullPointerException if the monitor is <code>null</code>
044         */
045        public ReflectionLifecycleStrategy(ComponentMonitor monitor) {
046            this(monitor, "start", "stop", "dispose");
047        }
048    
049        /**
050         * Construct a ReflectionLifecycleStrategy with individual method names. Note, that a lifecycle
051         * method does not have any arguments.
052         * 
053         * @param monitor the monitor to use
054         * @param startMethodName the name of the start method
055         * @param stopMethodName the name of the stop method
056         * @param disposeMethodName the name of the dispose method
057         * @throws NullPointerException if the monitor is <code>null</code>
058         */
059        public ReflectionLifecycleStrategy(
060                ComponentMonitor monitor, String startMethodName, String stopMethodName,
061                String disposeMethodName) {
062            super(monitor);
063            methodNames = new String[]{startMethodName, stopMethodName, disposeMethodName};
064        }
065    
066        public void start(Object component) {
067            Method[] methods = init(component.getClass());
068            invokeMethod(component, methods[START]);
069        }
070    
071        public void stop(Object component) {
072            Method[] methods = init(component.getClass());
073            invokeMethod(component, methods[STOP]);
074        }
075    
076        public void dispose(Object component) {
077            Method[] methods = init(component.getClass());
078            invokeMethod(component, methods[DISPOSE]);
079        }
080    
081        private void invokeMethod(Object component, Method method) {
082            if (component != null && method != null) {
083                try {
084                    long str = System.currentTimeMillis();
085                    currentMonitor().invoking(method, component);
086                    method.invoke(component, new Object[0]);
087                    currentMonitor().invoked(method, component, System.currentTimeMillis() - str);
088                } catch (IllegalAccessException e) {
089                    RuntimeException re = new ReflectionLifecycleException(method.getName(), e);
090                    currentMonitor().lifecycleInvocationFailed(method, component, re);
091                    throw re;
092                } catch (InvocationTargetException e) {
093                    RuntimeException re = new ReflectionLifecycleException(method.getName(), e);
094                    currentMonitor().lifecycleInvocationFailed(method, component, re);
095                    throw re;
096                }
097            }
098        }
099    
100        /**
101         * {@inheritDoc} The component has a lifecylce if at least one of the three methods is present.
102         */
103        public boolean hasLifecycle(Class type) {
104            Method[] methods = init(type);
105            for (int i = 0; i < methods.length; i++) {
106                if (methods[i] != null) {
107                    return true;
108                }
109            }
110            return false;
111        }
112    
113        private Method[] init(Class type) {
114            Method[] methods;
115            synchronized (methodMap) {
116                methods = (Method[])methodMap.get(type);
117                if (methods == null) {
118                    methods = new Method[methodNames.length];
119                    for (int i = 0; i < methods.length; i++) {
120                        try {
121                            methods[i] = type.getMethod(methodNames[i], new Class[0]);
122                        } catch (NoSuchMethodException e) {
123                            continue;
124                        }
125                    }
126                    methodMap.put(type, methods);
127                }
128            }
129            return methods;
130        }
131    }