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

org.wildfly.transaction.client.provider.jboss.JBossLocalTransactionProvider 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 2017 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.provider.jboss;

import static com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome.INVALID_TRANSACTION;
import static com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome.PREPARE_NOTOK;
import static com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome.PREPARE_OK;
import static com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome.PREPARE_READONLY;
import static java.lang.Long.signum;

import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import com.arjuna.ats.arjuna.common.arjPropertyManager;
import jakarta.transaction.HeuristicCommitException;
import jakarta.transaction.HeuristicMixedException;
import jakarta.transaction.HeuristicRollbackException;
import jakarta.transaction.InvalidTransactionException;
import jakarta.transaction.NotSupportedException;
import jakarta.transaction.RollbackException;
import jakarta.transaction.Status;
import jakarta.transaction.Synchronization;
import jakarta.transaction.SystemException;
import jakarta.transaction.Transaction;
import jakarta.transaction.TransactionManager;
import org.jboss.tm.ExtendedJBossXATerminator;
import org.jboss.tm.ImportedTransaction;
import org.jboss.tm.TransactionImportResult;
import org.jboss.tm.XAResourceRecovery;
import org.jboss.tm.XAResourceRecoveryRegistry;
import org.wildfly.common.Assert;
import org.wildfly.common.annotation.NotNull;
import org.wildfly.transaction.client.ImportResult;
import org.wildfly.transaction.client.LocalTransaction;
import org.wildfly.transaction.client.SimpleXid;
import org.wildfly.transaction.client.XAImporter;
import org.wildfly.transaction.client.XAResourceRegistry;
import org.wildfly.transaction.client.XAResourceRegistryProviderHolder;
import org.wildfly.transaction.client._private.Log;
import org.wildfly.transaction.client.spi.LocalTransactionProvider;
import org.wildfly.transaction.client.spi.SubordinateTransactionControl;

/**
 * The local transaction provider for JBoss application servers.
 *
 * @author David M. Lloyd
 */
public abstract class JBossLocalTransactionProvider implements LocalTransactionProvider {
    private static final Object ENTRY_KEY = new Object();

    private final int staleTransactionTime;
    private final ExtendedJBossXATerminator ext;
    private final TransactionManager tm;
    private final XAImporterImpl xi = new XAImporterImpl();
    private final ConcurrentSkipListSet timeoutSet = new ConcurrentSkipListSet<>();
    private final ConcurrentMap known = new ConcurrentHashMap<>();
    private final FileSystemXAResourceRegistry fileSystemXAResourceRegistry;
    private final XAResourceRecovery xaResourceRecovery;

    JBossLocalTransactionProvider(final ExtendedJBossXATerminator ext, final int staleTransactionTime, final TransactionManager tm,
                                  final XAResourceRecoveryRegistry registry, final Path xaRecoveryDirRelativeToPath) {
        Assert.checkMinimumParameter("setTransactionTimeout", 0, staleTransactionTime);
        this.staleTransactionTime = staleTransactionTime;
        this.ext = Assert.checkNotNullParam("ext", ext);
        this.tm = Assert.checkNotNullParam("tm", tm);

        try {
            ext.doRecover(null, null);
        } catch (Exception e) {
            // the recover method is called to load transactions from Narayana object store at startup
            // if it fails we ignore, troubles will be adjusted during runtime
            Log.log.doRecoverFailureOnIntialization(e);
        }
        this.fileSystemXAResourceRegistry = new FileSystemXAResourceRegistry(this, xaRecoveryDirRelativeToPath);
        XAResourceRegistryProviderHolder.register(fileSystemXAResourceRegistry::getInDoubtXAResources);
        xaResourceRecovery = fileSystemXAResourceRegistry::getInDoubtXAResources;
        registry.addXAResourceRecovery(xaResourceRecovery);
    }

    /**
     * Remove the registered xaResourceRecovery from the registry passed as parameter.
     * Allow the consumer to remove the xaResourceRecovery from the registry.
     * If not removed, the xaResourceRecovery is held by XAResourceRegistryProviderHolder.INSTANCE
     * and it cannot be garbage collected (i.e. during consumer's stop)
     *
     * @param registry XAResourceRecovery registry
     */
    public void removeXAResourceRecovery(XAResourceRecoveryRegistry registry) {
        registry.removeXAResourceRecovery(xaResourceRecovery);
    }

    /**
     * Create a builder for the transaction provider.
     *
     * @return the builder
     */
    public static Builder builder() {
        return new Builder();
    }

    @NotNull
    public TransactionManager getTransactionManager() {
        return tm;
    }

    @NotNull
    public XAImporter getXAImporter() {
        return xi;
    }

    @NotNull
    public Transaction createNewTransaction(final int timeout) throws SystemException, SecurityException {
        final TransactionManager tm = this.tm;
        final int oldTimeout = getTransactionManagerTimeout();
        tm.setTransactionTimeout(timeout);
        try {
            final Transaction suspended = tm.suspend();
            try {
                tm.begin();
                final Transaction transaction = tm.suspend();
                SimpleXid gtid = SimpleXid.of(getXid(transaction)).withoutBranch();
                known.put(gtid, getEntryFor(transaction, gtid));
                // Narayana doesn't actually throw exceptions here so this should be fine
                tm.setTransactionTimeout(oldTimeout);
                return transaction;
            } catch (NotSupportedException e) {
                throw Log.log.unexpectedFailure(e);
            } catch (Throwable t) {
                if (suspended != null) try {
                    tm.resume(suspended);
                } catch (InvalidTransactionException e) {
                    e.addSuppressed(t);
                    throw Log.log.unexpectedFailure(e);
                }
                throw t;
            }
        } catch (Throwable t) {
            try {
                tm.setTransactionTimeout(oldTimeout);
            } catch (Throwable t2) {
                // Narayana doesn't actually throw exceptions here so this should be fine
                t2.addSuppressed(t);
                throw t2;
            }
            throw t;
        }
    }

    @Override
    public XAResourceRegistry getXAResourceRegistry(LocalTransaction transaction) throws SystemException {
        return fileSystemXAResourceRegistry.getXAResourceRegistryFile(transaction);
    }

    abstract int getTransactionManagerTimeout() throws SystemException;

    public boolean isImported(@NotNull final Transaction transaction) throws IllegalArgumentException {
        return transaction instanceof ImportedTransaction;
    }

    public abstract void registerInterposedSynchronization(@NotNull Transaction transaction, @NotNull Synchronization sync) throws IllegalArgumentException;

    @NotNull
    public abstract Object getKey(@NotNull Transaction transaction) throws IllegalArgumentException;

    public void dropLocal(@NotNull final Transaction transaction) {
        final Xid xid = getXid(transaction);
        final SimpleXid gtid = SimpleXid.of(xid).withoutBranch();
        final Entry entry = known.remove(gtid);
        if (entry != null) {
            timeoutSet.remove(entry.getXidKey());
        }
    }

    public void dropRemote(@NotNull final Transaction transaction) {
        final Xid xid = getXid(transaction);
        final SimpleXid gtid = SimpleXid.of(xid).withoutBranch();
        final Entry entry = known.get(gtid);
        if (entry != null) {
            final XidKey oldXidKey = entry.getXidKey();
            // redefinition of time when memory mapping is cleaned from the transaction with remote enlistment
            // the transaction with remote enlistment will be removed from when stale txn time elapsed
            final XidKey newXidKey = new XidKey(gtid, getTimeTick() + staleTransactionTime * 1_000_000_000L);
            timeoutSet.add(newXidKey);
            timeoutSet.remove(oldXidKey);
        }
    }

    public abstract int getTimeout(@NotNull Transaction transaction);

    @NotNull
    public abstract Xid getXid(@NotNull Transaction transaction);

    @NotNull
    public String getNodeName() {
        final String nodeIdentifier = arjPropertyManager.getCoreEnvironmentBean().getNodeIdentifier();
        if (nodeIdentifier == null) {
            throw Log.log.noLocalTransactionProviderNodeName();
        }
        return nodeIdentifier;
    }

    Entry getEntryFor(Transaction transaction, SimpleXid gtid) {
        Entry entry = (Entry) getResource(transaction, ENTRY_KEY);
        if (entry != null) {
            return entry;
        }
        final XidKey xidKey;
        synchronized (transaction) {
            entry = (Entry) getResource(transaction, ENTRY_KEY);
            if (entry != null) {
                return entry;
            }
            int lifetime = getTimeout(transaction) + staleTransactionTime;
            final long timeTick = getTimeTick();
            // this is the maximum amount of time we expect any potential incoming peer might know about this transaction ID
            xidKey = new XidKey(gtid, timeTick + lifetime * 1_000_000_000L);
            putResource(transaction, ENTRY_KEY, entry = new Entry(gtid, transaction, xidKey));
        }
        timeoutSet.add(xidKey);
        if (! isStatusInactive(transaction)) {
            try {
                registerInterposedSynchronization(transaction, new Synchronization() {
                    public void beforeCompletion() {
                        // no operation
                    }

                    public void afterCompletion(final int status) {
                        // let the TM do some heavy lifting for us
                        final long timeTick = getTimeTick();
                        // clear off all expired entries
                        final ConcurrentMap known = JBossLocalTransactionProvider.this.known;
                        final Iterator iterator = timeoutSet.headSet(new XidKey(SimpleXid.EMPTY, timeTick)).iterator();
                        while (iterator.hasNext()) {
                            SimpleXid xidToRemove = iterator.next().getId();
                            Entry knownEntry = known.remove(xidToRemove);

                            if (knownEntry == null) {
                                Log.log.unknownXidToBeRemovedFromTheKnownTransactionInstances(xidToRemove);
                            } else {
                                final Transaction transaction = knownEntry.getTransaction();
                                if (transaction instanceof ImportedTransaction) {
                                    try {
                                        ext.removeImportedTransaction(getXid(transaction));
                                    } catch (XAException xae) {
                                        Log.log.cannotRemoveImportedTransaction(xidToRemove, xae);
                                    }
                                }
                            }

                            iterator.remove();
                        }
                    }
                });
            } catch (IllegalStateException e) {
                if (! isStatusInactive(transaction)) {
                    // we don't know why it happened because the tx is not inactive; could be unknown or something weird
                    throw e;
                }
            }
        }
        return entry;
    }

    boolean isStatusInactive(Transaction transaction) {
        switch (getStatus(transaction)) {
            case Status.STATUS_PREPARING:
            case Status.STATUS_PREPARED:
            case Status.STATUS_COMMITTING:
            case Status.STATUS_COMMITTED:
            case Status.STATUS_ROLLING_BACK:
            case Status.STATUS_ROLLEDBACK: {
                return true;
            }
            default: {
                return false;
            }
        }
    }

    int getStatus(Transaction transaction) {
        try {
            return transaction.getStatus();
        } catch (SystemException e) {
            return Status.STATUS_UNKNOWN;
        }
    }

    private static final long TIME_START = System.nanoTime();

    long getTimeTick() {
        return System.nanoTime() - TIME_START;
    }

    private static final int UID_LEN = 28;

    public String getNameFromXid(@NotNull final Xid xid) {
        final int formatId = xid.getFormatId();
        if (formatId == 0x20000 || formatId == 0x20005 || formatId == 0x20008) {
            final byte[] gtid = xid.getGlobalTransactionId();
            final int length = gtid.length;
            if (length <= UID_LEN) {
                // no parent name encoded there
                return null;
            }
            return new String(gtid, UID_LEN, length - UID_LEN, StandardCharsets.UTF_8);
        } else {
            return null;
        }
    }

    public  T getProviderInterface(final Transaction transaction, final Class providerInterfaceType) {
        // access to underlying txn
        return providerInterfaceType.isInstance(transaction) ? providerInterfaceType.cast(transaction) : null;
    }

    static final class XidKey implements Comparable {
        private final SimpleXid gtid;
        private final long expiration;

        XidKey(final SimpleXid gtid, final long expiration) {
            this.gtid = gtid;
            this.expiration = expiration;
        }

        public int compareTo(final XidKey o) {
            final int res = signum(expiration - o.expiration);
            return res == 0 ? gtid.compareTo(o.gtid) : res;
        }

        SimpleXid getId() {
            return gtid;
        }
    }

    final class Entry implements SubordinateTransactionControl {
        private final SimpleXid gtid;
        private final Transaction transaction;
        private final XidKey xidKey;

        Entry(final SimpleXid gtid, final Transaction transaction, final XidKey xidKey) {
            this.gtid = gtid;
            this.transaction = transaction;
            this.xidKey = xidKey;
        }

        XidKey getXidKey() {
            return xidKey;
        }

        Transaction getTransaction() {
            return transaction;
        }

        void rollbackLocal() throws SystemException {
            if (transaction instanceof ImportedTransaction) {
                throw Log.log.rollbackOnImported();
            }
            transaction.rollback();
        }

        void commitLocal() throws HeuristicRollbackException, RollbackException, HeuristicMixedException, SystemException {
            if (transaction instanceof ImportedTransaction) {
                throw Log.log.commitOnImported();
            }
            transaction.commit();
        }

        public void rollback() throws XAException {
            final Transaction transaction = this.transaction;
            try {
                final int status = transaction.getStatus();
                if (status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_ROLLEDBACK) {
                    // no harm here
                    return;
                }
            } catch (SystemException e) {
                // can't determine status; fall out
            }
            if (! (transaction instanceof ImportedTransaction)) {
                throw Log.log.notImportedXa(XAException.XAER_NOTA);
            }
            final ImportedTransaction importedTransaction = (ImportedTransaction) transaction;
            if (importedTransaction.activated()) try {
                importedTransaction.doRollback();
            } catch (HeuristicCommitException e) {
                throw Log.log.heuristicCommitXa(XAException.XA_HEURCOM, e);
            } catch (HeuristicMixedException e) {
                throw Log.log.heuristicMixedXa(XAException.XA_HEURMIX, e);
            } catch (HeuristicRollbackException e) {
                throw Log.log.heuristicRollbackXa(XAException.XA_HEURRB, e);
            } catch (IllegalStateException e) {
                throw Log.log.illegalStateXa(XAException.XAER_NOTA, e);
            } catch (Throwable /* RuntimeException | SystemException */ e) {
                throw Log.log.resourceManagerErrorXa(XAException.XAER_RMERR, e);
            } finally {
                ext.removeImportedTransaction(gtid);
            }
        }

        public void end(final int flags) throws XAException {
            if (flags != XAResource.TMFAIL) {
                return;
            }
            final Transaction transaction = this.transaction;
            try {
                final int status = transaction.getStatus();
                if (status == Status.STATUS_MARKED_ROLLBACK || status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_ROLLEDBACK) {
                    // no harm here
                    return;
                }
            } catch (SystemException e) {
                // can't determine status; fall out
            }
            if (! (transaction instanceof ImportedTransaction)) {
                throw Log.log.notImportedXa(XAException.XAER_NOTA);
            }
            try {
                transaction.setRollbackOnly();
            } catch (IllegalStateException e) {
                throw Log.log.illegalStateXa(XAException.XAER_NOTA, e);
            } catch (Throwable /* RuntimeException | SystemException */ e) {
                throw Log.log.resourceManagerErrorXa(XAException.XAER_RMERR, e);
            }
        }

        public void beforeCompletion() throws XAException {
            final Transaction transaction = this.transaction;
            if (! (transaction instanceof ImportedTransaction)) {
                throw Log.log.notImportedXa(XAException.XAER_NOTA);
            }
            final ImportedTransaction importedTransaction = (ImportedTransaction) transaction;
            try {
                if (! importedTransaction.doBeforeCompletion()) {
                    throw new XAException(XAException.XAER_RMERR);
                }
            } catch (IllegalStateException e) {
                throw Log.log.illegalStateXa(XAException.XAER_NOTA, e);
            } catch (Throwable /* RuntimeException | SystemException */ e) {
                throw Log.log.resourceManagerErrorXa(XAException.XAER_RMERR, e);
            }
        }

        public int prepare() throws XAException {
            final Transaction transaction = this.transaction;
            if (! (transaction instanceof ImportedTransaction)) {
                throw Log.log.notImportedXa(XAException.XAER_NOTA);
            }
            final ImportedTransaction importedTransaction = (ImportedTransaction) transaction;
            final int tpo = importedTransaction.doPrepare();
            switch (tpo) {
                case PREPARE_READONLY:
                    ext.removeImportedTransaction(gtid);
                    return XAResource.XA_RDONLY;

                case PREPARE_OK:
                    return XAResource.XA_OK;

                case PREPARE_NOTOK:
                    //noinspection TryWithIdenticalCatches
                    try {
                        importedTransaction.doRollback();
                    } catch (HeuristicCommitException | HeuristicMixedException | HeuristicRollbackException | SystemException e) {
                        // TODO: the old code removes the transaction here, but JBTM-427 implies that the TM should do this explicitly later; for now keep old behavior
                        ext.removeImportedTransaction(gtid);
                        // JBTM-427; JTA doesn't allow heuristic codes on prepare :(
                        throw initializeSuppressed(Log.log.resourceManagerErrorXa(XAException.XAER_RMERR, e), importedTransaction);
                    } catch (Throwable t) {
                        // maybe still remove the transaction...
                        ext.removeImportedTransaction(gtid);
                        throw initializeSuppressed(Log.log.resourceManagerErrorXa(XAException.XAER_RMERR, t), importedTransaction);
                    }
                    throw initializeSuppressed(new XAException(XAException.XA_RBROLLBACK), importedTransaction);

                case INVALID_TRANSACTION:
                    throw new XAException(XAException.XAER_NOTA);

                default:
                    throw new XAException(XAException.XA_RBOTHER);
            }
        }

        public void forget() throws XAException {
            final Transaction transaction = this.transaction;
            if (! (transaction instanceof ImportedTransaction)) {
                throw Log.log.notImportedXa(XAException.XAER_NOTA);
            }
            final ImportedTransaction importedTransaction = (ImportedTransaction) transaction;
            try {
                importedTransaction.doForget();
            } catch (IllegalStateException e) {
                throw Log.log.illegalStateXa(XAException.XAER_NOTA, e);
            } catch (Throwable /* RuntimeException | SystemException */ e) {
                throw Log.log.resourceManagerErrorXa(XAException.XAER_RMERR, e);
            }
        }

        public void commit(final boolean onePhase) throws XAException {
            final Transaction transaction = this.transaction;
            if (! (transaction instanceof ImportedTransaction)) {
                throw Log.log.notImportedXa(XAException.XAER_NOTA);
            }
            final ImportedTransaction importedTransaction = (ImportedTransaction) transaction;
            try {
                if (onePhase) {
                    importedTransaction.doOnePhaseCommit();
                } else {
                    if (! importedTransaction.doCommit()) {
                        dropLocal(importedTransaction);
                        ext.doRecover(null, null);
                        throw new XAException(XAException.XA_RETRY);
                    }
                }
            } catch (XAException e) {
                throw initializeSuppressed(e, importedTransaction);
            } catch (HeuristicMixedException e) {
                throw initializeSuppressed(Log.log.heuristicMixedXa(XAException.XA_HEURMIX, e), importedTransaction);
            } catch (RollbackException e) {
                throw initializeSuppressed(Log.log.rollbackXa(XAException.XA_RBROLLBACK, e), importedTransaction);
            } catch (HeuristicCommitException e) {
                throw initializeSuppressed(Log.log.heuristicCommitXa(XAException.XA_HEURCOM, e), importedTransaction);
            } catch (HeuristicRollbackException e) {
                throw initializeSuppressed(Log.log.heuristicRollbackXa(XAException.XA_HEURRB, e), importedTransaction);
            } catch (IllegalStateException e) {
                throw initializeSuppressed(Log.log.illegalStateXa(XAException.XAER_NOTA, e), importedTransaction);
            } catch (Throwable e) {
                throw initializeSuppressed(Log.log.resourceManagerErrorXa(XAException.XAER_RMERR, e), importedTransaction);
            }
        }

        private XAException initializeSuppressed(final XAException ex, final ImportedTransaction transaction) {
            if(ex != null && transaction.supportsDeferredThrowables()) {
                for(Throwable suppressedThrowable: transaction.getDeferredThrowables()) {
                    ex.addSuppressed(suppressedThrowable);
                }
            }
            return ex;
        }
    }

    final class XAImporterImpl implements XAImporter {

        public ImportResult findOrImportTransaction(final Xid xid, final int timeout, final boolean doNotImport) throws XAException {
            try {
                final SimpleXid simpleXid = SimpleXid.of(xid);
                final SimpleXid gtid = simpleXid.withoutBranch();
                final ConcurrentMap known = JBossLocalTransactionProvider.this.known;
                Entry entry = known.get(gtid);
                if (entry != null) {
                    return new ImportResult(entry.getTransaction(), entry, false);
                }
                final boolean imported;
                Transaction transaction;
                if (doNotImport) {
                    imported = false;
                    transaction = ext.getTransaction(xid);

                    if (transaction == null) {
                        return null;
                    }
                } else {
                    final TransactionImportResult result = ext.importTransaction(xid, timeout);
                    transaction = result.getTransaction();
                    imported = result.isNewImportedTransaction();
                }
                entry = getEntryFor(transaction, gtid);
                final Entry appearing = known.putIfAbsent(gtid, entry);
                if (appearing != null) {
                    // even if someone else beat us to the map, we still might have imported first... preserve the original Entry for economy though
                    return new ImportResult(transaction, appearing, imported);
                } else {
                    return new ImportResult(transaction, entry, imported);
                }
            } catch (XAException e) {
                throw e;
            } catch (Throwable t) {
                throw Log.log.resourceManagerErrorXa(XAException.XAER_RMFAIL, t);
            }
        }

        public Transaction findExistingTransaction(final Xid xid) throws XAException {
            try {
                final SimpleXid simpleXid = SimpleXid.of(xid);
                final SimpleXid gtid = simpleXid.withoutBranch();
                final ConcurrentMap known = JBossLocalTransactionProvider.this.known;
                Entry entry = known.get(gtid);
                if (entry != null) {
                    return entry.getTransaction();
                }
                final Transaction transaction = ext.getTransaction(xid);
                if (transaction == null) {
                    return null;
                }
                return known.computeIfAbsent(gtid, g -> getEntryFor(transaction, g)).getTransaction();
            } catch (XAException e) {
                throw e;
            } catch (Throwable t) {
                throw Log.log.resourceManagerErrorXa(XAException.XAER_RMFAIL, t);
            }
        }

        public void commit(final Xid xid, final boolean onePhase) throws XAException {
            try {
                Entry entry = known.get(SimpleXid.of(xid).withoutBranch());
                if (entry != null) {
                    entry.commit(onePhase);
                } else {
                    throw new XAException(XAException.XAER_NOTA);
                }
            } catch (XAException e) {
                throw e;
            } catch (Throwable t) {
                throw Log.log.resourceManagerErrorXa(XAException.XAER_RMFAIL, t);
            }
        }

        public void forget(final Xid xid) throws XAException {
            try {
                Entry entry = known.get(SimpleXid.of(xid).withoutBranch());
                if (entry != null) {
                    entry.forget();
                } else {
                    throw new XAException(XAException.XAER_NOTA);
                }
            } catch (XAException e) {
                throw e;
            } catch (Throwable t) {
                throw Log.log.resourceManagerErrorXa(XAException.XAER_RMFAIL, t);
            }
        }

        @NotNull
        public Xid[] recover(final int flag, final String parentNodeName) throws XAException {
            try {
                try {
                    final Xid[] xids = ext.doRecover(null, parentNodeName);
                    return xids == null ? SimpleXid.NO_XIDS : xids;
                } catch (NotSupportedException e) {
                    throw new XAException(XAException.XAER_RMFAIL);
                }
            } catch (XAException e) {
                throw e;
            } catch (Throwable t) {
                throw Log.log.resourceManagerErrorXa(XAException.XAER_RMFAIL, t);
            }
        }
    }

    /**
     * A builder for a JBoss local transaction provider.
     */
    public static final class Builder {
        private int staleTransactionTime = 600;
        private ExtendedJBossXATerminator extendedJBossXATerminator;
        private TransactionManager transactionManager;
        private XAResourceRecoveryRegistry xaResourceRecoveryRegistry;
        private Path xaRecoveryLogDirRelativeToPath;

        Builder() {
        }

        /**
         * Get the stale transaction time, in seconds.
         *
         * @return the stale transaction time, in seconds
         */
        public int getStaleTransactionTime() {
            return staleTransactionTime;
        }

        /**
         * Set the stale transaction time, in seconds.  The time must be no less than one second.
         *
         * @param staleTransactionTime the stale transaction time, in seconds
         */
        public Builder setStaleTransactionTime(final int staleTransactionTime) {
            Assert.checkMinimumParameter("staleTransactionTime", 1, staleTransactionTime);
            this.staleTransactionTime = staleTransactionTime;
            return this;
        }

        /**
         * Get the extended JBoss XA terminator.
         *
         * @return the extended JBoss XA terminator
         */
        public ExtendedJBossXATerminator getExtendedJBossXATerminator() {
            return extendedJBossXATerminator;
        }

        /**
         * Set the extended JBoss XA terminator.
         *
         * @param ext the extended JBoss XA terminator (must not be {@code null})
         */
        public Builder setExtendedJBossXATerminator(final ExtendedJBossXATerminator ext) {
            Assert.checkNotNullParam("ext", ext);
            this.extendedJBossXATerminator = ext;
            return this;
        }

        /**
         * Get the transaction manager.
         *
         * @return the transaction manager
         */
        public TransactionManager getTransactionManager() {
            return transactionManager;
        }

        /**
         * Set the transaction manager.
         *
         * @param tm the transaction manager
         */
        public Builder setTransactionManager(final TransactionManager tm) {
            Assert.checkNotNullParam("tm", tm);
            this.transactionManager = tm;
            return this;
        }

        /**
         * Set the xa resource recovery registry.
         *
         * @param reg xa resource recovery registry (must not be {@code null})
         */
        public Builder setXAResourceRecoveryRegistry(final XAResourceRecoveryRegistry reg) {
            Assert.checkNotNullParam("reg", reg);
            this.xaResourceRecoveryRegistry = reg;
            return this;
        }

        /**
         * Set the xa recovery log dir relative to path
         *
         * @param path the xa recovery log file relative to path (must not be {@code null}
         */
        public Builder setXARecoveryLogDirRelativeToPath(Path path) {
            Assert.checkNotNullParam("path", path);
            this.xaRecoveryLogDirRelativeToPath = path;
            return this;
        }

        /**
         * Build this provider.  If any required properties are {@code null}, an exception is thrown.
         *
         * @return the built provider (not {@code null})
         * @throws IllegalArgumentException if a required property is {@code null}
         */
        public JBossLocalTransactionProvider build() {
            ExtendedJBossXATerminator extendedJBossXATerminator = this.extendedJBossXATerminator;
            TransactionManager transactionManager = this.transactionManager;
            int staleTransactionTime = this.staleTransactionTime;
            Assert.checkNotNullParam("extendedJBossXATerminator", extendedJBossXATerminator);
            Assert.checkNotNullParam("transactionManager", transactionManager);
            Assert.checkMinimumParameter("staleTransactionTime", 0, staleTransactionTime);
            Assert.checkNotNullParam("xaResourceRecoveryRegistry", xaResourceRecoveryRegistry);
            if (transactionManager instanceof com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple
             || transactionManager instanceof com.arjuna.ats.jbossatx.jta.TransactionManagerDelegate) {
                return new JBossJTALocalTransactionProvider(staleTransactionTime, extendedJBossXATerminator,
                        transactionManager, xaResourceRecoveryRegistry, xaRecoveryLogDirRelativeToPath);
            } else if (transactionManager instanceof com.arjuna.ats.internal.jta.transaction.jts.TransactionManagerImple
             || transactionManager instanceof com.arjuna.ats.jbossatx.jts.TransactionManagerDelegate) {
                return new JBossJTSLocalTransactionProvider(staleTransactionTime, extendedJBossXATerminator,
                        transactionManager, xaResourceRecoveryRegistry, xaRecoveryLogDirRelativeToPath);
            } else {
                throw Log.log.unknownTransactionManagerType(transactionManager.getClass());
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy