1 /*
2 * Copyright 2002-2007 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.springframework.dao.support;
18
19 import java.util.Iterator;
20 import java.util.Map;
21
22 import org.aopalliance.intercept.MethodInterceptor;
23 import org.aopalliance.intercept.MethodInvocation;
24
25 import org.springframework.beans.BeansException;
26 import org.springframework.beans.factory.BeanFactory;
27 import org.springframework.beans.factory.BeanFactoryAware;
28 import org.springframework.beans.factory.BeanFactoryUtils;
29 import org.springframework.beans.factory.InitializingBean;
30 import org.springframework.beans.factory.ListableBeanFactory;
31 import org.springframework.util.Assert;
32 import org.springframework.util.ReflectionUtils;
33
34 /**
35 * AOP Alliance MethodInterceptor that provides persistence exception translation
36 * based on a given PersistenceExceptionTranslator.
37 *
38 * <p>Delegates to the given {@link PersistenceExceptionTranslator} to translate
39 * a RuntimeException thrown into Spring's DataAccessException hierarchy
40 * (if appropriate). If the RuntimeException in question is declared on the
41 * target method, it is always propagated as-is (with no translation applied).
42 *
43 * @author Rod Johnson
44 * @author Juergen Hoeller
45 * @since 2.0
46 * @see PersistenceExceptionTranslator
47 */
48 public class PersistenceExceptionTranslationInterceptor
49 implements MethodInterceptor, BeanFactoryAware, InitializingBean {
50
51 private PersistenceExceptionTranslator persistenceExceptionTranslator;
52
53
54 /**
55 * Create a new PersistenceExceptionTranslationInterceptor.
56 * Needs to be configured with a PersistenceExceptionTranslator afterwards.
57 * @see #setPersistenceExceptionTranslator
58 */
59 public PersistenceExceptionTranslationInterceptor() {
60 }
61
62 /**
63 * Create a new PersistenceExceptionTranslationInterceptor
64 * for the given PersistenceExceptionTranslator.
65 * @param persistenceExceptionTranslator the PersistenceExceptionTranslator to use
66 */
67 public PersistenceExceptionTranslationInterceptor(PersistenceExceptionTranslator persistenceExceptionTranslator) {
68 setPersistenceExceptionTranslator(persistenceExceptionTranslator);
69 }
70
71 /**
72 * Create a new PersistenceExceptionTranslationInterceptor, autodetecting
73 * PersistenceExceptionTranslators in the given BeanFactory.
74 * @param beanFactory the ListableBeanFactory to obtaining all
75 * PersistenceExceptionTranslators from
76 */
77 public PersistenceExceptionTranslationInterceptor(ListableBeanFactory beanFactory) {
78 this.persistenceExceptionTranslator = detectPersistenceExceptionTranslators(beanFactory);
79 }
80
81
82 /**
83 * Specify the PersistenceExceptionTranslator to use.
84 * <p>Default is to autodetect all PersistenceExceptionTranslators
85 * in the containing BeanFactory, using them in a chain.
86 * @see #detectPersistenceExceptionTranslators
87 */
88 public void setPersistenceExceptionTranslator(PersistenceExceptionTranslator pet) {
89 Assert.notNull(pet, "PersistenceExceptionTranslator must not be null");
90 this.persistenceExceptionTranslator = pet;
91 }
92
93 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
94 if (this.persistenceExceptionTranslator == null) {
95 // No explicit exception translator specified - perform autodetection.
96 if (!(beanFactory instanceof ListableBeanFactory)) {
97 throw new IllegalArgumentException(
98 "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory");
99 }
100 this.persistenceExceptionTranslator =
101 detectPersistenceExceptionTranslators((ListableBeanFactory) beanFactory);
102 }
103 }
104
105 public void afterPropertiesSet() {
106 if (this.persistenceExceptionTranslator == null) {
107 throw new IllegalArgumentException("Property 'persistenceExceptionTranslator' is required");
108 }
109 }
110
111
112 /**
113 * Detect all PersistenceExceptionTranslators in the given BeanFactory.
114 * @param beanFactory the ListableBeanFactory to obtaining all
115 * PersistenceExceptionTranslators from
116 * @return a chained PersistenceExceptionTranslator, combining all
117 * PersistenceExceptionTranslators found in the factory
118 * @see ChainedPersistenceExceptionTranslator
119 */
120 protected PersistenceExceptionTranslator detectPersistenceExceptionTranslators(ListableBeanFactory beanFactory) {
121 // Find all translators, being careful not to activate FactoryBeans.
122 Map pets = BeanFactoryUtils.beansOfTypeIncludingAncestors(
123 beanFactory, PersistenceExceptionTranslator.class, false, false);
124 if (pets.isEmpty()) {
125 throw new IllegalStateException(
126 "No persistence exception translators found in bean factory. Cannot perform exception translation.");
127 }
128 ChainedPersistenceExceptionTranslator cpet = new ChainedPersistenceExceptionTranslator();
129 for (Iterator it = pets.values().iterator(); it.hasNext();) {
130 cpet.addDelegate((PersistenceExceptionTranslator) it.next());
131 }
132 return cpet;
133 }
134
135
136 public Object invoke(MethodInvocation mi) throws Throwable {
137 try {
138 return mi.proceed();
139 }
140 catch (RuntimeException ex) {
141 // Let it throw raw if the type of the exception is on the throws clause of the method.
142 if (ReflectionUtils.declaresException(mi.getMethod(), ex.getClass())) {
143 throw ex;
144 }
145 else {
146 throw DataAccessUtils.translateIfNecessary(ex, this.persistenceExceptionTranslator);
147 }
148 }
149 }
150
151 }