View Javadoc
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 }