/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat, Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.jboss.narayana.blacktie.jatmibroker.core.tx;
import java.util.Hashtable;
import java.util.Properties;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jboss.narayana.blacktie.jatmibroker.core.conf.AtmiBrokerEnvXML;
import org.jboss.narayana.blacktie.jatmibroker.core.conf.ConfigurationException;
import org.jboss.narayana.blacktie.jatmibroker.core.transport.OrbManagement;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.CosTransactions.Control;
import org.omg.CosTransactions.Status;
import org.omg.CosTransactions.Terminator;
import org.omg.CosTransactions.TransactionFactory;
import org.omg.CosTransactions.TransactionFactoryHelper;
import org.omg.CosTransactions.Unavailable;
import org.omg.PortableServer.POAManagerPackage.AdapterInactive;
public class TransactionImpl {
private static final Logger log = LogManager.getLogger(TransactionImpl.class);
static TransactionFactory transactionFactory;
private int timeout;
protected Control control;
private Terminator terminator;
private Hashtable _childThreads;
private boolean active = true;
private OrbManagement orbManagement;
public void finalize() throws Throwable {
// TODO use ThreadActionData.purgeAction(this); not popAction
ThreadActionData.popAction();
super.finalize();
}
public TransactionImpl(int aTimeout) throws TransactionException, NotFound, CannotProceed,
org.omg.CosNaming.NamingContextPackage.InvalidName, InvalidName, AdapterInactive, ConfigurationException {
log.debug("TransactionImpl constructor");
if (current() != null)
throw new TransactionException("Nested transactions are not supported");
timeout = aTimeout;
control = null;
terminator = null;
try {
AtmiBrokerEnvXML client = new AtmiBrokerEnvXML();
Properties properties = client.getProperties();
orbManagement = OrbManagement.getInstance(properties);
String toLookup = (String) properties.get("blacktie.trans.factoryid");
org.omg.CORBA.Object aObject = orbManagement.getNamingContextExt().resolve_str(toLookup);
transactionFactory = TransactionFactoryHelper.narrow(aObject);
} catch (org.omg.CORBA.UserException cue) {
throw new TransactionException(cue.getMessage(), cue);
}
log.debug(" creating Control");
control = transactionFactory.create(timeout);
ThreadActionData.pushAction(this);
log.debug(" created Control " + control);
setTerminator(control);
}
public TransactionImpl(String controlIOR) throws ConfigurationException, TransactionException {
TransactionImpl curr = current();
timeout = -1;
try {
AtmiBrokerEnvXML client = new AtmiBrokerEnvXML();
Properties properties = client.getProperties();
orbManagement = OrbManagement.getInstance(properties);
} catch (org.omg.CORBA.UserException cue) {
throw new TransactionException(cue.getMessage(), cue);
}
org.omg.CORBA.Object obj = orbManagement.getOrb().string_to_object(controlIOR);
if (curr != null) {
log.debug("current() != null comparing IORs");
String pIOR = curr.getControlIOR();
org.omg.CORBA.Object pObj = orbManagement.getOrb().string_to_object(pIOR);
log.debug("pIOR=" + pIOR + " pObj=" + pObj);
if (pObj != null && pObj._is_equivalent(obj)) {
log.debug("Different IORs same object");
ThreadActionData.popAction();
} else {
log.info("Different IORs and different object");
throw new TransactionException("Nested transactions are not supported");
}
}
control = org.omg.CosTransactions.ControlHelper.narrow(obj);
ThreadActionData.pushAction(this);
setTerminator(control);
}
public Status getStatus() throws Unavailable {
return control.get_coordinator().get_status();
}
public boolean equals(java.lang.Object obj) {
if (obj instanceof TransactionImpl) {
TransactionImpl other = (TransactionImpl) obj;
return control.equals(other.control);
}
return false;
}
private void setTerminator(Control c) throws TransactionException {
try {
terminator = control.get_terminator();
log.debug("Terminator is " + terminator);
} catch (Unavailable e) {
throw new TransactionException("Could not get the terminator", e);
}
}
public String getControlIOR() {
return orbManagement.getOrb().object_to_string(control);
}
public static TransactionImpl current() {
log.trace("Getting current");
return ThreadActionData.currentAction();
}
public Control getControl() {
log.debug("TransactionImpl getControl");
return control;
}
public void commit() throws TransactionException {
log.debug("TransactionImpl commit");
try {
log.debug("calling commit");
terminator.commit(true);
active = false;
ThreadActionData.popAction();
log.debug("called commit on terminator");
} catch (Exception e) {
// TODO build an TransactionImpl hierarchy so we can perform
// better
// error reporting
// presume abort and dissassociate the tx from the the current
// thread
active = false;
ThreadActionData.popAction();
throw new TransactionException("Could not commit the transaction: " + e.getMessage(), e);
}
}
public void rollback() throws TransactionException {
log.debug("TransactionImpl rollback");
try {
terminator.rollback();
active = false;
ThreadActionData.popAction();
log.debug("called rollback on terminator");
} catch (Exception e) {
// presume abort and dissassociate the tx from the the current
// thread
active = false;
ThreadActionData.popAction();
throw new TransactionException("Could not rollback the transaction: " + e.getMessage(), e);
}
}
public void rollback_only() throws TransactionException {
log.debug("TransactionImpl rollback_only");
try {
control.get_coordinator().rollback_only();
log.debug("tx marked rollback only");
} catch (Unavailable e) {
throw new TransactionException("Tx Manager unavailable for set rollback only", e);
} catch (Exception e) {
throw new TransactionException("Error setting rollback only", e);
}
}
/**
* Add the specified thread to the list of threads associated with this transaction.
*
* @return <code>true</code> if successful, <code>false</code> otherwise.
*/
public final boolean addChildThread(Thread t) {
if (t == null)
return false;
synchronized (this) {
// if (actionStatus <= ActionStatus.ABORTING)
if (active) {
if (_childThreads == null)
_childThreads = new Hashtable();
// TODO _childThreads.put(ThreadUtil.getThreadId(t), t); //
// makes sure so we don't get duplicates
return true;
}
}
return false;
}
/**
* Remove a child thread. The current thread is removed.
*
* @return <code>true</code> if successful, <code>false</code> otherwise.
*/
public final boolean removeChildThread() // current thread
{
return removeChildThread(ThreadUtil.getThreadId());
}
/**
* Remove the specified thread from the transaction.
*
* @return <code>true</code> if successful, <code>false</code> otherwise.
*/
public final boolean removeChildThread(String threadId) {
if (threadId == null)
return false;
synchronized (this) {
if (_childThreads != null) {
_childThreads.remove(threadId);
return true;
}
}
return false;
}
public final TransactionImpl parent() {
return null;
}
/**
* Suspend the transaction association from the invoking thread. When this operation returns, the thread will not be
* associated with a transaction.
*
* @return a handle on the current TransactionImpl (if any) so that the thread can later resume association if required.
*/
public static final TransactionImpl suspend() {
TransactionImpl curr = ThreadActionData.currentAction();
if (curr != null)
ThreadActionData.purgeActions();
return curr;
}
/**
* Resume transaction association on the current thread. If the specified transaction is null, then this is the same as
* doing a suspend. If the current thread is associated with a transaction then that association will be lost.
*
* @param act the transaction to associate.
* @return <code>true</code> if association is successful, <code>false</code> otherwise.
*/
public static final boolean resume(TransactionImpl act) {
if (act == null)
suspend();
else
ThreadActionData.restoreActions(act);
return true;
}
}