@Test
  public void testCounter() throws Exception {
    ClientSessionFactory sf = createSessionFactory(sl);
    ClientSession session = sf.createSession();

    try {
      Queue queue =
          server.createQueue(new SimpleString("A1"), new SimpleString("A1"), null, true, false);

      PageSubscriptionCounter counter = locateCounter(queue);

      StorageManager storage = server.getStorageManager();

      Transaction tx = new TransactionImpl(server.getStorageManager());

      counter.increment(tx, 1);

      assertEquals(0, counter.getValue());

      tx.commit();

      storage.waitOnOperations();

      assertEquals(1, counter.getValue());
    } finally {
      sf.close();
      session.close();
    }
  }
  @Test
  public void testCleanupCounterNonPersistent() throws Exception {
    ClientSessionFactory sf = createSessionFactory(sl);
    ClientSession session = sf.createSession();

    try {
      Queue queue =
          server.createQueue(new SimpleString("A1"), new SimpleString("A1"), null, true, false);

      PageSubscriptionCounter counter = locateCounter(queue);

      ((PageSubscriptionCounterImpl) counter).setPersistent(false);

      StorageManager storage = server.getStorageManager();

      Transaction tx = new TransactionImpl(server.getStorageManager());

      for (int i = 0; i < 2100; i++) {

        counter.increment(tx, 1);

        if (i % 200 == 0) {
          tx.commit();

          storage.waitOnOperations();

          assertEquals(i + 1, counter.getValue());

          tx = new TransactionImpl(server.getStorageManager());
        }
      }

      tx.commit();

      storage.waitOnOperations();

      assertEquals(2100, counter.getValue());

      server.stop();

      server = newActiveMQServer();

      server.start();

      queue = server.locateQueue(new SimpleString("A1"));

      assertNotNull(queue);

      counter = locateCounter(queue);

      assertEquals(0, counter.getValue());

    } finally {
      sf.close();
      session.close();
    }
  }
  @Test
  public void testPrepareCounter() throws Exception {
    Xid xid = newXID();

    Queue queue =
        server.createQueue(new SimpleString("A1"), new SimpleString("A1"), null, true, false);

    PageSubscriptionCounter counter = locateCounter(queue);

    StorageManager storage = server.getStorageManager();

    Transaction tx = new TransactionImpl(xid, server.getStorageManager(), 300);

    for (int i = 0; i < 2000; i++) {
      counter.increment(tx, 1);
    }

    assertEquals(0, counter.getValue());

    tx.prepare();

    storage.waitOnOperations();

    assertEquals(0, counter.getValue());

    server.stop();

    server = newActiveMQServer();

    server.start();

    storage = server.getStorageManager();

    queue = server.locateQueue(new SimpleString("A1"));

    assertNotNull(queue);

    counter = locateCounter(queue);

    tx = server.getResourceManager().removeTransaction(xid);

    assertNotNull(tx);

    assertEquals(0, counter.getValue());

    tx.commit(false);

    storage.waitOnOperations();

    assertEquals(2000, counter.getValue());
  }
  /** @throws Exception */
  protected void createStorage() throws Exception {

    if (storeType == StoreConfiguration.StoreType.DATABASE) {
      journal = createJDBCJournalStorageManager(createDefaultJDBCConfig(true));
    } else {
      journal = createJournalStorageManager(createDefaultInVMConfig());
    }

    journal.start();

    journal.loadBindingJournal(new ArrayList<QueueBindingInfo>(), new ArrayList<GroupingInfo>());

    journal.loadMessageJournal(
        new FakePostOffice(), null, null, null, null, null, null, new FakeJournalLoader());
  }
  @Override
  @After
  public void tearDown() throws Exception {
    Exception exception = null;

    if (journal != null) {
      try {
        journal.stop();
      } catch (Exception e) {
        exception = e;
      }

      journal = null;
    }

    if (jmsJournal != null) {
      try {
        jmsJournal.stop();
      } catch (Exception e) {
        if (exception != null) exception = e;
      }

      jmsJournal = null;
    }

    destroyTables(Arrays.asList(new String[] {"MESSAGE", "BINDINGS", "LARGE_MESSAGE"}));
    super.tearDown();
    if (exception != null) throw exception;
  }
  @Override
  public synchronized void stop() throws Exception {
    if (!started) {
      return;
    }

    // Channel may be null if there isn't a connection to a live server
    if (channel != null) {
      channel.close();
    }

    for (ReplicatedLargeMessage largeMessage : largeMessages.values()) {
      largeMessage.releaseResources();
    }
    largeMessages.clear();

    for (Entry<JournalContent, Map<Long, JournalSyncFile>> entry :
        filesReservedForSync.entrySet()) {
      for (JournalSyncFile filesReserved : entry.getValue().values()) {
        filesReserved.close();
      }
    }

    filesReservedForSync.clear();
    if (journals != null) {
      for (Journal j : journals) {
        if (j instanceof FileWrapperJournal) j.stop();
      }
    }

    for (ConcurrentMap<Integer, Page> map : pageIndex.values()) {
      for (Page page : map.values()) {
        try {
          page.sync();
          page.close();
        } catch (Exception e) {
          ActiveMQServerLogger.LOGGER.errorClosingPageOnReplication(e);
        }
      }
    }
    pageManager.stop();

    pageIndex.clear();
    final CountDownLatch latch = new CountDownLatch(1);
    executor.execute(
        new Runnable() {

          @Override
          public void run() {
            latch.countDown();
          }
        });
    latch.await(30, TimeUnit.SECONDS);

    // Storage needs to be the last to stop
    storageManager.stop();

    started = false;
  }
 @Override
 public void sync() throws Exception {
   if (syncTimer != null) {
     syncTimer.addSync(storageManager.getContext());
   } else {
     ioSync();
   }
 }
  /**
   * 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<>();
    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) {
        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<>();
        perQueueMap.put(queueID, pendingCounters);
      }

      pendingCounters.add(pgCount);

      perQueueMap.put(queueID, pendingCounters);
    }
    return perAddressMap;
  }
  private void createLargeMessage(final long id, boolean liveToBackupSync) {
    ReplicatedLargeMessage msg;
    if (liveToBackupSync) {
      msg = new LargeServerMessageInSync(storageManager);
    } else {
      msg = storageManager.createLargeMessage();
    }

    msg.setDurable(true);
    msg.setMessageID(id);
    largeMessages.put(id, msg);
  }
 @Override
 public void handleNoMessageReferences(Map<Long, ServerMessage> messages) {
   for (ServerMessage msg : messages.values()) {
     if (msg.getRefCount() == 0) {
       ActiveMQServerLogger.LOGGER.journalUnreferencedMessage(msg.getMessageID());
       try {
         storageManager.deleteMessage(msg.getMessageID());
       } catch (Exception ignored) {
         ActiveMQServerLogger.LOGGER.journalErrorDeletingMessage(ignored, msg.getMessageID());
       }
     }
   }
 }
  @Override
  public synchronized void start() throws Exception {
    Configuration config = server.getConfiguration();
    try {
      storageManager = server.getStorageManager();
      storageManager.start();

      server.getManagementService().setStorageManager(storageManager);

      journalsHolder.put(JournalContent.BINDINGS, storageManager.getBindingsJournal());
      journalsHolder.put(JournalContent.MESSAGES, storageManager.getMessageJournal());

      for (JournalContent jc : EnumSet.allOf(JournalContent.class)) {
        filesReservedForSync.put(jc, new HashMap<Long, JournalSyncFile>());
        // We only need to load internal structures on the backup...
        journalLoadInformation[jc.typeByte] =
            journalsHolder.get(jc).loadSyncOnly(JournalState.SYNCING);
      }

      pageManager =
          new PagingManagerImpl(
              new PagingStoreFactoryNIO(
                  storageManager,
                  config.getPagingLocation(),
                  config.getJournalBufferSize_NIO(),
                  server.getScheduledPool(),
                  server.getExecutorFactory(),
                  config.isJournalSyncNonTransactional(),
                  criticalErrorListener),
              server.getAddressSettingsRepository());

      pageManager.start();

      started = true;
    } catch (Exception e) {
      if (server.isStarted()) throw e;
    }
  }
  @Test
  public void testRestartCounter() throws Exception {
    Queue queue =
        server.createQueue(new SimpleString("A1"), new SimpleString("A1"), null, true, false);

    PageSubscriptionCounter counter = locateCounter(queue);

    StorageManager storage = server.getStorageManager();

    Transaction tx = new TransactionImpl(server.getStorageManager());

    counter.increment(tx, 1);

    assertEquals(0, counter.getValue());

    tx.commit();

    storage.waitOnOperations();

    assertEquals(1, counter.getValue());

    sl.close();

    server.stop();

    server = newActiveMQServer();

    server.start();

    queue = server.locateQueue(new SimpleString("A1"));

    assertNotNull(queue);

    counter = locateCounter(queue);

    assertEquals(1, counter.getValue());
  }
  @Override
  public String sendMessage(
      final Map<String, String> headers,
      final int type,
      final String body,
      final String userID,
      boolean durable,
      final String user,
      final String password)
      throws Exception {
    securityStore.check(
        queue.getAddress(),
        CheckType.SEND,
        new SecurityAuth() {
          @Override
          public String getUsername() {
            return user;
          }

          @Override
          public String getPassword() {
            return password;
          }

          @Override
          public RemotingConnection getRemotingConnection() {
            return null;
          }
        });
    ServerMessageImpl message = new ServerMessageImpl(storageManager.generateID(), 50);
    for (String header : headers.keySet()) {
      message.putStringProperty(new SimpleString(header), new SimpleString(headers.get(header)));
    }
    message.setType((byte) type);
    message.setDurable(durable);
    message.setTimestamp(System.currentTimeMillis());
    message.setUserID(new UUID(UUID.TYPE_TIME_BASED, UUID.stringToBytes(userID)));
    if (body != null) {
      message.getBodyBuffer().writeBytes(Base64.decode(body));
    }
    message.setAddress(queue.getAddress());
    postOffice.route(message, null, true);
    return "" + message.getMessageID();
  }
  /**
   * This method will recover the counters after failures making sure the page counter doesn't get
   * out of sync
   *
   * @param pendingNonTXPageCounter
   * @throws Exception
   */
  @Override
  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<>();

          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()) {
              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) {
              logger.debug("Page " + entry.getKey() + " wasn't open, so we will just ignore");
            } else {
              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
          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) {
              logger.debug("Removing pending page counter " + record.getID());
              storageManager.deletePendingPageCounter(txRecoverCounter.getID(), record.getID());
              txRecoverCounter.setContainsPersistent();
            }
          }
        }
      }
    }

    txRecoverCounter.commit();
  }
  @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(ActiveMQServerImpl.GENERIC_IGNORED_FILTER);

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

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

          SimpleString newName = queueBindingInfo.getQueueName().concat("-" + (duplicateID++));
          ActiveMQServerLogger.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,
              queueBindingInfo.getUser(),
              true,
              false,
              queueBindingInfo.isAutoCreated());

      if (queueBindingInfo.isAutoCreated()) {
        queue.setConsumersRefCount(
            new AutoCreatedQueueManagerImpl(
                ((PostOfficeImpl) postOffice).getServer().getJMSQueueDeleter(),
                queueBindingInfo.getQueueName()));
      }

      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);
    }
  }