All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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