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: *****************************************************************************/package org.picocontainer.lifecycle;
008:
009: import java.lang.reflect.InvocationTargetException;
010: import java.lang.reflect.Method;
011: import java.util.HashMap;
012: import java.util.Map;
013:
014: import org.picocontainer.ComponentMonitor;
015:
016: /**
017: * Reflection lifecycle strategy. Starts, stops, disposes of component if appropriate methods are
018: * present. The component may implement only one of the three methods.
019: *
020: * @author Paul Hammant
021: * @author Mauro Talevi
022: * @author Jörg Schaible
023: * @see org.picocontainer.Startable
024: * @see org.picocontainer.Disposable
025: * @see org.picocontainer.lifecycle.StartableLifecycleStrategy
026: */
027: public final class ReflectionLifecycleStrategy extends
028: AbstractMonitoringLifecycleStrategy {
029:
030: /**
031: * Serialization UUID.
032: */
033: private static final long serialVersionUID = 1887156774166378597L;
034:
035: /**
036: * Index in the methodnames array that contains the name of the 'start'
037: * method.
038: */
039: private final static int START = 0;
040:
041: /**
042: * Index in the methodNames array that contains the name of the 'stop'
043: * method.
044: */
045: private final static int STOP = 1;
046:
047: /**
048: * Index in the methodNames array that contains the name of the 'dispose'
049: * method.
050: */
051: private final static int DISPOSE = 2;
052:
053: /**
054: * An array of method names that are part of the lifecycle functions.
055: */
056: private final String[] methodNames;
057:
058: /**
059: * Map of classes mapped to method arrays that are cached for reflection.
060: */
061: private final transient Map<Class<?>, Method[]> methodMap = new HashMap<Class<?>, Method[]>();
062:
063: /**
064: * Construct a ReflectionLifecycleStrategy.
065: *
066: * @param monitor the monitor to use
067: * @throws NullPointerException if the monitor is <code>null</code>
068: */
069: public ReflectionLifecycleStrategy(final ComponentMonitor monitor) {
070: this (monitor, "start", "stop", "dispose");
071: }
072:
073: /**
074: * Construct a ReflectionLifecycleStrategy with individual method names. Note, that a lifecycle
075: * method does not have any arguments.
076: *
077: * @param monitor the monitor to use
078: * @param startMethodName the name of the start method
079: * @param stopMethodName the name of the stop method
080: * @param disposeMethodName the name of the dispose method
081: * @throws NullPointerException if the monitor is <code>null</code>
082: */
083: public ReflectionLifecycleStrategy(final ComponentMonitor monitor,
084: final String startMethodName, final String stopMethodName,
085: final String disposeMethodName) {
086: super (monitor);
087: methodNames = new String[] { startMethodName, stopMethodName,
088: disposeMethodName };
089: }
090:
091: /** {@inheritDoc} **/
092: public void start(final Object component) {
093: Method[] methods = init(component.getClass());
094: invokeMethod(component, methods[START]);
095:
096: }
097:
098: /** {@inheritDoc} **/
099: public void stop(final Object component) {
100: Method[] methods = init(component.getClass());
101: invokeMethod(component, methods[STOP]);
102: }
103:
104: /** {@inheritDoc} **/
105: public void dispose(final Object component) {
106: Method[] methods = init(component.getClass());
107: invokeMethod(component, methods[DISPOSE]);
108: }
109:
110: private void invokeMethod(final Object component,
111: final Method method) {
112: if (component != null && method != null) {
113: try {
114: long str = System.currentTimeMillis();
115: currentMonitor()
116: .invoking(null, null, method, component);
117: method.invoke(component);
118: currentMonitor().invoked(null, null, method, component,
119: System.currentTimeMillis() - str);
120: } catch (IllegalAccessException e) {
121: monitorAndThrowReflectionLifecycleException(method, e,
122: component);
123: } catch (InvocationTargetException e) {
124: monitorAndThrowReflectionLifecycleException(method, e,
125: component);
126: }
127: }
128: }
129:
130: protected void monitorAndThrowReflectionLifecycleException(
131: final Method method, final Exception e,
132: final Object component) {
133: RuntimeException re = new ReflectionLifecycleException(method
134: .getName(), e);
135: currentMonitor().lifecycleInvocationFailed(null, null, method,
136: component, re);
137: throw re;
138: }
139:
140: /**
141: * {@inheritDoc} The component has a lifecycle if at least one of the three methods is present.
142: */
143: public boolean hasLifecycle(final Class<?> type) {
144: Method[] methods = init(type);
145: for (Method method : methods) {
146: if (method != null) {
147: return true;
148: }
149: }
150: return false;
151: }
152:
153: /**
154: * Initializes the method array with the given type.
155: * @param type the type to examine for reflection lifecycle methods.
156: * @return Method array containing start/stop/dispose methods.
157: */
158: private Method[] init(final Class<?> type) {
159: Method[] methods;
160: synchronized (methodMap) {
161: methods = methodMap.get(type);
162: if (methods == null) {
163: methods = new Method[methodNames.length];
164: for (int i = 0; i < methods.length; i++) {
165: try {
166: methods[i] = type.getMethod(methodNames[i]);
167: } catch (NoSuchMethodException e) {
168: continue;
169: }
170: }
171: methodMap.put(type, methods);
172: }
173: }
174: return methods;
175: }
176: }
|