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, 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 methods[i] = type.getMethod(methodNames[i]);
162 } catch (NoSuchMethodException e) {
163 continue;
164 }
165 }
166 methodMap.put(type, methods);
167 }
168 }
169 return methods;
170 }
171 }