org.wildfly.transaction.client.AbstractTransaction Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2016 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.wildfly.transaction.client;
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import jakarta.transaction.HeuristicMixedException;
import jakarta.transaction.HeuristicRollbackException;
import jakarta.transaction.RollbackException;
import jakarta.transaction.Status;
import jakarta.transaction.Synchronization;
import jakarta.transaction.SystemException;
import jakarta.transaction.Transaction;
import org.wildfly.common.Assert;
import org.wildfly.common.function.ExceptionBiConsumer;
import org.wildfly.common.function.ExceptionBiFunction;
import org.wildfly.common.function.ExceptionConsumer;
import org.wildfly.common.function.ExceptionFunction;
import org.wildfly.common.function.ExceptionObjIntConsumer;
import org.wildfly.common.function.ExceptionRunnable;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.common.function.ExceptionToIntBiFunction;
import org.wildfly.common.function.ExceptionToIntFunction;
import org.wildfly.transaction.TransactionPermission;
import org.wildfly.transaction.client._private.Log;
/**
* A managed transaction.
*
* @author David M. Lloyd
*/
public abstract class AbstractTransaction implements Transaction {
private static final TransactionPermission ASSOCIATION_LISTENER_PERMISSION = TransactionPermission.forName("registerAssociationListener");
private static final Object RB_EX_KEY = new Object();
private final Object outflowLock = new Object();
private final long start = System.nanoTime();
final List associationListeners = new CopyOnWriteArrayList<>();
AbstractTransaction() {
}
abstract void registerInterposedSynchronization(Synchronization synchronization) throws IllegalStateException;
public abstract Object getResource(Object key) throws NullPointerException;
public abstract void putResource(Object key, Object value) throws NullPointerException;
public abstract Object putResourceIfAbsent(Object key, Object value) throws IllegalArgumentException;
abstract Object getKey();
boolean getRollbackOnly() {
try {
return getStatus() == Status.STATUS_MARKED_ROLLBACK;
} catch (SystemException e) {
return false;
}
}
@SuppressWarnings("unchecked")
public void setRollbackOnly() throws IllegalStateException, SystemException {
List list = (List) getResource(RB_EX_KEY);
if (list == null) {
List appearing = (List) putResourceIfAbsent(RB_EX_KEY, list = new ArrayList<>());
if (appearing != null) {
list = appearing;
}
}
synchronized (list) {
list.add(Log.log.markedRollbackOnly());
}
}
@SuppressWarnings("unchecked")
void addRollbackExceptions(Exception ex) {
List list = (List) getResource(RB_EX_KEY);
if (list != null) {
for (Throwable throwable : list) {
ex.addSuppressed(throwable);
}
}
}
abstract void suspend() throws SystemException;
void notifyAssociationListeners(final boolean associated) {
for (AssociationListener associationListener : associationListeners) {
try {
associationListener.associationChanged(this, associated);
} catch (Throwable t) {
Log.log.tracef(t, "An association listener %s threw an exception on transaction %s", associationListener, this);
}
}
}
abstract void resume() throws SystemException;
abstract void verifyAssociation();
/**
* Get the transaction timeout that was in force when the transaction began.
*
* @return the transaction timeout
*/
public abstract int getTransactionTimeout();
/**
* Get an estimate of the amount of time remaining in this transaction.
*
* @return the estimate in seconds, or 0 if the transaction is estimated to have expired
*/
public final int getEstimatedRemainingTime() {
final long elapsed = System.nanoTime() - start;
final int transactionTimeout = getTransactionTimeout();
return transactionTimeout - (int) min(max(elapsed, 0L) / 1_000_000_000L, transactionTimeout);
}
/**
* Get a provider-specific interface from this transaction.
*
* @param providerInterfaceType the provider interface type class (must not be {@code null})
* @param the provider interface type
* @return the provider interface, or {@code null} if the given type isn't supported by this transaction's provider
*/
public T getProviderInterface(Class providerInterfaceType) {
return null;
}
/**
* Register an association listener for this transaction, which will be called any time this thread is suspended
* or resumed.
*
* @param associationListener the association listener (must not be {@code null})
*/
public void registerAssociationListener(AssociationListener associationListener) {
Assert.checkNotNullParam("associationListener", associationListener);
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(ASSOCIATION_LISTENER_PERMISSION);
}
associationListeners.add(associationListener);
}
public abstract void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException;
abstract void commitAndDissociate() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException;
public abstract void rollback() throws IllegalStateException, SystemException;
abstract void rollbackAndDissociate() throws IllegalStateException, SystemException;
interface SysExTry extends AutoCloseable {
void close() throws SystemException;
}
private static void doNothing() {}
private static SysExTry whileSuspended() throws SystemException {
final ContextTransactionManager tm = ContextTransactionManager.INSTANCE;
final AbstractTransaction suspended = tm.suspend();
return suspended == null ? AbstractTransaction::doNothing : () -> tm.resume(suspended);
}
private static SysExTry whileResumed(AbstractTransaction transaction) throws SystemException {
if (transaction == null) {
return AbstractTransaction::doNothing;
}
final ContextTransactionManager tm = ContextTransactionManager.INSTANCE;
tm.resume(transaction);
return tm::suspend;
}
public R performFunction(ExceptionBiFunction function, T param1, U param2) throws E, SystemException {
final ContextTransactionManager tm = ContextTransactionManager.INSTANCE;
if (Objects.equals(tm.getStateRef().get().transaction, this)) {
return function.apply(param1, param2);
}
try (SysExTry ignored = whileSuspended()) {
try (SysExTry ignored2 = whileResumed(this)) {
return function.apply(param1, param2);
}
}
}
public R performFunction(ExceptionFunction function, T param) throws E, SystemException {
return performFunction(ExceptionFunction::apply, function, param);
}
public R performSupplier(ExceptionSupplier function) throws E, SystemException {
return performFunction(ExceptionSupplier::get, function);
}
public void performConsumer(ExceptionObjIntConsumer consumer, T param1, int param2) throws E, SystemException {
final ContextTransactionManager tm = ContextTransactionManager.INSTANCE;
if (Objects.equals(tm.getStateRef().get().transaction, this)) {
consumer.accept(param1, param2);
return;
}
try (SysExTry ignored = whileSuspended()) {
try (SysExTry ignored2 = whileResumed(this)) {
consumer.accept(param1, param2);
}
}
}
public void performConsumer(ExceptionBiConsumer consumer, T param1, U param2) throws E, SystemException {
final ContextTransactionManager tm = ContextTransactionManager.INSTANCE;
if (Objects.equals(tm.getStateRef().get().transaction, this)) {
consumer.accept(param1, param2);
return;
}
try (SysExTry ignored = whileSuspended()) {
try (SysExTry ignored2 = whileResumed(this)) {
consumer.accept(param1, param2);
}
}
}
public void performConsumer(ExceptionConsumer consumer, T param) throws E, SystemException {
performConsumer(ExceptionConsumer::accept, consumer, param);
}
public int performToIntFunction(ExceptionToIntBiFunction function, T param1, U param2) throws E, SystemException {
final ContextTransactionManager tm = ContextTransactionManager.INSTANCE;
if (Objects.equals(tm.getStateRef().get().transaction, this)) {
return function.apply(param1, param2);
}
try (SysExTry ignored = whileSuspended()) {
try (SysExTry ignored2 = whileResumed(this)) {
return function.apply(param1, param2);
}
}
}
public int performToIntFunction(ExceptionToIntFunction function, T param) throws E, SystemException {
return performToIntFunction(ExceptionToIntFunction::apply, function, param);
}
public void performAction(ExceptionRunnable action) throws E, SystemException {
performConsumer((ExceptionConsumer, E>) ExceptionRunnable::run, action);
}
Object getOutflowLock() {
return outflowLock;
}
abstract boolean importBacking() throws SystemException;
abstract void unimportBacking();
final class AssociatingSynchronization implements Synchronization {
private final Synchronization sync;
AssociatingSynchronization(final Synchronization sync) {
this.sync = sync;
}
public void beforeCompletion() {
try {
if (importBacking()) try {
sync.beforeCompletion();
} finally {
unimportBacking();
} else {
performConsumer(Synchronization::beforeCompletion, sync);
}
} catch (SystemException e) {
throw new SynchronizationException(e);
}
}
public void afterCompletion(final int status) {
try {
if (importBacking()) try {
sync.afterCompletion(status);
} finally {
unimportBacking();
} else {
performConsumer(Synchronization::afterCompletion, sync, status);
}
} catch (SystemException e) {
throw new SynchronizationException(e);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy