001/* 002 * Copyright 2002-2020 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 * https://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 017package org.springframework.dao.support; 018 019import java.util.Map; 020 021import org.aopalliance.intercept.MethodInterceptor; 022import org.aopalliance.intercept.MethodInvocation; 023 024import org.springframework.beans.BeansException; 025import org.springframework.beans.factory.BeanFactory; 026import org.springframework.beans.factory.BeanFactoryAware; 027import org.springframework.beans.factory.BeanFactoryUtils; 028import org.springframework.beans.factory.InitializingBean; 029import org.springframework.beans.factory.ListableBeanFactory; 030import org.springframework.util.Assert; 031import org.springframework.util.ReflectionUtils; 032 033/** 034 * AOP Alliance MethodInterceptor that provides persistence exception translation 035 * based on a given PersistenceExceptionTranslator. 036 * 037 * <p>Delegates to the given {@link PersistenceExceptionTranslator} to translate 038 * a RuntimeException thrown into Spring's DataAccessException hierarchy 039 * (if appropriate). If the RuntimeException in question is declared on the 040 * target method, it is always propagated as-is (with no translation applied). 041 * 042 * @author Rod Johnson 043 * @author Juergen Hoeller 044 * @since 2.0 045 * @see PersistenceExceptionTranslator 046 */ 047public class PersistenceExceptionTranslationInterceptor 048 implements MethodInterceptor, BeanFactoryAware, InitializingBean { 049 050 private volatile PersistenceExceptionTranslator persistenceExceptionTranslator; 051 052 private boolean alwaysTranslate = false; 053 054 private ListableBeanFactory beanFactory; 055 056 057 /** 058 * Create a new PersistenceExceptionTranslationInterceptor. 059 * Needs to be configured with a PersistenceExceptionTranslator afterwards. 060 * @see #setPersistenceExceptionTranslator 061 */ 062 public PersistenceExceptionTranslationInterceptor() { 063 } 064 065 /** 066 * Create a new PersistenceExceptionTranslationInterceptor 067 * for the given PersistenceExceptionTranslator. 068 * @param pet the PersistenceExceptionTranslator to use 069 */ 070 public PersistenceExceptionTranslationInterceptor(PersistenceExceptionTranslator pet) { 071 Assert.notNull(pet, "PersistenceExceptionTranslator must not be null"); 072 this.persistenceExceptionTranslator = pet; 073 } 074 075 /** 076 * Create a new PersistenceExceptionTranslationInterceptor, autodetecting 077 * PersistenceExceptionTranslators in the given BeanFactory. 078 * @param beanFactory the ListableBeanFactory to obtaining all 079 * PersistenceExceptionTranslators from 080 */ 081 public PersistenceExceptionTranslationInterceptor(ListableBeanFactory beanFactory) { 082 Assert.notNull(beanFactory, "ListableBeanFactory must not be null"); 083 this.beanFactory = beanFactory; 084 } 085 086 087 /** 088 * Specify the PersistenceExceptionTranslator to use. 089 * <p>Default is to autodetect all PersistenceExceptionTranslators 090 * in the containing BeanFactory, using them in a chain. 091 * @see #detectPersistenceExceptionTranslators 092 */ 093 public void setPersistenceExceptionTranslator(PersistenceExceptionTranslator pet) { 094 this.persistenceExceptionTranslator = pet; 095 } 096 097 /** 098 * Specify whether to always translate the exception ("true"), or whether throw the 099 * raw exception when declared, i.e. when the originating method signature's exception 100 * declarations allow for the raw exception to be thrown ("false"). 101 * <p>Default is "false". Switch this flag to "true" in order to always translate 102 * applicable exceptions, independent from the originating method signature. 103 * <p>Note that the originating method does not have to declare the specific exception. 104 * Any base class will do as well, even {@code throws Exception}: As long as the 105 * originating method does explicitly declare compatible exceptions, the raw exception 106 * will be rethrown. If you would like to avoid throwing raw exceptions in any case, 107 * switch this flag to "true". 108 */ 109 public void setAlwaysTranslate(boolean alwaysTranslate) { 110 this.alwaysTranslate = alwaysTranslate; 111 } 112 113 @Override 114 public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 115 if (this.persistenceExceptionTranslator == null) { 116 // No explicit exception translator specified - perform autodetection. 117 if (!(beanFactory instanceof ListableBeanFactory)) { 118 throw new IllegalArgumentException( 119 "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory"); 120 } 121 this.beanFactory = (ListableBeanFactory) beanFactory; 122 } 123 } 124 125 @Override 126 public void afterPropertiesSet() { 127 if (this.persistenceExceptionTranslator == null && this.beanFactory == null) { 128 throw new IllegalArgumentException("Property 'persistenceExceptionTranslator' is required"); 129 } 130 } 131 132 133 @Override 134 public Object invoke(MethodInvocation mi) throws Throwable { 135 try { 136 return mi.proceed(); 137 } 138 catch (RuntimeException ex) { 139 // Let it throw raw if the type of the exception is on the throws clause of the method. 140 if (!this.alwaysTranslate && ReflectionUtils.declaresException(mi.getMethod(), ex.getClass())) { 141 throw ex; 142 } 143 else { 144 if (this.persistenceExceptionTranslator == null) { 145 this.persistenceExceptionTranslator = detectPersistenceExceptionTranslators(this.beanFactory); 146 } 147 throw DataAccessUtils.translateIfNecessary(ex, this.persistenceExceptionTranslator); 148 } 149 } 150 } 151 152 /** 153 * Detect all PersistenceExceptionTranslators in the given BeanFactory. 154 * @param bf the ListableBeanFactory to obtain PersistenceExceptionTranslators from 155 * @return a chained PersistenceExceptionTranslator, combining all 156 * PersistenceExceptionTranslators found in the given bean factory 157 * @see ChainedPersistenceExceptionTranslator 158 */ 159 protected PersistenceExceptionTranslator detectPersistenceExceptionTranslators(ListableBeanFactory bf) { 160 // Find all translators, being careful not to activate FactoryBeans. 161 Map<String, PersistenceExceptionTranslator> pets = BeanFactoryUtils.beansOfTypeIncludingAncestors( 162 bf, PersistenceExceptionTranslator.class, false, false); 163 ChainedPersistenceExceptionTranslator cpet = new ChainedPersistenceExceptionTranslator(); 164 for (PersistenceExceptionTranslator pet : pets.values()) { 165 cpet.addDelegate(pet); 166 } 167 return cpet; 168 } 169 170}