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.dao.annotation;
018:
019: import java.lang.annotation.Annotation;
020:
021: import org.springframework.aop.framework.Advised;
022: import org.springframework.aop.framework.ProxyFactory;
023: import org.springframework.aop.support.AopUtils;
024: import org.springframework.beans.BeansException;
025: import org.springframework.beans.factory.BeanClassLoaderAware;
026: import org.springframework.beans.factory.BeanFactory;
027: import org.springframework.beans.factory.BeanFactoryAware;
028: import org.springframework.beans.factory.ListableBeanFactory;
029: import org.springframework.beans.factory.config.BeanPostProcessor;
030: import org.springframework.core.Ordered;
031: import org.springframework.stereotype.Repository;
032: import org.springframework.util.Assert;
033: import org.springframework.util.ClassUtils;
034:
035: /**
036: * Bean post-processor that automatically applies persistence exception
037: * translation to any bean that carries the
038: * {@link org.springframework.stereotype.Repository} annotation,
039: * adding a corresponding {@link PersistenceExceptionTranslationAdvisor}
040: * to the exposed proxy (either an existing AOP proxy or a newly generated
041: * proxy that implements all of the target's interfaces).
042: *
043: * <p>Translates native resource exceptions to Spring's
044: * {@link org.springframework.dao.DataAccessException} hierarchy.
045: * Autodetects beans that implement the
046: * {@link org.springframework.dao.support.PersistenceExceptionTranslator}
047: * interface, which are subsequently asked to translate candidate exceptions.
048: *
049: * <p>All of Spring's applicable resource factories implement the
050: * <code>PersistenceExceptionTranslator</code> interface out of the box.
051: * As a consequence, all that is usually needed to enable automatic exception
052: * translation is marking all affected beans (such as DAOs) with the
053: * <code>Repository</code> annotation, along with defining this post-processor
054: * as bean in the application context.
055: *
056: * @author Rod Johnson
057: * @author Juergen Hoeller
058: * @since 2.0
059: * @see PersistenceExceptionTranslationAdvisor
060: * @see org.springframework.stereotype.Repository
061: * @see org.springframework.dao.DataAccessException
062: * @see org.springframework.dao.support.PersistenceExceptionTranslator
063: */
064: public class PersistenceExceptionTranslationPostProcessor implements
065: BeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware,
066: Ordered {
067:
068: private Class<? extends Annotation> repositoryAnnotationType = Repository.class;
069:
070: private ClassLoader beanClassLoader = ClassUtils
071: .getDefaultClassLoader();
072:
073: private PersistenceExceptionTranslationAdvisor persistenceExceptionTranslationAdvisor;
074:
075: /**
076: * Set the 'repository' annotation type.
077: * The default required annotation type is the {@link Repository} annotation.
078: * <p>This setter property exists so that developers can provide their own
079: * (non-Spring-specific) annotation type to indicate that a class has a
080: * repository role.
081: * @param repositoryAnnotationType the desired annotation type
082: */
083: public void setRepositoryAnnotationType(
084: Class<? extends Annotation> repositoryAnnotationType) {
085: Assert.notNull(repositoryAnnotationType,
086: "'requiredAnnotationType' must not be null");
087: this .repositoryAnnotationType = repositoryAnnotationType;
088: }
089:
090: public void setBeanClassLoader(ClassLoader classLoader) {
091: this .beanClassLoader = classLoader;
092: }
093:
094: public void setBeanFactory(BeanFactory beanFactory)
095: throws BeansException {
096: if (!(beanFactory instanceof ListableBeanFactory)) {
097: throw new IllegalArgumentException(
098: "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory");
099: }
100: this .persistenceExceptionTranslationAdvisor = new PersistenceExceptionTranslationAdvisor(
101: (ListableBeanFactory) beanFactory,
102: this .repositoryAnnotationType);
103: }
104:
105: public int getOrder() {
106: // This should run after all other post-processors, so that it can just add
107: // an advisor to existing proxies rather than double-proxy.
108: return LOWEST_PRECEDENCE;
109: }
110:
111: public Object postProcessBeforeInitialization(Object bean,
112: String beanName) throws BeansException {
113: return bean;
114: }
115:
116: public Object postProcessAfterInitialization(Object bean,
117: String beanName) throws BeansException {
118: Class<?> targetClass = (bean instanceof Advised ? ((Advised) bean)
119: .getTargetSource().getTargetClass()
120: : bean.getClass());
121: if (targetClass == null) {
122: // Can't do much here
123: return bean;
124: }
125:
126: if (AopUtils.canApply(
127: this .persistenceExceptionTranslationAdvisor,
128: targetClass)) {
129: if (bean instanceof Advised) {
130: ((Advised) bean)
131: .addAdvisor(this .persistenceExceptionTranslationAdvisor);
132: return bean;
133: } else {
134: ProxyFactory pf = new ProxyFactory(bean);
135: pf
136: .addAdvisor(this .persistenceExceptionTranslationAdvisor);
137: return pf.getProxy(this .beanClassLoader);
138: }
139: } else {
140: // This is not a repository.
141: return bean;
142: }
143: }
144:
145: }
|