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

org.csc.phynixx.xa.XATransactionalBranch Maven / Gradle / Ivy

package org.csc.phynixx.xa;

/*
 * #%L
 * phynixx-xa
 * %%
 * Copyright (C) 2014 Christoph Schmidt-Casdorff
 * %%
 * 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 javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import org.csc.phynixx.common.exceptions.SampleTransactionalException;
import org.csc.phynixx.common.logger.IPhynixxLogger;
import org.csc.phynixx.common.logger.PhynixxLogManager;
import org.csc.phynixx.connection.IManagedConnectionEvent;
import org.csc.phynixx.connection.IPhynixxConnection;
import org.csc.phynixx.connection.IPhynixxManagedConnection;
import org.csc.phynixx.connection.IPhynixxManagedConnectionListener;
import org.csc.phynixx.connection.PhynixxManagedConnectionListenerAdapter;

/**
 * an XID represents a transaction branch. A transaction branch represents the
 * context a rollback/commit affects.

 * A transactional branch corresponds to an physical connection

 * Created by Christoph Schmidt-Casdorff on 10.02.14.
 */
class XATransactionalBranch extends
		PhynixxManagedConnectionListenerAdapter implements
		IPhynixxManagedConnectionListener {

	private static final IPhynixxLogger LOG = PhynixxLogManager
			.getLogger(XATransactionalBranch.class);

	private XAResourceProgressState progressState = XAResourceProgressState.ACTIVE;

	private boolean readOnly = true;

	private XAResourceActivationState activeState = XAResourceActivationState.ACTIVE;

	private volatile boolean rollbackOnly = false;

	private int heuristicState = 0;

	private Xid xid;

	private IPhynixxManagedConnection managedConnection;

	XATransactionalBranch(Xid xid,
			IPhynixxManagedConnection managedConnection) {
		this.xid = xid;
		this.managedConnection = managedConnection;
		this.managedConnection.addConnectionListener(this);
	}

	Xid getXid() {
		return xid;
	}

	IPhynixxManagedConnection getManagedConnection() {
		return managedConnection;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;

		XATransactionalBranch that = (XATransactionalBranch) o;

		if (!xid.equals(that.xid))
			return false;

		return true;
	}

	@Override
	public int hashCode() {
		return xid.hashCode();
	}

	void suspend() {
		if (this.activeState == XAResourceActivationState.SUSPENDED) {
			return;
		}
		if (this.progressState != XAResourceProgressState.ACTIVE) {
			throw new IllegalStateException(
					"A already prepared or preparing TX can not be suspended");

		}
		this.activeState = XAResourceActivationState.ACTIVE;
	}

	void resume() {
		if (this.activeState == XAResourceActivationState.SUSPENDED) {
			return;
		}
		this.activeState = XAResourceActivationState.ACTIVE;
	}

	public boolean isInActive() {
		return this.getActiveState() != XAResourceActivationState.ACTIVE;
	}

	public boolean isXAProtocolFinished() {
		return !this.isProgressStateIn(XAResourceProgressState.ACTIVE);
	}

	boolean isSuspended() {
		return this.getActiveState() == XAResourceActivationState.SUSPENDED;
	}

	boolean isReadOnly() {
		return readOnly;
	}

	void setReadOnly(boolean readOnly) {
		this.readOnly = readOnly;
	}

	private XAResourceActivationState getActiveState() {
		return activeState;
	}

	void setRollbackOnly(boolean rbOnly) {
		this.rollbackOnly = rbOnly;
	}

	boolean isRollbackOnly() {
		// new Exception("Read " +
		// this.rollbackOnly+" ObjectId"+super.toString()).printStackTrace();
		return rollbackOnly;
	}

	XAResourceProgressState getProgressState() {
		return progressState;
	}

	private void setProgressState(XAResourceProgressState progressState) {
		this.progressState = progressState;
	}

	boolean hasHeuristicOutcome() {
		return (this.heuristicState > 0);
	}

	int getHeuristicState() {
		return this.heuristicState;
	}

	/**
	 * maintains the state of the transactional branch if a commit is executed.

	 * The commit is executed
	 *
	 * @param onePhase
	 * @throws XAException
	 */
	void commit(boolean onePhase) throws XAException {
		if (this.getProgressState() == XAResourceProgressState.COMMITTED) {
			return;
		}

		if (this.hasHeuristicOutcome()) {
			throw new XAException(this.heuristicState);
		}
		if (!checkTXRequirements()) {
			throw new SampleTransactionalException("State not transactional "
					+ this);
		}

		this.checkRollback();

		if (this.getProgressState() != XAResourceProgressState.ACTIVE
				&& onePhase) {
			throw new XAException(XAException.XAER_RMFAIL);
		} else if (this.getProgressState() != XAResourceProgressState.PREPARED
				&& !onePhase) {
			throw new XAException(XAException.XAER_RMFAIL);
		} else if (this.getProgressState() != XAResourceProgressState.PREPARED
				&& this.getProgressState() != XAResourceProgressState.ACTIVE) {
			throw new XAException(XAException.XAER_RMFAIL);
		}

		this.setProgressState(XAResourceProgressState.COMMITTING);
		try {
			this.getManagedConnection().commit(onePhase);
		} catch (Exception e) {
			throwXAException(XAException.XAER_PROTO, e);
		}

		this.setProgressState(XAResourceProgressState.COMMITTED);

	}

	private void checkRollback() throws XAException {
		if (!this.getManagedConnection().hasCoreConnection()
				|| this.getManagedConnection().isClosed()) {
			this.setProgressState(XAResourceProgressState.ROLLING_BACK);
			throw new XAException(XAException.XA_RBROLLBACK);
		}

		if (this.isRollbackOnly()) {
			this.setProgressState(XAResourceProgressState.ROLLING_BACK);
			throw new XAException(XAException.XA_RBROLLBACK);
		}

		if (LOG.isInfoEnabled()) {
			LOG.info("RollbackOnly " + this.rollbackOnly);
		}
	}

	int prepare() throws XAException {
		// must find connection for this transaction
		if (!this.isProgressStateIn(XAResourceProgressState.ACTIVE)) {// must
																		// have
																		// had
																		// start()
																		// called
			LOG.error("XAResource " + this + " must have start() called ");
			throw new XAException(XAException.XAER_PROTO);
		}

		if (this.progressState == XAResourceProgressState.PREPARED) {
			if (this.isReadOnly()) {
				return (XAResource.XA_RDONLY);
			} else {
				return XAResource.XA_OK;
			}
		}

		// check if resource is READONLY -> no prepare is required
		if (this.isReadOnly()) {
			this.setProgressState(XAResourceProgressState.PREPARED);
			return (XAResource.XA_RDONLY);
		}

		if (this.progressState == XAResourceProgressState.PREPARING) {
			throw new IllegalStateException("XAResource already preparing");
		}
		if (!checkTXRequirements()) {
			throw new SampleTransactionalException("State not transactional "
					+ this);
		}

		if (this.hasHeuristicOutcome()) {
			throw new XAException(this.heuristicState);
		}
		this.checkRollback();
		this.setProgressState(XAResourceProgressState.PREPARING);
		try {
			this.getManagedConnection().prepare();
		} catch (Exception e) {
			throwXAException(XAException.XAER_PROTO, e);
		}
		this.setProgressState(XAResourceProgressState.PREPARED);

		// anything to commit?
		if (this.isReadOnly()) {
			return (XAResource.XA_RDONLY);
		} else {
			return XAResource.XA_OK;
		}
	}

	private boolean isProgressStateIn(XAResourceProgressState... states) {
		if (states == null || states.length == 0) {
			return false;
		}
		XAResourceProgressState currentStates = this.getProgressState();
		for (int i = 0; i < states.length; i++) {
			if (states[i] == currentStates) {
				return true;
			}
		}
		return false;
	}

	void rollback() throws XAException {
		if (!checkTXRequirements()) {
			LOG.error("State not transactional " + this);
			throw new XAException(XAException.XAER_PROTO);
		}

		LOG.debug("XATransactionalBranch:rollback for xid=" + xid
				+ " current Status="
				+ ConstantsPrinter.getStatusMessage(this.getProgressState()));
		switch (this.getProgressState()) {
		case PREPARED: // ready to do rollback
		case ACTIVE:
			try {
				LOG.debug("XATransactionalBranch:rollback try to perform the rollback operation");
				this.setProgressState(XAResourceProgressState.ROLLING_BACK);

				// do the rollback
				if (this.getManagedConnection() != null
						&& !this.getManagedConnection().isClosed()) {
					try {
						this.getManagedConnection().rollback();
					} catch (Exception e) {
						throwXAException(XAException.XAER_PROTO, e);
					}
				} else {
					LOG.info("XATransactionalBranch connection already closed -> no rollback");
				}

				this.setProgressState(XAResourceProgressState.ROLLEDBACK);
				// perform the rollback operation
				LOG.debug("XATransactionalBranch:rollback performed the rollback");
			} catch (Exception e) {
				LOG.error("Error in " + this + " \n" + e.getMessage());
				throwXAException(XAException.XAER_PROTO, e);
				// rollback will have been performed
			}
			break;
		default:
			LOG.error("XATransactionalBranch : rollback not permitted on TX state "
					+ ConstantsPrinter.getStatusMessage(this.getProgressState())
					+ " XAResoureTXState=" + this);
			throw new XAException(XAException.XAER_RMFAIL);
		}

	}

	private void throwXAException(int code, Throwable th) throws XAException {
		XAException xaException = new XAException(code);
		xaException.initCause(th);
	}

	/**
	 * checks, if the current state complies with the requirements of an active
	 * TX
	 *
	 * @return
	 */
	private boolean checkTXRequirements() {
		if (this.getManagedConnection() == null) {
			return false;
		}
		if (isInActive()) {
			return false;
		}

		return true;
	}

	public void close() {
		this.getManagedConnection().close();
		this.setProgressState(XAResourceProgressState.CLOSED);
	}

	@Override
	public void connectionRequiresTransaction(IManagedConnectionEvent event) {
		this.setReadOnly(false);
	}

	@Override
	public void connectionReleased(IManagedConnectionEvent event) {
		event.getManagedConnection().removeConnectionListener(this);
	}

	@Override
	public void connectionFreed(IManagedConnectionEvent event) {
		event.getManagedConnection().removeConnectionListener(this);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy