/*
* JacORB - a free Java ORB
*
* Copyright (C) 1999-2004 Gerald Brose
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
package org.jacorb.transaction;
import java.util.Hashtable;
import org.slf4j.Logger;
import org.jacorb.orb.ORB;
import org.omg.CORBA.Any;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosTransactions.Control;
import org.omg.CosTransactions.ControlHelper;
import org.omg.CosTransactions.Current;
import org.omg.CosTransactions.HeuristicHazard;
import org.omg.CosTransactions.HeuristicMixed;
import org.omg.CosTransactions.InvalidControl;
import org.omg.CosTransactions.NoTransaction;
import org.omg.CosTransactions.PropagationContext;
import org.omg.CosTransactions.PropagationContextHelper;
import org.omg.CosTransactions.Status;
import org.omg.CosTransactions.SubtransactionsUnavailable;
import org.omg.CosTransactions.TransIdentity;
import org.omg.CosTransactions.TransactionFactory;
import org.omg.CosTransactions.TransactionFactoryHelper;
import org.omg.CosTransactions.otid_t;
/**
* This class represents the transaction current.
* It is a very simple implementation wich mostly
* maps to the methods in the control.
*
* @author Nicolas Noffke
* @author Vladimir Mencl
* @version $Id: TransactionCurrentImpl.java,v 1.19 2009-05-03 21:36:59 andre.spiegel Exp $
*/
public class TransactionCurrentImpl
extends org.omg.CORBA.LocalObject
implements Current
{
private static final int DEFAULT_TIMEOUT = 30;
private Hashtable contexts = null;
private Hashtable timeouts = null;
private ORB orb = null;
private static int slot_id = -1; /* used from static getControl */
/** there will only be ony logger instance for all TX services in a process...*/
private static Logger logger;
private TransactionFactory factory = null;
public TransactionCurrentImpl(ORB orb, int slot_id)
{
this.orb = orb;
this.slot_id = slot_id;
logger =
orb.getConfiguration().getLogger("jacorb.tx_service.current");
contexts = new Hashtable();
timeouts = new Hashtable();
try
{
NamingContextExt nc =
NamingContextExtHelper.narrow(orb.resolve_initial_references("NameService"));
NameComponent [] name = new NameComponent[1];
name[0] = new NameComponent( "TransactionService", "service");
factory = TransactionFactoryHelper.narrow(nc.resolve(name));
}
catch (Exception e)
{
if (logger.isErrorEnabled())
logger.error("Unable to obtain Transaction Service reference. Giving up.", e );
throw new Error(e.getMessage());
}
}
/**
* Creates a non-functional current.
*/
public TransactionCurrentImpl()
{
contexts = new Hashtable();
timeouts = new Hashtable();
}
/**
* This method is a convenience method for the server
* programmer the exctract the Control from the
* PICurrent.
*/
public static Control getControl(org.omg.CORBA.ORB orb)
{
try
{
org.omg.PortableInterceptor.Current pi_current =
(org.omg.PortableInterceptor.Current)orb.resolve_initial_references("PICurrent");
PropagationContext context = PropagationContextHelper.extract
(pi_current.get_slot(slot_id));
return ControlHelper.extract(context.implementation_specific_data);
}
catch(Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Unable to obtain Transaction Service reference. Giving up.", e );
}
return null;
}
// implementation of org.omg.CosTransactions.CurrentOperations interface
/**
* Start a new transaction. The propagation context will be transfered
* on ALL communication (~1k extra data) from now on, until
* the transaction is committed or rolled back. <br>
* NOTICE: the PropagationContext might not be set up fully
* compliant to the Spec.
*/
public void begin()
throws SubtransactionsUnavailable
{
Thread thread = Thread.currentThread();
if (contexts.containsKey(thread))
throw new SubtransactionsUnavailable();
int timeout = (timeouts.containsKey(thread))?
((Integer) timeouts.get(thread)).intValue() : DEFAULT_TIMEOUT;
Control control = factory.create(timeout);
contexts.put(thread, control);
try
{
org.omg.PortableInterceptor.Current pi_current =
(org.omg.PortableInterceptor.Current) orb.resolve_initial_references("PICurrent");
// the info inserted here is actually never needed and mostly a waste of
// space/bandwidth, since the control itself is transfered also.
TransIdentity id = new TransIdentity(control.get_coordinator(),
control.get_terminator(),
new otid_t(0, 0, new byte[0]));
Any control_any = orb.create_any();
ControlHelper.insert(control_any, control);
PropagationContext context = new PropagationContext(timeout,
id, new TransIdentity[0],
control_any);
Any context_any = orb.create_any();
PropagationContextHelper.insert(context_any, context);
pi_current.set_slot(slot_id, context_any);
}
catch (Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Exception: ", e);
}
}
public void commit(boolean report_heuristics)
throws NoTransaction, HeuristicMixed, HeuristicHazard
{
Thread current = Thread.currentThread();
if (! contexts.containsKey(current))
throw new NoTransaction();
Control control = null;
try
{
control = (Control) contexts.get(current);
control.get_terminator().commit(report_heuristics);
control._release();
removeContext(current);
}
catch (org.omg.CORBA.TRANSACTION_ROLLEDBACK tr)
{
// Transaction was rolledback.
if (logger.isDebugEnabled())
logger.debug("TRANSACTION_ROLLEDBACK: ", tr);
control._release();
removeContext(current);
throw tr; // re-throw the exception
}
catch (Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Exception: ", e);
}
}
/**
* This and the following method should actually throw
* NoTransaction, but that is against the spec.
*/
public Control get_control()
{
return (Control) contexts.get(Thread.currentThread());
}
public Status get_status()
{
Thread current = Thread.currentThread();
if (! contexts.containsKey(current))
return null;
try
{
return ((Control) contexts.get(current)).get_coordinator().get_status();
}
catch (Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Exception: ", e);
}
return null;
}
public String get_transaction_name()
{
Thread current = Thread.currentThread();
if (! contexts.containsKey(current))
return null;
try
{
return ((Control) contexts.get(current)).get_coordinator().get_transaction_name();
}
catch (Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Exception: ", e);
}
return null;
}
public void resume(Control which)
throws InvalidControl
{
setCurrentThreadContext(which);
}
public void rollback()
throws NoTransaction
{
Thread current = Thread.currentThread();
if (! contexts.containsKey(current))
throw new NoTransaction();
try
{
Control control = (Control) contexts.get(current);
control.get_terminator().rollback();
control._release();
removeContext(current);
}
catch (Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Exception: ", e);
}
}
public void rollback_only()
throws NoTransaction
{
Thread current = Thread.currentThread();
if (! contexts.containsKey(current))
throw new NoTransaction();
try
{
Control control = (Control) contexts.get(current);
control.get_coordinator().rollback_only();
control._release();
removeContext(current);
}
catch (Exception e){
if (logger.isDebugEnabled())
logger.debug("Exception: ", e);
}
}
public void set_timeout(int seconds)
{
timeouts.put(Thread.currentThread(), new Integer(seconds));
}
public Control suspend()
{
Control result = get_control();
removeContext(Thread.currentThread());
return result;
}
public void setCurrentThreadContext(Control control)
{
Thread thread = Thread.currentThread();
contexts.put(thread, control);
try
{
org.omg.PortableInterceptor.Current pi_current =
(org.omg.PortableInterceptor.Current)orb.resolve_initial_references("PICurrent");
// the info inserted here is actually never needed and mostly a waste of
// space/bandwidth, since the control itself is transfered also.
TransIdentity id =
new TransIdentity(control.get_coordinator(),
control.get_terminator(),
new otid_t(0, 0, new byte[0]));
Any control_any = orb.create_any();
ControlHelper.insert(control_any, control);
int timeout =
(timeouts.containsKey(thread))?
((Integer) timeouts.get(thread)).intValue() :
DEFAULT_TIMEOUT;
PropagationContext context =
new PropagationContext(timeout,
id,
new TransIdentity[0],
control_any);
Any context_any = orb.create_any();
PropagationContextHelper.insert(context_any, context);
pi_current.set_slot(slot_id, context_any);
}
catch (Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Exception: ", e);
}
}
private void removeContext(Thread current)
{
//remove control from Hashtable
contexts.remove(current);
try
{
org.omg.PortableInterceptor.Current pi_current =
(org.omg.PortableInterceptor.Current)orb.resolve_initial_references("PICurrent");
//remove control from PICurrent by overwriting it with
//an empty any
Any empty = orb.create_any();
pi_current.set_slot(slot_id, empty);
}
catch (Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Exception: ", e);
}
}
} // TransactionCurrentImpl