Package org.hornetq.tests.unit.core.journal.impl

Source Code of org.hornetq.tests.unit.core.journal.impl.JournalImplTestBase$TransactionHolder

/*
* Copyright 2009 Red Hat, Inc.
* Red Hat licenses this file to you under the Apache License, version
* 2.0 (the "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*    http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.  See the License for the specific language governing
* permissions and limitations under the License.
*/

package org.hornetq.tests.unit.core.journal.impl;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import junit.framework.Assert;

import org.hornetq.core.journal.EncodingSupport;
import org.hornetq.core.journal.PreparedTransactionInfo;
import org.hornetq.core.journal.RecordInfo;
import org.hornetq.core.journal.SequentialFileFactory;
import org.hornetq.core.journal.TestableJournal;
import org.hornetq.core.journal.impl.ExportJournal;
import org.hornetq.core.journal.impl.ImportJournal;
import org.hornetq.core.journal.impl.JournalImpl;
import org.hornetq.core.logging.Logger;
import org.hornetq.tests.util.UnitTestCase;
import org.hornetq.utils.ReusableLatch;

/**
*
* A JournalImplTestBase
*
* @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a>
*
*/
public abstract class JournalImplTestBase extends UnitTestCase
{
   private static final Logger log = Logger.getLogger(JournalImplTestBase.class);

   protected List<RecordInfo> records = new LinkedList<RecordInfo>();

   protected TestableJournal journal;

   protected int recordLength = 1024;

   protected Map<Long, TransactionHolder> transactions = new LinkedHashMap<Long, TransactionHolder>();

   protected int maxAIO;

   protected int minFiles;

   protected int fileSize;

   protected boolean sync;

   protected String filePrefix = "hq";

   protected String fileExtension = "hq";

   protected SequentialFileFactory fileFactory;

   private ReusableLatch latchDone = new ReusableLatch(0);

   private ReusableLatch latchWait = new ReusableLatch(0);

   private Thread compactThread;

   @Override
   protected void setUp() throws Exception
   {
      super.setUp();

      resetFileFactory();

      fileFactory.start();

      transactions.clear();

      records.clear();
   }

   @Override
   protected void tearDown() throws Exception
   {
      if (journal != null)
      {
         try
         {
            journal.stop();
         }
         catch (Exception ignore)
         {
         }
      }

      if (fileFactory != null)
      {
         fileFactory.stop();
      }

      fileFactory = null;

      journal = null;

      super.tearDown();
   }

   protected void resetFileFactory() throws Exception
   {
      fileFactory = getFileFactory();
   }

   protected void checkAndReclaimFiles() throws Exception
   {
      journal.debugWait();
      journal.checkReclaimStatus();
      journal.debugWait();
   }

   protected abstract SequentialFileFactory getFileFactory() throws Exception;

   // Private
   // ---------------------------------------------------------------------------------

   protected void setup(final int minFreeFiles, final int fileSize, final boolean sync, final int maxAIO)
   {
      minFiles = minFreeFiles;
      this.fileSize = fileSize;
      this.sync = sync;
      this.maxAIO = maxAIO;
   }

   protected void setup(final int minFreeFiles, final int fileSize, final boolean sync)
   {
      minFiles = minFreeFiles;
      this.fileSize = fileSize;
      this.sync = sync;
      maxAIO = 50;
   }

   public void createJournal() throws Exception
   {
      journal = new JournalImpl(fileSize, minFiles, 0, 0, fileFactory, filePrefix, fileExtension, maxAIO)
      {
         @Override
         public void onCompactDone()
         {
            latchDone.countDown();
            System.out.println("Waiting on Compact");
            try
            {
               latchWait.await();
            }
            catch (InterruptedException e)
            {
               e.printStackTrace();
            }
            System.out.println("Waiting on Compact Done");
         }
      };

      journal.setAutoReclaim(false);
   }

   // It will start compacting, but it will let the thread in wait mode at onCompactDone, so we can validate command
   // executions
   protected void startCompact() throws Exception
   {
      latchDone.setCount(1);
      latchWait.setCount(1);
      this.compactThread = new Thread()
      {
         public void run()
         {
            try
            {
               journal.testCompact();
            }
            catch (Throwable e)
            {
               e.printStackTrace();
            }
         }
      };

      this.compactThread.start();

      latchDone.await();
   }

   protected void finishCompact() throws Exception
   {
      latchWait.countDown();
      compactThread.join();
   }

   protected void startJournal() throws Exception
   {
      journal.start();
   }

   protected void stopJournal() throws Exception
   {
      stopJournal(true);
   }

   protected void stopJournal(final boolean reclaim) throws Exception
   {
      // We do a reclaim in here
      if (reclaim)
      {
         checkAndReclaimFiles();
      }

      journal.stop();
   }

   /**
    * @throws Exception
    */
   protected void exportImportJournal() throws Exception
   {
      System.out.println("Exporting to " + getTestDir() + "/output.log");

      ExportJournal.exportJournal(getTestDir(),
                                  this.filePrefix,
                                  this.fileExtension,
                                  this.minFiles,
                                  this.fileSize,
                                  getTestDir() + "/output.log");

      File dir = new File(getTestDir());

      FilenameFilter fnf = new FilenameFilter()
      {
         public boolean accept(final File file, final String name)
         {
            return name.endsWith("." + fileExtension);
         }
      };

      System.out.println("file = " + dir);

      File files[] = dir.listFiles(fnf);

      for (File file : files)
      {
         System.out.println("Deleting " + file);
         file.delete();
      }

      ImportJournal.importJournal(getTestDir(),
                                  filePrefix,
                                  fileExtension,
                                  minFiles,
                                  fileSize,
                                  getTestDir() + "/output.log");
   }

   protected void loadAndCheck() throws Exception
   {
      loadAndCheck(false);
   }

   protected void loadAndCheck(final boolean printDebugJournal) throws Exception
   {
      List<RecordInfo> committedRecords = new ArrayList<RecordInfo>();

      List<PreparedTransactionInfo> preparedTransactions = new ArrayList<PreparedTransactionInfo>();

      journal.load(committedRecords, preparedTransactions, null);

      checkRecordsEquivalent(records, committedRecords);

      if (printDebugJournal)
      {
         printJournalLists(records, committedRecords);
      }

      // check prepared transactions

      List<PreparedTransactionInfo> prepared = new ArrayList<PreparedTransactionInfo>();

      for (Map.Entry<Long, TransactionHolder> entry : transactions.entrySet())
      {
         if (entry.getValue().prepared)
         {
            PreparedTransactionInfo info = new PreparedTransactionInfo(entry.getKey(), null);

            info.records.addAll(entry.getValue().records);

            info.recordsToDelete.addAll(entry.getValue().deletes);

            prepared.add(info);
         }
      }

      checkTransactionsEquivalent(prepared, preparedTransactions);
   }

   protected void load() throws Exception
   {
      journal.load(null, null, null);
   }

   protected void beforeJournalOperation() throws Exception
   {
   }

   protected void add(final long... arguments) throws Exception
   {
      addWithSize(recordLength, arguments);
   }

   protected void addWithSize(final int size, final long... arguments) throws Exception
   {
      for (long element : arguments)
      {
         byte[] record = generateRecord(size);

         beforeJournalOperation();

         journal.appendAddRecord(element, (byte)0, record, sync);

         records.add(new RecordInfo(element, (byte)0, record, false, (short)0));
      }

      journal.debugWait();
   }

   protected void update(final long... arguments) throws Exception
   {
      for (long element : arguments)
      {
         byte[] updateRecord = generateRecord(recordLength);

         beforeJournalOperation();

         journal.appendUpdateRecord(element, (byte)0, updateRecord, sync);

         records.add(new RecordInfo(element, (byte)0, updateRecord, true, (short)0));
      }

      journal.debugWait();
   }

   protected void delete(final long... arguments) throws Exception
   {
      for (long element : arguments)
      {
         beforeJournalOperation();

         journal.appendDeleteRecord(element, sync);

         removeRecordsForID(element);
      }

      journal.debugWait();
   }

   protected void addTx(final long txID, final long... arguments) throws Exception
   {
      TransactionHolder tx = getTransaction(txID);

      for (long element : arguments)
      {
         // SIZE_BYTE + SIZE_LONG + SIZE_LONG + SIZE_INT + record.length +
         // SIZE_BYTE
         byte[] record = generateRecord(recordLength - (JournalImpl.SIZE_ADD_RECORD_TX + 1));

         beforeJournalOperation();

         journal.appendAddRecordTransactional(txID, element, (byte)0, record);

         tx.records.add(new RecordInfo(element, (byte)0, record, false, (short)0));

      }

      journal.debugWait();
   }

   protected void updateTx(final long txID, final long... arguments) throws Exception
   {
      TransactionHolder tx = getTransaction(txID);

      for (long element : arguments)
      {
         byte[] updateRecord = generateRecord(recordLength - (JournalImpl.SIZE_ADD_RECORD_TX + 1));

         beforeJournalOperation();

         journal.appendUpdateRecordTransactional(txID, element, (byte)0, updateRecord);

         tx.records.add(new RecordInfo(element, (byte)0, updateRecord, true, (short)0));
      }
      journal.debugWait();
   }

   protected void deleteTx(final long txID, final long... arguments) throws Exception
   {
      TransactionHolder tx = getTransaction(txID);

      for (long element : arguments)
      {
         beforeJournalOperation();

         journal.appendDeleteRecordTransactional(txID, element);

         tx.deletes.add(new RecordInfo(element, (byte)0, null, true, (short)0));
      }

      journal.debugWait();
   }

   protected void prepare(final long txID, final EncodingSupport xid) throws Exception
   {
      TransactionHolder tx = transactions.get(txID);

      if (tx == null)
      {
         tx = new TransactionHolder();
         transactions.put(txID, tx);
      }

      if (tx.prepared)
      {
         throw new IllegalStateException("Transaction is already prepared");
      }

      beforeJournalOperation();

      journal.appendPrepareRecord(txID, xid, sync);

      tx.prepared = true;

      journal.debugWait();
   }

   protected void commit(final long txID) throws Exception
   {
      TransactionHolder tx = transactions.remove(txID);

      if (tx == null)
      {
         throw new IllegalStateException("Cannot find tx " + txID);
      }

      beforeJournalOperation();

      journal.appendCommitRecord(txID, sync);

      records.addAll(tx.records);

      for (RecordInfo l : tx.deletes)
      {
         removeRecordsForID(l.id);
      }

      journal.debugWait();
   }

   protected void rollback(final long txID) throws Exception
   {
      TransactionHolder tx = transactions.remove(txID);

      if (tx == null)
      {
         throw new IllegalStateException("Cannot find tx " + txID);
      }

      beforeJournalOperation();

      journal.appendRollbackRecord(txID, sync);

      journal.debugWait();
   }

   protected void removeRecordsForID(final long id)
   {
      for (ListIterator<RecordInfo> iter = records.listIterator(); iter.hasNext();)
      {
         RecordInfo info = iter.next();

         if (info.id == id)
         {
            iter.remove();
         }
      }
   }

   protected TransactionHolder getTransaction(final long txID)
   {
      TransactionHolder tx = transactions.get(txID);

      if (tx == null)
      {
         tx = new TransactionHolder();

         transactions.put(txID, tx);
      }

      return tx;
   }

   protected void checkTransactionsEquivalent(final List<PreparedTransactionInfo> expected,
                                              final List<PreparedTransactionInfo> actual)
   {
      Assert.assertEquals("Lists not same length", expected.size(), actual.size());

      Iterator<PreparedTransactionInfo> iterExpected = expected.iterator();

      Iterator<PreparedTransactionInfo> iterActual = actual.iterator();

      while (iterExpected.hasNext())
      {
         PreparedTransactionInfo rexpected = iterExpected.next();

         PreparedTransactionInfo ractual = iterActual.next();

         Assert.assertEquals("ids not same", rexpected.id, ractual.id);

         checkRecordsEquivalent(rexpected.records, ractual.records);

         Assert.assertEquals("deletes size not same", rexpected.recordsToDelete.size(), ractual.recordsToDelete.size());

         Iterator<RecordInfo> iterDeletesExpected = rexpected.recordsToDelete.iterator();

         Iterator<RecordInfo> iterDeletesActual = ractual.recordsToDelete.iterator();

         while (iterDeletesExpected.hasNext())
         {
            long lexpected = iterDeletesExpected.next().id;

            long lactual = iterDeletesActual.next().id;

            Assert.assertEquals("Delete ids not same", lexpected, lactual);
         }
      }
   }

   protected void checkRecordsEquivalent(final List<RecordInfo> expected, final List<RecordInfo> actual)
   {
      if (expected.size() != actual.size())
      {
         printJournalLists(expected, actual);
      }

      Assert.assertEquals("Lists not same length", expected.size(), actual.size());

      Iterator<RecordInfo> iterExpected = expected.iterator();

      Iterator<RecordInfo> iterActual = actual.iterator();

      while (iterExpected.hasNext())
      {
         RecordInfo rexpected = iterExpected.next();

         RecordInfo ractual = iterActual.next();

         if (rexpected.id != ractual.id || rexpected.isUpdate != ractual.isUpdate)
         {
            printJournalLists(expected, actual);
         }

         Assert.assertEquals("ids not same", rexpected.id, ractual.id);

         Assert.assertEquals("type not same", rexpected.isUpdate, ractual.isUpdate);

         UnitTestCase.assertEqualsByteArrays(rexpected.data, ractual.data);
      }
   }

   /**
    * @param expected
    * @param actual
    */
   protected void printJournalLists(final List<RecordInfo> expected, final List<RecordInfo> actual)
   {
      System.out.println("***********************************************");
      System.out.println("Expected list:");
      for (RecordInfo info : expected)
      {
         System.out.println("Record " + info.id + " isUpdate = " + info.isUpdate);
      }
      if (actual != null)
      {
         System.out.println("***********************************************");
         System.out.println("Actual list:");
         for (RecordInfo info : actual)
         {
            System.out.println("Record " + info.id + " isUpdate = " + info.isUpdate);
         }
      }
      System.out.println("***********************************************");
   }

   protected byte[] generateRecord(final int length)
   {
      byte[] record = new byte[length];
      for (int i = 0; i < length; i++)
      {
         // record[i] = RandomUtil.randomByte();
         record[i] = UnitTestCase.getSamplebyte(i);
      }
      return record;
   }

   protected String debugJournal() throws Exception
   {
      return "***************************************************\n" + ((JournalImpl)journal).debug() +
             "***************************************************\n";
   }

   class TransactionHolder
   {
      List<RecordInfo> records = new ArrayList<RecordInfo>();

      List<RecordInfo> deletes = new ArrayList<RecordInfo>();

      boolean prepared;
   }

}
TOP

Related Classes of org.hornetq.tests.unit.core.journal.impl.JournalImplTestBase$TransactionHolder

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.