org.wildfly.transaction.client.LocalTransaction 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).
/*
* 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 jakarta.transaction.HeuristicMixedException;
import jakarta.transaction.HeuristicRollbackException;
import jakarta.transaction.InvalidTransactionException;
import jakarta.transaction.RollbackException;
import jakarta.transaction.Synchronization;
import jakarta.transaction.SystemException;
import jakarta.transaction.Transaction;
import jakarta.transaction.TransactionManager;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.wildfly.common.Assert;
import org.wildfly.transaction.client._private.Log;
import org.wildfly.transaction.client.spi.LocalTransactionProvider;
/**
* A transaction from a local transaction provider.
*
* @author David M. Lloyd
*/
public final class LocalTransaction extends AbstractTransaction {
private final LocalTransactionContext owner;
private final Transaction transaction;
LocalTransaction(final LocalTransactionContext owner, final Transaction transaction) {
this.owner = owner;
this.transaction = transaction;
}
public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException {
if (isImported()) {
throw Log.log.commitOnImported();
}
try {
owner.getProvider().commitLocal(transaction);
} catch (RollbackException re) {
addRollbackExceptions(re);
throw re;
} finally {
final XAOutflowedResources outflowedResources = RemoteTransactionContext.getOutflowedResources(this);
if (outflowedResources == null || outflowedResources.getEnlistedSubordinates() == 0) {
// we can drop the mapping, since we are both a master and have no enlisted subordinates
owner.getProvider().dropLocal(transaction);
} else {
// the memory mapping of transaction with subordinate enlistment may need to be adjusted
owner.getProvider().dropRemote(transaction);
}
}
}
void commitAndDissociate() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException {
if (isImported()) {
throw Log.log.commitOnImported();
}
notifyAssociationListeners(false);
try {
owner.getProvider().getTransactionManager().commit();
} catch (RollbackException re) {
addRollbackExceptions(re);
throw re;
} finally {
final XAOutflowedResources outflowedResources = RemoteTransactionContext.getOutflowedResources(this);
if (outflowedResources == null || outflowedResources.getEnlistedSubordinates() == 0) {
// we can drop the mapping, since we are both a master and have no enlisted subordinates
owner.getProvider().dropLocal(transaction);
} else {
// the memory mapping of transaction with subordinate enlistment may need to be adjusted
owner.getProvider().dropRemote(transaction);
}
}
}
public void rollback() throws IllegalStateException, SystemException {
if (isImported()) {
throw Log.log.rollbackOnImported();
}
try {
owner.getProvider().rollbackLocal(transaction);
} finally {
final XAOutflowedResources outflowedResources = RemoteTransactionContext.getOutflowedResources(this);
if (outflowedResources == null || outflowedResources.getEnlistedSubordinates() == 0) {
// we can drop the mapping, since we are both a master and have no enlisted subordinates
owner.getProvider().dropLocal(transaction);
} else {
// the memory mapping of transaction with subordinate enlistment may need to be adjusted
owner.getProvider().dropRemote(transaction);
}
}
}
void rollbackAndDissociate() throws IllegalStateException, SystemException {
if (isImported()) {
throw Log.log.rollbackOnImported();
}
notifyAssociationListeners(false);
try {
owner.getProvider().getTransactionManager().rollback();
} finally {
final XAOutflowedResources outflowedResources = RemoteTransactionContext.getOutflowedResources(this);
if (outflowedResources == null || outflowedResources.getEnlistedSubordinates() == 0) {
// we can drop the mapping, since we are both a master and have no enlisted subordinates
owner.getProvider().dropLocal(transaction);
} else {
// the memory mapping of transaction with subordinate enlistment may need to be adjusted
owner.getProvider().dropRemote(transaction);
}
}
}
boolean importBacking() throws SystemException {
final ContextTransactionManager.State state = ContextTransactionManager.INSTANCE.getStateRef().get();
final Transaction transaction = owner.getProvider().getTransactionManager().getTransaction();
if (transaction == null) {
return false;
}
final LocalTransaction localTransaction = owner.getOrAttach(transaction, CreationListener.CreatedBy.MERGE);
if (state.transaction == null) {
state.transaction = localTransaction;
return true;
} else {
localTransaction.verifyAssociation();
return false;
}
}
void unimportBacking() {
final ContextTransactionManager.State state = ContextTransactionManager.INSTANCE.getStateRef().get();
if (state.transaction.equals(this)) {
state.transaction = null;
}
}
LocalTransactionProvider getProvider() {
return owner.getProvider();
}
public void setRollbackOnly() throws IllegalStateException, SystemException {
super.setRollbackOnly();
transaction.setRollbackOnly();
}
public int getStatus() throws SystemException {
return transaction.getStatus();
}
public int getTransactionTimeout() {
return owner.getProvider().getTimeout(transaction);
}
public boolean enlistResource(final XAResource xaRes) throws RollbackException, IllegalStateException, SystemException {
Assert.checkNotNullParam("xaRes", xaRes);
final int estimatedRemainingTime = getEstimatedRemainingTime();
if(estimatedRemainingTime == 0) throw Log.log.cannotEnlistToTimeOutTransaction(xaRes, this);
try {
if (!xaRes.setTransactionTimeout(estimatedRemainingTime)) {
Log.log.setTimeoutUnsuccessful(estimatedRemainingTime);
}
} catch (XAException e) {
throw Log.log.setTimeoutFailed(estimatedRemainingTime, e);
}
return transaction.enlistResource(xaRes);
}
public boolean delistResource(final XAResource xaRes, final int flag) throws IllegalStateException, SystemException {
Assert.checkNotNullParam("xaRes", xaRes);
return transaction.delistResource(xaRes, flag);
}
public void registerSynchronization(final Synchronization sync) throws RollbackException, IllegalStateException, SystemException {
Assert.checkNotNullParam("sync", sync);
transaction.registerSynchronization(new AssociatingSynchronization(sync));
}
/**
* Get the name of the node which initiated the transaction.
*
* @return the name of the node which initiated the transaction, or {@code null} if it could not be determined
*/
public String getParentName() {
return owner.getProvider().getNameFromXid(owner.getProvider().getXid(transaction));
}
/**
* Get the XID of the local transaction.
*
* @return the transaction XID (not {@code null})
*/
public Xid getXid() {
return owner.getProvider().getXid(transaction);
}
void registerInterposedSynchronization(final Synchronization sync) throws IllegalStateException {
Assert.checkNotNullParam("sync", sync);
owner.getProvider().registerInterposedSynchronization(transaction, new AssociatingSynchronization(sync));
}
public Object getResource(final Object key) throws NullPointerException {
return owner.getProvider().getResource(transaction, Assert.checkNotNullParamWithNullPointerException("key", key));
}
public void putResource(final Object key, final Object value) throws NullPointerException {
owner.getProvider().putResource(transaction, Assert.checkNotNullParamWithNullPointerException("key", key), value);
}
public Object putResourceIfAbsent(final Object key, final Object value) throws IllegalArgumentException {
return owner.getProvider().putResourceIfAbsent(transaction, Assert.checkNotNullParamWithNullPointerException("key", key), value);
}
Object getKey() {
return owner.getProvider().getKey(transaction);
}
boolean getRollbackOnly() {
return owner.getProvider().getRollbackOnly(transaction);
}
void suspend() throws SystemException {
notifyAssociationListeners(false);
TransactionManager transactionManager = owner.getProvider().getTransactionManager();
if (! transaction.equals(transactionManager.getTransaction())) {
throw Log.log.unexpectedProviderTransactionMismatch(transaction, transactionManager.getTransaction());
}
final Transaction transactionManagerTransaction = transactionManager.suspend();
if (! transaction.equals(transactionManagerTransaction)) {
throw Log.log.unexpectedProviderTransactionMismatch(transaction, transactionManagerTransaction);
}
}
void resume() throws SystemException {
TransactionManager transactionManager = owner.getProvider().getTransactionManager();
try {
transactionManager.resume(transaction);
} catch (InvalidTransactionException e) {
// should be impossible
throw Log.log.invalidTxnState();
}
final Transaction transactionManagerTransaction = transactionManager.getTransaction();
if (! transaction.equals(transactionManagerTransaction)) {
throw Log.log.unexpectedProviderTransactionMismatch(transaction, transactionManagerTransaction);
}
notifyAssociationListeners(true);
}
void verifyAssociation() {
TransactionManager transactionManager = owner.getProvider().getTransactionManager();
try {
final Transaction transactionManagerTransaction = transactionManager.getTransaction();
if (! transaction.equals(transactionManagerTransaction)) {
throw Log.log.unexpectedProviderTransactionMismatch(transaction, transactionManagerTransaction);
}
} catch (SystemException e) {
// should be impossible
throw Log.log.invalidTxnState();
}
}
/**
* Determine if this transaction was imported.
*
* @return {@code true} if the transaction was imported, {@code false} if it was initiated locally
*/
public boolean isImported() {
return owner.getProvider().isImported(transaction);
}
public T getProviderInterface(final Class providerInterfaceType) {
return owner.getProvider().getProviderInterface(transaction, providerInterfaceType);
}
public int hashCode() {
return transaction.hashCode();
}
public boolean equals(final Object obj) {
return obj instanceof LocalTransaction && equals((LocalTransaction) obj);
}
private boolean equals(final LocalTransaction obj) {
return this == obj || obj != null && transaction.equals(obj.transaction);
}
public String toString() {
return String.format("Local transaction (delegate=%s, owner=%s)", transaction, owner);
}
}