/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jcr.txn; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.xa.XAResource; import org.modeshape.common.annotation.Immutable; import org.modeshape.common.annotation.ThreadSafe; /** * Transaction type returned by {@link LocalTransactionManager} * * @author Horia Chiorean (hchiorea@redhat.com) * @since 5.0 */ @Immutable @ThreadSafe public class LocalTransaction implements Transaction { private final List<Synchronization> synchronizations = new ArrayList<>(); private final AtomicInteger status; private final String id; protected LocalTransaction() { this.status = new AtomicInteger(Status.STATUS_ACTIVE); this.id = UUID.randomUUID().toString(); } @Override public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException { try { validateThreadOwnership(); if (markedForRollback()) { rollback(); throw new RollbackException("Transaction was marked for rollback"); } if (!isActive()) { throw new SystemException("Illegal transaction status:" + status); } synchronizations.forEach(Synchronization::beforeCompletion); status.set(Status.STATUS_COMMITTED); synchronizations.forEach((sync)->sync.afterCompletion(Status.STATUS_COMMITTED)); } finally { LocalTransactionManager.clear(); } } private void validateThreadOwnership() { LocalTransaction txForThread = LocalTransactionManager.hasActiveTransaction() ? LocalTransactionManager.transactionForThread() : null; if (!this.equals(txForThread)) { throw new IllegalStateException("Current thread does not own the transaction"); } } private boolean markedForRollback() { return status.get() == Status.STATUS_MARKED_ROLLBACK; } private boolean isActive() { return status.get() == Status.STATUS_ACTIVE; } @Override public void rollback() throws IllegalStateException, SystemException { try { validateThreadOwnership(); if (!isActive() && !markedForRollback()) { throw new SystemException("Illegal transaction status:" + status); } synchronizations.forEach(Synchronization::beforeCompletion); status.set(Status.STATUS_ROLLEDBACK); synchronizations.forEach((sync)->sync.afterCompletion(Status.STATUS_ROLLEDBACK)); } finally { LocalTransactionManager.clear(); } } @Override public void setRollbackOnly() throws IllegalStateException, SystemException { if (!isActive()) { throw new IllegalStateException("Current status is invalid" + status.get()); } this.status.compareAndSet(Status.STATUS_ACTIVE, Status.STATUS_MARKED_ROLLBACK); } @Override public int getStatus() throws SystemException { return status.get(); } @Override public boolean enlistResource(XAResource xaRes) throws RollbackException, IllegalStateException, SystemException { throw new UnsupportedOperationException("Local transactions do not support XA resources..."); } @Override public boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException, SystemException { throw new UnsupportedOperationException("Local transactions do not support XA resources..."); } @Override public void registerSynchronization(Synchronization sync) throws RollbackException, IllegalStateException, SystemException { synchronizations.add(sync); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } LocalTransaction that = (LocalTransaction) o; return Objects.equals(id, that.id); } @Override public int hashCode() { return Objects.hash(id); } }