Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.multiverse.commitbarriers;
import org.multiverse.api.Transaction;
import org.multiverse.api.TransactionStatus;
import org.multiverse.api.exceptions.DeadTransactionException;
import org.multiverse.utils.StandardThreadFactory;
import org.multiverse.utils.TodoException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.lang.String.format;
/**
* A CommitBarrier is a blocking structure like the {@link java.util.concurrent.CyclicBarrier} but
* tailored to work with transactions. Based on this functionality, it is possible to create
* a 2-phase commit for example.
*
* @author Peter Veentjer.
*/
public abstract class CommitBarrier {
private static int corePoolSize = 5;
private static boolean runAsDaemon = true;
private final static ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(
corePoolSize, new StandardThreadFactory(Thread.NORM_PRIORITY, runAsDaemon));
private volatile ScheduledExecutorService executorService = EXECUTOR;
protected final Lock lock;
protected final Condition statusCondition;
private volatile Status status;
private volatile int numberWaiting = 0;
//for all non final non volatile variables; they only should be accessed while the lock is acquired.
private List onAbortTasks = new ArrayList();
private List onCommitTasks = new ArrayList();
/**
* Creates a new CommitBarrier.
*
* @param status the initial status of the CommitBarrier.
* @param fair if waking up threads is going to be fair.
* @throws NullPointerException if status is null.
*/
public CommitBarrier(Status status, boolean fair) {
if (status == null) {
throw new NullPointerException();
}
this.status = status;
this.lock = new ReentrantLock(fair);
this.statusCondition = lock.newCondition();
}
protected final Status getStatus() {
return status;
}
/**
* Returns the number of Transactions that have prepared and are waiting to commit. Value eventually
* becomes null after a commit or abort.
*
* @return the number of transactions prepared.
*/
public final int getNumberWaiting() {
return numberWaiting;
}
/**
* Checks if this CommitBarrier is closed. This is the initial status of the barrier.
*
* @return true if closed, false otherwise.
*/
public final boolean isClosed() {
return status == Status.Closed;
}
/**
* Checks if this CommitBarrier already is committed.
*
* @return true if committed, false otherwise.
*/
public final boolean isCommitted() {
return status == Status.Committed;
}
/**
* Checks if this CommitBarrier already is aborted.
*
* @return true if aborted, false otherwise.
*/
public final boolean isAborted() {
return status == Status.Aborted;
}
/**
* Only should be made when the lock is acquired.
*
* @return the List of onCommitTasks that needs to be executed (is allowed to be null).
*/
protected final List signalCommit() {
numberWaiting = 0;
status = Status.Committed;
statusCondition.signalAll();
onAbortTasks = null;
List result = onCommitTasks;
onCommitTasks = new ArrayList();
return result;
}
/**
* Only should be made when the lock is acquired.
*
* @return the List of onAbortTasks that needs to be executed (is allowed to be null).
*/
protected final List signalAborted() {
numberWaiting = 0;
status = Status.Aborted;
statusCondition.signalAll();
onCommitTasks = new ArrayList();
List result = onAbortTasks;
onAbortTasks = new ArrayList();
return result;
}
/**
* Aborts this CommitBarrier. If there are any prepared transactions that are waiting for this CommitBarrier
* to complete, they are aborted as well.
*
* If the CommitBarrier already is aborted, this call is ignored.
*
* @throws CommitBarrierOpenException if this CommitBarrier already is committed.
*/
public final void abort() {
List postAbortTasks = null;
lock.lock();
try {
switch (status) {
case Closed:
postAbortTasks = signalAborted();
break;
case Aborted:
return;
case Committed:
String commitMsg = "Can't abort already committed CommitBarrier";
throw new CommitBarrierOpenException(commitMsg);
default:
throw new IllegalStateException();
}
} finally {
lock.unlock();
}
executeTasks(postAbortTasks);
}
/**
* Executes the tasks. Can be called with a null argument.
*
* @param tasks the tasks to execute.
*/
protected static void executeTasks(List tasks) {
if(tasks == null){
return;
}
for (Runnable task : tasks) {
task.run();
}
}
/**
* Awaits for this barrier to open (commit or abort). This call doesn't influence the state of this
* CommitBarrier.
*
* @throws InterruptedException if the calling thread is interrupted while waiting.
*/
public final void awaitOpen() throws InterruptedException {
if (status != Status.Closed) {
return;
}
lock.lockInterruptibly();
try {
while (status == Status.Closed) {
statusCondition.await();
}
} finally {
lock.unlock();
}
}
/**
* Awaits for this barrier to open (commit or abort). This call doesn't influence the state of this
* CommitBarrier.
*
* This call is not responsive to interrupts.
*/
public final void awaitOpenUninterruptibly() {
if (status == Status.Closed) {
lock.lock();
try {
while (status == Status.Closed) {
statusCondition.awaitUninterruptibly();
}
} finally {
lock.unlock();
}
}
}
/**
* Waits for this barrier to open (abort or commit). This call doesn't influence the state of this
* CommitBarrier.
*
* @param timeout the maximum amount of time to wait for the barrier to close.
* @param unit the TimeUnit for the timeout argument.
* @return true if the wait was a success, false if the barrier still is closed.
* @throws InterruptedException if the thread is interrupted while waiting.
* @throws NullPointerException if unit is null.
*/
public final boolean tryAwaitOpen(long timeout, TimeUnit unit) throws InterruptedException {
if (unit == null) {
throw new NullPointerException();
}
if (status == Status.Closed) {
long timeoutNs = unit.toNanos(timeout);
lock.lockInterruptibly();
try {
while (status == Status.Closed) {
timeoutNs = statusCondition.awaitNanos(timeoutNs);
if (timeoutNs <= 0) {
return false;
}
}
} finally {
lock.unlock();
}
}
return true;
}
/**
* Tries to await the close of the barrier. This call doesn't influence the state of this
* CommitBarrier.
*
* This call is not responsive to interrupts.
*
* @param timeout the maximum amount of time to wait for the barrier to be closed.
* @param unit the timeunit for the timeout argument.
* @return true if the wait was a success, false otherwise.
*/
public final boolean tryAwaitOpenUninterruptibly(long timeout, TimeUnit unit) {
if (unit == null) {
throw new NullPointerException();
}
if (status == Status.Closed) {
long timeoutNs = unit.toNanos(timeout);
lock.lock();
try {
while (status == Status.Closed) {
timeoutNs = awaitNanosUninterruptible(timeoutNs);
if (timeoutNs <= 0) {
return false;
}
}
} finally {
lock.unlock();
}
}
return true;
}
private long awaitNanosUninterruptible(long timeoutNs) {
boolean restoreInterrupt = Thread.interrupted();
try {
while (true) {
long startNs = System.nanoTime();
try {
return statusCondition.awaitNanos(timeoutNs);
} catch (InterruptedException ex) {
timeoutNs -= (System.nanoTime() - startNs);
restoreInterrupt = true;
}
}
} finally {
//restore interrupt if needed
if (restoreInterrupt) {
Thread.currentThread().interrupt();
}
}
}
/**
* Sets the ScheduledExecutorService to be used by this CommitBarrier for the timeout. This method can always
* be called no matter the state of the CommitBarrier.
*
* @param executorService the ScheduledExecutorService this CommitBarrier is going to use for timeout.
* @throws NullPointerException if executorService is null.
*/
public void setScheduledExecutorService(ScheduledExecutorService executorService) {
if (executorService == null) {
throw new NullPointerException();
}
this.executorService = executorService;
}
/**
* Sets the timeout on this CommitBarrier. If the barrier hasn't committed/aborted before the timeout
* it automatically is aborted. This is a function that typically is used when initializing the CommitBarrier.
*
* The timeout starts running when this method is called.
*
* @param timeout the maximum amount of time this barrier is allowed to run.
* @param unit the TimeUnit of the timeout parameter.
* @throws NullPointerException if unit is null.
* @throws CommitBarrierOpenException if the CommitBarrier already is aborted or committed.
*/
public final void setTimeout(long timeout, TimeUnit unit) {
lock.lock();
try {
switch (status) {
case Closed:
Runnable command = new Runnable() {
@Override
public void run() {
try {
abort();
} catch (IllegalStateException ignore) {
}
}
};
executorService.schedule(command, timeout, unit);
break;
case Committed:
String commitMsg = "Can't set a timeout on an already commit CommitBarrier.";
throw new CommitBarrierOpenException(commitMsg);
case Aborted:
String abortMsg = "Can't set a timeout on an already aborted CommitBarrier.";
throw new CommitBarrierOpenException(abortMsg);
default:
throw new IllegalStateException();
}
} finally {
lock.unlock();
}
}
/**
* Registers a task that is executed once the CommitBarrier aborts.
*
* The task will be executed after the abort and will be executed by the thread that does the actual abort.
*
* The tasks will be executed in the order they are registered and will be executed at most once. If one of the
* tasks throws a RuntimeException, the following will not be executed.
*
* @param task the task that is executed once the CommitBarrier commits.
* @throws NullPointerException if task is null.
* @throws CommitBarrierOpenException if this CommitBarrier already is aborted or committed.
*/
public final void registerOnAbortTask(Runnable task) {
lock.lock();
try {
switch (status) {
case Closed:
if (task == null) {
throw new NullPointerException();
}
if (onAbortTasks == null) {
onAbortTasks = new LinkedList();
}
onAbortTasks.add(task);
break;
case Committed:
String commitMsg = "Can't register on abort task on already committed CommitBarrier";
throw new CommitBarrierOpenException(commitMsg);
case Aborted:
String abortMsg = "Can't register on abort task on already aborted CommitBarrier";
throw new CommitBarrierOpenException(abortMsg);
default:
throw new IllegalStateException();
}
} finally {
lock.unlock();
}
}
/**
* Registers a task that is executed once the CommitBarrier commits.
*
* The task will be executed after the commit and will be executed by the thread that does the actual commit.
*
* The tasks will be executed in the order they are registered and will be executed at most once. If one of the
* tasks throws a RuntimeException, the following will not be executed.
*
* @param task the task that is executed once the CommitBarrier commits.
* @throws NullPointerException if task is null.
* @throws CommitBarrierOpenException if this CommitBarrier already is aborted or committed.
*/
public final void registerOnCommitTask(Runnable task) {
lock.lock();
try {
switch (status) {
case Closed:
if (task == null) {
throw new NullPointerException();
}
if (onCommitTasks == null) {
onCommitTasks = new LinkedList();
}
onCommitTasks.add(task);
break;
case Committed:
String commitMsg = "Can't register on commit task on already committed CommitBarrier";
throw new CommitBarrierOpenException(commitMsg);
case Aborted:
String abortMsg = "Can't register on commit task on already aborted CommitBarrier";
throw new CommitBarrierOpenException(abortMsg);
default:
throw new IllegalStateException();
}
} finally {
lock.unlock();
}
}
/**
* Adds a waiters.
*
* Should only be called when the main lock is acquired.
*
* @throws IllegalStateException if the transaction isn't closed.
*/
protected final void addJoiner() {
if (status != Status.Closed) {
throw new IllegalStateException();
}
numberWaiting++;
}
/**
* Finishes a Transaction.
*
* Can be called without the mainlock is acquired.
*
* @param tx the transaction to finish
*/
protected final void finish(Transaction tx) {
if (tx == null) {
return;
}
if (isCommitted()) {
tx.commit();
} else if (isAborted()) {
tx.abort();
//todo: better message
throw new IllegalStateException();
}
}
/**
* Ensures that a transaction is not dead.
*
* Can be called without the mainlock is acquired.
*
* @param tx the transaction to check.
* @throws DeadTransactionException if tx is dead.
* @throws NullPointerException if tx is null.
*/
protected final void ensureNotDead(Transaction tx) {
if (tx == null) {
throw new NullPointerException();
}
TransactionStatus status = tx.getStatus();
if (status.isDead()) {
throw new DeadTransactionException();
}
}
/**
* Joins this CommitBarrier with the provided transaction. If the CommitBarrier can't commit yet, the method
* will block.
*
* If the CommitBarrier already is aborted or committed, the transaction is aborted.
*
* This method is responsive to interrupts. If the waiting thread is interrupted, it will abort itself and
* this CommitGroup.
*
* @param tx the Transaction to commit.
* @throws InterruptedException if the thread is interrupted while waiting.
* @throws NullPointerException if tx is null.
* @throws DeadTransactionException if tx is committed/aborted.
* @throws CommitBarrierOpenException if this VetoCommitBarrier is committed or aborted.
*/
public void joinCommit(Transaction tx) throws InterruptedException {
ensureNotDead(tx);
List tasks = null;
lock.lock();
try {
switch (getStatus()) {
case Closed:
tx.prepare();
addJoiner();
if (isLastParty()) {
tasks = signalCommit();
} else {
while (getStatus() == Status.Closed) {
try {
statusCondition.await();
} catch (InterruptedException ex) {
signalAborted();
tx.abort();
throw ex;
}
}
}
break;
case Committed:
String committedMsg = format("Can't await commit on already committed VetoCommitBarrier " +
"with transaction %s", tx.getConfiguration().getFamilyName());
throw new CommitBarrierOpenException(committedMsg);
case Aborted:
String abortMsg = format("Can't await commit on already aborted VetoCommitBarrier " +
"with transaction %s", tx.getConfiguration().getFamilyName());
throw new CommitBarrierOpenException(abortMsg);
default:
throw new IllegalStateException();
}
} finally {
lock.unlock();
}
//todo: the the thread is interrupted, tx is not aborted.
finish(tx);
executeTasks(tasks);
}
/**
* Joins this CommitBarrier with the provided transaction. If the CommitBarrier can't commit yet, this
* method will block without being interruptible.
*
* If the CommitBarrier already is aborted or committed, the transaction is aborted.
*
* @param tx the Transaction to join in the commit.
* @throws NullPointerException if tx is null.
* @throws DeadTransactionException if tx is committed/aborted.
* @throws CommitBarrierOpenException if this VetoCommitBarrier is committed or aborted.
*/
public void joinCommitUninterruptibly(Transaction tx) {
ensureNotDead(tx);
List postCommitTasks = new ArrayList();
lock.lock();
try {
switch (getStatus()) {
case Closed:
tx.prepare();
addJoiner();
if (isLastParty()) {
postCommitTasks = signalCommit();
} else {
while (getStatus() == Status.Closed) {
statusCondition.awaitUninterruptibly();
}
}
break;
case Aborted:
tx.abort();
String abortedMsg = format("Can't call joinCommitUninterruptible on already aborted " +
"CountDownCommitBarrier with transaction %s ", tx.getConfiguration().getFamilyName());
throw new CommitBarrierOpenException(abortedMsg);
case Committed:
tx.abort();
String commitMsg = format("Can't call joinCommitUninterruptible on already committed " +
"CountDownCommitBarrier with transaction %s ", tx.getConfiguration().getFamilyName());
throw new CommitBarrierOpenException(commitMsg);
default:
throw new IllegalStateException();
}
} finally {
lock.unlock();
}
finish(tx);
executeTasks(postCommitTasks);
}
/**
* Tries to joins this CommitBarrier with the provided transaction. If the CommitBarrier can't commit yet, the
* transaction and CommitBarrier will be aborted. So this method will not block (for a long period).
*
* If the CommitBarrier already is aborted or committed, the transaction is aborted.
*
* @param tx the Transaction that wants to join the other parties to commit with.
* @return true if CountDownCommitBarrier was committed, false if aborted.
* @throws CommitBarrierOpenException if tx or this CountDownCommitBarrier is aborted or committed.
* @throws NullPointerException if tx is null.
*/
public boolean tryJoinCommit(Transaction tx) {
ensureNotDead(tx);
List postCommitTasks = null;
boolean abort = true;
lock.lock();
try {
try {
switch (getStatus()) {
case Closed:
tx.prepare();
addJoiner();
if (isLastParty()) {
postCommitTasks = signalCommit();
abort = false;
} else {
postCommitTasks = signalAborted();
}
break;
case Aborted:
String abortMsg = format("Can't call tryJoinCommit on already aborted " +
"CountDownCommitBarrier with transaction %s ", tx.getConfiguration().getFamilyName());
throw new CommitBarrierOpenException(abortMsg);
case Committed:
String commitMsg = format("Can't call tryJoinCommit on already committed " +
"CountDownCommitBarrier with transaction %s ", tx.getConfiguration().getFamilyName());
throw new CommitBarrierOpenException(commitMsg);
default:
throw new IllegalStateException();
}
} finally {
lock.unlock();
}
} finally {
if (abort) {
tx.abort();
} else {
tx.commit();
}
}
executeTasks(postCommitTasks);
return isCommitted();
}
/**
* Tries to joins this CommitBarrier with the provided transaction. If the CommitBarrier can't commit yet, this call
* will block until one of the following things happens:
*
*
the CommitBarrier is committed before timeing out: the transaction also is committed
*
the CommitBarrier is aborted before timeing out: the transaction also is aborted
*
the thread is interrupted: the transaction and commit barrier also is aborted
*
the thread times out: the transaction and commit barrier are aborted
*
*
* If the CommitBarrier already is aborted or committed, the transaction is aborted.
*
* This method is responsive to interrupts. If the waiting thread is interrupted, it will abort itself and
* this CommitBarrier.
*
* @param tx the Transaction that wants to join the other parties to commit with.
* @param timeout the maximum time to wait.
* @param unit the TimeUnit for the timeout argument.
* @return true if CountDownCommitBarrier was committed, false if aborted.
* @throws CommitBarrierOpenException if tx or this CountDownCommitBarrier is aborted or committed.
* @throws NullPointerException if tx or unit is null is null.
* @throws InterruptedException if the calling thread is interrupted while waiting.
*/
public boolean tryJoinCommit(Transaction tx, long timeout, TimeUnit unit) throws InterruptedException {
ensureNotDead(tx);
long timeoutNs = unit.toNanos(timeout);
List postCommitTasks = null;
lock.lock();
try {
switch (getStatus()) {
case Closed:
tx.prepare();
addJoiner();
if (isLastParty()) {
postCommitTasks = signalCommit();
} else {
while (getStatus() == Status.Closed) {
try {
timeoutNs = statusCondition.awaitNanos(timeoutNs);
if (timeoutNs <= 0) {
signalAborted();
tx.abort();
return false;
}
} catch (InterruptedException ex) {
signalAborted();
tx.abort();
//for the time being.. needs to be replaced with a really uninterruptible version
throw ex;
}
}
}
break;
case Committed:
String commitMsg = "Can't await commit on an already committed VetoCommitBarrier";
throw new CommitBarrierOpenException(commitMsg);
case Aborted:
String abortMsg = "Can't await commit on an already aborted VetoCommitBarrier";
throw new CommitBarrierOpenException(abortMsg);
default:
throw new NullPointerException();
}
} finally {
lock.unlock();
}
finish(tx);
executeTasks(postCommitTasks);
return true;
}
/**
* Tries to joins this CommitBarrier with the provided transaction. If the CommitBarrier can't commit yet, this call
* will block until one of the following things happens:
*
*
the CommitBarrier is committed
*
the CommitBarrier is aborted
*
*
* If the CommitBarrier already is aborted or committed, the transaction is aborted.
*
* This method is not responsive to interrupts.
*
* @param tx the Transaction that wants to join the other parties to commit with.
* @param timeout the maximum time to wait.
* @param unit the TimeUnit for the timeout argument.
* @return true if CountDownCommitBarrier was committed, false if aborted.
* @throws CommitBarrierOpenException if tx or this CountDownCommitBarrier is aborted or committed.
* @throws NullPointerException if tx or unit is null is null.
*/
public boolean tryJoinCommitUninterruptibly(Transaction tx, long timeout, TimeUnit unit) {
ensureNotDead(tx);
long timeoutNs = unit.toNanos(timeout);
lock.lock();
try {
switch (getStatus()) {
case Closed:
tx.prepare();
addJoiner();
while (getStatus() == Status.Closed) {
try {
timeoutNs = statusCondition.awaitNanos(timeoutNs);
if (timeoutNs <= 0) {
signalAborted();
tx.abort();
return false;
}
} catch (InterruptedException ex) {
signalAborted();
tx.abort();
//for the time being.. needs to be replaced with a really uninterruptible version
throw new RuntimeException(ex);
}
}
break;
case Committed:
String commitMsg = "Can't await commit on an already committed VetoCommitBarrier";
throw new CommitBarrierOpenException(commitMsg);
case Aborted:
String abortMsg = "Can't await commit on an already aborted VetoCommitBarrier";
throw new CommitBarrierOpenException(abortMsg);
default:
throw new NullPointerException();
}
} finally {
lock.unlock();
}
finish(tx);
throw new TodoException();
}
protected abstract boolean isLastParty();
enum Status {
Closed, Committed, Aborted
}
}