001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.spring.transaction;
016    
017    import com.liferay.portal.kernel.annotation.TransactionDefinition;
018    import com.liferay.portal.kernel.annotation.Transactional;
019    import com.liferay.portal.kernel.util.MethodTargetClassKey;
020    import com.liferay.portal.util.PropsValues;
021    
022    import java.lang.reflect.Method;
023    
024    import java.util.ArrayList;
025    import java.util.LinkedList;
026    import java.util.List;
027    import java.util.Map;
028    import java.util.Queue;
029    import java.util.concurrent.ConcurrentHashMap;
030    
031    import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
032    import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
033    import org.springframework.transaction.interceptor.RollbackRuleAttribute;
034    import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
035    import org.springframework.transaction.interceptor.TransactionAttribute;
036    import org.springframework.transaction.interceptor.TransactionAttributeSource;
037    
038    /**
039     * @author Shuyang Zhou
040     */
041    public class AnnotationTransactionAttributeSource
042            implements TransactionAttributeSource {
043    
044            @SuppressWarnings("rawtypes")
045            public TransactionAttribute getTransactionAttribute(
046                    Method method, Class targetClass) {
047    
048                    MethodTargetClassKey methodTargetClassKey = new MethodTargetClassKey(
049                            method, targetClass);
050    
051                    TransactionAttribute transactionAttribute = _transactionAttributes.get(
052                            methodTargetClassKey);
053    
054                    if (transactionAttribute != null) {
055                            if (transactionAttribute == _nullTransactionAttribute) {
056                                    return null;
057                            }
058                            else {
059                                    return transactionAttribute;
060                            }
061                    }
062    
063                    Queue<Class<?>> candidateQueue = new LinkedList<Class<?>>();
064    
065                    if (targetClass == null) {
066                            candidateQueue.offer(method.getDeclaringClass());
067                    }
068                    else {
069                            candidateQueue.offer(targetClass);
070                    }
071    
072                    Transactional transactional = _findTransactionAnnotation(
073                            method, candidateQueue);
074    
075                    transactionAttribute = _parseTransactionAnnotation(transactional);
076    
077                    if (transactionAttribute == null) {
078                            _transactionAttributes.put(
079                                    methodTargetClassKey, _nullTransactionAttribute);
080                    }
081                    else {
082                            _transactionAttributes.put(
083                                    methodTargetClassKey, transactionAttribute);
084                    }
085    
086                    return transactionAttribute;
087            }
088    
089            private Transactional _findTransactionAnnotation(
090                    Method method, Queue<Class<?>> candidateQueue) {
091    
092                    if (candidateQueue.isEmpty()) {
093                            return null;
094                    }
095    
096                    Transactional transactional = null;
097    
098                    Class<?> clazz = candidateQueue.poll();
099    
100                    try {
101                            Method specificMethod = clazz.getDeclaredMethod(
102                                    method.getName(), method.getParameterTypes());
103    
104                            transactional = specificMethod.getAnnotation(Transactional.class);
105    
106                            if (transactional != null) {
107                                    return transactional;
108                            }
109                    }
110                    catch (Exception e) {
111                    }
112    
113                    transactional = clazz.getAnnotation(Transactional.class);
114    
115                    if (transactional != null) {
116                            return transactional;
117                    }
118    
119                    _queueSuperTypes(clazz, candidateQueue);
120    
121                    return _findTransactionAnnotation(method, candidateQueue);
122            }
123    
124            private TransactionAttribute _parseTransactionAnnotation(
125                    Transactional transactional) {
126    
127                    if (transactional == null) {
128                            return null;
129                    }
130    
131                    RuleBasedTransactionAttribute ruleBasedTransactionAttribute =
132                            new RuleBasedTransactionAttribute();
133    
134                    int isolationLevel = transactional.isolation().value();
135    
136                    if (isolationLevel == TransactionDefinition.ISOLATION_PORTAL) {
137                            ruleBasedTransactionAttribute.setIsolationLevel(
138                                    PropsValues.TRANSACTION_ISOLATION_PORTAL);
139                    }
140                    else {
141                            ruleBasedTransactionAttribute.setIsolationLevel(isolationLevel);
142                    }
143    
144                    ruleBasedTransactionAttribute.setPropagationBehavior(
145                            transactional.propagation().value());
146                    ruleBasedTransactionAttribute.setReadOnly(transactional.readOnly());
147                    ruleBasedTransactionAttribute.setTimeout(transactional.timeout());
148    
149                    List<RollbackRuleAttribute> rollBackAttributes =
150                            new ArrayList<RollbackRuleAttribute>();
151    
152                    Class<?>[] rollbackFor = transactional.rollbackFor();
153    
154                    for (int i = 0; i < rollbackFor.length; i++) {
155                            RollbackRuleAttribute rollbackRuleAttribute =
156                                    new RollbackRuleAttribute(rollbackFor[i]);
157    
158                            rollBackAttributes.add(rollbackRuleAttribute);
159                    }
160    
161                    String[] rollbackForClassName = transactional.rollbackForClassName();
162    
163                    for (int i = 0; i < rollbackForClassName.length; i++) {
164                            RollbackRuleAttribute rollbackRuleAttribute =
165                                    new RollbackRuleAttribute(rollbackForClassName[i]);
166    
167                            rollBackAttributes.add(rollbackRuleAttribute);
168                    }
169    
170                    Class<?>[] noRollbackFor = transactional.noRollbackFor();
171    
172                    for (int i = 0; i < noRollbackFor.length; ++i) {
173                            NoRollbackRuleAttribute noRollbackRuleAttribute =
174                                    new NoRollbackRuleAttribute(noRollbackFor[i]);
175    
176                            rollBackAttributes.add(noRollbackRuleAttribute);
177                    }
178    
179                    String[] noRollbackForClassName =
180                            transactional.noRollbackForClassName();
181    
182                    for (int i = 0; i < noRollbackForClassName.length; ++i) {
183                            NoRollbackRuleAttribute noRollbackRuleAttribute =
184                                    new NoRollbackRuleAttribute(noRollbackForClassName[i]);
185    
186                            rollBackAttributes.add(noRollbackRuleAttribute);
187                    }
188    
189                    ruleBasedTransactionAttribute.getRollbackRules().addAll(
190                            rollBackAttributes);
191    
192                    return ruleBasedTransactionAttribute;
193            }
194    
195            private void _queueSuperTypes(
196                    Class<?> clazz, Queue<Class<?>> candidateQueue) {
197    
198                    Class<?> supperClass = clazz.getSuperclass();
199    
200                    if ((supperClass != null) && (supperClass != Object.class)) {
201                            candidateQueue.offer(supperClass);
202                    }
203    
204                    Class<?>[] interfaces = clazz.getInterfaces();
205    
206                    for (Class<?> inter : interfaces) {
207                            candidateQueue.offer(inter);
208                    }
209            }
210    
211            private static TransactionAttribute _nullTransactionAttribute =
212                    new DefaultTransactionAttribute();
213            private Map<MethodTargetClassKey, TransactionAttribute>
214                    _transactionAttributes =
215                            new ConcurrentHashMap<MethodTargetClassKey, TransactionAttribute>();
216    
217    }