/* ==================================================================== * 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.logging.ILogger; import java.util.ArrayList; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * This class is responsible for closing flushed transactions. Flushed * transactions have finished traversing the pipeline, and need only to have the * final commit and closing done on them. This is externalised into a separate * thread for performance reasons. * * @author tgdspia1 */ public class TransactionFlusher implements Runnable { // used for managing overlaid transactions private ReentrantReadWriteLock clientLock = new ReentrantReadWriteLock(); // The list of the transactions we are closing private ArrayList<TransactionInfo>transFlushedList = new ArrayList<>(); // Common Definitions for the transaction manager private TMDefs TMD = new TMDefs(); // Callback to the TM TransactionManager TM; // The name of the pipe we are working for private String pipelineName; // Our logger private ILogger pipeLog; // Scheduler private int sleepTime; @Override public void run() { while(true) { if (getFlushedTransactionCount() > 0) { updateTransactionStatus(); sleepTime = 0; } else { sleepTime = 1000; } /*try { Thread.sleep(sleepTime); } catch (InterruptedException ex) { // Ignore }*/ // If not marked for shutdown, wait for notification from the // suppler that new records are available for processing. try { synchronized (this) { wait(); } } catch (InterruptedException e) { // ignore interrupt exceptions } } } /** * Put a transaction into the flush list * * @param trans */ public synchronized void addTransactionToFlushList(TransactionInfo trans) { transFlushedList.add(trans); pipeLog.debug("Added transaction <"+trans.getTransactionNumber()+"> to flusher for pipe <"+pipelineName+">"); this.notify(); } /** * Update the overall status and in the case that we have a state change (for * example during the asynchronous closing portion of the transaction) deal * with the state change. In the future we might put this into a separate * thread. */ public void updateTransactionStatus() { int i; int NewOverallStatus; TransactionInfo cachedTrans; Integer transNumber; if (clientLock.isWriteLocked()) { return; } try { // Get the lock clientLock.writeLock().lock(); // Check the status of the transactions while (transFlushedList.size() > 0) { cachedTrans = transFlushedList.get(0); transNumber = cachedTrans.getTransactionNumber(); // Calculate the new status NewOverallStatus = TM.getOverallStatus(transNumber,cachedTrans); // If we had a state change, inform the clients if there was an overall state change while (transNumber > 0) { if (NewOverallStatus == TMD.TM_FLUSHED) { // inform each of the clients in turn for (i = 1; i <= TM.getClientCount(); i++) { if (TM.getClient(i).updateTransactionStatusFlush(transNumber)) { // Set the overall status for this client to OK cachedTrans.setClientStatus(i, TMD.TM_FINISHED_OK); } else { // Set the overall status for this client to OK cachedTrans.setClientStatus(i, TMD.TM_FINISHED_ERR); } // Update the status NewOverallStatus = TM.getOverallStatus(transNumber,cachedTrans); } } else if (NewOverallStatus == TMD.TM_FINISHED_OK) { // inform each of the clients in turn for (i = 1; i <= TM.getClientCount(); i++) { TM.getClient(i).updateTransactionStatusCommit(transNumber); // Set the overall status for this client to OK cachedTrans.setClientStatus(i, TMD.TM_CLOSING); // Update the status NewOverallStatus = TM.getOverallStatus(transNumber,cachedTrans); } } else if (NewOverallStatus == TMD.TM_FINISHED_ERR) { // inform each of the clients in turn for (i = 1; i <= TM.getClientCount(); i++) { TM.getClient(i).updateTransactionStatusRollback(transNumber); // Set the overall status for this client to closing cachedTrans.setClientStatus(i, TMD.TM_CLOSING); // Update the status NewOverallStatus = TM.getOverallStatus(transNumber,cachedTrans); } } else if (NewOverallStatus == TMD.TM_CLOSING) { // inform each of the clients in turn for (i = 1; i <= TM.getClientCount(); i++) { TM.getClient(i).updateTransactionStatusClose(transNumber); // Set the overall status for this client to all done cachedTrans.setClientStatus(i, TMD.TM_CLOSED); // Update the status NewOverallStatus = TM.getOverallStatus(transNumber,cachedTrans); } } else if (NewOverallStatus == TMD.TM_CLOSED) { // Finish up and remove the transaction TM.closeTransaction(transNumber); transFlushedList.remove(0); pipeLog.debug(transFlushedList.size() + " transactions to flush for pipe <"+pipelineName+">"); transNumber = 0; } else { // Update the status NewOverallStatus = TM.getOverallStatus(transNumber,cachedTrans); } } } } finally { // Release the lock clientLock.writeLock().unlock(); } } /** * Get the number of transactions which have been flushed but not yet * finalised. * * @return the count. */ public int getFlushedTransactionCount() { return transFlushedList.size(); } /** * Set the reference for sending updates back to the transaction manager. * * @param newTM The TM we are serving. */ public void setTMReference(TransactionManager newTM) { TM = newTM; } /** * Set the pipeline name for use in the log. * * @param newPipelineName The pipeline name */ void setPipelineName(String newPipelineName) { pipelineName = newPipelineName; } /** * Set the logger we are using for this flusher. * * @param newPipeLog The pipe log we are logging to */ void setLogger(ILogger newPipeLog) { pipeLog = newPipeLog; } }