• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/db-4.7.25.NC/java/src/com/sleepycat/collections/
1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2000,2008 Oracle.  All rights reserved.
5 *
6 * $Id: TransactionRunner.java,v 12.10 2008/02/07 17:12:26 mark Exp $
7 */
8
9package com.sleepycat.collections;
10
11import com.sleepycat.compat.DbCompat;
12import com.sleepycat.db.DatabaseException;
13import com.sleepycat.db.DeadlockException;
14import com.sleepycat.db.Environment;
15import com.sleepycat.db.Transaction;
16import com.sleepycat.db.TransactionConfig;
17import 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 */
74public 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(
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 {
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 e) {
235                    e = ExceptionUnwrapper.unwrapAny(e);
236                    if (txn != null && txn == currentTxn.getTransaction()) {
237                        try {
238                            currentTxn.abortTransaction();
239                        } catch (Throwable e2) {
240
241                            /*
242                             * We print this stack trace so that the
243                             * information is not lost when we throw the
244                             * original exception.
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) {
255                            throw (Exception) e;
256                        } else {
257                            throw (Error) 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 e) {
270                throw ExceptionUnwrapper.unwrap(e);
271            }
272        }
273    }
274}
275