All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hornetq.core.journal.impl.JournalTransaction Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 32.0.0.Final
Show newest version
/*
 * Copyright 2005-2014 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.core.journal.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import org.hornetq.api.core.HornetQExceptionType;
import org.hornetq.core.journal.impl.dataformat.JournalInternalRecord;

/**
 * A JournalTransaction
 *
 * @author Clebert Suconic
 *
 *
 */
public class JournalTransaction
{

   private JournalRecordProvider journal;

   private List pos;

   private List neg;

   private final long id;

   // All the files this transaction is touching on.
   // We can't have those files being reclaimed if there is a pending transaction
   private Set pendingFiles;

   private TransactionCallback currentCallback;

   private boolean compacting = false;

   private Map callbackList;

   private JournalFile lastFile = null;

   private final AtomicInteger counter = new AtomicInteger();

   public JournalTransaction(final long id, final JournalRecordProvider journal)
   {
      this.id = id;
      this.journal = journal;
   }

   public void replaceRecordProvider(final JournalRecordProvider provider)
   {
      journal = provider;
   }

   /**
    * @return the id
    */
   public long getId()
   {
      return id;
   }

   public int getCounter(final JournalFile file)
   {
      return internalgetCounter(file).intValue();
   }

   public void incCounter(final JournalFile file)
   {
      internalgetCounter(file).incrementAndGet();
   }

   public long[] getPositiveArray()
   {
      if (pos == null)
      {
         return new long[0];
      }
      else
      {
         int i = 0;
         long[] ids = new long[pos.size()];
         for (JournalUpdate el : pos)
         {
            ids[i++] = el.getId();
         }
         return ids;
      }
   }

   public void setCompacting()
   {
      compacting = true;

      // Everything is cleared on the transaction...
      // since we are compacting, everything is at the compactor's level
      clear();
   }

   /** This is used to merge transactions from compacting */
   public void merge(final JournalTransaction other)
   {
      if (other.pos != null)
      {
         if (pos == null)
         {
            pos = new ArrayList();
         }

         pos.addAll(other.pos);
      }

      if (other.neg != null)
      {
         if (neg == null)
         {
            neg = new ArrayList();
         }

         neg.addAll(other.neg);
      }

      if (other.pendingFiles != null)
      {
         if (pendingFiles == null)
         {
            pendingFiles = new HashSet();
         }

         pendingFiles.addAll(other.pendingFiles);
      }

      compacting = false;
   }

   /**
    *
    */
   public void clear()
   {
      // / Compacting is recreating all the previous files and everything
      // / so we just clear the list of previous files, previous pos and previous adds
      // / The transaction may be working at the top from now

      if (pendingFiles != null)
      {
         pendingFiles.clear();
      }

      if (callbackList != null)
      {
         callbackList.clear();
      }

      if (pos != null)
      {
         pos.clear();
      }

      if (neg != null)
      {
         neg.clear();
      }

      counter.set(0);

      lastFile = null;

      currentCallback = null;
   }

   /**
    * @param currentFile
    * @param data
    */
   public void fillNumberOfRecords(final JournalFile currentFile, final JournalInternalRecord data)
   {
      data.setNumberOfRecords(getCounter(currentFile));
   }

   public TransactionCallback getCallback(final JournalFile file) throws Exception
   {
      if (callbackList == null)
      {
         callbackList = new HashMap();
      }

      currentCallback = callbackList.get(file);

      if (currentCallback == null)
      {
         currentCallback = new TransactionCallback();
         callbackList.put(file, currentCallback);
      }

      if (currentCallback.getErrorMessage() != null)
      {
         throw HornetQExceptionType.createException(currentCallback.getErrorCode(), currentCallback.getErrorMessage());
      }

      currentCallback.countUp();

      return currentCallback;
   }

   public void addPositive(final JournalFile file, final long id, final int size)
   {
      incCounter(file);

      addFile(file);

      if (pos == null)
      {
         pos = new ArrayList();
      }

      pos.add(new JournalUpdate(file, id, size));
   }

   public void addNegative(final JournalFile file, final long id)
   {
      incCounter(file);

      addFile(file);

      if (neg == null)
      {
         neg = new ArrayList();
      }

      neg.add(new JournalUpdate(file, id, 0));
   }

   /**
    * The caller of this method needs to guarantee appendLock.lock at the journal. (unless this is being called from load what is a single thread process).
    * */
   public void commit(final JournalFile file)
   {
      JournalCompactor compactor = journal.getCompactor();

      if (compacting)
      {
         compactor.addCommandCommit(this, file);
      }
      else
      {

         if (pos != null)
         {
            for (JournalUpdate trUpdate : pos)
            {
               JournalRecord posFiles = journal.getRecords().get(trUpdate.id);

               if (compactor != null && compactor.lookupRecord(trUpdate.id))
               {
                  // This is a case where the transaction was opened after compacting was started,
                  // but the commit arrived while compacting was working
                  // We need to cache the counter update, so compacting will take the correct files when it is done
                  compactor.addCommandUpdate(trUpdate.id, trUpdate.file, trUpdate.size);
               }
               else if (posFiles == null)
               {
                  posFiles = new JournalRecord(trUpdate.file, trUpdate.size);

                  journal.getRecords().put(trUpdate.id, posFiles);
               }
               else
               {
                  posFiles.addUpdateFile(trUpdate.file, trUpdate.size);
               }
            }
         }

         if (neg != null)
         {
            for (JournalUpdate trDelete : neg)
            {
               if (compactor != null)
               {
                  compactor.addCommandDelete(trDelete.id, trDelete.file);
               }
               else
               {
                  JournalRecord posFiles = journal.getRecords().remove(trDelete.id);

                  if (posFiles != null)
                  {
                     posFiles.delete(trDelete.file);
                  }
               }
            }
         }

         // Now add negs for the pos we added in each file in which there were
         // transactional operations

         for (JournalFile jf : pendingFiles)
         {
            file.incNegCount(jf);
         }
      }
   }

   public void waitCallbacks() throws InterruptedException
   {
      if (callbackList != null)
      {
         for (TransactionCallback callback : callbackList.values())
         {
            callback.waitCompletion();
         }
      }
   }

   /** Wait completion at the latest file only */
   public void waitCompletion() throws Exception
   {
      if (currentCallback != null)
      {
         currentCallback.waitCompletion();
      }
   }

   /**
    * The caller of this method needs to guarantee appendLock.lock before calling this method if being used outside of the lock context.
    * or else potFilesMap could be affected
    * */
   public void rollback(final JournalFile file)
   {
      JournalCompactor compactor = journal.getCompactor();

      if (compacting && compactor != null)
      {
         compactor.addCommandRollback(this, file);
      }
      else
      {
         // Now add negs for the pos we added in each file in which there were
         // transactional operations
         // Note that we do this on rollback as we do on commit, since we need
         // to ensure the file containing
         // the rollback record doesn't get deleted before the files with the
         // transactional operations are deleted
         // Otherwise we may run into problems especially with XA where we are
         // just left with a prepare when the tx
         // has actually been rolled back

         for (JournalFile jf : pendingFiles)
         {
            file.incNegCount(jf);
         }
      }
   }

   /**
    * The caller of this method needs to guarantee appendLock.lock before calling this method if being used outside of the lock context.
    * or else potFilesMap could be affected
    * */
   public void prepare(final JournalFile file)
   {
      // We don't want the prepare record getting deleted before time

      addFile(file);
   }

   /** Used by load, when the transaction was not loaded correctly */
   public void forget()
   {
      // The transaction was not committed or rolled back in the file, so we
      // reverse any pos counts we added
      for (JournalFile jf : pendingFiles)
      {
         jf.decPosCount();
      }

   }

   @Override
   public String toString()
   {
      return "JournalTransaction(" + id + ")";
   }

   private AtomicInteger internalgetCounter(final JournalFile file)
   {
      if (lastFile != file)

      {
         lastFile = file;
         counter.set(0);
      }
      return counter;
   }

   private void addFile(final JournalFile file)
   {
      if (pendingFiles == null)
      {
         pendingFiles = new HashSet();
      }

      if (!pendingFiles.contains(file))
      {
         pendingFiles.add(file);

         // We add a pos for the transaction itself in the file - this
         // prevents any transactional operations
         // being deleted before a commit or rollback is written
         file.incPosCount();
      }
   }

   private static class JournalUpdate
   {
      private final JournalFile file;

      long id;

      int size;

      /**
       * @param file
       * @param id
       * @param size
       */
      private JournalUpdate(final JournalFile file, final long id, final int size)
      {
         super();
         this.file = file;
         this.id = id;
         this.size = size;
      }

      /**
       * @return the id
       */
      public long getId()
      {
         return id;
      }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy