/* ====================================================================
* Limited Evaluation License:
*
* This software is open source, but licensed. The license with this package
* is an evaluation license, which may not be used for productive systems. If
* you want a full license, please contact us.
*
* The exclusive owner of this work is the OpenRate project.
* This work, including all associated documents and components
* is Copyright of the OpenRate project 2006-2015.
*
* The following restrictions apply unless they are expressly relaxed in a
* contractual agreement between the license holder or one of its officially
* assigned agents and you or your organisation:
*
* 1) This work may not be disclosed, either in full or in part, in any form
* electronic or physical, to any third party. This includes both in the
* form of source code and compiled modules.
* 2) This work contains trade secrets in the form of architecture, algorithms
* methods and technologies. These trade secrets may not be disclosed to
* third parties in any form, either directly or in summary or paraphrased
* form, nor may these trade secrets be used to construct products of a
* similar or competing nature either by you or third parties.
* 3) This work may not be included in full or in part in any application.
* 4) You may not remove or alter any proprietary legends or notices contained
* in or on this work.
* 5) This software may not be reverse-engineered or otherwise decompiled, if
* you received this work in a compiled form.
* 6) This work is licensed, not sold. Possession of this software does not
* imply or grant any right to you.
* 7) You agree to disclose any changes to this work to the copyright holder
* and that the copyright holder may include any such changes at its own
* discretion into the work
* 8) You agree not to derive other works from the trade secrets in this work,
* and that any such derivation may make you liable to pay damages to the
* copyright holder
* 9) You agree to use this software exclusively for evaluation purposes, and
* that you shall not use this software to derive commercial profit or
* support your business or personal activities.
*
* This software is provided "as is" and any expressed or impled warranties,
* including, but not limited to, the impled warranties of merchantability
* and fitness for a particular purpose are disclaimed. In no event shall
* The OpenRate Project or its officially assigned agents be liable to any
* direct, indirect, incidental, special, exemplary, or consequential damages
* (including but not limited to, procurement of substitute goods or services;
* Loss of use, data, or profits; or any business interruption) however caused
* and on theory of liability, whether in contract, strict liability, or tort
* (including negligence or otherwise) arising in any way out of the use of
* this software, even if advised of the possibility of such damage.
* This software contains portions by The Apache Software Foundation, Robert
* Half International.
* ====================================================================
*/
package OpenRate.transaction;
import OpenRate.IPipeline;
import OpenRate.OpenRate;
import OpenRate.configurationmanager.ClientManager;
import OpenRate.configurationmanager.IEventInterface;
import OpenRate.exception.ExceptionHandler;
import OpenRate.exception.InitializationException;
import OpenRate.logging.ILogger;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
/**
* This is the pipeline wide transaction manager, used for coordinating the processing
* across multiple modules. The idea is that each module registers itself as a
* client to the transaction manager, and using the client number and the
* transaction number, it tells the transaction manager about the status of
* the transaction from its point of view, and the records it has processed.
*
* The transaction manager module then calculates an overall transaction status
* and this is used to control the pipeline. All client modules are obliged to
* read the overall transaction status at opportune moments to understand how
* the transaction processing should progress.
*
* This version of the Transaction Manager is for the asynchronous pipeline,
* using buffers between the modules and therefore has a simplified handshaking
* of the transaction:
*
* NONE - Transaction opened but not initialised
* PROCESSING - transaction processing input stream
* FLUSHED - All input done
* FINISHED_OK - Transaction committed
* FINISHED_ERR - Transaction rolled back or cancelled
* COMPLETE - All finalisation processing completed
*
* Notification to the client modules is triggered on FLUSHED status so that
* the input and output modules can complete the persistent storage calculation
* and in this case, the output module gets to work first, then the input module
* as there may be the need to cancel due to output problems more probably than
* input problems.
*
* Notification also happens in the case of an ABORT request.
*
* For the transaction from FLUSHED and FINISHED, the check of the status is
* initiated by the pipe controller, which avoids re-entry into the TM at very
* little overhead cost.
*
* ToDo:
* Make the modules wait for notifications of transaction finishing?
*/
public class TransactionManager implements ITransactionManager, IEventInterface
{
// This is the pipeline that we are in, used for logging and property retrieval
private IPipeline pipeline;
// Tells us whether we are in a transaction
private boolean TMStarted = false;
private ConcurrentHashMap<Integer, TransactionInfo> transactionList; // This is the map of the transactions in progress
private static int nextTransactionNumber = 0; // The transaction number sequence generator
private int numberOfClients = 0; // The number of clients we are dealing with
private ITMClient[] clients; // This is the map of the client objects
private int[] clientTypeArray; // This is the map of the client objects
private int[] clientTransNumber; // The transaction number the client is working on
// This is the count of the transactions we have open right now
private int activeTransactionCount = 0;
// Wheter we are allowed to create new transactions or not
private boolean newTransactionAllowed = true;
// This holds the maximum number of transactions that this TM can have open
// at any one time. Defaults to 1, but can be set to any other value.
private int maxTransactions = 1;
// This defines if we should abort all transactions that are open if one aborts
private boolean abortConcurrentTransactions = false;
// List of Services that this Client supports
private final static String SERVICE_ABORT = "SetAbort";
private final static String SERVICE_ABORT_CONCURRENT_TRANS = "AbortConcurrentTransactions";
private final static String SERVICE_ALLOWTRANS = "AllowTransaction";
private final static String SERVICE_TRANSCOUNT = "TransactionCount";
private final static String SERVICE_CLIENT_STATUS = "ClientStatus";
private final static String SERVICE_FLUSH_STATUS = "FlushStatus";
private final static String SERVICE_MAX_TRANSACTIONS = "MaxTransactions";
private final static String SERVICE_ABORT_HARD = "AbortHard";
// module symbolic name: set during initialisation
private String symbolicName = "TransactionManager";
// Common Definitions for the transaction manager
private TMDefs TMD = TMDefs.getTMDefs();
// used to simplify logging and exception handling
public String message;
// Transaction Flusher Thread
TransactionFlusher tmf;
/**
* Constructor
*/
public TransactionManager()
{
}
/**
* Initialise the transaction manager
*
* @param pipelineName The name of the pipeline that this TM is working for
* @throws OpenRate.exception.InitializationException
*/
public void init(String pipelineName) throws InitializationException
{
OpenRate.getOpenRateFrameworkLog().info("Starting TransactionManager initialisation for pipeline <" +
pipelineName + ">");
// store the pipe we are in - used for pipeline level logging and exception handling
setPipeline(OpenRate.getPipelineFromMap(pipelineName));
// The list of current transactions
transactionList = new ConcurrentHashMap<>(50);
// The list of clients
clients = new ITMClient[50];
// Tells us what type of client a client is (Input, processing, output)
clientTypeArray = new int[50];
// Tells us the transaction a client is in (it can't always find out by itself when we are overlaying transactions)
clientTransNumber = new int[50];
// Set the initial status of the transaction manager
requestTMStart();
// Start the TM Flusher thread
tmf = new TransactionFlusher();
ThreadGroup flusher = new ThreadGroup("TransactionFlusher");
Thread flusherThread = new Thread(flusher, tmf, "TransFlusher."+pipelineName+"-Inst-0");
tmf.setTMReference(this);
tmf.setPipelineName(pipelineName);
tmf.setLogger(getPipeLog());
flusherThread.setDaemon(true);
flusherThread.start();
System.out.println(" Started 1 Transaction Flusher Thread for pipeline <"+pipelineName+">");
OpenRate.getOpenRateFrameworkLog().info("TransactionManager initialised");
}
/**
* Creates a new transaction instance, ready accept transaction information
* Sets the transaction start time
*
* @param pipeline The name of the pipeline that opened the transaction
* @return The transaction number
*/
@Override
public synchronized int openTransaction(String pipeline)
{
int tmpTransactionNumber;
if (TMStarted)
{
tmpTransactionNumber = getNextTransactionNumber();
TransactionInfo CachedTrans = new TransactionInfo();
CachedTrans.setTransactionStart(System.currentTimeMillis());
CachedTrans.setTransactionNumber(tmpTransactionNumber);
transactionList.put(tmpTransactionNumber, CachedTrans);
message = "Opened transaction <" + tmpTransactionNumber + "> for pipeline <" + pipeline + ">";
getPipeLog().info(message);
OpenRate.getOpenRateStatsLog().info(message);
// Maintain the count
activeTransactionCount++;
return tmpTransactionNumber;
}
else
{
getPipeLog().error("Transaction Manager halted. Cannot open new transactions.");
return -1;
}
}
/**
* Commit the transaction and print the transaction information to the log.
*
* @param transNumber The transaction number to close
*/
@Override
public synchronized void closeTransaction(int transNumber)
{
TransactionInfo CachedTrans;
CachedTrans = transactionList.get(transNumber);
CachedTrans.setTransactionEnd(System.currentTimeMillis());
long TransactionTime = (CachedTrans.getTransactionEnd() - CachedTrans.getTransactionStart());
// Deal with the case that we have a zero transaction time
if (TransactionTime == 0)
{
TransactionTime = 1;
}
getPipeLog().info("Transaction <" + transNumber + "> closed");
OpenRate.getOpenRateStatsLog().info("Transaction <" + transNumber + "> closed");
OpenRate.getOpenRateStatsLog().info("Statistics: Records <" + CachedTrans.getTransactionRecords() + ">");
OpenRate.getOpenRateStatsLog().info(" Duration <" +
(CachedTrans.getTransactionEnd() - CachedTrans.getTransactionStart()) + "> ms");
OpenRate.getOpenRateStatsLog().info(" Speed <" +
((CachedTrans.getTransactionRecords() * 1000) / TransactionTime) + "> records /sec");
// remove the old transaction
transactionList.remove(transNumber);
getPipeLog().debug("Removed transaction <" + transNumber + ">");
// Maintain the count
activeTransactionCount--;
}
/**
* Signal that the transaction number should be aborted at the first possible
* opportunity
*
* @param transNumber The transaction number to abort
*/
@Override
public void requestTransactionAbort(int transNumber)
{
TransactionInfo tmpCachedTrans;
TransactionInfo CachedTrans;
CachedTrans = transactionList.get(transNumber);
CachedTrans.setAbortRequested(true);
getPipeLog().info("Request Abort for Transaction <" + transNumber + ">");
// if we should abort concurrent transactions do so
if (abortConcurrentTransactions)
{
Iterator<Integer> TransactionIterator = transactionList.keySet().iterator();
while (TransactionIterator.hasNext())
{
Integer tmpTransactionKey = TransactionIterator.next();
tmpCachedTrans = transactionList.get(tmpTransactionKey);
tmpCachedTrans.setAbortRequested(true);
getPipeLog().info("Request Subordinate Abort for Transaction <" + tmpTransactionKey + ">");
}
}
}
/**
* Find if the transaction has been aborted
*
* @param transNumber The transaction number to abort
* @return true if the transaction has been aborted, otherwise false
*/
public boolean getTransactionAborted(int transNumber)
{
if (transactionList.containsKey(transNumber))
{
return transactionList.get(transNumber).isAbortRequested();
}
else
{
// This is a closed transaction, we didn't abort it
OpenRate.getOpenRateFrameworkLog().warning("No trans info found for trans<" + transNumber + ">");
return false;
}
}
/**
* Stop the Transaction Manager, so that new transactions cannot be started
*/
public void requestTMStop()
{
if (TMStarted)
{
getPipeLog().info("Request Stop for Transaction manager. Will stop after current transaction is closed.");
TMStarted = false;
}
else
{
getPipeLog().error("Transaction manager already stopped. No Change made.");
}
}
/**
* Start the Transaction Manager, so that new transactions can be started
*/
public void requestTMStart()
{
if (TMStarted)
{
getPipeLog().error("Transaction manager already started. No Change made.");
}
else
{
getPipeLog().info("Request Start for Transaction manager.");
TMStarted = true;
}
}
/**
* Get the status of the Transaction Manager
*
* @return If the TM is started
*/
public boolean getTMStarted()
{
return TMStarted;
}
/**
* Return the number of registered Transaction Manager clients
*
* @return The number of clients attached to this TM
*/
public int getNumberOfClients()
{
return numberOfClients;
}
/**
* Cancel (delete, remove) a given transaction.
*
* @param transNumber The transaction number to cancel
*/
@Override
public void cancelTransaction(int transNumber)
{
getPipeLog().info("Cancel Transaction <" + transNumber + ">");
transactionList.remove(transNumber);
activeTransactionCount--;
}
/**
* Register a module as a client of the transaction manager. This is required
* so that we can track when all clients have finished their work, meaning
* that the entire transaction was finished. There are three types of clients:
* Input module
* Processing module
* Output module
*
* The transaction can be closed when the last module in the pipe returns a status
* of TM_FINISHED.
*
* @param clientType The type of the client (input, output, processing)
* @param clientReference The object reference
* @return Client index
*/
public int registerClient(int clientType, ITMClient clientReference)
{
// Need to add something here to track the status of the transaction, and
// perhaps the clients
numberOfClients++;
clients[numberOfClients] = clientReference;
clientTypeArray[numberOfClients] = clientType;
getPipeLog().debug("Registered client <" + clientReference.toString() +"> as client type <" + clientType + "> as client number <" + numberOfClients + ">");
return numberOfClients;
}
/**
* Update the transaction status from the current client. This will have the
* effect of recalculating the new overall status and will return it immediately
*
* The transaction can be closed when the last module in the pipe returns a status
* of TM_FINISHED.
*
* @param transNumber The transaction we are working on
* @param clientNumber The client number
* @param newStatus The new client status to set
*/
public synchronized void setClientStatus(int transNumber, int clientNumber, int newStatus)
{
try
{
// Update the client status
transactionList.get(transNumber).setClientStatus(clientNumber, newStatus);
// Print something to the log, so we can understand the state changes
getPipeLog().debug("Client <" + clientNumber+ "> set status <" + newStatus + "> " +
" for transaction <" + transNumber + ">");
// Store away the transaction number
clientTransNumber[clientNumber] = transNumber;
// Set the transaction into the "finish transaction list" (if it is ready)
if ((clientNumber==numberOfClients) && (newStatus==TMD.TM_FLUSHED))
{
tmf.addTransactionToFlushList(transactionList.get(transNumber));
}
}
catch (NullPointerException npe)
{
message = "Error setting client status <" + newStatus +
"> for client <" + clientNumber + "> in transaction <" +
transNumber + ">";
OpenRate.getOpenRateFrameworkLog().error(message);
}
}
int getClientCount()
{
return numberOfClients;
}
/**
* The the client reference.
*
* @param i The index of the client to get
* @return The client reference
*/
public ITMClient getClient(int i)
{
return clients[i];
}
/**
* Return the transaction a client is working on. This is required because the
* management of overlaid transactions means that clients are not able to track
* this correctly.
*
* @param clientNumber The client who is requesting the information
* @return The current transaction number
*/
public int getTransactionNumber(int clientNumber)
{
return clientTransNumber[clientNumber];
}
/**
* Get the next transaction number. This is a synchronised method to ensure
* that we never return the same transaction number twice, nor leave a gap.
*
* @return the nextTransactionNumber
*/
public synchronized static int getNextTransactionNumber() {
nextTransactionNumber++;
return nextTransactionNumber;
}
/**
* Calculates the new overall status for this transaction
*
* @param transNumber The transaction we are working on
* @param cachedTrans The information object for the transaction
* @return the new overall status
*/
public synchronized int getOverallStatus(int transNumber, TransactionInfo cachedTrans)
{
int newOverallStatus;
boolean AllFinished = true;
boolean AllClosed = true;
boolean ErrFlag = false;
int i;
// Calculate the new overall status for this transaction. This will be:
// The maximum value of statuses up to TM_PROCESSING
// TM_PROCESSING until the last client goes to TM_FLUSHED
// TM_ABORT_REQUEST if any client requests an abort
// we should report the lowest overall status, so we initialise high
// and go down
newOverallStatus = TMD.TM_CLOSED;
for (i = 1; i <= numberOfClients; i++)
{
int tmpStatus = cachedTrans.getClientStatus(i);
AllFinished = (AllFinished &
((tmpStatus == TMD.TM_FINISHED_OK) ||
(tmpStatus == TMD.TM_FINISHED_ERR)));
AllClosed = (AllClosed & (tmpStatus == TMD.TM_CLOSING));
ErrFlag = (ErrFlag | (cachedTrans.getClientStatus(i) == TMD.TM_FINISHED_ERR));
if (tmpStatus < newOverallStatus)
{
newOverallStatus = tmpStatus;
}
}
// We have to be a little bit clever with the status FINISHED, because
// we have to calculate either TM_FINISHED_OK if *ALL* the clients report
// OK, or else TM_FINISHED_ERR if any one of the clients had an error
if ((ErrFlag) | (cachedTrans.isAbortRequested()))
{
cachedTrans.setTransactionErrored(true);
}
if ((newOverallStatus == TMD.TM_FINISHED_OK) | (newOverallStatus == TMD.TM_FINISHED_ERR))
{
if (cachedTrans.isTransactionErrored())
{
newOverallStatus = TMD.TM_FINISHED_ERR;
}
else
{
newOverallStatus = TMD.TM_FINISHED_OK;
}
}
// Mark if we had a state change
cachedTrans.setStateChange(cachedTrans.getTransactionStatus() != newOverallStatus);
// Store it in the transaction
cachedTrans.setTransactionStatus(newOverallStatus);
// log if we need to
if (getPipeLog().isDebugEnabled())
{
logTransactionStatus(transNumber);
}
// return it
return newOverallStatus;
}
/**
* Put an overview of the transaction status into the log. This is a costly
* operation and should be used with care for performance reasons.
*
* @param transNumber the transaction number
*/
private void logTransactionStatus(int transNumber)
{
int i;
String ClientType = "";
String ClientStatus = "";
if (transactionList.containsKey(transNumber))
{
TransactionInfo CachedTrans = transactionList.get(transNumber);
for (i = 1; i <= numberOfClients; i++)
{
ClientType += Integer.toString(clientTypeArray[i]);
ClientStatus += Integer.toString(CachedTrans.getClientStatus(i));
}
getPipeLog().debug("New Status for transaction <" + transNumber + ">:");
ClientStatus = ClientStatus + " --> " + CachedTrans.getTransactionStatus();
if (CachedTrans.isStateChange())
{
ClientStatus += "(*)";
}
getPipeLog().debug(" " + ClientType);
getPipeLog().debug(" " + ClientStatus);
}
}
/**
* Get the current status of a transaction client
*
* @param transNumber The transaction number
* @param clientNumber The client number
* @return The current status of the client
*/
public int getClientStatus(int transNumber, int clientNumber)
{
return transactionList.get(transNumber).getClientStatus(clientNumber);
}
/**
* Get the overall transaction status. This is the processed summation of the
* statuses of the individual client processes, processed to give the current
* processing status of the transaction
*
* @param transNumber The number of the transaction
* @return The current status
*/
@Override
public int getTransactionStatus(int transNumber)
{
return transactionList.get(transNumber).getTransactionStatus();
}
/**
* Update the number of records that have been processed
*
* @param transNumber The transaction
* @param clientNumber The client
* @param recordCount The new record count
*/
public synchronized void updateClientRecordCount(int transNumber, int clientNumber,
int recordCount)
{
TransactionInfo CachedTrans;
CachedTrans = transactionList.get(transNumber);
transactionList.get(transNumber).setRecordCount(clientNumber, recordCount);
// update the overall transaction record count
if (clientTypeArray[clientNumber] == TMD.CT_CLIENT_INPUT)
{
CachedTrans.setTransactionRecords(recordCount);
}
}
/**
* Return the count of the active transactions. This is used primarily in
* pipeline scheduling.
*
* @return The number of active transactions
*/
@Override
public int getActiveTransactionCount()
{
return activeTransactionCount;
}
/**
* Return the count of the transactions waiting to be flushed.
*
* @return The number of transactions waiting to be flushed
*/
@Override
public int getFlushedTransactionCount()
{
return tmf.getFlushedTransactionCount();
}
/**
* Return whether the input adapter for this pipe is allowed to start new
* transactions, based on the fact that the transaction manager is enabled,
* and that the maximum number of concurrent transactions has not been
* reached.
*
* @return True if new transactions can be started
*/
public boolean getNewTransactionAllowed()
{
return newTransactionAllowed & (activeTransactionCount < maxTransactions);
}
/**
* Return whether the input adapter for this pipe is allowed to start new
* transactions
*
* @param newState True if new transactions are to be allowed, otherwise false
*/
public void setNewTransactionAllowed(boolean newState)
{
newTransactionAllowed = newState;
}
/**
* Get the maximum number of transactions allowed
*
* @return the max transactions value
*/
public int getMaxTransactions()
{
return maxTransactions;
}
/**
* Set the maximum number of transactions allowed
*
* @param newMaxValue the new max transactions value
*/
public void setMaxTransactions(int newMaxValue)
{
maxTransactions = newMaxValue;
}
/**
* Perform any close down activities that are needed, the inverse of the
* init() procedure
*/
public void close()
{
// clean up the clients
resetClients();
}
// -----------------------------------------------------------------------------
// ------------- Start of inherited IEventInterface functions ------------------
// -----------------------------------------------------------------------------
/**
* registerClientManager registers this class as a client of the ECI listener
* and publishes the commands that the plug in understands. The listener is
* responsible for delivering only these commands to the plug in.
*
*/
@Override
public void registerClientManager() throws InitializationException
{
//Register this Client
ClientManager.getClientManager().registerClient("Resource",getSymbolicName(), this);
//Register services for this Client
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_ABORT, ClientManager.PARAM_DYNAMIC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_ALLOWTRANS, ClientManager.PARAM_DYNAMIC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_TRANSCOUNT, ClientManager.PARAM_DYNAMIC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_CLIENT_STATUS, ClientManager.PARAM_DYNAMIC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_FLUSH_STATUS, ClientManager.PARAM_DYNAMIC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_MAX_TRANSACTIONS, ClientManager.PARAM_DYNAMIC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_ABORT_CONCURRENT_TRANS, ClientManager.PARAM_DYNAMIC);
ClientManager.getClientManager().registerClientService(getSymbolicName(), SERVICE_ABORT_HARD, ClientManager.PARAM_DYNAMIC);
}
/**
* processControlEvent is the event processing hook for the External Control
* Interface (ECI). This allows interaction with the external world, for
* example turning the dumping on and off.
*/
@Override
public String processControlEvent(String Command, boolean Init, String Parameter)
{
int ResultCode = -1;
int i;
String result = "";
if (Command.equalsIgnoreCase(SERVICE_ABORT))
{
if (Parameter.equalsIgnoreCase("true"))
{
// abort all open transactions
Iterator<Integer> transIter = transactionList.keySet().iterator();
while (transIter.hasNext())
{
requestTransactionAbort(transIter.next());
}
ResultCode=0;
}
if (Parameter.equalsIgnoreCase("false"))
{
ResultCode=0;
}
if (Parameter.equalsIgnoreCase(""))
{
return "false";
}
}
if (Command.equalsIgnoreCase(SERVICE_ABORT_CONCURRENT_TRANS))
{
if (Parameter.equalsIgnoreCase("true"))
{
abortConcurrentTransactions = true;
ResultCode=0;
}
if (Parameter.equalsIgnoreCase("false"))
{
abortConcurrentTransactions = false;
ResultCode=0;
}
if (Parameter.equalsIgnoreCase(""))
{
if (abortConcurrentTransactions)
{
return "true";
}
else
{
return "false";
}
}
}
if (Command.equalsIgnoreCase(SERVICE_ALLOWTRANS))
{
// put the transaction manager on hold
if (Parameter.equalsIgnoreCase("true"))
{
setNewTransactionAllowed(true);
ResultCode=0;
}
// release it
if (Parameter.equalsIgnoreCase("false"))
{
setNewTransactionAllowed(false);
ResultCode=0;
}
// Query current status
if (Parameter.equalsIgnoreCase(""))
{
if (newTransactionAllowed)
{
return "true";
}
else
{
return "false";
}
}
}
if (Command.equalsIgnoreCase(SERVICE_TRANSCOUNT))
{
return Integer.toString(activeTransactionCount);
}
if (Command.equalsIgnoreCase(SERVICE_CLIENT_STATUS))
{
int tmpTransNumber;
try
{
tmpTransNumber = Integer.parseInt(Parameter);
}
catch (NumberFormatException nfe)
{
result = "Parameter <" + Parameter + "> is not numeric";
return result;
}
// Show the client status list
TransactionInfo CachedTrans = transactionList.get(tmpTransNumber);
if (CachedTrans == null)
{
result = "No transaction";
return result;
}
// Get the status for each client
for (i = 1; i <= getClientCount(); i++)
{
result = result + "Client <"+i+"> status for transaction <" + tmpTransNumber + "> is <" +
CachedTrans.getClientStatus(i) + ">\n";
}
result = result + "New Status for transaction <" + tmpTransNumber + "> is <" +
CachedTrans.getTransactionStatus() + ">";
return result;
}
if (Command.equalsIgnoreCase(SERVICE_FLUSH_STATUS))
{
result = "Flusher has <" + tmf.getFlushedTransactionCount() +
"> transactions waiting";
return result;
}
// Set the maximum number of transactions
if (Command.equalsIgnoreCase(SERVICE_MAX_TRANSACTIONS))
{
if (Parameter.equals(""))
{
return String.valueOf(getMaxTransactions());
}
else
{
try
{
setMaxTransactions(Integer.parseInt(Parameter));
}
catch (NumberFormatException nfe)
{
getPipeLog().error("Invalid number for batch size. Passed value = <" + Parameter + ">");
}
ResultCode = 0;
}
}
// Set the maximum number of transactions
if (Command.equalsIgnoreCase(SERVICE_ABORT_HARD))
{
if (Parameter.equalsIgnoreCase("true"))
{
int transactionsChanged = 0;
Iterator<Integer> transIter = transactionList.keySet().iterator();
while (transIter.hasNext())
{
Integer transNumber = transIter.next();
// Get the transaction
TransactionInfo CachedTrans = transactionList.get(transNumber);
// Abort it
CachedTrans.setAbortRequested(true);
transactionsChanged++;
// and unblock the clients
for (i = 1; i <= getClientCount(); i++)
{
CachedTrans.setClientStatus(i,TMDefs.getTMDefs().TM_FLUSHED);
}
}
return "Aborted <" + transactionsChanged + "> transactions";
}
}
// Return the response
if (ResultCode == 0)
{
String logStr = "Command " + Command + " handled";
getPipeLog().debug(logStr);
return "OK";
}
else
{
return "Command Not Understood";
}
}
/**
* return the symbolic name
*
* @return The symbolic name for this plugin
*/
public String getSymbolicName()
{
return symbolicName;
}
/**
* set the symbolic name
*
* @param Name The new symbolic name for this plug in
*/
public void setSymbolicName(String Name)
{
symbolicName=Name;
}
/**
* Test support - not normally needed in real life: Resets the clients between
* tests: Allows us to use a generic test set for different modules. If we
* don't do this, we collect clients, but during tests we don't maintain them
* correctly.
*/
@Override
public void resetClients() {
numberOfClients = 0;
}
// -----------------------------------------------------------------------------
// -------------------- Standard getter/setter functions -----------------------
// -----------------------------------------------------------------------------
/**
* @return the pipeName
*/
public String getPipeName() {
return pipeline.getSymbolicName();
}
/**
* @return the pipeline
*/
public IPipeline getPipeline() {
return pipeline;
}
/**
* Set the pipeline reference so the input adapter can control the scheduler
*
* @param pipeline the Pipeline to set
*/
public void setPipeline(IPipeline pipeline)
{
this.pipeline = pipeline;
}
/**
* Return the pipeline logger.
*
* @return The logger
*/
protected ILogger getPipeLog() {
return pipeline.getPipeLog();
}
/**
* Return the exception handler.
*
* @return The exception handler
*/
protected ExceptionHandler getExceptionHandler() {
return pipeline.getPipelineExceptionHandler();
}
}