2 * Copyright 2009 Electronic Business Systems Ltd.
4 * This file is part of GSS.
6 * GSS is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GSS is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GSS. If not, see <http://www.gnu.org/licenses/>.
19 package gr.ebs.gss.server.ejb;
21 import java.util.concurrent.Callable;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.Executors;
24 import java.util.concurrent.ScheduledExecutorService;
25 import java.util.concurrent.ScheduledFuture;
26 import java.util.concurrent.TimeUnit;
28 import javax.ejb.EJBTransactionRolledbackException;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
34 * A helper class that provides a method for repeatedly trying a transaction
35 * that is rolled back due to optimistic locking exceptions.
39 public class TransactionHelper<T> {
43 private static Log logger = LogFactory.getLog(TransactionHelper.class);
46 * Number of times to retry a transaction that was rolled back due to
49 private static final int TRANSACTION_RETRIES = 20;
52 * The minimum retry timeout in milliseconds.
54 private static final int MIN_TIMEOUT = 200;
57 * Execute the supplied command until it completes, ignoring transaction
58 * rollbacks. Try at least TRANSACTION_RETRIES times before giving up,
59 * each time waiting a random amount of time, using an exponential
60 * backoff scheme. See http://en.wikipedia.org/wiki/Exponential_backoff
63 * @param command the command to execute
64 * @return the value returned by the command
65 * @throws Exception any other exception thrown by the command
67 public T tryExecute(final Callable<T> command) throws Exception {
69 // Schedule a Future task to call the command after delay milliseconds.
71 ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
72 for (int i = 0; i < TRANSACTION_RETRIES; i++) {
74 ScheduledFuture<T> future = executor.schedule(new Callable<T>() {
77 public T call() throws Exception {
78 return command.call();
80 }, delay, TimeUnit.MILLISECONDS);
83 returnValue = future.get();
85 } catch (ExecutionException e) {
86 Throwable cause = e.getCause();
87 if (!(cause instanceof EJBTransactionRolledbackException) ||
88 retry == TRANSACTION_RETRIES - 1) {
89 logger.info("Transaction retry #" + (i+1) +
90 " failed due to " + cause);
91 executor.shutdownNow();
92 if (cause instanceof Exception)
93 throw (Exception) cause;
94 if (cause instanceof Error)
97 delay = MIN_TIMEOUT + (int) (MIN_TIMEOUT * Math.random() * (i + 1));
98 String origCause = cause.getCause() == null ?
99 cause.getClass().getName() :
100 cause.getCause().getClass().getName();
101 logger.info("Transaction retry #" + (i+1) + " scheduled in " + delay +
102 " msec due to " + origCause);
106 executor.shutdownNow();