KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > collections > TransactionRunner


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2000,2006 Oracle. All rights reserved.
5  *
6  * $Id: TransactionRunner.java,v 1.44 2006/12/04 18:47:40 cwl Exp $
7  */

8
9 package com.sleepycat.collections;
10
11 import com.sleepycat.compat.DbCompat;
12 import com.sleepycat.je.DatabaseException;
13 import com.sleepycat.je.DeadlockException;
14 import com.sleepycat.je.Environment;
15 import com.sleepycat.je.Transaction;
16 import com.sleepycat.je.TransactionConfig;
17 import com.sleepycat.util.ExceptionUnwrapper;
18
19 /**
20  * Starts a transaction, calls {@link TransactionWorker#doWork}, and handles
21  * transaction retry and exceptions. To perform a transaction, the user
22  * implements the {@link TransactionWorker} interface and passes an instance of
23  * that class to the {@link #run} method.
24  *
25  * <p>A single TransactionRunner instance may be used by any number of threads
26  * for any number of transactions.</p>
27  *
28  * <p>The behavior of the run() method depends on whether the environment is
29  * transactional, whether nested transactions are enabled, and whether a
30  * transaction is already active.</p>
31  *
32  * <ul>
33  * <li>When the run() method is called in a transactional environment and no
34  * transaction is active for the current thread, a new transaction is started
35  * before calling doWork(). If DeadlockException is thrown by doWork(), the
36  * transaction will be aborted and the process will be repeated up to the
37  * maximum number of retries. If another exception is thrown by doWork() or
38  * the maximum number of retries has occurred, the transaction will be aborted
39  * and the exception will be rethrown by the run() method. If no exception is
40  * thrown by doWork(), the transaction will be committed. The run() method
41  * will not attempt to commit or abort a transaction if it has already been
42  * committed or aborted by doWork().</li>
43  *
44  * <li>When the run() method is called and a transaction is active for the
45  * current thread, and nested transactions are enabled, a nested transaction is
46  * started before calling doWork(). The transaction that is active when
47  * calling the run() method will become the parent of the nested transaction.
48  * The nested transaction will be committed or aborted by the run() method
49  * following the same rules described above. Note that nested transactions may
50  * not be enabled for the JE product, since JE does not support nested
51  * transactions.</li>
52  *
53  * <li>When the run() method is called in a non-transactional environment, the
54  * doWork() method is called without starting a transaction. The run() method
55  * will return without committing or aborting a transaction, and any exceptions
56  * thrown by the doWork() method will be thrown by the run() method.</li>
57  *
58  * <li>When the run() method is called and a transaction is active for the
59  * current thread and nested transactions are not enabled (the default) the
60  * same rules as above apply. All the operations performed by the doWork()
61  * method will be part of the currently active transaction.</li>
62  * </ul>
63  *
64  * <p>In a transactional environment, the rules described above support nested
65  * calls to the run() method and guarantee that the outermost call will cause
66  * the transaction to be committed or aborted. This is true whether or not
67  * nested transactions are supported or enabled. Note that nested transactions
68  * are provided as an optimization for improving concurrency but do not change
69  * the meaning of the outermost transaction. Nested transactions are not
70  * currently supported by the JE product.</p>
71  *
72  * @author Mark Hayes
73  */

74 public class TransactionRunner {
75
76     /** The default maximum number of retries. */
77     public static final int DEFAULT_MAX_RETRIES = 10;
78
79     private CurrentTransaction currentTxn;
80     private int maxRetries;
81     private TransactionConfig config;
82     private boolean allowNestedTxn;
83
84     /**
85      * Creates a transaction runner for a given Berkeley DB environment.
86      * The default maximum number of retries ({@link #DEFAULT_MAX_RETRIES}) and
87      * a null (default) {@link TransactionConfig} will be used.
88      *
89      * @param env is the environment for running transactions.
90      */

91     public TransactionRunner(Environment env) {
92
93         this(env, DEFAULT_MAX_RETRIES, null);
94     }
95
96     /**
97      * Creates a transaction runner for a given Berkeley DB environment and
98      * with a given number of maximum retries.
99      *
100      * @param env is the environment for running transactions.
101      *
102      * @param maxRetries is the maximum number of retries that will be
103      * performed when deadlocks are detected.
104      *
105      * @param config the transaction configuration used for calling
106      * {@link Environment#beginTransaction}, or null to use the default
107      * configuration. The configuration object is not cloned, and
108      * any modifications to it will impact subsequent transactions.
109      */

110     public TransactionRunner(Environment env,
111                  int maxRetries,
112                              TransactionConfig config) {
113
114         this.currentTxn = CurrentTransaction.getInstance(env);
115         this.maxRetries = maxRetries;
116         this.config = config;
117     }
118
119     /**
120      * Returns the maximum number of retries that will be performed when
121      * deadlocks are detected.
122      */

123     public int getMaxRetries() {
124
125         return maxRetries;
126     }
127
128     /**
129      * Changes the maximum number of retries that will be performed when
130      * deadlocks are detected.
131      * Calling this method does not impact transactions already running.
132      */

133     public void setMaxRetries(int maxRetries) {
134
135         this.maxRetries = maxRetries;
136     }
137
138     /**
139      * Returns whether nested transactions will be created if
140      * <code>run()</code> is called when a transaction is already active for
141      * the current thread.
142      * By default this property is false.
143      *
144      * <p>Note that this method always returns false in the JE product, since
145      * nested transactions are not supported by JE.</p>
146      */

147     public boolean getAllowNestedTransactions() {
148
149         return allowNestedTxn;
150     }
151
152     /**
153      * Changes whether nested transactions will be created if
154      * <code>run()</code> is called when a transaction is already active for
155      * the current thread.
156      * Calling this method does not impact transactions already running.
157      *
158      * <p>Note that true may not be passed to this method in the JE product,
159      * since nested transactions are not supported by JE.</p>
160      */

161     public void setAllowNestedTransactions(boolean allowNestedTxn) {
162
163         if (allowNestedTxn && !DbCompat.NESTED_TRANSACTIONS) {
164             throw new UnsupportedOperationException JavaDoc(
165                     "Nested transactions are not supported.");
166         }
167         this.allowNestedTxn = allowNestedTxn;
168     }
169
170     /**
171      * Returns the transaction configuration used for calling
172      * {@link Environment#beginTransaction}.
173      *
174      * <p>If this property is null, the default configuration is used. The
175      * configuration object is not cloned, and any modifications to it will
176      * impact subsequent transactions.</p>
177      *
178      * @return the transaction configuration.
179      */

180     public TransactionConfig getTransactionConfig() {
181
182         return config;
183     }
184
185     /**
186      * Changes the transaction configuration used for calling
187      * {@link Environment#beginTransaction}.
188      *
189      * <p>If this property is null, the default configuration is used. The
190      * configuration object is not cloned, and any modifications to it will
191      * impact subsequent transactions.</p>
192      *
193      * @param config the transaction configuration.
194      */

195     public void setTransactionConfig(TransactionConfig config) {
196
197         this.config = config;
198     }
199
200     /**
201      * Calls the {@link TransactionWorker#doWork} method and, for transactional
202      * environments, may begin and end a transaction. If the environment given
203      * is non-transactional, a transaction will not be used but the doWork()
204      * method will still be called. See the class description for more
205      * information.
206      *
207      * @throws DeadlockException when it is thrown by doWork() and the
208      * maximum number of retries has occurred. The transaction will have been
209      * aborted by this method.
210      *
211      * @throws Exception when any other exception is thrown by doWork(). The
212      * exception will first be unwrapped by calling {@link
213      * ExceptionUnwrapper#unwrap}. The transaction will have been aborted by
214      * this method.
215      */

216     public void run(TransactionWorker worker)
217         throws DatabaseException, Exception JavaDoc {
218
219         if (currentTxn != null &&
220             (allowNestedTxn || currentTxn.getTransaction() == null)) {
221
222             /*
223              * Transactional and (not nested or nested txns allowed).
224              */

225             for (int i = 0;; i += 1) {
226                 Transaction txn = null;
227                 try {
228                     txn = currentTxn.beginTransaction(config);
229                     worker.doWork();
230                     if (txn != null && txn == currentTxn.getTransaction()) {
231                         currentTxn.commitTransaction();
232                     }
233                     return;
234                 } catch (Throwable JavaDoc e) {
235                     e = ExceptionUnwrapper.unwrapAny(e);
236                     if (txn != null && txn == currentTxn.getTransaction()) {
237                         try {
238                             currentTxn.abortTransaction();
239                         } catch (Throwable JavaDoc e2) {
240
241                             /*
242                              * XXX We should really throw a 3rd exception that
243                              * wraps both e and e2, to give the user a complete
244                              * set of error information.
245                              */

246                 if (DbCompat.TRANSACTION_RUNNER_PRINT_STACK_TRACES) {
247                 e2.printStackTrace();
248                 }
249                             /* Force the original exception to be thrown. */
250                             i = maxRetries + 1;
251                         }
252                     }
253                     if (i >= maxRetries || !(e instanceof DeadlockException)) {
254                         if (e instanceof Exception JavaDoc) {
255                             throw (Exception JavaDoc) e;
256                         } else {
257                             throw (Error JavaDoc) e;
258                         }
259                     }
260                 }
261             }
262         } else {
263
264             /*
265              * Non-transactional or (nested and no nested txns allowed).
266              */

267             try {
268                 worker.doWork();
269             } catch (Exception JavaDoc e) {
270                 throw ExceptionUnwrapper.unwrap(e);
271             }
272         }
273     }
274 }
275
Popular Tags