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.util.ArrayList;
023: import java.util.Iterator;
024: import java.util.List;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.springframework.beans.BeanUtils;
030: import org.springframework.beans.factory.DisposableBean;
031: import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
032: import org.springframework.util.Assert;
033: import org.springframework.util.ReflectionUtils;
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 boolean invokeDisposableBean;
062:
063: private final String destroyMethodName;
064:
065: private final boolean enforceDestroyMethod;
066:
067: private List beanPostProcessors;
068:
069: /**
070: * Create a new DisposableBeanAdapter for the given bean.
071: * @param bean the bean instance (never <code>null</code>)
072: * @param beanName the name of the bean
073: * @param beanDefinition the merged bean definition
074: * @param postProcessors the List of BeanPostProcessors
075: * (potentially DestructionAwareBeanPostProcessor), if any
076: */
077: public DisposableBeanAdapter(Object bean, String beanName,
078: RootBeanDefinition beanDefinition, List postProcessors) {
079:
080: Assert.notNull(bean, "Bean must not be null");
081: this .bean = bean;
082: this .beanName = beanName;
083: this .invokeDisposableBean = !beanDefinition
084: .isExternallyManagedDestroyMethod("destroy");
085: this .destroyMethodName = (!beanDefinition
086: .isExternallyManagedDestroyMethod(beanDefinition
087: .getDestroyMethodName()) ? beanDefinition
088: .getDestroyMethodName() : null);
089: this .enforceDestroyMethod = beanDefinition
090: .isEnforceDestroyMethod();
091: this .beanPostProcessors = filterPostProcessors(postProcessors);
092: }
093:
094: /**
095: * Create a new DisposableBeanAdapter for the given bean.
096: * @param bean the bean instance (never <code>null</code>)
097: * @param beanName the name of the bean
098: * @param invokeDisposableBean whether to actually invoke
099: * DisposableBean's destroy method here
100: * @param destroyMethodName the name of the custom destroy method
101: * (<code>null</code> if there is none)
102: * @param enforceDestroyMethod whether to the specified custom
103: * destroy method (if any) has to be present on the bean object
104: * @param postProcessors the List of DestructionAwareBeanPostProcessors, if any
105: */
106: private DisposableBeanAdapter(Object bean, String beanName,
107: boolean invokeDisposableBean, String destroyMethodName,
108: boolean enforceDestroyMethod, List postProcessors) {
109:
110: this .bean = bean;
111: this .beanName = beanName;
112: this .invokeDisposableBean = invokeDisposableBean;
113: this .destroyMethodName = destroyMethodName;
114: this .enforceDestroyMethod = enforceDestroyMethod;
115: this .beanPostProcessors = postProcessors;
116: }
117:
118: /**
119: * Search for all DestructionAwareBeanPostProcessors in the List.
120: * @param postProcessors the List to search
121: * @return the filtered List of DestructionAwareBeanPostProcessors
122: */
123: private List filterPostProcessors(List postProcessors) {
124: List filteredPostProcessors = null;
125: if (postProcessors != null && !postProcessors.isEmpty()) {
126: filteredPostProcessors = new ArrayList(postProcessors
127: .size());
128: for (Iterator it = postProcessors.iterator(); it.hasNext();) {
129: Object postProcessor = it.next();
130: if (postProcessor instanceof DestructionAwareBeanPostProcessor) {
131: filteredPostProcessors.add(postProcessor);
132: }
133: }
134: }
135: return filteredPostProcessors;
136: }
137:
138: public void run() {
139: destroy();
140: }
141:
142: public void destroy() {
143: if (this .beanPostProcessors != null
144: && !this .beanPostProcessors.isEmpty()) {
145: if (logger.isTraceEnabled()) {
146: logger
147: .trace("Applying DestructionAwareBeanPostProcessors to bean with name '"
148: + this .beanName + "'");
149: }
150: for (int i = this .beanPostProcessors.size() - 1; i >= 0; i--) {
151: ((DestructionAwareBeanPostProcessor) this .beanPostProcessors
152: .get(i)).postProcessBeforeDestruction(
153: this .bean, this .beanName);
154: }
155: }
156:
157: boolean isDisposableBean = (this .bean instanceof DisposableBean);
158: if (isDisposableBean && this .invokeDisposableBean) {
159: if (logger.isDebugEnabled()) {
160: logger.debug("Invoking destroy() on bean with name '"
161: + this .beanName + "'");
162: }
163: try {
164: ((DisposableBean) this .bean).destroy();
165: } catch (Throwable ex) {
166: logger.error(
167: "Couldn't invoke destroy method of bean with name '"
168: + this .beanName + "'", ex);
169: }
170: }
171:
172: if (this .destroyMethodName != null
173: && !(isDisposableBean && "destroy"
174: .equals(this .destroyMethodName))) {
175: invokeCustomDestroyMethod();
176: }
177: }
178:
179: /**
180: * Invoke the specified custom destroy method on the given bean.
181: * <p>This implementation invokes a no-arg method if found, else checking
182: * for a method with a single boolean argument (passing in "true",
183: * assuming a "force" parameter), else logging an error.
184: */
185: private void invokeCustomDestroyMethod() {
186: try {
187: Method destroyMethod = BeanUtils
188: .findMethodWithMinimalParameters(this .bean
189: .getClass(), this .destroyMethodName);
190: if (destroyMethod == null) {
191: if (this .enforceDestroyMethod) {
192: logger
193: .error("Couldn't find a destroy method named '"
194: + this .destroyMethodName
195: + "' on bean with name '"
196: + this .beanName + "'");
197: }
198: }
199:
200: else {
201: Class[] paramTypes = destroyMethod.getParameterTypes();
202: if (paramTypes.length > 1) {
203: logger
204: .error("Method '"
205: + this .destroyMethodName
206: + "' of bean '"
207: + this .beanName
208: + "' has more than one parameter - not supported as destroy method");
209: } else if (paramTypes.length == 1
210: && !paramTypes[0].equals(boolean.class)) {
211: logger
212: .error("Method '"
213: + this .destroyMethodName
214: + "' of bean '"
215: + this .beanName
216: + "' has a non-boolean parameter - not supported as destroy method");
217: }
218:
219: else {
220: Object[] args = new Object[paramTypes.length];
221: if (paramTypes.length == 1) {
222: args[0] = Boolean.TRUE;
223: }
224: if (logger.isDebugEnabled()) {
225: logger
226: .debug("Invoking custom destroy method on bean with name '"
227: + this .beanName + "'");
228: }
229: ReflectionUtils.makeAccessible(destroyMethod);
230: try {
231: destroyMethod.invoke(this .bean, args);
232: } catch (InvocationTargetException ex) {
233: logger.error("Couldn't invoke destroy method '"
234: + this .destroyMethodName
235: + "' of bean with name '"
236: + this .beanName + "'", ex
237: .getTargetException());
238: } catch (Throwable ex) {
239: logger.error("Couldn't invoke destroy method '"
240: + this .destroyMethodName
241: + "' of bean with name '"
242: + this .beanName + "'", ex);
243: }
244: }
245: }
246: } catch (IllegalArgumentException ex) {
247: // thrown from findMethodWithMinimalParameters
248: logger
249: .error("Couldn't find a unique destroy method on bean with name '"
250: + this .beanName + ": " + ex.getMessage());
251: }
252: }
253:
254: /**
255: * Serializes a copy of the state of this class,
256: * filtering out non-serializable BeanPostProcessors.
257: */
258: protected Object writeReplace() {
259: List serializablePostProcessors = null;
260: if (this .beanPostProcessors != null) {
261: serializablePostProcessors = new ArrayList();
262: for (Iterator it = this .beanPostProcessors.iterator(); it
263: .hasNext();) {
264: Object postProcessor = it.next();
265: if (postProcessor instanceof Serializable) {
266: serializablePostProcessors.add(postProcessor);
267: }
268: }
269: }
270: return new DisposableBeanAdapter(this.bean, this.beanName,
271: this.invokeDisposableBean, this.destroyMethodName,
272: this.enforceDestroyMethod, serializablePostProcessors);
273: }
274:
275: }
|