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 org.picocontainer.ComponentMonitor;
011    import org.picocontainer.lifecycle.AbstractMonitoringLifecycleStrategy;
012    import org.picocontainer.lifecycle.ReflectionLifecycleException;
013    
014    import java.lang.reflect.InvocationTargetException;
015    import java.lang.reflect.Method;
016    import java.util.HashMap;
017    import java.util.Map;
018    
019    
020    /**
021     * Reflection lifecycle strategy. Starts, stops, disposes of component if appropriate methods are
022     * present. The component may implement only one of the three methods.
023     * 
024     * @author Paul Hammant
025     * @author Mauro Talevi
026     * @author Jörg Schaible
027     * @see org.picocontainer.Startable
028     * @see org.picocontainer.Disposable
029     * @see org.picocontainer.lifecycle.StartableLifecycleStrategy
030     */
031    public final 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 final String[] methodNames;
037        private final transient Map<Class, Method[]> methodMap = new HashMap<Class, Method[]>();
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(null, null, method, component);
086                    method.invoke(component);
087                    currentMonitor().invoked(null, null, method, component, System.currentTimeMillis() - str);
088                } catch (IllegalAccessException e) {
089                    monitorAndThrowReflectionLifecycleException(method, e, component);
090                } catch (InvocationTargetException e) {
091                    monitorAndThrowReflectionLifecycleException(method, e, component);
092                }
093            }
094        }
095    
096        protected void monitorAndThrowReflectionLifecycleException(Method method,
097                                                                 Exception e,
098                                                                 Object component) {
099            RuntimeException re = new ReflectionLifecycleException(method.getName(), e);
100            currentMonitor().lifecycleInvocationFailed(null, null, method, component, re);
101            throw re;
102        }
103    
104        /**
105         * {@inheritDoc} The component has a lifecylce if at least one of the three methods is present.
106         */
107        public boolean hasLifecycle(Class type) {
108            Method[] methods = init(type);
109            for (Method method : methods) {
110                if (method != null) {
111                    return true;
112                }
113            }
114            return false;
115        }
116    
117        private Method[] init(Class type) {
118            Method[] methods;
119            synchronized (methodMap) {
120                methods = methodMap.get(type);
121                if (methods == null) {
122                    methods = new Method[methodNames.length];
123                    for (int i = 0; i < methods.length; i++) {
124                        try {
125                            methods[i] = type.getMethod(methodNames[i]);
126                        } catch (NoSuchMethodException e) {
127                            continue;
128                        }
129                    }
130                    methodMap.put(type, methods);
131                }
132            }
133            return methods;
134        }
135    }