org.csc.phynixx.xa.PhynixxManagedXAConnection Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of phynixx-xa Show documentation
Show all versions of phynixx-xa Show documentation
enabled the connection of phynixx-connection take part in a XA protocol
package org.csc.phynixx.xa;
/*
* #%L
* phynixx-xa
* %%
* Copyright (C) 2014 csc
* %%
* 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.
* #L%
*/
import org.csc.phynixx.common.cast.ImplementorUtils;
import org.csc.phynixx.common.exceptions.DelegatedRuntimeException;
import org.csc.phynixx.common.exceptions.ExceptionUtils;
import org.csc.phynixx.common.logger.IPhynixxLogger;
import org.csc.phynixx.common.logger.PhynixxLogManager;
import org.csc.phynixx.connection.IPhynixxConnection;
import org.csc.phynixx.connection.IPhynixxManagedConnection;
import org.csc.phynixx.connection.IPhynixxManagedConnectionFactory;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
/**
* keeps the XAresource's association to the transactional branch (given via XID).
*
* @author Christoph Schmidt-Casdorff
*/
class PhynixxManagedXAConnection implements IPhynixxXAConnection {
private static final IPhynixxLogger LOG = PhynixxLogManager.getLogger(PhynixxManagedXAConnection.class);
private final PhynixxXAResource xaResource;
private IPhynixxManagedConnectionFactory managedConnectionFactory;
private transient ITransactionBinding transactionBinding = null;
private TransactionManager transactionManager = null;
private final IXATransactionalBranchRepository xaTransactionalBranchDictionary;
private boolean enlisted;
PhynixxManagedXAConnection(PhynixxXAResource xaResource,
TransactionManager transactionManager,
IXATransactionalBranchRepository xaTransactionalBranchDictionary,
IPhynixxManagedConnectionFactory managedConnectionFactory) {
this.xaResource = xaResource;
this.xaTransactionalBranchDictionary = xaTransactionalBranchDictionary;
this.managedConnectionFactory = managedConnectionFactory;
this.transactionManager = transactionManager;
}
public XAResource getXAResource() {
return xaResource;
}
/**
* @return !=null if an if the XAConnection is bound to a global transaction
*/
XATransactionalBranch toGlobalTransactionBranch() {
if (this.transactionBinding != null && this.transactionBinding.getTransactionBindingType() == TransactionBindingType.GlobalTransaction) {
return ImplementorUtils.cast(this.transactionBinding, GlobalTransactionProxy.class).getGlobalTransactionalBranch();
}
return null;
}
/**
* call by the XCAResource when {@link javax.transaction.xa.XAResource#start(javax.transaction.xa.Xid, int)} is called.
* A TransactionalBranch for the given XID ist expected to be created.
*
* @param xid
*/
void startTransactionalBranch(Xid xid) {
cleanupTransactionBinding();
final TransactionBindingType transactionBindingType = getTransactionBindingType();
// already associated to a local transaction
if (transactionBindingType == TransactionBindingType.LocalTransaction) {
LocalTransactionProxy localTransactionProxy = ImplementorUtils.cast(this.transactionBinding, LocalTransactionProxy.class);
if (localTransactionProxy.hasTransactionalData()) {
throw new IllegalStateException("Connection is associated to a local transaction and has uncommitted transactional data");
}
XATransactionalBranch xaTransactionalBranch = this.xaTransactionalBranchDictionary.findTransactionalBranch(xid);
// xaTransactionalBranch!=null => joining a existing transactional branch
if (xaTransactionalBranch == null) {
xaTransactionalBranch= this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, localTransactionProxy.getConnection());
}
this.transactionBinding = new GlobalTransactionProxy().assignTransactionalBranch(this.xaTransactionalBranchDictionary.findTransactionalBranch(xid));
// no transaction binding
} else if (transactionBindingType == TransactionBindingType.NoTransaction) {
XATransactionalBranch xaTransactionalBranch = this.xaTransactionalBranchDictionary.findTransactionalBranch(xid);
/// xaTransactionalBranch!=null => joining a existing transactional branch
if (xaTransactionalBranch == null) {
IPhynixxManagedConnection physicalConnection = this.createPhysicalConnection();
xaTransactionalBranch =
this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, physicalConnection);
this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, physicalConnection);
}
this.transactionBinding = new GlobalTransactionProxy().assignTransactionalBranch(this.xaTransactionalBranchDictionary.findTransactionalBranch(xid));
// if bound to a global TX it has to be the same XID
} else if (transactionBindingType == TransactionBindingType.GlobalTransaction) {
GlobalTransactionProxy globalTransactionProxy = ImplementorUtils.cast(this.transactionBinding, GlobalTransactionProxy.class);
XATransactionalBranch xaTransactionalBranch = this.xaTransactionalBranchDictionary.findTransactionalBranch(xid);
/// xaTransactionalBranch!=null => joining a existing transactional branch
if (xaTransactionalBranch == null) {
IPhynixxManagedConnection physicalConnection = this.createPhysicalConnection();
xaTransactionalBranch =
this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, physicalConnection);
this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, physicalConnection);
} else {
if (globalTransactionProxy.getGlobalTransactionalBranch()==null) {
throw new IllegalStateException("XAConnection already associated toXID but not to a global Transaction ");
}
if (!globalTransactionProxy.getXid().equals(xid)) {
throw new IllegalStateException("XAConnection already associated to a global Transaction "+globalTransactionProxy.getXid());
}
}
this.transactionBinding = globalTransactionProxy.assignTransactionalBranch(this.xaTransactionalBranchDictionary.findTransactionalBranch(xid));
}
}
private void cleanupTransactionBinding() {
// cleanup
if (getTransactionBindingType() == TransactionBindingType.LocalTransaction) {
LocalTransactionProxy localTransactionProxy = ImplementorUtils.cast(this.transactionBinding, LocalTransactionProxy.class);
if (localTransactionProxy.isClosed()) {
this.transactionBinding.release();
this.transactionBinding = null;
}
}
}
private TransactionBindingType getTransactionBindingType() {
return (this.transactionBinding != null) ? this.transactionBinding.getTransactionBindingType() : TransactionBindingType.NoTransaction;
}
private IPhynixxManagedConnection createPhysicalConnection() {
return this.managedConnectionFactory.getManagedConnection();
}
void resumeTransactionalBranch(Xid xid) {
this.cleanupTransactionBinding();
TransactionBindingType transactionBindingType = getTransactionBindingType();
if (transactionBindingType == TransactionBindingType.GlobalTransaction) {
throw new IllegalStateException("XAConnection associated to a global transaction and can not be resumed");
}
XATransactionalBranch transactionalBranch = this.xaTransactionalBranchDictionary.findTransactionalBranch(xid);
if (transactionalBranch == null) {
throw new IllegalStateException("No suspended branch for XID " + xid);
}
transactionalBranch.resume();
this.transactionBinding = new GlobalTransactionProxy().assignTransactionalBranch(transactionalBranch);
}
void suspendTransactionalBranch(Xid xid) {
this.cleanupTransactionBinding();
TransactionBindingType transactionBindingType = getTransactionBindingType();
if (transactionBindingType != TransactionBindingType.GlobalTransaction) {
throw new IllegalStateException("XAConnection not associated to a global transaction and can not be suspended");
}
GlobalTransactionProxy globalTransactionProxy = ImplementorUtils.cast(this.transactionBinding, GlobalTransactionProxy.class);
if (!globalTransactionProxy.getXid().equals(xid)) {
throw new IllegalStateException("XAConnection already associated to a global Transaction");
}
globalTransactionProxy.getGlobalTransactionalBranch().suspend();
this.transactionBinding.release();
this.transactionBinding = null;
}
void joinTransactionalBranch(Xid xid) {
cleanupTransactionBinding();
TransactionBindingType transactionBindingType = getTransactionBindingType();
if (transactionBindingType == TransactionBindingType.GlobalTransaction) {
throw new IllegalStateException("XAConnection already associated to a global transaction and can not be joined to XID " + xid);
}
this.transactionBinding.release();
this.transactionBinding = null;
}
/**
* call by the XCAResource when {@link javax.transaction.xa.XAResource#end(javax.transaction.xa.Xid, int)} is called
*
* @param xid
*/
void closeTransactionalBranch(Xid xid) {
if (this.getTransactionBindingType() != TransactionBindingType.GlobalTransaction) {
throw new IllegalStateException("XAConnection not associated to a global transaction");
}
GlobalTransactionProxy globalTransactionProxy = ImplementorUtils.cast(this.transactionBinding, GlobalTransactionProxy.class);
XATransactionalBranch transactionalBranch = globalTransactionProxy.getGlobalTransactionalBranch();
transactionalBranch.close();
delistTransaction();
}
@Override
public void close() {
if(this.transactionBinding!=null) {
this.transactionBinding.close();
this.transactionBinding=null;
}
}
private void delistTransaction() {
if (this.transactionBinding != null) {
this.transactionBinding.release();
}
this.transactionBinding = null;
}
public IPhynixxManagedConnection getManagedConnection() {
// Connection wid in TX eingetragen ....
this.checkTransactionBinding();
return this.transactionBinding.getConnection();
}
public C getConnection() {
return this.getManagedConnection().toConnection();
}
boolean isInGlobalTransaction() {
try {
return transactionManager!=null && this.transactionManager.getTransaction()!=null;
} catch (SystemException e) {
throw new DelegatedRuntimeException(e);
}
}
/**
* if necessary the current xa resource is enlisted in the current TX.
*
* The enlistment calls the{@link javax.transaction.xa.XAResource#start(javax.transaction.xa.Xid, int)}. This call associates the Xid with the current instance
*/
private void enlistTransaction() {
this.cleanupTransactionBinding();
TransactionBindingType transactionBindingType = this.getTransactionBindingType();
// not associated to global transaction, try to associate a global transaction
if (this.isInGlobalTransaction() && transactionBindingType != TransactionBindingType.GlobalTransaction) {
try {
Transaction ntx = this.transactionManager.getTransaction();
// Bitronix calls start on reaction of enlist --- check if cycle
if (!enlisted && ntx != null) {
this.enlisted=true;
// enlisted makes startTransaactionalBranch calling
this.enlisted = ntx.enlistResource(this.xaResource);
if (!enlisted) {
LOG.error("Enlisting " + xaResource + " failed");
} else {
}
} else {
LOG.debug(
"SampleXAConnection:connectionRequiresTransaction (no globalTransaction found)");
}
} catch (RollbackException n) {
LOG.error(
"SampleXAConnection:prevokeAction enlistResource exception : "
+ n.toString());
} catch (SystemException n) {
LOG.error("SampleXAConnection:connectionRequiresTransaction " + n + "\n" + ExceptionUtils.getStackTrace(n));
throw new DelegatedRuntimeException(n);
}
} else if (transactionBindingType == TransactionBindingType.NoTransaction) {
this.transactionBinding = new LocalTransactionProxy(this.managedConnectionFactory.getManagedConnection());
} else // In Global Transaction and associated to a global transaction => nothing to do
if (this.isInGlobalTransaction() && transactionBindingType == TransactionBindingType.GlobalTransaction) {
} else
// Not in Global Transaction and associated to a local transaction => nothing to do
if (!this.isInGlobalTransaction() && transactionBindingType == TransactionBindingType.LocalTransaction) {
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PhynixxManagedXAConnection that = (PhynixxManagedXAConnection) o;
return xaResource.equals(that.xaResource);
}
@Override
public int hashCode() {
return xaResource.hashCode();
}
@Override
public String toString() {
return "PhynixxManagedXAConnection{" +
"xaResource=" + xaResource +
", transactionBinding=" + this.transactionBinding +
'}';
}
void checkTransactionBinding() {
if( this.isInGlobalTransaction()) {
this.enlistTransaction();
}
if (this.transactionBinding == null || this.transactionBinding.getTransactionBindingType() == TransactionBindingType.NoTransaction) {
this.transactionBinding = new LocalTransactionProxy(this.createPhysicalConnection());
}
}
}