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.MethodInterceptor;
23  import org.aopalliance.intercept.MethodInvocation;
24  
25  import javax.inject.Inject;
26  
27  /**
28   * Interceptor for methods and classes annotated with {@link Transactional @Transactional} annotation.
29   */
30  class TxnInterceptor
31      implements MethodInterceptor
32  {
33  
34      /**
35       * Unit of work.
36       */
37      private UnitOfWork unitOfWork;
38  
39      /**
40       * Factory for {@link TransactionFacade}.
41       */
42      private TransactionFacadeFactory tfProvider;
43  
44      /**
45       * Helper for working with the concrete transactional annotations on methods and classes.
46       */
47      private TransactionalAnnotationHelper txnAnnotationHelper;
48  
49      @Inject
50      @VisibleForTesting
51      void init( UnitOfWork unitOfWork, TransactionFacadeFactory tfProvider,
52                 TransactionalAnnotationHelper txnAnnotationHelper )
53      {
54          this.unitOfWork = unitOfWork;
55          this.tfProvider = tfProvider;
56          this.txnAnnotationHelper = txnAnnotationHelper;
57      }
58  
59      /**
60       * {@inheritDoc}
61       */
62      // @Override
63      public final Object invoke( MethodInvocation methodInvocation )
64          throws Throwable
65      {
66          if ( persistenceUnitParticipatesInTransactionFor( methodInvocation ) )
67          {
68              return invokeInTransactionAndUnitOfWork( methodInvocation );
69          }
70          else
71          {
72              return methodInvocation.proceed();
73          }
74  
75      }
76  
77      /**
78       * Decides if the current persistence unit participates in a transaction for the given method invocation.
79       * For a detailed description of when a persistence unit participates see the documentation at the
80       * {@link Transactional @Transactional} annotation.
81       *
82       * @param methodInvocation the method invocation which may be wrapped in a transaction.
83       * @return {@code true} if the current persistence unit participates in a transaction for the given method.
84       */
85      private boolean persistenceUnitParticipatesInTransactionFor( MethodInvocation methodInvocation )
86      {
87          return txnAnnotationHelper.persistenceUnitParticipatesInTransactionFor( methodInvocation );
88      }
89  
90      /**
91       * Invokes the original method within a unit of work and a transaction.
92       *
93       * @param methodInvocation the method to be executed within the transaction
94       * @return the result of the invocation of the original method.
95       * @throws Throwable if an exception occurs during the call to the original method.
96       */
97      private Object invokeInTransactionAndUnitOfWork( MethodInvocation methodInvocation )
98          throws Throwable
99      {
100         final boolean weStartedTheUnitOfWork = !unitOfWork.isActive();
101         if ( weStartedTheUnitOfWork )
102         {
103             unitOfWork.begin();
104         }
105 
106         Throwable originalException = null;
107         try
108         {
109             return invokeInTransaction( methodInvocation );
110         }
111         catch ( Throwable exc )
112         {
113             originalException = exc;
114             throw exc;
115         }
116         finally
117         {
118             if ( weStartedTheUnitOfWork )
119             {
120                 endUnitOfWork( originalException );
121             }
122         }
123     }
124 
125     /**
126      * Ends the unit of work. If an exception occurs while ending the unit of work it is neglected in preference of an
127      * original exception.
128      *
129      * @param originalException the original exception. will be thrown in preference to an exception occurring during
130      *                          execution of this method.
131      * @throws Throwable if an exception happened while ending the unit of work.
132      */
133     private void endUnitOfWork( Throwable originalException )
134         throws Throwable
135     {
136         try
137         {
138             unitOfWork.end();
139         }
140         catch ( Throwable exc )
141         {
142             if ( originalException != null )
143             {
144                 throw originalException;
145             }
146             else
147             {
148                 throw exc;
149             }
150         }
151     }
152 
153     /**
154      * Invoke the original method within a transaction.
155      *
156      * @param methodInvocation the original method invocation.
157      * @return the result of the invocation of the original method.
158      * @throws Throwable if an exception occurs during the call to the original method.
159      */
160     private Object invokeInTransaction( MethodInvocation methodInvocation )
161         throws Throwable
162     {
163         final TransactionFacade transactionFacade = tfProvider.createTransactionFacade();
164         transactionFacade.begin();
165         final Object result = invokeAndHandleException( methodInvocation, transactionFacade );
166         transactionFacade.commit();
167 
168         return result;
169     }
170 
171     /**
172      * Invoke the original method assuming a transaction has already been started.
173      * This method is responsible of calling rollback if necessary.
174      *
175      * @param methodInvocation  the original method invocation.
176      * @param transactionFacade the facade to the underlying resource local or jta transaction.
177      * @return the result of the invocation of the original method.
178      * @throws Throwable if an exception occurs during the call to the original method.
179      */
180     private Object invokeAndHandleException( MethodInvocation methodInvocation, TransactionFacade transactionFacade )
181         throws Throwable
182     {
183         try
184         {
185             return methodInvocation.proceed();
186         }
187         catch ( Throwable exc )
188         {
189             handleException( methodInvocation, transactionFacade, exc );
190             throw exc;
191         }
192     }
193 
194     /**
195      * Handles the case that an exception was thrown by the original method.
196      *
197      * @param methodInvocation  the original method invocation.
198      * @param transactionFacade the facade to the underlying resource local or jta transaction.
199      * @param exc               the exception thrown by the original method.
200      */
201     private void handleException( MethodInvocation methodInvocation, TransactionFacade transactionFacade,
202                                   Throwable exc )
203         throws Throwable
204     {
205         try
206         {
207             if ( isRollbackNecessaryFor( methodInvocation, exc ) )
208             {
209                 transactionFacade.rollback();
210             }
211             else
212             {
213                 transactionFacade.commit();
214             }
215         }
216         catch ( Exception swallowedException )
217         {
218             // swallow exception from transaction facade in favor of th exception thrown by the original method.
219             throw exc;
220         }
221     }
222 
223     /**
224      * Decides if a rollback is necessary for the given method invocation and a thrown exception.
225      *
226      * @param methodInvocation the method invocation during which an exception was thrown.
227      * @param exc              the exception which was thrown
228      * @return {@code true} if the transaction needs to be rolled back.
229      */
230     private boolean isRollbackNecessaryFor( MethodInvocation methodInvocation, Throwable exc )
231     {
232         return txnAnnotationHelper.isRollbackNecessaryFor( methodInvocation, exc );
233     }
234 
235 }