Package org.hornetq.core.server.impl

Source Code of org.hornetq.core.server.impl.PostOfficeJournalLoader

/*
* 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.server.impl;

import javax.transaction.xa.Xid;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.hornetq.api.core.Message;
import org.hornetq.api.core.Pair;
import org.hornetq.api.core.SimpleString;
import org.hornetq.core.config.Configuration;
import org.hornetq.core.filter.Filter;
import org.hornetq.core.filter.impl.FilterImpl;
import org.hornetq.core.journal.Journal;
import org.hornetq.core.paging.PagedMessage;
import org.hornetq.core.paging.PagingManager;
import org.hornetq.core.paging.PagingStore;
import org.hornetq.core.paging.cursor.PageSubscription;
import org.hornetq.core.paging.cursor.PageSubscriptionCounter;
import org.hornetq.core.paging.impl.Page;
import org.hornetq.core.persistence.GroupingInfo;
import org.hornetq.core.persistence.QueueBindingInfo;
import org.hornetq.core.persistence.StorageManager;
import org.hornetq.core.persistence.impl.PageCountPending;
import org.hornetq.core.persistence.impl.journal.AddMessageRecord;
import org.hornetq.core.postoffice.Binding;
import org.hornetq.core.postoffice.DuplicateIDCache;
import org.hornetq.core.postoffice.PostOffice;
import org.hornetq.core.postoffice.impl.LocalQueueBinding;
import org.hornetq.core.server.HornetQServerLogger;
import org.hornetq.core.server.MessageReference;
import org.hornetq.core.server.NodeManager;
import org.hornetq.core.server.Queue;
import org.hornetq.core.server.QueueFactory;
import org.hornetq.core.server.ServerMessage;
import org.hornetq.core.server.group.GroupingHandler;
import org.hornetq.core.server.group.impl.GroupBinding;
import org.hornetq.core.server.management.ManagementService;
import org.hornetq.core.transaction.ResourceManager;
import org.hornetq.core.transaction.Transaction;
import org.hornetq.core.transaction.impl.TransactionImpl;

public class PostOfficeJournalLoader implements JournalLoader
{
   private final PostOffice postOffice;
   private final PagingManager pagingManager;
   private StorageManager storageManager;
   private final QueueFactory queueFactory;
   private final NodeManager nodeManager;
   private final ManagementService managementService;
   private final GroupingHandler groupingHandler;
   private Configuration configuration;
   private Map<Long, Queue> queues;

   public PostOfficeJournalLoader(PostOffice postOffice,
                                  PagingManager pagingManager,
                                  StorageManager storageManager,
                                  QueueFactory queueFactory,
                                  NodeManager nodeManager,
                                  ManagementService managementService,
                                  GroupingHandler groupingHandler,
                                  Configuration configuration)
   {

      this.postOffice = postOffice;
      this.pagingManager = pagingManager;
      this.storageManager = storageManager;
      this.queueFactory = queueFactory;
      this.nodeManager = nodeManager;
      this.managementService = managementService;
      this.groupingHandler = groupingHandler;
      this.configuration = configuration;
      queues = new HashMap<>();
   }

   public PostOfficeJournalLoader(PostOffice postOffice,
                                  PagingManager pagingManager,
                                  StorageManager storageManager,
                                  QueueFactory queueFactory,
                                  NodeManager nodeManager,
                                  ManagementService managementService,
                                  GroupingHandler groupingHandler,
                                  Configuration configuration,
                                  Map<Long, Queue> queues)
   {

      this(postOffice, pagingManager, storageManager, queueFactory, nodeManager, managementService, groupingHandler, configuration);
      this.queues = queues;
   }

   @Override
   public void initQueues(Map<Long, QueueBindingInfo> queueBindingInfosMap, List<QueueBindingInfo> queueBindingInfos) throws Exception
   {
      int duplicateID = 0;
      for (QueueBindingInfo queueBindingInfo : queueBindingInfos)
      {
         queueBindingInfosMap.put(queueBindingInfo.getId(), queueBindingInfo);

         Filter filter = FilterImpl.createFilter(queueBindingInfo.getFilterString());

         boolean isTopicIdentification =
            filter != null && filter.getFilterString() != null &&
               filter.getFilterString().toString().equals(HornetQServerImpl.GENERIC_IGNORED_FILTER);

         if (postOffice.getBinding(queueBindingInfo.getQueueName()) != null)
         {

            if (isTopicIdentification)
            {
               long tx = storageManager.generateUniqueID();
               storageManager.deleteQueueBinding(tx, queueBindingInfo.getId());
               storageManager.commitBindings(tx);
               continue;
            }
            else
            {

               SimpleString newName = queueBindingInfo.getQueueName().concat("-" + (duplicateID++));
               HornetQServerLogger.LOGGER.queueDuplicatedRenaming(queueBindingInfo.getQueueName().toString(), newName.toString());
               queueBindingInfo.replaceQueueName(newName);
            }
         }

         PageSubscription subscription = null;

         if (!isTopicIdentification)
         {
            subscription = pagingManager.getPageStore(queueBindingInfo.getAddress())
               .getCursorProvider()
               .createSubscription(queueBindingInfo.getId(), filter, true);
         }

         Queue queue = queueFactory.createQueue(queueBindingInfo.getId(),
                                                queueBindingInfo.getAddress(),
                                                queueBindingInfo.getQueueName(),
                                                filter,
                                                subscription,
                                                true,
                                                false);

         Binding binding = new LocalQueueBinding(queueBindingInfo.getAddress(), queue, nodeManager.getNodeId());

         queues.put(queueBindingInfo.getId(), queue);

         postOffice.addBinding(binding);

         managementService.registerAddress(queueBindingInfo.getAddress());
         managementService.registerQueue(queue, queueBindingInfo.getAddress(), storageManager);

      }
   }

   public void handleAddMessage(Map<Long, Map<Long, AddMessageRecord>> queueMap) throws Exception
   {
      for (Map.Entry<Long, Map<Long, AddMessageRecord>> entry : queueMap.entrySet())
      {
         long queueID = entry.getKey();

         Map<Long, AddMessageRecord> queueRecords = entry.getValue();

         Queue queue = this.queues.get(queueID);

         if (queue == null)
         {
            if (queueRecords.values().size() != 0)
            {
               HornetQServerLogger.LOGGER.journalCannotFindQueueForMessage(queueID);
            }

            continue;
         }

         // Redistribution could install a Redistributor while we are still loading records, what will be an issue with
         // prepared ACKs
         // We make sure te Queue is paused before we reroute values.
         queue.pause();

         Collection<AddMessageRecord> valueRecords = queueRecords.values();

         long currentTime = System.currentTimeMillis();

         for (AddMessageRecord record : valueRecords)
         {
            long scheduledDeliveryTime = record.getScheduledDeliveryTime();

            if (scheduledDeliveryTime != 0 && scheduledDeliveryTime <= currentTime)
            {
               scheduledDeliveryTime = 0;
               record.getMessage().removeProperty(Message.HDR_SCHEDULED_DELIVERY_TIME);
            }

            if (scheduledDeliveryTime != 0)
            {
               record.getMessage().putLongProperty(Message.HDR_SCHEDULED_DELIVERY_TIME, scheduledDeliveryTime);
            }

            MessageReference ref = postOffice.reroute(record.getMessage(), queue, null);

            ref.setDeliveryCount(record.getDeliveryCount());

            if (scheduledDeliveryTime != 0)
            {
               record.getMessage().removeProperty(Message.HDR_SCHEDULED_DELIVERY_TIME);
            }
         }
      }
   }

   public void handleNoMessageReferences(Map<Long, ServerMessage> messages)
   {
      for (ServerMessage msg : messages.values())
      {
         if (msg.getRefCount() == 0)
         {
            HornetQServerLogger.LOGGER.journalUnreferencedMessage(msg.getMessageID());
            try
            {
               storageManager.deleteMessage(msg.getMessageID());
            }
            catch (Exception ignored)
            {
               HornetQServerLogger.LOGGER.journalErrorDeletingMessage(ignored, msg.getMessageID());
            }
         }
      }
   }

   @Override
   public void handleGroupingBindings(List<GroupingInfo> groupingInfos)
   {
      for (GroupingInfo groupingInfo : groupingInfos)
      {
         if (groupingHandler != null)
         {
            groupingHandler.addGroupBinding(new GroupBinding(groupingInfo.getId(),
                                                             groupingInfo.getGroupId(),
                                                             groupingInfo.getClusterName()));
         }
      }
   }

   @Override
   public void handleDuplicateIds(Map<SimpleString, List<Pair<byte[], Long>>> duplicateIDMap) throws Exception
   {
      for (Map.Entry<SimpleString, List<Pair<byte[], Long>>> entry : duplicateIDMap.entrySet())
      {
         SimpleString address = entry.getKey();

         DuplicateIDCache cache = postOffice.getDuplicateIDCache(address);

         if (configuration.isPersistIDCache())
         {
            cache.load(entry.getValue());
         }
      }
   }

   @Override
   public void postLoad(Journal messageJournal) throws Exception
   {
      for (Queue queue : queues.values())
      {
         queue.resume();
      }

      if (System.getProperty("org.hornetq.opt.directblast") != null)
      {
         messageJournal.runDirectJournalBlast();
      }
   }

   @Override
   public void handlePreparedSendMessage(ServerMessage message, Transaction tx, long queueID) throws Exception
   {
      Queue queue = queues.get(queueID);

      if (queue == null)
      {
         HornetQServerLogger.LOGGER.journalMessageInPreparedTX(queueID);
         return;
      }
      postOffice.reroute(message, queue, tx);
   }

   @Override
   public void handlePreparedAcknowledge(long messageID, List<MessageReference> referencesToAck, long queueID) throws Exception
   {
      Queue queue = queues.get(queueID);

      if (queue == null)
      {
         throw new IllegalStateException("Cannot find queue with id " + queueID);
      }

      MessageReference removed = queue.removeReferenceWithID(messageID);

      if (removed == null)
      {
         HornetQServerLogger.LOGGER.journalErrorRemovingRef(messageID);
      }
      else
      {
         referencesToAck.add(removed);
      }
   }

   @Override
   public void handlePreparedTransaction(Transaction tx, List<MessageReference> referencesToAck, Xid xid, ResourceManager resourceManager) throws Exception
   {
      for (MessageReference ack : referencesToAck)
      {
         ack.getQueue().reacknowledge(tx, ack);
      }

      tx.setState(Transaction.State.PREPARED);

      resourceManager.putTransaction(xid, tx);
   }

   /**
    * This method will recover the counters after failures making sure the page counter doesn't get out of sync
    *
    * @param pendingNonTXPageCounter
    * @throws Exception
    */
   public void recoverPendingPageCounters(List<PageCountPending> pendingNonTXPageCounter) throws Exception
   {
      // We need a structure of the following
      // Address -> PageID -> QueueID -> List<PageCountPending>
      // The following loop will sort the records according to the hierarchy we need

      Transaction txRecoverCounter = new TransactionImpl(storageManager);

      Map<SimpleString, Map<Long, Map<Long, List<PageCountPending>>>> perAddressMap = generateMapsOnPendingCount(queues, pendingNonTXPageCounter, txRecoverCounter);

      for (SimpleString address : perAddressMap.keySet())
      {
         PagingStore store = pagingManager.getPageStore(address);
         Map<Long, Map<Long, List<PageCountPending>>> perPageMap = perAddressMap.get(address);

         // We have already generated this before, so it can't be null
         assert (perPageMap != null);

         for (Long pageId : perPageMap.keySet())
         {
            Map<Long, List<PageCountPending>> perQueue = perPageMap.get(pageId);

            // This can't be true!
            assert (perQueue != null);

            if (store.checkPageFileExists(pageId.intValue()))
            {
               // on this case we need to recalculate the records
               Page pg = store.createPage(pageId.intValue());
               pg.open();

               List<PagedMessage> pgMessages = pg.read(storageManager);
               Map<Long, AtomicInteger> countsPerQueueOnPage = new HashMap<Long, AtomicInteger>();

               for (PagedMessage pgd : pgMessages)
               {
                  if (pgd.getTransactionID() <= 0)
                  {
                     for (long q : pgd.getQueueIDs())
                     {
                        AtomicInteger countQ = countsPerQueueOnPage.get(q);
                        if (countQ == null)
                        {
                           countQ = new AtomicInteger(0);
                           countsPerQueueOnPage.put(q, countQ);
                        }
                        countQ.incrementAndGet();
                     }
                  }
               }

               for (Map.Entry<Long, List<PageCountPending>> entry : perQueue.entrySet())
               {
                  for (PageCountPending record : entry.getValue())
                  {
                     HornetQServerLogger.LOGGER.debug("Deleting pg tempCount " + record.getID());
                     storageManager.deletePendingPageCounter(txRecoverCounter.getID(), record.getID());
                  }

                  PageSubscriptionCounter counter = store.getCursorProvider().getSubscription(entry.getKey()).getCounter();

                  AtomicInteger value = countsPerQueueOnPage.get(entry.getKey());

                  if (value == null)
                  {
                     HornetQServerLogger.LOGGER.debug("Page " + entry.getKey() + " wasn't open, so we will just ignore");
                  }
                  else
                  {
                     HornetQServerLogger.LOGGER.debug("Replacing counter " + value.get());
                     counter.increment(txRecoverCounter, value.get());
                  }
               }
            }
            else
            {
               // on this case the page file didn't exist, we just remove all the records since the page is already gone
               HornetQServerLogger.LOGGER.debug("Page " + pageId + " didn't exist on address " + address + ", so we are just removing records");
               for (List<PageCountPending> records : perQueue.values())
               {
                  for (PageCountPending record : records)
                  {
                     HornetQServerLogger.LOGGER.debug("Removing pending page counter " + record.getID());
                     storageManager.deletePendingPageCounter(txRecoverCounter.getID(), record.getID());
                     txRecoverCounter.setContainsPersistent();
                  }
               }
            }
         }
      }

      txRecoverCounter.commit();
   }

   @Override
   public void cleanUp()
   {
      queues.clear();
   }

   /**
    * This generates a map for use on the recalculation and recovery of pending maps after reloading it
    *
    * @param queues
    * @param pendingNonTXPageCounter
    * @param txRecoverCounter
    * @return
    * @throws Exception
    */
   private Map<SimpleString, Map<Long, Map<Long, List<PageCountPending>>>>
   generateMapsOnPendingCount(Map<Long, Queue> queues, List<PageCountPending>
      pendingNonTXPageCounter, Transaction txRecoverCounter) throws Exception
   {
      Map<SimpleString, Map<Long, Map<Long, List<PageCountPending>>>> perAddressMap = new HashMap<SimpleString, Map<Long, Map<Long, List<PageCountPending>>>>();
      for (PageCountPending pgCount : pendingNonTXPageCounter)
      {
         long queueID = pgCount.getQueueID();
         long pageID = pgCount.getPageID();

         // We first figure what Queue is based on the queue id
         Queue queue = queues.get(queueID);

         if (queue == null)
         {
            HornetQServerLogger.LOGGER.debug("removing pending page counter id = " + pgCount.getID() + " as queueID=" + pgCount.getID() + " no longer exists");
            // this means the queue doesn't exist any longer, we will remove it from the storage
            storageManager.deletePendingPageCounter(txRecoverCounter.getID(), pgCount.getID());
            txRecoverCounter.setContainsPersistent();
            continue;
         }

         // Level 1 on the structure, per address
         SimpleString address = queue.getAddress();

         Map<Long, Map<Long, List<PageCountPending>>> perPageMap = perAddressMap.get(address);

         if (perPageMap == null)
         {
            perPageMap = new HashMap();
            perAddressMap.put(address, perPageMap);
         }


         Map<Long, List<PageCountPending>> perQueueMap = perPageMap.get(pageID);

         if (perQueueMap == null)
         {
            perQueueMap = new HashMap();
            perPageMap.put(pageID, perQueueMap);
         }

         List<PageCountPending> pendingCounters = perQueueMap.get(queueID);

         if (pendingCounters == null)
         {
            pendingCounters = new LinkedList<PageCountPending>();
            perQueueMap.put(queueID, pendingCounters);
         }

         pendingCounters.add(pgCount);

         perQueueMap.put(queueID, pendingCounters);
      }
      return perAddressMap;
   }
}
TOP

Related Classes of org.hornetq.core.server.impl.PostOfficeJournalLoader

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.