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

org.wildfly.transaction.client.RemoteTransactionContext 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.security.AccessController.doPrivileged;

import java.net.URI;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;

import jakarta.transaction.RollbackException;
import jakarta.transaction.SystemException;
import jakarta.transaction.UserTransaction;

import org.wildfly.common.Assert;
import org.wildfly.common.context.ContextManager;
import org.wildfly.common.context.Contextual;
import org.wildfly.security.auth.client.AuthenticationContext;
import org.wildfly.transaction.TransactionPermission;
import org.wildfly.transaction.client._private.Log;
import org.wildfly.transaction.client.spi.RemoteTransactionProvider;

/**
 * The remote transaction context.
 *
 * @author David M. Lloyd
 */
public final class RemoteTransactionContext implements Contextual {

    private static final RemoteTransactionProvider[] NO_PROVIDERS = new RemoteTransactionProvider[0];
    private static final TransactionPermission CREATION_LISTENER_PERMISSION = TransactionPermission.forName("registerCreationListener");

    private final List providers;
    private final List creationListeners = new CopyOnWriteArrayList<>();

    /**
     * Construct a new instance.  The given class loader is scanned for transaction providers.
     *
     * @param classLoader the class loader to scan for transaction providers ({@code null} indicates the application or bootstrap class loader)
     */
    public RemoteTransactionContext(final ClassLoader classLoader) {
        this(System.getSecurityManager() == null? getProviders(classLoader):
                doPrivileged((PrivilegedAction>) () -> {
                    return getProviders(classLoader);
                }), false);
    }

    /**
     * Construct a new instance.  The given non-empty list of providers is used.
     *
     * @param providers the list of providers to use (must not be {@code null} or empty)
     */
    public RemoteTransactionContext(final List providers) {
        this(providers, true);
    }

    RemoteTransaction notifyCreationListeners(RemoteTransaction transaction, CreationListener.CreatedBy createdBy) {
        for (CreationListener creationListener : creationListeners) {
            try {
                creationListener.transactionCreated(transaction, createdBy);
            } catch (Throwable t) {
                Log.log.trace("Transaction creation listener throws an exception", t);
            }
        }
        return transaction;
    }

    private static List getProviders(ClassLoader classLoader) {
        final ServiceLoader loader = ServiceLoader.load(RemoteTransactionProvider.class, classLoader);
        final ArrayList providers = new ArrayList();
        final Iterator iterator = loader.iterator();
        for (;;) try {
            if (! iterator.hasNext()) break;
            providers.add(iterator.next());
        } catch (ServiceConfigurationError e) {
            Log.log.serviceConfigurationFailed(e);
        }
        providers.trimToSize();
        return providers;
    }

    /**
     * Register a transaction creation listener.
     *
     * @param creationListener the creation listener (must not be {@code null})
     */
    public void registerCreationListener(CreationListener creationListener) {
        Assert.checkNotNullParam("creationListener", creationListener);
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(CREATION_LISTENER_PERMISSION);
        }
        creationListeners.add(creationListener);
    }

    /**
     * Remove a transaction creation listener.
     *
     * @param creationListener the creation listener (must not be {@code null})
     */
    public void removeCreationListener(CreationListener creationListener) {
        Assert.checkNotNullParam("creationListener", creationListener);
        creationListeners.removeIf(c -> c == creationListener);
    }

    RemoteTransactionContext(List providers, boolean clone) {
        Assert.checkNotNullParam("providers", providers);
        if (clone) {
            providers = Arrays.asList(providers.toArray(NO_PROVIDERS));
        }
        Assert.checkNotEmptyParam("providers", providers);
        final int size = providers.size();
        for (int i = 0; i < size; i++) {
            Assert.checkNotNullArrayParam("providers", i, providers.get(i));
        }
        this.providers = providers;
    }

    private static final ContextManager CONTEXT_MANAGER = new ContextManager(RemoteTransactionContext.class, "org.wildfly.transaction.client.context.remote");
    private static final Supplier PRIVILEGED_SUPPLIER = doPrivileged((PrivilegedAction>) CONTEXT_MANAGER::getPrivilegedSupplier);

    static {
        doPrivileged((PrivilegedAction) () -> {
            CONTEXT_MANAGER.setGlobalDefault(new RemoteTransactionContext(RemoteTransactionContext.class.getClassLoader()));
            return null;
        });
    }

    /**
     * Get the remote transaction context manager.
     *
     * @return the context manager
     */
    public static ContextManager getContextManager() {
        return CONTEXT_MANAGER;
    }

    /** {@inheritDoc} */
    public ContextManager getInstanceContextManager() {
        return getContextManager();
    }

    /**
     * Get the active remote transaction context instance.
     *
     * @return the remote transaction context instance (not {@code null})
     */
    public static RemoteTransactionContext getInstance() {
        return getContextManager().get();
    }

    static RemoteTransactionContext getInstancePrivate() {
        return PRIVILEGED_SUPPLIER.get();
    }

    /**
     * Get a {@code UserTransaction} that controls a remote transactions state at the given {@code location}.  The transaction
     * context may cache these instances by location.
     *
     * @return the {@code UserTransaction} (not {@code null})
     */
    public RemoteUserTransaction getUserTransaction() {
        return new RemoteUserTransaction(AuthenticationContext.captureCurrent());
    }

    /**
     * Compatibility bridge method.
     *
     * @return the {@code UserTransaction} (not {@code null})
     * @deprecated Please use {@link #getUserTransaction()} instead.
     */
    public UserTransaction getUserTransaction$$bridge_compat() {
        return getUserTransaction();
    }

    private static final Object outflowKey = new Object();

    /**
     * Outflow the given local transaction to the given location.  The returned handle
     * must be used to confirm or forget the enlistment attempt either immediately or at some point in the future;
     * failure to do so may cause the transaction to be rolled back with an error.
     *
     * @param location the location to outflow to (must not be {@code null})
     * @param transaction the transaction (must not be {@code null})
     * @return the enlistment handle (not {@code null})
     * @throws SystemException if the transaction manager fails for some reason
     * @throws RollbackException if the transaction has been rolled back in the meantime
     * @throws IllegalStateException if no transaction is active
     * @throws UnsupportedOperationException if the provider for the location does not support outflow
     */
    public XAOutflowHandle outflowTransaction(final URI location, final LocalTransaction transaction) throws SystemException, IllegalStateException, UnsupportedOperationException, RollbackException {
        Assert.checkNotNullParam("location", location);
        Assert.checkNotNullParam("transaction", transaction);

        XAOutflowedResources outflowedResources = getOutflowedResources(transaction);
        if (outflowedResources == null) {
            synchronized (transaction.getOutflowLock()) {
                outflowedResources = getOutflowedResources(transaction);
                if (outflowedResources == null) {
                    transaction.putResource(outflowKey, outflowedResources = new XAOutflowedResources(transaction));
                }
            }
        }
        SubordinateXAResource resource = outflowedResources.getOrEnlist(location, transaction.getParentName());
        return resource.addHandle(resource.getXid());
    }

    static XAOutflowedResources getOutflowedResources(final LocalTransaction transaction) {
        return (XAOutflowedResources) transaction.getResource(outflowKey);
    }

    RemoteTransactionProvider getProvider(final URI location) {
        for (RemoteTransactionProvider provider : providers) {
            if (provider.supportsScheme(location.getScheme())) {
                return provider;
            }
        }
        return null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy