Package com.sun.messaging.jmq.jmsserver.persist.file

Source Code of com.sun.messaging.jmq.jmsserver.persist.file.TransactionLogManager

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package com.sun.messaging.jmq.jmsserver.persist.file;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.sun.messaging.jmq.io.SysMessageID;
import com.sun.messaging.jmq.jmsserver.Globals;
import com.sun.messaging.jmq.jmsserver.config.BrokerConfig;
import com.sun.messaging.jmq.jmsserver.core.Destination;
import com.sun.messaging.jmq.jmsserver.core.DestinationUID;
import com.sun.messaging.jmq.jmsserver.core.Subscription;
import com.sun.messaging.jmq.jmsserver.data.BaseTransaction;
import com.sun.messaging.jmq.jmsserver.data.TransactionState;
import com.sun.messaging.jmq.jmsserver.data.TransactionUID;
import com.sun.messaging.jmq.jmsserver.data.TransactionWorkMessage;
import com.sun.messaging.jmq.jmsserver.data.TransactionWorkMessageAck;
import com.sun.messaging.jmq.jmsserver.persist.Store;
import com.sun.messaging.jmq.jmsserver.persist.TransactionInfo;
import com.sun.messaging.jmq.jmsserver.resources.BrokerResources;
import com.sun.messaging.jmq.jmsserver.util.BrokerException;
import com.sun.messaging.jmq.util.FileUtil;
import com.sun.messaging.jmq.util.SizeString;
import com.sun.messaging.jmq.util.log.Logger;
import com.sun.messaging.jmq.util.txnlog.CheckPointListener;
import com.sun.messaging.jmq.util.txnlog.TransactionLogRecord;
import com.sun.messaging.jmq.util.txnlog.TransactionLogWriter;
import com.sun.messaging.jmq.util.txnlog.file.FileTransactionLogWriter;

/**
* @author gsivewright
*
* This class manages the transfer of data between the message store and the
* transaction log.
*
*/

/*
* Order of handling prepared txns
*
* The following scenario describes a sequence of updates to the transaction log
* and message store. This sequence is used to illustrate how consistent state
* can be recreated on restart after a failure of the broker at various specific
* processing points.
*
* 1.write prepared txn to txnLog 2.checkpoint1 (see checkpoint sequence)
* 3.write commit record to txnLog 4.write txn to store 5.write commit record to
* preparedTxnStore 6.checkpoint2 (see checkpoint sequence)
*
* checkpoint sequence:
*
* 1.sync message store 2.write prepared txns (since last checkpoint) to
* prepared store 3.sync prepareTxnStore 4.reset txn log 5.remove committed txns
* (since last checkpoint) from prepared store
*
*
* replay after failure at 1 �> 2.2
*
* 1.read preparedTxnStore (empty) 2.read prepared txn from txnLog 3.check if
* txn is in preparedTxnStore. (add). 4.checkpoint
*
* replay after failure at 2.3 -> 2.4
*
* 1.read txns from preparedTxnStore 2.read prepared txn from txnLog 3.check if
* txn is in preparedTxnStore. (already there) 4.checkpoint
*
* replay after failure at 2.5
*
* 1.read preparedTxn from preparedTxnStore 2.checkpoint
*
* replay after failure at 3 -> 5
*
* 1.read txns from preparedTxnStore 2.read commit record from txnLog 3.write
* txn to message store (replace if there) 4.write commit record to
* preparedTxnStore (if not committed) 5.checkpoint
*
* replay after failure at 6.4 1)read txns from preparedTxnStore (find committed
* txn) 2)do not replay (not in reset txnLog) 3)checkpoint
*
*/

public class TransactionLogManager implements CheckPointListener {

  static final String TXNLOG_PROP_PREFIX = Globals.IMQ
      + ".persist.file.txnLog";
  static final String TXNLOG_FILE_SIZE_PROP = TXNLOG_PROP_PREFIX
      + ".file.size";
 

  static final long DEFAULT_TXNLOG_FILE_SIZE = FileTransactionLogWriter.DEFAULT_MAX_SIZE;

  static final String MSG_LOG_FILENAME = "txnlog";

 
  static final String INCOMPLETE_TXN_STORE = "incompleteTxnStore";
  public static final BrokerResources br = Globals.getBrokerResources();
  Store store;
  MsgStore msgStore;
  File rootDir; 
 
   // whether non-transacted persistent message sent should be logged
  // by default, log non transacted message sends and acks if sync is enabled.
  // user can override this behavior by setiing properties explicitly.
  private static boolean defaultLogNonTransactedMsgSend = Destination.PERSIST_SYNC;
  public static final String LOG_NON_TRANSACTED_MSG_SEND_PROP = Globals.IMQ
      + ".persist.file.txnLog.logNonTransactedMsgSend";
  public static boolean logNonTransactedMsgSend = Globals.getConfig()
      .getBooleanProperty(LOG_NON_TRANSACTED_MSG_SEND_PROP,
          defaultLogNonTransactedMsgSend);

  private static boolean defaultLogNonTransactedMsgAck = Destination.PERSIST_SYNC;
  public static final String LOG_NON_TRANSACTED_MSG_ACK_PROP = Globals.IMQ
      + ".persist.file.txnLog.logNonTransactedMsgAck";
  public static boolean logNonTransactedMsgAck = Globals.getConfig()
      .getBooleanProperty(LOG_NON_TRANSACTED_MSG_ACK_PROP,
          defaultLogNonTransactedMsgAck);
    /**
     * whether to use a separate thread for writing to the txn log
     */
  
    public static final String TXN_LOG_GROUP_COMMITS_PROP = Globals.IMQ
      + ".persist.file.txnLog.groupCommits";

  public static boolean isTxnLogGroupCommits = Globals.getConfig()
      .getBooleanProperty(TXN_LOG_GROUP_COMMITS_PROP, false);

     
    private static boolean replayInProgress=false;

  private TransactionLogWriter msgLogWriter = null;
 
  TransactionLogReplayer transactionLogReplayer;
 
  LocalTransactionManager localTransactionManager;
  ClusterTransactionManager clusterTransactionManager;
  RemoteTransactionManager remoteTransactionManager;
 
  LoggedMessageHelper loggedMessageHelper;

  CheckpointManager checkpointManager;
  PreparedTxnStore preparedTxnStore;
 
    public static final Logger logger = Globals.getLogger();
   


  public TransactionLogManager(Store store, MsgStore msgStore, File rootDir, boolean resetStore) throws BrokerException{
   
    this.rootDir = rootDir;
    this.store = store;
    File preparedTxnStoreDir = new File(rootDir, INCOMPLETE_TXN_STORE);
    if (resetStore) {
      clearPreparedTxnStore(preparedTxnStoreDir);
      resetTransactionLogOnStartUp();
    }

    preparedTxnStore = new PreparedTxnStore(msgStore, preparedTxnStoreDir,
        false);

    this.msgStore = msgStore;
   
    this.transactionLogReplayer = new TransactionLogReplayer(msgStore);
    this.localTransactionManager = new LocalTransactionManager(this);
    this.clusterTransactionManager = new ClusterTransactionManager(this);
    this.remoteTransactionManager = new RemoteTransactionManager(this);
    this.checkpointManager = new CheckpointManager(this);
    this.loggedMessageHelper = new LoggedMessageHelper(this);
   
  }
 
  

  public void startup() throws BrokerException {
    // These are the stages on broker startup
    // 1) read in any prepared transactions held in preparedTxnStore.
    // 2) check if the transaction log needs replaying.
    // 3) replay committed transaction to message store
    // 4) after replay is complete, add any remaining transactions to
    // preparedTxnStore.
    // 5) sync preparedTxnStore
    // 6) sync message store.
    // 7) reset transaction log.
    // 8) remove any committed transactions from preparedTxnStore

    if (Store.getDEBUG()) {
      logger.log(Logger.DEBUG, getPrefix() + " startup");
    }
    // 1 read in any prepared transactions held in preparedTxnStore.
    processStoredTxnsOnStartup();

    // 2) check if the transaction log needs replaying.
    initTransactionLogOnStartUp();

    // 3) replay committed transaction to message store
    replayTransactionLogOnStartup();

 
    // 4. write prepared transactions to prepared transaction store
    localTransactionManager
        .writePreparedTransactionsToPreparedTxnStoreOnCheckpoint();
    clusterTransactionManager
        .writePreparedTransactionsToPreparedTxnStoreOnCheckpoint();
    remoteTransactionManager
        .writePreparedTransactionsToPreparedTxnStoreOnCheckpoint();

    // 5. sync prepareTxnStore
    preparedTxnStore.sync();

    // 6) checkpoint message store.
    store.syncDestination(null);

    try {
      // 7) reset transaction log.
      msgLogWriter.reset();
    } catch (IOException e) {
      throw new BrokerException("failed to reset transaction log", e);
    }

    // 8) remove any committed transactions from preparedTxnStore
    removeCommittedTransactionsOnStartup();

  }
 
  public void close()
  {
    try{
      if(msgLogWriter!=null)
        msgLogWriter.close(false);
      if(preparedTxnStore!=null)
        preparedTxnStore.close(true);
    }
    catch(IOException e)
    {
      logger.logStack(Logger.ERROR, "caught exception closing", e);
    }
  }
 
  public static void deleteAllFileState(File rootDir) throws BrokerException {
    // delete txnLog file first
    // as deleting directory will not be atomic, and will probably be bottom
    // up
    // (harder to detect if some elements are missing)
    logger.log(Logger.DEBUG, "deleteAllFileState "+rootDir);
    File txnLogFile = new File(rootDir, MSG_LOG_FILENAME);
    boolean deleted = false;
    if (txnLogFile.exists()) {
      deleted = txnLogFile.delete();
      if (!deleted) {
        throw new BrokerException("Could not delete txnLog file "
            + txnLogFile);
      }
    }
    File incompleteTxnStore = new File(rootDir, INCOMPLETE_TXN_STORE);
    try {
      if (incompleteTxnStore.exists())
        FileUtil.removeFiles(incompleteTxnStore, true);
    } catch (IOException e) {
      String msg = "Can not delete incomplete txn store "
          + incompleteTxnStore;
      logger.log(Logger.ERROR, msg, e);
      throw new BrokerException(msg, e);
    }

  }
 
 
  public static boolean txnLogExists(File rootDir) {
    File txnLogFile = new File(rootDir, MSG_LOG_FILENAME);
    return txnLogFile.exists();
  }

  public static boolean incompleteTxnStoreExists(File rootDir) {
    File file = new File(rootDir, INCOMPLETE_TXN_STORE);
    return file.exists();
  }

  public static void assertAllFilesExist(File rootDir) throws BrokerException {

    if (!txnLogExists(rootDir)) {
      throw new BrokerException("assertion failure: " + MSG_LOG_FILENAME
          + " file does not exist");
    }
    if (!incompleteTxnStoreExists(rootDir)) {
      throw new BrokerException("assertion failure: "
          + INCOMPLETE_TXN_STORE + " file does not exist");
    }
  }
 
 
/* 
  List<BaseTransaction> getAllIncompleteTransactions()
  {
    List<BaseTransaction> local = localTransactionManager.getAllIncompleteTransactions();
    List<BaseTransaction> cluster = clusterTransactionManager.getAllIncompleteTransactions();
    List<BaseTransaction> remote = remoteTransactionManager.getAllIncompleteTransactions();
   
    List<BaseTransaction> all = new ArrayList<BaseTransaction>();
    all.addAll(local);
    all.addAll(cluster);
    all.addAll(remote);
    return all;
  }
 
*/
 
 
 
 
  HashMap getAllTransactionStates() throws IOException {
   
    HashMap localTxnMap = localTransactionManager.getAllTransactionsMap();
    HashMap clusterTxnMap = clusterTransactionManager.getAllTransactionsMap();
   
    HashMap txnMap = new HashMap(localTxnMap.size()+clusterTxnMap.size());
    txnMap.putAll(localTxnMap);
    txnMap.putAll(clusterTxnMap);
   
    return txnMap;
  }
 
  void rollbackAllTransactions()
  {
    localTransactionManager.rollbackAllTransactions();
    clusterTransactionManager.rollbackAllTransactions();
    remoteTransactionManager.rollbackAllTransactions();
   
  }
 

  void processStoredTxnsOnStartup() throws BrokerException {
    if (Store.getDEBUG()) {
      String msg = getPrefix() + " processStoredTxnsOnStartup";
      logger.log(Logger.DEBUG, msg);
    }

    preparedTxnStore.loadTransactions();
       
    // for now just read and sort into different types
    Enumeration<BaseTransaction> transactions = preparedTxnStore
        .txnEnumeration();
    while (transactions.hasMoreElements()) {
      BaseTransaction baseTxn = transactions.nextElement();
      // need to switch on all types
      int type = baseTxn.getType();
      BaseTransactionManager baseTxnMan = this
          .getTransactionManager(type);
      baseTxnMan.processStoredTxnOnStartup(baseTxn);
    }

  }

  void removeCommittedTransactionsOnStartup() throws BrokerException {

    if (Store.getDEBUG()) {
      String msg = getPrefix()
          + " removeCommitedTransactionsInPreparedTxnStore";
      logger.log(Logger.DEBUG, msg);
    }
    // for now just report on any transactions
    Enumeration<BaseTransaction> transactions = preparedTxnStore
        .txnEnumeration();
    List<BaseTransaction> committed = new ArrayList<BaseTransaction>();
    while (transactions.hasMoreElements()) {
      BaseTransaction baseTxn = transactions.nextElement();
      if (baseTxn.getState() == TransactionState.COMMITTED) {
        if (baseTxn.getType() == BaseTransaction.CLUSTER_TRANSACTION_TYPE
            && !baseTxn.getTransactionDetails().isComplete()) {
          if (Store.getDEBUG()) {
            String msg = getPrefix()
                + " not removing incomplete cluster transaction "
                + baseTxn.getTransactionDetails();
            logger.log(Logger.DEBUG, msg);
          }
        } else {
          if (Store.getDEBUG()) {
            String msg = getPrefix() + " removing transaction "
                + baseTxn.getTransactionDetails();
            logger.log(Logger.DEBUG, msg);
          }
          committed.add(baseTxn);
        }
      }

    }
    Iterator<BaseTransaction> iter = committed.iterator();
    while (iter.hasNext()) {
      BaseTransaction baseTxn = iter.next();
      TransactionUID tid = baseTxn.getTid();
      preparedTxnStore.removeTransaction(tid, true);
      if (Store.getDEBUG()) {
        String msg = getPrefix()
            + " removed committed transaction from preparedTxnStore. Tid="
            + tid;
        logger.log(Logger.DEBUG, msg);
      }
    }

  }
  private void resetTransactionLogOnStartUp() {
   
    String filename = MSG_LOG_FILENAME;
    File file = new File(rootDir, filename);
    logger.log(Logger.INFO, "resetting txn Log file "+file);
    if(!file.exists())
    {
      logger.log(Logger.INFO, "nothing to reset. txn Log file "+file+ " does not exist");
      return;
    }
    boolean deleted = file.delete();
    if (!deleted) {
      String msg = getPrefix() + " could not delete " + file;

      logger.log(Logger.DEBUG, msg);
    }
  }

  private void clearPreparedTxnStore(File preparedTxnStoreDir)
      throws BrokerException {
    // delete all files under the preparedTxnStoreDir directory
    try {
     
      FileUtil.removeFiles(preparedTxnStoreDir, false);
    } catch (IOException e) {
      logger.log(Logger.ERROR, BrokerResources.X_RESET_MESSAGES_FAILED,
          preparedTxnStoreDir, e);
      throw new BrokerException(br.getString(BrokerResources.X_RESET_MESSAGES_FAILED,
          preparedTxnStoreDir), e);
    }
  }
 
  public static boolean transactionLogExists(File rootDir) {
    boolean result = false;
    String filename = MSG_LOG_FILENAME;
    File txnLogFile = new File(rootDir, filename);
    result = txnLogFile.exists();
    return result;
  }

  private void initTransactionLogOnStartUp() throws BrokerException {

    if (Store.getDEBUG()) {
      String msg = getPrefix() + " initTransactionLogOnStartUp";
      logger.log(Logger.DEBUG, msg);
    }
    logger.log(Logger.INFO, "new transaction log enabled");
    logger.log(Logger.INFO, "sync writes to disk = "+ Destination.PERSIST_SYNC);
    logger.log(Logger.INFO, "logNonTransactedMsgSend = "+ logNonTransactedMsgSend);
    logger.log(Logger.INFO, "logNonTransactedMsgAck = "+ logNonTransactedMsgAck);
   

    // create txn log writers
    String filename = null;
    try {
      BrokerConfig config = Globals.getConfig();
      SizeString filesize = config.getSizeProperty(TXNLOG_FILE_SIZE_PROP,
          DEFAULT_TXNLOG_FILE_SIZE);
         
      filename = MSG_LOG_FILENAME;

      String mode = "rwd";
      boolean synch = true;
      if (!Destination.PERSIST_SYNC) {
        mode = "rw";
        synch = false;
      }
      logger.log(Logger.INFO, br.getKString(BrokerResources.I_OPEN_TXNLOG,
            mode, Long.valueOf(filesize.getBytes())));

      FileTransactionLogWriter ftlw = new FileTransactionLogWriter(
          rootDir, filename, filesize.getBytes(), mode, synch,
          isTxnLogGroupCommits,
          BaseTransaction.CURRENT_FORMAT_VERSION);
      long existingFormatVersion = ftlw.getExistingAppCookie();
     
      //
      // Check version here
      // if(existingFormatVersion!=BaseTransaction.CURRENT_FORMAT_VERSION)
      // then file may need to be converted.
      //
      // Note this appCookie format specifies the format of the transactions
      // being stored and not the format of the transaction log itself, which may
      // change less frequently and should be checked separately
      // Note also that transaction format is also dependent on any changes to packet format etc.
      //
      // No need to do convert yet as we are on the first version
      // so this is just a sanity check.
      if(existingFormatVersion!=BaseTransaction.CURRENT_FORMAT_VERSION)
      {
        throw new BrokerException("Unexpected transaction log format. Format on file = "+existingFormatVersion +
            " Current software version = "+BaseTransaction.CURRENT_FORMAT_VERSION);
      }
     
      msgLogWriter = ftlw;
     
      msgLogWriter.setCheckPointListener(this);
      if (Store.getDEBUG()) {
        logger.log(Logger.DEBUG, "created txn log");
      }
    } catch (IOException ex) {
      logger.logStack(Logger.ERROR,
          BrokerResources.E_CREATE_TXNLOG_FILE_FAILED, filename, ex);
      throw new BrokerException(br.getString(
          BrokerResources.E_CREATE_TXNLOG_FILE_FAILED, filename), ex);
    }

  }
 
 

  public void replayTransactionLogOnStartup() throws BrokerException {

    if (Store.getDEBUG()) {
      logger.log(Logger.DEBUG, getPrefix()
          + " replayTransactionLogOnStartup");
    }

    try {
      setReplayInProgress(true);     
      if (msgLogWriter.playBackRequired()) {
        if (Store.getDEBUG()) {
          String msg = getPrefix()
              + " replayTransactionLogOnStartup: playBackRequired";
          logger.log(Logger.DEBUG, msg);
        }

        logger.log(Logger.FORCE, BrokerResources.I_PROCESS_MSG_TXNLOG);

        // All destinations need to be loaded
        Destination.init();
        Subscription.initSubscriptions();
        HashSet dstLoadedSet = new HashSet(); // Keep track of loaded
        // dst

        int count = 0;

       
        Iterator itr = msgLogWriter.iterator();
        while (itr.hasNext()) {
          count++; // Keep track the number of records processed

          // Read in the acks or msgs & acks
          TransactionLogRecord rec = (TransactionLogRecord) itr
              .next();

          byte[] data = rec.getBody();
          TransactionEvent txnEvent = readTransactionEvent(data);
          int type = txnEvent.getType();
          if (Store.getDEBUG()) {
            String msg = getPrefix()
                + " replayTransactionLogOnStartup() recordSeq= " + rec.getSequence()+ " txnEvent= "
                + txnEvent;
            logger.log(Logger.DEBUG, msg);
          }
          if (type == BaseTransaction.NON_TRANSACTED_MSG_TYPE) {
            transactionLogReplayer.replayNonTxnMsg((NonTransactedMsgEvent)txnEvent,dstLoadedSet);
          } else if (type == BaseTransaction.NON_TRANSACTED_ACK_TYPE) {
            transactionLogReplayer.replayNonTxnMsgAck((NonTransactedMsgAckEvent)txnEvent,dstLoadedSet);
          } else if (type == BaseTransaction.MSG_REMOVAL_TYPE) {
            transactionLogReplayer.replayMessageRemoval((MsgRemovalEvent)txnEvent,dstLoadedSet);
          } else {

            BaseTransactionManager tm = getTransactionManager(type);
            tm.replayTransactionEvent(txnEvent, dstLoadedSet);
          }

        }

      } else {
        if (Store.getDEBUG()) {
          logger.log(Logger.DEBUG, "no playBackRequired");
        }
      }
    }

    catch (IOException e) {
      logger.log(Logger.ERROR, "exception in playback",e);
      throw new BrokerException("exception in playback",e);

    }finally{
      setReplayInProgress(false);
    }
  }

 

  TransactionEvent readTransactionEvent(byte[] data) throws IOException,
      BrokerException {
    TransactionEvent event = TransactionEvent.createFromBytes(data);
    return event;
  }

 
  public void loggedCommitWrittenToMessageStore(TransactionUID tid, int type) {
    BaseTransactionManager txnMan = getTransactionManager(type);

    txnMan.playingToMessageStoreComplete(tid);

  }

  BaseTransactionManager getTransactionManager(int type) {
    BaseTransactionManager r = null;
    switch (type) {
    case BaseTransaction.LOCAL_TRANSACTION_TYPE:
      r = localTransactionManager;
      break;
    case BaseTransaction.CLUSTER_TRANSACTION_TYPE:
      r = clusterTransactionManager;
      break;
    case BaseTransaction.REMOTE_TRANSACTION_TYPE:
      r = remoteTransactionManager;
      break;
    case BaseTransaction.UNDEFINED_TRANSACTION_TYPE:
      throw new UnsupportedOperationException(
          "UNDEFINED_TRANSACTION_TYPE");
    default:
      throw new UnsupportedOperationException("unknown type:" + type);
    }
    return r;
  }
 
 
  public void logMsgRemoval(DestinationUID dstID, SysMessageID mid)
      throws BrokerException {
    if (Store.getDEBUG()) {
      String msg = getPrefix() + " logMsgRemoval() dstID=" + dstID + " mid="+mid;
      logger.log(Logger.DEBUG, msg);
    }
    try {
      store.txnLogSharedLock.lock();

      MsgRemovalEvent txnEvent = new MsgRemovalEvent(dstID,mid);
      byte[] data = txnEvent.writeToBytes();

      TransactionLogRecord record = msgLogWriter
          .newTransactionLogRecord();
      record.setBody(data);
      msgLogWriter.write(record);

    } catch (IOException ioe) {
      throw new BrokerException("error logging transaction", ioe);
    } finally {
      store.txnLogSharedLock.unlock();
    }
  }
 
 
  public void logNonTxnMessage(TransactionWorkMessage twm)
      throws BrokerException {
    if (Store.getDEBUG()) {
      String msg = getPrefix() + " logNonTxnMessage() " + twm;
      logger.log(Logger.DEBUG, msg);
    }
    try {
      store.txnLogSharedLock.lock();

      NonTransactedMsgEvent txnEvent = new NonTransactedMsgEvent(twm);
      writeTransactionEvent(txnEvent);
      loggedMessageHelper.messageLogged(twm);
    } finally {
      store.txnLogSharedLock.unlock();
    }
  }
 
 
  public void logNonTxnMessageAck(TransactionWorkMessageAck twma)
      throws BrokerException {
    if (Store.getDEBUG()) {
      String msg = getPrefix() + " logNonTxnMessageAck() " + twma;
      logger.log(Logger.DEBUG, msg);
    }
    try {
      store.txnLogSharedLock.lock();
      NonTransactedMsgAckEvent txnEvent = new NonTransactedMsgAckEvent(twma);
      writeTransactionEvent(txnEvent);         
    } finally {
      store.txnLogSharedLock.unlock();
    }
  }

  public void logTxn(BaseTransaction baseTxn) throws BrokerException {
    if (Store.getDEBUG()) {
      logger.log(Logger.DEBUG, getPrefix() + " logTxn() " + baseTxn);
    }

    try {
      store.txnLogSharedLock.lock();
      int type = baseTxn.getType();
      BaseTransactionManager txnManager = getTransactionManager(type);
      txnManager.processTxn(baseTxn);
      TransactionEvent txnEvent = txnManager
          .generateEvent(baseTxn, false);
      writeTransactionEvent(txnEvent);
     
    } catch (IOException ioe) {
      throw new BrokerException("error logging transaction", ioe);
    } finally {
      store.txnLogSharedLock.unlock();
    }
  }

  public void logTxnCompletion(TransactionUID tid, int completionState,
      int type) throws BrokerException {
    if (Store.getDEBUG()) {
      logger
          .log(Logger.DEBUG, getPrefix() + " logTxnCompletion() "
              + tid);
    }
    try {
      store.txnLogSharedLock.lock();
      BaseTransactionManager txnMan = getTransactionManager(type);
      BaseTransaction existing = txnMan.processTxnCompletion(tid,
          completionState);
      TransactionEvent txnEvent = txnMan.generateEvent(existing, true);

      writeTransactionEvent(txnEvent);
    } catch (IOException ioe) {
      throw new BrokerException("error logging transaction", ioe);

    } finally {
      store.txnLogSharedLock.unlock();
    }
  }
 
 
  public void writeTransactionEvent(TransactionEvent txnEvent) throws BrokerException
  {
    try {     
      byte[] data = txnEvent.writeToBytes();
      TransactionLogRecord record = msgLogWriter
          .newTransactionLogRecord();
      record.setBody(data);
      msgLogWriter.write(record);

    } catch (IOException ioe) {
      throw new BrokerException("error logging transaction", ioe);
    }
  }

  public void doCheckpoint() {

    if (Store.getDEBUG()) {
      logger.log(Logger.DEBUG, getPrefix() + " doCheckpoint");
    }
    // 1. get exclusive lock on transaction log
    // 2. wait for all logged commits to be written to message store
    // 3. sync message store
    // 4. write prepared txns (since last checkpoint) to prepared store
    // 5. sync prepareTxnStore
    // 6. reset txn log
    // 7. remove committed txns (since last checkpoint) from prepared store

    Store store = null;
    try {
      store = Globals.getStore();
    } catch (Throwable e) {
      logger.logStack(Logger.ERROR, "failed to getStore", e);

      // fatal
    }

    try {

      // 1. get an exclusive lock on txnLog to prevent any other updates
      // occurring
      store.txnLogExclusiveLock.lock();

      // 2. wait for all logged commits to be written to message store
      localTransactionManager.waitForPlayingToMessageStoreCompletion();
      clusterTransactionManager.waitForPlayingToMessageStoreCompletion();
      remoteTransactionManager.waitForPlayingToMessageStoreCompletion();
     
      //2.1 wait for pending removes to be completed for logged last ack on message
      loggedMessageHelper.waitForPendingRemoveCompletion();

      // 3. sync the message store
      store.syncStoreOnCheckpoint();

      // 4. write prepared transactions to prepared transaction store
      localTransactionManager
          .writePreparedTransactionsToPreparedTxnStoreOnCheckpoint();
      clusterTransactionManager
          .writePreparedTransactionsToPreparedTxnStoreOnCheckpoint();
      remoteTransactionManager
          .writePreparedTransactionsToPreparedTxnStoreOnCheckpoint();

      // 5. sync prepareTxnStore
      preparedTxnStore.sync();

      // 6. reset txn log
      msgLogWriter.checkpoint();

      // 7. remove committed txns (since last checkpoint) from prepared
      // store
      localTransactionManager.removeCompleteTransactionsAfterCheckpoint();
      clusterTransactionManager
          .removeCompleteTransactionsAfterCheckpoint();
      remoteTransactionManager
          .removeCompleteTransactionsAfterCheckpoint();
     
      // 8. reset logged message list
      loggedMessageHelper.onCheckpoint();

    } catch (Throwable e) {
      String msg = getPrefix()
          + "Failed to synchronize persistence store for transaction log checkpoint";
      logger.logStack(Logger.ERROR,
          BrokerResources.E_INTERNAL_BROKER_ERROR, msg, e);
    } finally {
      store.txnLogExclusiveLock.unlock();

      if (Store.getDEBUG()) {
        String msg = getPrefix() + " doCheckpoint complete";
        logger.log(Logger.DEBUG, msg);
      }
    }
  }

  // checkpoint handling

  public final void checkpoint() {
    if (Store.getDEBUG()) {
      logger.log(Logger.DEBUG, getPrefix() + " request a checkpoint");
    }

    checkpointManager.enqueueCheckpoint();
  }

  String getPrefix() {
    return "TransactionLogManager: " + Thread.currentThread().getName();
  }

  public LocalTransactionManager getLocalTransactionManager() {
    return localTransactionManager;
  }

  public ClusterTransactionManager getClusterTransactionManager() {
    return clusterTransactionManager;
  }

  public RemoteTransactionManager getRemoteTransactionManager() {
    return remoteTransactionManager;
  }

  public LoggedMessageHelper getLoggedMessageHelper() {
    return loggedMessageHelper;
  }



  public static boolean isReplayInProgress() {
    return replayInProgress;
  }



  public static void setReplayInProgress(boolean val) {
    TransactionLogManager.replayInProgress = val;
  }
}
TOP

Related Classes of com.sun.messaging.jmq.jmsserver.persist.file.TransactionLogManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.