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, (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 }