1 package org.apache.onami.persist;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import org.aopalliance.intercept.MethodInvocation;
23
24 import javax.inject.Inject;
25 import javax.inject.Singleton;
26 import java.lang.annotation.Annotation;
27
28 import static java.util.Arrays.asList;
29 import static org.apache.onami.persist.Preconditions.checkNotNull;
30
31 /**
32 * Helper class for working with {@link Transactional @Transactional} annotations.
33 */
34 @Singleton
35 class TransactionalAnnotationHelper
36 {
37
38 /**
39 * Annotation of the persistence unit.
40 */
41 private final Class<? extends Annotation> puAnnotation;
42
43 /**
44 * Reader for {@link Transactional @Transactional} annotations.
45 */
46 private final TransactionalAnnotationReader txnAnnoReader;
47
48 /**
49 * Constructor.
50 *
51 * @param annotationHolder Holder of teh annotation of the persistence unit.
52 * @param txnAnnoReader reader for {@link Transactional @Transactional} annotations.
53 */
54 @Inject
55 TransactionalAnnotationHelper( AnnotationHolder annotationHolder, TransactionalAnnotationReader txnAnnoReader )
56 {
57 this.puAnnotation = annotationHolder.getAnnotation();
58 this.txnAnnoReader = checkNotNull( txnAnnoReader, "txnAnnoReader is mandatory!" );
59 }
60
61 /**
62 * Decides if the current persistence unit participates in a transaction for the given method invocation.
63 * For a detailed description of when a persistence unit participates see the documentation at the
64 * {@link Transactional @Transactional} annotation.
65 *
66 * @param methodInvocation the method invocation which may be wrapped in a transaction.
67 * @return {@code true} if the current persistence unit participates in a transaction for the given method.
68 */
69 boolean persistenceUnitParticipatesInTransactionFor( MethodInvocation methodInvocation )
70 {
71 return puAnnotation == null || participates( methodInvocation );
72 }
73
74 /**
75 * Decides if the current persistence unit participates in a transaction for the given method invocation.
76 * The persistence unit has is annotated.
77 *
78 * @param methodInvocation the method invocation which may be wrapped in a transaction.
79 * @return {@code true} if the current persistence unit participates in a transaction for the given method.
80 */
81 private boolean participates( MethodInvocation methodInvocation )
82 {
83 final Transactional transactional = txnAnnoReader.readAnnotationFrom( methodInvocation );
84 final Class<? extends Annotation>[] onUnits = transactional.onUnits();
85 return isEmpty( onUnits ) || contains( onUnits, puAnnotation );
86 }
87
88 /**
89 * Returns {@code true} if the given array is empty.
90 *
91 * @param array the array to test for emptiness.
92 * @return {@code true} if the the given array has is {@code null} or has length 0.
93 */
94 private boolean isEmpty( Object[] array )
95 {
96 return array == null || array.length == 0;
97 }
98
99 /**
100 * Returns {@code true} if the given array contains the specified element.
101 *
102 * @param array the array in which to search for the specified element.
103 * @param key the element to look for.
104 * @return {@code true} if the given array contains the specified element.
105 */
106 private boolean contains( Object[] array, Object key )
107 {
108 return asList( array ).contains( key );
109 }
110
111 /**
112 * Decides if a rollback is necessary for the given method invocation and a thrown exception.
113 *
114 * @param methodInvocation the method invocation during which an exception was thrown.
115 * @param exc the exception which was thrown
116 * @return {@code true} if the transaction needs to be rolled back.
117 */
118 boolean isRollbackNecessaryFor( MethodInvocation methodInvocation, Throwable exc )
119 {
120 final Transactional transactional = txnAnnoReader.readAnnotationFrom( methodInvocation );
121 return isRollbackNecessaryFor( transactional, exc );
122 }
123
124 /**
125 * Decides if a rollback is necessary for the given transactional annotation and a thrown exception.
126 *
127 * @param transactional the transactional annotation of the method during which an exception was thrown.
128 * @param exc the exception which was thrown
129 * @return {@code true} if the transaction needs to be rolled back.
130 */
131 private boolean isRollbackNecessaryFor( Transactional transactional, Throwable exc )
132 {
133 return containsSuper( transactional.rollbackOn(), exc ) && !containsSuper( transactional.ignore(), exc );
134 }
135
136 /**
137 * Decides if the array of classes contains a super class of exc.
138 *
139 * @param classes the classes in which to look fore
140 * @param exc the class to search for
141 * @return {@code true} when the array contains a super class of exc.
142 */
143 private boolean containsSuper( Class<? extends Exception>[] classes, Throwable exc )
144 {
145 for ( Class<? extends Exception> c : classes )
146 {
147 if ( c.isInstance( exc ) )
148 {
149 return true;
150 }
151 }
152 return false;
153 }
154
155 }