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 javax.inject.Singleton;
23  import javax.transaction.HeuristicMixedException;
24  import javax.transaction.HeuristicRollbackException;
25  import javax.transaction.NotSupportedException;
26  import javax.transaction.RollbackException;
27  import javax.transaction.Status;
28  import javax.transaction.SystemException;
29  import javax.transaction.UserTransaction;
30  import java.util.Arrays;
31  import java.util.HashSet;
32  import java.util.Set;
33  
34  import static org.apache.onami.persist.Preconditions.checkNotNull;
35  
36  /**
37   * Facade to the {@link javax.transaction.UserTransaction} which wraps all checked exception into runtime exceptions.
38   * Adds some convenience methods.
39   */
40  @Singleton
41  class UserTransactionFacade
42  {
43  
44      /**
45       * Transaction states in which only a rollback is possible
46       */
47      private static final Set<Integer> ROLLBACK_ONLY_STATES = new HashSet<Integer>(
48          Arrays.asList( Status.STATUS_MARKED_ROLLBACK, Status.STATUS_ROLLING_BACK, Status.STATUS_ROLLEDBACK ) );
49  
50      /**
51       * The wrapped user transaction.
52       */
53      private final UserTransaction txn;
54  
55      /**
56       * Constructor.
57       *
58       * @param txn the actual user transaction to wrap. Must not be {@code null}.
59       */
60      UserTransactionFacade( UserTransaction txn )
61      {
62          this.txn = checkNotNull( txn, "txn is mandatory!" );
63      }
64  
65      /**
66       * @see {@link javax.transaction.UserTransaction#begin()}.
67       */
68      void begin()
69      {
70          try
71          {
72              txn.begin();
73          }
74          catch ( NotSupportedException e )
75          {
76              throw new RuntimeException( "nested transactions are not supported by the user transaction " + txn, e );
77          }
78          catch ( SystemException e )
79          {
80              throw new RuntimeException( "unexpected error occurred", e );
81          }
82      }
83  
84      /**
85       * @see {@link javax.transaction.UserTransaction#commit()}.
86       */
87      void commit()
88      {
89          try
90          {
91              txn.commit();
92          }
93          catch ( SecurityException e )
94          {
95              throw new RuntimeException( "not allowed to commit the transaction", e );
96          }
97          catch ( IllegalStateException e )
98          {
99              throw new RuntimeException( "no transaction associated with userTransaction", e );
100         }
101         catch ( RollbackException e )
102         {
103             throw new RuntimeException( "rollback during commit", e );
104         }
105         catch ( HeuristicMixedException e )
106         {
107             throw new RuntimeException( "heuristic partial rollback during commit", e );
108         }
109         catch ( HeuristicRollbackException e )
110         {
111             throw new RuntimeException( "heuristic rollback during commit", e );
112         }
113         catch ( SystemException e )
114         {
115             throw new RuntimeException( "unexpected error occurred", e );
116         }
117     }
118 
119     /**
120      * @see {@link javax.transaction.UserTransaction#rollback()}.
121      */
122     void rollback()
123     {
124         try
125         {
126             txn.rollback();
127         }
128         catch ( IllegalStateException e )
129         {
130             throw new RuntimeException( "no transaction associated with userTransaction", e );
131         }
132         catch ( SecurityException e )
133         {
134             throw new RuntimeException( "not allowed to rollback the transaction", e );
135         }
136         catch ( SystemException e )
137         {
138             throw new RuntimeException( "unexpected error occurred", e );
139         }
140     }
141 
142     /**
143      * @see {@link javax.transaction.UserTransaction#setRollbackOnly()}.
144      */
145     void setRollbackOnly()
146     {
147         try
148         {
149             txn.setRollbackOnly();
150         }
151         catch ( IllegalStateException e )
152         {
153             throw new RuntimeException( "no transaction associated with userTransaction", e );
154         }
155         catch ( SystemException e )
156         {
157             throw new RuntimeException( "unexpected error occurred", e );
158         }
159     }
160 
161     /**
162      * @return {@code true} if this transaction may onl roll back. {@code false} otherwise.
163      */
164     boolean getRollbackOnly()
165     {
166         return ROLLBACK_ONLY_STATES.contains( getStatus() );
167     }
168 
169     /**
170      * @return {@code true} if there is already a transaction active. {@code false} otherwise.
171      */
172     boolean isActive()
173     {
174         return getStatus() != Status.STATUS_NO_TRANSACTION;
175     }
176 
177     /**
178      * Retries several times when the status is {@link Status#STATUS_UNKNOWN}.
179      * Will abort retrying after approximately one second.
180      *
181      * @see {@link javax.transaction.UserTransaction#getStatus()}.
182      */
183     private int getStatus()
184     {
185         try
186         {
187             int status = txn.getStatus();
188             for ( int i = 0; status == Status.STATUS_UNKNOWN && i < 8; i++ )
189             {
190                 try
191                 {
192                     Thread.sleep( ( 30L * i ) + 30L );
193                 }
194                 catch ( InterruptedException e )
195                 {
196                     // do nothing
197                 }
198                 status = txn.getStatus();
199             }
200             return status;
201         }
202         catch ( SystemException e )
203         {
204             throw new RuntimeException( "unexpected error occurred", e );
205         }
206     }
207 
208 }