/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* 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.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
/*
* Copyright (C) 1999-2001 by HP Bluestone Software, Inc. All rights Reserved.
*
* HP Arjuna Labs,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: TransactionStatusConnectionManager.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.arjuna.recovery ;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import com.arjuna.ats.arjuna.common.Uid;
import com.arjuna.ats.arjuna.coordinator.ActionStatus;
import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.objectstore.RecoveryStore;
import com.arjuna.ats.arjuna.objectstore.StoreManager;
import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.internal.arjuna.common.UidHelper;
import com.arjuna.ats.internal.arjuna.recovery.TransactionStatusConnector;
import com.arjuna.ats.internal.arjuna.recovery.TransactionStatusManagerItem;
public class TransactionStatusConnectionManager
{
/**
* Gets a reference to the Object Store.
*/
public TransactionStatusConnectionManager()
{
if ( _recoveryStore == null )
{
_recoveryStore = StoreManager.getRecoveryStore();
}
updateTSMI() ;
}
/**
* Obtain the transaction status for the specified transaction.
* At this point we don't know the type of the transaction, only it's
* Uid. So, we're going to have to search through the object store.
* This assumes that the transaction id is present in the local object
* store. If it isn't, or there is a possibility it may not be, then
* you should use the other variant of this method and determine the
* type through another method.
*/
public int getTransactionStatus( Uid tranUid )
{
String transactionType = "" ;
int status = getTransactionStatus( transactionType, tranUid );
return status ;
}
/**
* Obtain the transaction status for the specified transaction type
* and transaction.
*/
public int getTransactionStatus( String transactionType, Uid tranUid )
{
int status = ActionStatus.INVALID ;
// extract process id from uid
String process_id = tranUid.getHexPid();
// if the tx is in the same JVM we rely on ActionStatusService directly.
// This skips the communication with TransactionStatusManager, which is just backed
// by ActionStatusService anyhow. That allows TSM to be turned off for local only cases if desired.
// Note: condition assumes ObjectStore is not shared between machines i.e. that processId is globally uniq.
if(! process_id.equals( _localUid.getHexPid()) ) {
status = getRemoteTransactionStatus(process_id, transactionType, tranUid);
}
/*
* Try to read status from disc locally if invalid status,
* as the tx may be local or, if it is remote, the
* TransactionStatusManager may have died or comms may
* have failed.
* Use an ActionStatusService instance as that's what the remote
* recovery manager would have used, and it contains all of the logic
* to find and map the state type.
*/
if ( status == ActionStatus.INVALID )
{
ActionStatusService ass = new ActionStatusService();
try
{
status = ass.getTransactionStatus(transactionType, tranUid.stringForm());
}
catch ( Exception ex ) {
tsLogger.i18NLogger.warn_recovery_TransactionStatusConnectionManager_1(ex);
}
}
return status ;
}
/**
* Use the TransactionStatusConnector to remotly query a transaction manager to get the tx status.
*
* @param process_id the process identifier
* @param transactionType the type of the transaction
* @param tranUid the Uid of the transaction
* @return the remote transaction status
*/
private int getRemoteTransactionStatus(String process_id, String transactionType, Uid tranUid ) {
int status = ActionStatus.INVALID ;
// tx is not local, so use process id to index into
// hash table to obtain transaction status connector
// with which to retrieve the transaction status.
// Note: assumes ObjectStore is not shared between machienes
// otherwise we need to key on hostname,process_id tuple.
if ( ! _tscTable.containsKey ( process_id ) )
{
updateTSMI();
}
if ( _tscTable.containsKey ( process_id ) )
{
TransactionStatusConnector tsc = (TransactionStatusConnector) _tscTable.get( process_id ) ;
if ( tsc.isDead() )
{
_tscTable.remove( process_id ) ;
tsc.delete() ;
tsc = null ;
}
else
{
status = tsc.getTransactionStatus( transactionType, tranUid ) ;
}
}
return status;
}
/**
* Examine the Object Store for any new TrasactionStatusManagerItem
* objects, and add to local hash table.
*/
public void updateTSMI()
{
boolean tsmis = false ;
InputObjectState uids = new InputObjectState() ;
Vector tsmiVector = new Vector() ;
try
{
tsmis = _recoveryStore.allObjUids( _typeName, uids ) ;
}
catch ( ObjectStoreException ex ) {
tsLogger.i18NLogger.warn_recovery_TransactionStatusConnectionManager_2(ex);
}
// cycle through each item, and update tsmTable with any
// new TransactionStatusManagerItems
if ( tsmis )
{
Uid theUid = null;
boolean moreUids = true ;
while (moreUids)
{
try
{
theUid = UidHelper.unpackFrom(uids);
if ( theUid.equals( Uid.nullUid() ) )
{
moreUids = false ;
}
else
{
Uid newUid = new Uid (theUid) ;
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("found process uid "+newUid);
}
tsmiVector.addElement(newUid) ;
}
}
catch (Exception ex )
{
moreUids = false;
}
}
}
// for each TransactionStatusManager found, if their is
// not an entry in the local hash table for it then add it.
Enumeration tsmiEnum = tsmiVector.elements() ;
while ( tsmiEnum.hasMoreElements() )
{
Uid currentUid = (Uid) tsmiEnum.nextElement() ;
String process_id = currentUid.getHexPid();
if ( ! _tscTable.containsKey( process_id ) )
{
TransactionStatusConnector tsc = new TransactionStatusConnector ( process_id, currentUid ) ;
if ( tsc.isDead() )
{
tsc.delete() ;
tsc = null ;
}
else
{
_tscTable.put ( process_id, tsc ) ;
}
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("added TransactionStatusConnector to table for process uid "+process_id);
}
}
}
}
// Type within ObjectStore.
private static String _typeName = TransactionStatusManagerItem.typeName() ;
// Table of process ids and their transaction status managers items.
private Hashtable _tscTable = new Hashtable() ;
// Reference to object store.
private static RecoveryStore _recoveryStore = null ;
private static Uid _localUid = new Uid();
}