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 }