001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.beans.factory.support;
018:
019: import java.io.Serializable;
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Method;
022: import java.lang.reflect.Modifier;
023: import java.util.ArrayList;
024: import java.util.Iterator;
025: import java.util.List;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029:
030: import org.springframework.beans.BeanUtils;
031: import org.springframework.beans.factory.DisposableBean;
032: import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
033: import org.springframework.util.Assert;
034:
035: /**
036: * Adapter that implements the DisposableBean interface performing
037: * various destruction steps on a given bean instance:
038: * <ul>
039: * <li>DestructionAwareBeanPostProcessors
040: * <li>the bean implementing DisposableBean itself
041: * <li>a custom destroy method specified on the bean definition
042: * </ul>
043: *
044: * @author Juergen Hoeller
045: * @since 2.0
046: * @see AbstractBeanFactory
047: * @see org.springframework.beans.factory.DisposableBean
048: * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
049: * @see AbstractBeanDefinition#getDestroyMethodName()
050: */
051: class DisposableBeanAdapter implements DisposableBean, Runnable,
052: Serializable {
053:
054: private static final Log logger = LogFactory
055: .getLog(DisposableBeanAdapter.class);
056:
057: private final Object bean;
058:
059: private final String beanName;
060:
061: private final String destroyMethodName;
062:
063: private final boolean enforceDestroyMethod;
064:
065: private List beanPostProcessors;
066:
067: /**
068: * Create a new DisposableBeanAdapter for the given bean.
069: * @param bean the bean instance (never <code>null</code>)
070: * @param beanName the name of the bean
071: * @param beanDefinition the merged bean definition, if any
072: * @param postProcessors the List of BeanPostProcessors
073: * (potentially DestructionAwareBeanPostProcessor), if any
074: */
075: public DisposableBeanAdapter(Object bean, String beanName,
076: RootBeanDefinition beanDefinition, List postProcessors) {
077:
078: Assert.notNull(bean, "Bean must not be null");
079: this .bean = bean;
080: this .beanName = beanName;
081: this .destroyMethodName = beanDefinition.getDestroyMethodName();
082: this .enforceDestroyMethod = beanDefinition
083: .isEnforceDestroyMethod();
084: this .beanPostProcessors = filterPostProcessors(postProcessors);
085: }
086:
087: /**
088: * Create a new DisposableBeanAdapter for the given bean.
089: * @param bean the bean instance (never <code>null</code>)
090: * @param beanName the name of the bean
091: * @param destroyMethodName the name of the custom destroy method
092: * (<code>null</code> if there is none)
093: * @param enforceDestroyMethod whether to the specified custom
094: * destroy method (if any) has to be present on the bean object
095: * @param postProcessors the List of DestructionAwareBeanPostProcessors, if any
096: */
097: private DisposableBeanAdapter(Object bean, String beanName,
098: String destroyMethodName, boolean enforceDestroyMethod,
099: List postProcessors) {
100:
101: this .bean = bean;
102: this .beanName = beanName;
103: this .destroyMethodName = destroyMethodName;
104: this .enforceDestroyMethod = enforceDestroyMethod;
105: this .beanPostProcessors = postProcessors;
106: }
107:
108: /**
109: * Search for all DestructionAwareBeanPostProcessors in the List.
110: * @param postProcessors the List to search
111: * @return the filtered List of DestructionAwareBeanPostProcessors
112: */
113: private List filterPostProcessors(List postProcessors) {
114: List filteredPostProcessors = null;
115: if (postProcessors != null && !postProcessors.isEmpty()) {
116: filteredPostProcessors = new ArrayList(postProcessors
117: .size());
118: for (Iterator it = postProcessors.iterator(); it.hasNext();) {
119: Object postProcessor = it.next();
120: if (postProcessor instanceof DestructionAwareBeanPostProcessor) {
121: filteredPostProcessors.add(postProcessor);
122: }
123: }
124: }
125: return filteredPostProcessors;
126: }
127:
128: public void run() {
129: destroy();
130: }
131:
132: public void destroy() {
133: if (this .beanPostProcessors != null
134: && !this .beanPostProcessors.isEmpty()) {
135: if (logger.isTraceEnabled()) {
136: logger
137: .trace("Applying DestructionAwareBeanPostProcessors to bean with name '"
138: + this .beanName + "'");
139: }
140: for (int i = this .beanPostProcessors.size() - 1; i >= 0; i--) {
141: ((DestructionAwareBeanPostProcessor) this .beanPostProcessors
142: .get(i)).postProcessBeforeDestruction(
143: this .bean, this .beanName);
144: }
145: }
146:
147: if (this .bean instanceof DisposableBean) {
148: if (logger.isDebugEnabled()) {
149: logger.debug("Invoking destroy() on bean with name '"
150: + this .beanName + "'");
151: }
152: try {
153: ((DisposableBean) this .bean).destroy();
154: } catch (Throwable ex) {
155: logger.error(
156: "Couldn't invoke destroy method of bean with name '"
157: + this .beanName + "'", ex);
158: }
159: }
160:
161: if (this .destroyMethodName != null) {
162: invokeCustomDestroyMethod();
163: }
164: }
165:
166: /**
167: * Invoke the specified custom destroy method on the given bean.
168: * <p>This implementation invokes a no-arg method if found, else checking
169: * for a method with a single boolean argument (passing in "true",
170: * assuming a "force" parameter), else logging an error.
171: */
172: private void invokeCustomDestroyMethod() {
173: if (this .destroyMethodName != null) {
174: try {
175: Method destroyMethod = BeanUtils
176: .findMethodWithMinimalParameters(this .bean
177: .getClass(), destroyMethodName);
178: if (destroyMethod == null) {
179: if (this .enforceDestroyMethod) {
180: logger
181: .error("Couldn't find a destroy method named '"
182: + destroyMethodName
183: + "' on bean with name '"
184: + this .beanName + "'");
185: }
186: }
187:
188: else {
189: Class[] paramTypes = destroyMethod
190: .getParameterTypes();
191: if (paramTypes.length > 1) {
192: logger
193: .error("Method '"
194: + destroyMethodName
195: + "' of bean '"
196: + this .beanName
197: + "' has more than one parameter - not supported as destroy method");
198: } else if (paramTypes.length == 1
199: && !paramTypes[0].equals(boolean.class)) {
200: logger
201: .error("Method '"
202: + destroyMethodName
203: + "' of bean '"
204: + this .beanName
205: + "' has a non-boolean parameter - not supported as destroy method");
206: }
207:
208: else {
209: Object[] args = new Object[paramTypes.length];
210: if (paramTypes.length == 1) {
211: args[0] = Boolean.TRUE;
212: }
213: if (!Modifier.isPublic(destroyMethod
214: .getModifiers())
215: || !Modifier.isPublic(destroyMethod
216: .getDeclaringClass()
217: .getModifiers())) {
218: destroyMethod.setAccessible(true);
219: }
220:
221: if (logger.isDebugEnabled()) {
222: logger
223: .debug("Invoking custom destroy method on bean with name '"
224: + this .beanName + "'");
225: }
226: try {
227: destroyMethod.invoke(this .bean, args);
228: } catch (InvocationTargetException ex) {
229: logger.error(
230: "Couldn't invoke destroy method '"
231: + destroyMethodName
232: + "' of bean with name '"
233: + this .beanName + "'", ex
234: .getTargetException());
235: } catch (Throwable ex) {
236: logger.error(
237: "Couldn't invoke destroy method '"
238: + destroyMethodName
239: + "' of bean with name '"
240: + this .beanName + "'", ex);
241: }
242: }
243: }
244: } catch (IllegalArgumentException ex) {
245: // thrown from findMethodWithMinimalParameters
246: logger
247: .error("Couldn't find a unique destroy method on bean with name '"
248: + this .beanName
249: + ": "
250: + ex.getMessage());
251: }
252: }
253: }
254:
255: /**
256: * Serializes a copy of the state of this class,
257: * filtering out non-serializable BeanPostProcessors.
258: */
259: protected Object writeReplace() {
260: List serializablePostProcessors = null;
261: if (this .beanPostProcessors != null) {
262: serializablePostProcessors = new ArrayList();
263: for (Iterator it = this .beanPostProcessors.iterator(); it
264: .hasNext();) {
265: Object postProcessor = it.next();
266: if (postProcessor instanceof Serializable) {
267: serializablePostProcessors.add(postProcessor);
268: }
269: }
270: }
271: return new DisposableBeanAdapter(this.bean, this.beanName,
272: this.destroyMethodName, this.enforceDestroyMethod,
273: serializablePostProcessors);
274: }
275:
276: }
|