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

org.wildfly.transaction.client.RemoteTransaction Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * 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 java.net.URI;
import java.security.AccessController;
import java.security.GeneralSecurityException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;

import javax.net.ssl.SSLContext;
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 javax.transaction.xa.XAResource;

import org.wildfly.common.Assert;
import org.wildfly.security.auth.client.AuthenticationConfiguration;
import org.wildfly.security.auth.client.AuthenticationContext;
import org.wildfly.security.auth.client.AuthenticationContextConfigurationClient;
import org.wildfly.transaction.TransactionPermission;
import org.wildfly.transaction.client._private.Log;
import org.wildfly.transaction.client.spi.RemoteTransactionProvider;
import org.wildfly.transaction.client.spi.SimpleTransactionControl;

/**
 * @author David M. Lloyd
 */
public final class RemoteTransaction extends AbstractTransaction {
    private final AtomicReference stateRef;
    private final ConcurrentMap resources = new ConcurrentHashMap<>();
    private final AuthenticationContext authenticationContext;
    private final Object key = new Object();
    private final int timeout;

    static final AuthenticationContextConfigurationClient CLIENT = AccessController.doPrivileged(AuthenticationContextConfigurationClient.ACTION);

    RemoteTransaction(final AuthenticationContext authenticationContext, final int timeout) {
        this.authenticationContext = authenticationContext;
        stateRef = new AtomicReference<>(Unlocated.ACTIVE);
        this.timeout = timeout;
    }

    public Object getResource(final Object key) throws NullPointerException {
        Assert.checkNotNullParamWithNullPointerException("key", key);
        return resources.get(key);
    }

    public void putResource(final Object key, final Object value) throws NullPointerException {
        Assert.checkNotNullParamWithNullPointerException("key", key);
        if (value == null) resources.remove(key); else resources.put(key, value);
    }

    public Object putResourceIfAbsent(final Object key, final Object value) throws IllegalArgumentException {
        Assert.checkNotNullParamWithNullPointerException("key", key);
        return value == null ? resources.get(key) : resources.putIfAbsent(key, value);
    }

    Object getKey() {
        return key;
    }

    void suspend() {
        notifyAssociationListeners(false);
        // no operation
    }

    void resume() {
        notifyAssociationListeners(true);
        // no operation
    }

    void verifyAssociation() {
        // no operation
    }

    public int getTransactionTimeout() {
        return timeout;
    }

    public  T getProviderInterface(final Class providerInterfaceType) {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(TransactionPermission.forName("getProviderInterface"));
        }
        return stateRef.get().getProviderInterface(providerInterfaceType);
    }

    public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException {
        final AtomicReference stateRef = this.stateRef;
        try {
            stateRef.get().commit(stateRef);
        } catch (RollbackException re) {
            addRollbackExceptions(re);
            throw re;
        }
    }

    void commitAndDissociate() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException {
        try {
            commit();
        } finally {
            suspend();
        }
    }

    public void rollback() throws IllegalStateException, SystemException {
        final AtomicReference stateRef = this.stateRef;
        stateRef.get().rollback(stateRef);
    }

    void rollbackAndDissociate() throws IllegalStateException, SystemException {
        try {
            rollback();
        } finally {
            suspend();
        }
    }

    boolean importBacking() {
        return false;
    }

    void unimportBacking() {
    }

    public void setRollbackOnly() throws IllegalStateException, SystemException {
        super.setRollbackOnly();
        final AtomicReference stateRef = this.stateRef;
        stateRef.get().setRollbackOnly(stateRef);
    }

    public int getStatus() {
        return stateRef.get().getStatus();
    }

    public boolean enlistResource(final XAResource xaRes) {
        Assert.checkNotNullParam("xaRes", xaRes);
        return false;
    }

    public boolean delistResource(final XAResource xaRes, final int flag) {
        Assert.checkNotNullParam("xaRes", xaRes);
        return false;
    }

    public void registerSynchronization(final Synchronization sync) throws RollbackException, IllegalStateException, SystemException {
        Assert.checkNotNullParam("sync", sync);
        stateRef.get().registerSynchronization(new AssociatingSynchronization(sync));
    }

    void registerInterposedSynchronization(final Synchronization sync) throws IllegalStateException {
        Assert.checkNotNullParam("sync", sync);
        stateRef.get().registerInterposedSynchronization(new AssociatingSynchronization(sync));
    }

    public int hashCode() {
        return System.identityHashCode(this);
    }

    public boolean equals(final Object obj) {
        return obj == this;
    }

    public String toString() {
        return String.format("Remote transaction %s", stateRef.get());
    }

    /**
     * Get the location of this remote transaction.
     *
     * @return the location of this remote transaction, or {@code null} if it has no location as of yet
     */
    public URI getLocation() {
        return stateRef.get().getLocation();
    }

    /**
     * Attempt to set the location of this transaction, binding it to a remote transport provider.
     *
     * @param location the location to set (must not be {@code null})
     * @throws IllegalArgumentException if there is no provider for the given location (or it is {@code null}), or the
     *  security context was invalid
     * @throws IllegalStateException if the transaction is in an invalid state for setting its location
     * @throws SystemException if the transport could not begin the transaction
     */
    public void setLocation(URI location) throws IllegalArgumentException, IllegalStateException, SystemException {
        Assert.checkNotNullParam("location", location);
        final RemoteTransactionContext context = RemoteTransactionContext.getInstancePrivate();
        final RemoteTransactionProvider provider = context.getProvider(location);
        if (provider == null) {
            throw Log.log.noProviderForUri(location);
        }
        final AuthenticationContextConfigurationClient client = CLIENT;
        final AuthenticationConfiguration authenticationConfiguration = client.getAuthenticationConfiguration(location, authenticationContext, - 1, "jta", "jboss");
        final SSLContext sslContext;
        try {
            sslContext = client.getSSLContext(location, authenticationContext, "jta", "jboss");
        } catch (GeneralSecurityException e) {
            throw new IllegalArgumentException(e);
        }
        final SimpleTransactionControl control;
        if(stateRef.get() instanceof Located) {
            control = null;
        } else {
            control = provider.getPeerHandle(location, sslContext, authenticationConfiguration).begin(getEstimatedRemainingTime());
        }
        try {
            stateRef.get().join(stateRef, location, control);
        } catch (Throwable t) {
            try {
                control.rollback();
            } catch (Throwable t2) {
                t2.addSuppressed(t);
                throw t2;
            }
            throw t;
        }
    }

    /**
     * Attempt to clear the location set on this transaction, disassociating it
     * with the remote transport provider. There is only a limited time window
     * at which this can occur, before the transaction is ever used. Typically
     * this is only useful for retrying an initial invocation on a transaction.
     *
     * @return whether the attempt was successful
     */
    public boolean tryClearLocation() {
        return stateRef.get().tryToDisassociate(stateRef);
    }

    abstract static class State {
        State() {
        }

        abstract void join(AtomicReference stateRef, URI location, SimpleTransactionControl control) throws IllegalStateException;

        boolean tryToDisassociate(AtomicReference stateRef) {
            return false;
        }

        abstract void commit(AtomicReference stateRef) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException;

        abstract void rollback(AtomicReference stateRef) throws IllegalStateException, SystemException;

        abstract void setRollbackOnly(AtomicReference stateRef) throws IllegalStateException, SystemException;

        abstract int getStatus();

        abstract void registerSynchronization(Synchronization sync) throws RollbackException, IllegalStateException, SystemException;

        abstract void registerInterposedSynchronization(Synchronization sync) throws IllegalStateException;

         T getProviderInterface(final Class providerInterfaceType) {
            return null;
        }

        URI getLocation() {
            return null;
        }
    }

    abstract static class Unresolved extends State {

        Unresolved() {
        }

        void registerSynchronization(final Synchronization sync) throws RollbackException, IllegalStateException, SystemException {
            throw Log.log.registerSynchRemoteTransaction();
        }

        void registerInterposedSynchronization(final Synchronization sync) throws IllegalStateException {
            throw Log.log.registerSynchRemoteTransaction();
        }
    }

    static final class Unlocated extends Unresolved {
        private final int status;

        static final Unlocated ACTIVE = new Unlocated(Status.STATUS_ACTIVE);
        static final Unlocated ROLLBACK_ONLY = new Unlocated(Status.STATUS_MARKED_ROLLBACK);

        Unlocated(final int status) {
            this.status = status;
        }

        void join(final AtomicReference stateRef, final URI location, final SimpleTransactionControl control) throws IllegalStateException {
            State newState;
            switch (status) {
                case Status.STATUS_ACTIVE: newState = new Active(location, control); break;
                case Status.STATUS_MARKED_ROLLBACK: newState = new RollbackOnly(location, control); break;
                default: throw Assert.impossibleSwitchCase(status);
            }
            if (stateRef.compareAndSet(this, newState)) {
                return;
            } else {
                stateRef.get().join(stateRef, location, control);
            }
        }

        void commit(final AtomicReference stateRef) {
            stateRef.set(InactiveState.COMMITTED);
        }

        void rollback(final AtomicReference stateRef) throws IllegalStateException, SystemException {
            stateRef.set(InactiveState.ROLLED_BACK);
        }

        void setRollbackOnly(final AtomicReference stateRef) throws IllegalStateException, SystemException {
            stateRef.set(Unlocated.ROLLBACK_ONLY);
        }

        int getStatus() {
            return status;
        }

        boolean tryToDisassociate(AtomicReference stateRef) {
            return true;
        }
    }

    abstract static class Located extends Unresolved {
        final URI location;
        final SimpleTransactionControl control;
        volatile boolean multipleJoins = false;

        Located(final URI location, final SimpleTransactionControl control) {
            this.location = location;
            this.control = control;
        }

        void join(final AtomicReference stateRef, final URI location, final SimpleTransactionControl control) throws IllegalStateException {
            if (! this.location.equals(location)) {
                throw Log.log.locationAlreadyInitialized(location, this.location);
            }
            multipleJoins = true;
        }

        void rollback(final AtomicReference stateRef) throws IllegalStateException, SystemException {
            if (! stateRef.compareAndSet(this, InactiveState.ROLLING_BACK)) {
                stateRef.get().rollback(stateRef);
                return;
            }
            try {
                control.rollback();
            } catch (SecurityException e) {
                stateRef.set(this);
                throw e;
            } catch (Throwable t) {
                stateRef.set(InactiveState.UNKNOWN);
                throw t;
            }
            stateRef.set(InactiveState.ROLLED_BACK);
        }

         T getProviderInterface(final Class providerInterfaceType) {
            return control.getProviderInterface(providerInterfaceType);
        }

        URI getLocation() {
            return location;
        }
    }

    static final class Active extends Located {

        Active(final URI location, final SimpleTransactionControl control) {
            super(location, control);
        }

        void commit(final AtomicReference stateRef) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException {
            if (! stateRef.compareAndSet(this, InactiveState.COMMITTING)) {
                stateRef.get().commit(stateRef);
                return;
            }
            try {
                control.commit();
            } catch (SecurityException e) {
                stateRef.set(this);
                throw e;
            } catch (RollbackException | HeuristicRollbackException e) {
                stateRef.set(InactiveState.ROLLED_BACK);
                throw e;
            } catch (Throwable t) {
                stateRef.set(InactiveState.UNKNOWN);
                throw t;
            }
            stateRef.set(InactiveState.COMMITTED);
        }

        int getStatus() {
            return Status.STATUS_ACTIVE;
        }

        void setRollbackOnly(final AtomicReference stateRef) throws IllegalStateException, SystemException {
            synchronized (stateRef) {
                // there's no "marking rollback" state
                final RollbackOnly newState = new RollbackOnly(location, control);
                if (! stateRef.compareAndSet(this, newState)) {
                    stateRef.get().setRollbackOnly(stateRef);
                }
                control.setRollbackOnly();
            }
        }

        boolean tryToDisassociate(AtomicReference stateRef) throws IllegalStateException {
            return !multipleJoins && (stateRef.compareAndSet(this, Unlocated.ACTIVE) || stateRef.get().tryToDisassociate(stateRef));
        }
    }

    static final class RollbackOnly extends Located {
        RollbackOnly(final URI location, final SimpleTransactionControl control) {
            super(location, control);
        }

        void commit(final AtomicReference stateRef) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException {
            rollback(stateRef);
            throw Log.log.rollbackOnlyRollback();
        }

        void setRollbackOnly(final AtomicReference stateRef) {
            // no operation
        }

        int getStatus() {
            return Status.STATUS_MARKED_ROLLBACK;
        }

        void registerSynchronization(final Synchronization sync) throws RollbackException, IllegalStateException, SystemException {
            throw Log.log.markedRollbackOnly();
        }
    }

    static final class InactiveState extends State {
        private final int status;

        static final InactiveState ROLLED_BACK = new InactiveState(Status.STATUS_ROLLEDBACK);
        static final InactiveState COMMITTED = new InactiveState(Status.STATUS_COMMITTED);
        static final InactiveState UNKNOWN = new InactiveState(Status.STATUS_UNKNOWN);

        static final InactiveState COMMITTING = new InactiveState(Status.STATUS_COMMITTING);
        static final InactiveState ROLLING_BACK = new InactiveState(Status.STATUS_ROLLING_BACK);

        private InactiveState(final int status) {
            this.status = status;
        }

        void join(final AtomicReference stateRef, final URI location, final SimpleTransactionControl control) throws IllegalStateException {
            throw Log.log.notActive();
        }

        void commit(final AtomicReference stateRef) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException {
            throw Log.log.notActive();
        }

        void rollback(final AtomicReference stateRef) throws IllegalStateException, SystemException {
            throw Log.log.notActive();
        }

        void setRollbackOnly(final AtomicReference stateRef) throws IllegalStateException, SystemException {
            if (status != Status.STATUS_ROLLING_BACK) {
                throw Log.log.notActive();
            }
        }

        int getStatus() {
            return status;
        }

        void registerSynchronization(final Synchronization sync) throws RollbackException, IllegalStateException, SystemException {
            throw Log.log.notActive();
        }

        void registerInterposedSynchronization(final Synchronization sync) throws IllegalStateException {
            throw Log.log.notActive();
        }
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy