001/*
002 * Copyright 2002-2018 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.transaction.annotation;
018
019import java.io.Serializable;
020import java.lang.reflect.AnnotatedElement;
021import java.lang.reflect.Method;
022import java.util.Collections;
023import java.util.LinkedHashSet;
024import java.util.Set;
025
026import org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource;
027import org.springframework.transaction.interceptor.TransactionAttribute;
028import org.springframework.util.Assert;
029import org.springframework.util.ClassUtils;
030
031/**
032 * Implementation of the
033 * {@link org.springframework.transaction.interceptor.TransactionAttributeSource}
034 * interface for working with transaction metadata in JDK 1.5+ annotation format.
035 *
036 * <p>This class reads Spring's JDK 1.5+ {@link Transactional} annotation and
037 * exposes corresponding transaction attributes to Spring's transaction infrastructure.
038 * Also supports JTA 1.2's {@link javax.transaction.Transactional} and EJB3's
039 * {@link javax.ejb.TransactionAttribute} annotation (if present).
040 * This class may also serve as base class for a custom TransactionAttributeSource,
041 * or get customized through {@link TransactionAnnotationParser} strategies.
042 *
043 * @author Colin Sampaleanu
044 * @author Juergen Hoeller
045 * @since 1.2
046 * @see Transactional
047 * @see TransactionAnnotationParser
048 * @see SpringTransactionAnnotationParser
049 * @see Ejb3TransactionAnnotationParser
050 * @see org.springframework.transaction.interceptor.TransactionInterceptor#setTransactionAttributeSource
051 * @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean#setTransactionAttributeSource
052 */
053@SuppressWarnings("serial")
054public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
055                implements Serializable {
056
057        private static final boolean jta12Present = ClassUtils.isPresent(
058                        "javax.transaction.Transactional", AnnotationTransactionAttributeSource.class.getClassLoader());
059
060        private static final boolean ejb3Present = ClassUtils.isPresent(
061                        "javax.ejb.TransactionAttribute", AnnotationTransactionAttributeSource.class.getClassLoader());
062
063        private final boolean publicMethodsOnly;
064
065        private final Set<TransactionAnnotationParser> annotationParsers;
066
067
068        /**
069         * Create a default AnnotationTransactionAttributeSource, supporting
070         * public methods that carry the {@code Transactional} annotation
071         * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
072         */
073        public AnnotationTransactionAttributeSource() {
074                this(true);
075        }
076
077        /**
078         * Create a custom AnnotationTransactionAttributeSource, supporting
079         * public methods that carry the {@code Transactional} annotation
080         * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
081         * @param publicMethodsOnly whether to support public methods that carry
082         * the {@code Transactional} annotation only (typically for use
083         * with proxy-based AOP), or protected/private methods as well
084         * (typically used with AspectJ class weaving)
085         */
086        public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
087                this.publicMethodsOnly = publicMethodsOnly;
088                this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(4);
089                this.annotationParsers.add(new SpringTransactionAnnotationParser());
090                if (jta12Present) {
091                        this.annotationParsers.add(new JtaTransactionAnnotationParser());
092                }
093                if (ejb3Present) {
094                        this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
095                }
096        }
097
098        /**
099         * Create a custom AnnotationTransactionAttributeSource.
100         * @param annotationParser the TransactionAnnotationParser to use
101         */
102        public AnnotationTransactionAttributeSource(TransactionAnnotationParser annotationParser) {
103                this.publicMethodsOnly = true;
104                Assert.notNull(annotationParser, "TransactionAnnotationParser must not be null");
105                this.annotationParsers = Collections.singleton(annotationParser);
106        }
107
108        /**
109         * Create a custom AnnotationTransactionAttributeSource.
110         * @param annotationParsers the TransactionAnnotationParsers to use
111         */
112        public AnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers) {
113                this.publicMethodsOnly = true;
114                Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
115                Set<TransactionAnnotationParser> parsers = new LinkedHashSet<TransactionAnnotationParser>(annotationParsers.length);
116                Collections.addAll(parsers, annotationParsers);
117                this.annotationParsers = parsers;
118        }
119
120        /**
121         * Create a custom AnnotationTransactionAttributeSource.
122         * @param annotationParsers the TransactionAnnotationParsers to use
123         */
124        public AnnotationTransactionAttributeSource(Set<TransactionAnnotationParser> annotationParsers) {
125                this.publicMethodsOnly = true;
126                Assert.notEmpty(annotationParsers, "At least one TransactionAnnotationParser needs to be specified");
127                this.annotationParsers = annotationParsers;
128        }
129
130
131        @Override
132        protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
133                return determineTransactionAttribute(clazz);
134        }
135
136        @Override
137        protected TransactionAttribute findTransactionAttribute(Method method) {
138                return determineTransactionAttribute(method);
139        }
140
141        /**
142         * Determine the transaction attribute for the given method or class.
143         * <p>This implementation delegates to configured
144         * {@link TransactionAnnotationParser TransactionAnnotationParsers}
145         * for parsing known annotations into Spring's metadata attribute class.
146         * Returns {@code null} if it's not transactional.
147         * <p>Can be overridden to support custom annotations that carry transaction metadata.
148         * @param element the annotated method or class
149         * @return the configured transaction attribute, or {@code null} if none was found
150         */
151        protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
152                if (element.getAnnotations().length > 0) {
153                        for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
154                                TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
155                                if (attr != null) {
156                                        return attr;
157                                }
158                        }
159                }
160                return null;
161        }
162
163        /**
164         * By default, only public methods can be made transactional.
165         */
166        @Override
167        protected boolean allowPublicMethodsOnly() {
168                return this.publicMethodsOnly;
169        }
170
171
172        @Override
173        public boolean equals(Object other) {
174                if (this == other) {
175                        return true;
176                }
177                if (!(other instanceof AnnotationTransactionAttributeSource)) {
178                        return false;
179                }
180                AnnotationTransactionAttributeSource otherTas = (AnnotationTransactionAttributeSource) other;
181                return (this.annotationParsers.equals(otherTas.annotationParsers) &&
182                                this.publicMethodsOnly == otherTas.publicMethodsOnly);
183        }
184
185        @Override
186        public int hashCode() {
187                return this.annotationParsers.hashCode();
188        }
189
190}