@Override
  @Before
  public void setUp() throws Exception {
    super.setUp();
    server = internalCreateServer();

    server.createAddressInfo(new AddressInfo(ADDRESS, RoutingType.ANYCAST));
    Queue queue = server.createQueue(ADDRESS, RoutingType.ANYCAST, ADDRESS, null, true, false);
    queue.getPageSubscription().getPagingStore().startPaging();

    for (int i = 0; i < 10; i++) {
      queue.getPageSubscription().getPagingStore().forceAnotherPage();
    }

    final ClientSessionFactory sf = createSessionFactory(locator);
    ClientSession session = sf.createSession(null, null, false, true, true, false, 0);
    ClientProducer prod = session.createProducer(ADDRESS);

    for (int i = 0; i < 500; i++) {
      ClientMessage msg = session.createMessage(true);
      msg.putIntProperty("key", i);
      prod.send(msg);
      if (i > 0 && i % 10 == 0) {
        session.commit();
      }
    }

    session.close();
    locator.close();

    server.stop();

    internalCreateServer();
  }
 // HORNETQ- 1084
 @Test
 public void testBasicSendWithDLAButNoBinding() throws Exception {
   SimpleString dla = new SimpleString("DLA");
   SimpleString qName = new SimpleString("q1");
   AddressSettings addressSettings =
       new AddressSettings().setMaxDeliveryAttempts(1).setDeadLetterAddress(dla);
   server.getAddressSettingsRepository().addMatch(qName.toString(), addressSettings);
   // SimpleString dlq = new SimpleString("DLQ1");
   // clientSession.createQueue(dla, dlq, null, false);
   clientSession.createQueue(qName, qName, null, false);
   ClientProducer producer = clientSession.createProducer(qName);
   producer.send(createTextMessage(clientSession, "heyho!"));
   clientSession.start();
   ClientConsumer clientConsumer = clientSession.createConsumer(qName);
   ClientMessage m = clientConsumer.receive(500);
   m.acknowledge();
   Assert.assertNotNull(m);
   Assert.assertEquals(m.getBodyBuffer().readString(), "heyho!");
   // force a cancel
   clientSession.rollback();
   m = clientConsumer.receiveImmediate();
   Assert.assertNull(m);
   clientConsumer.close();
   Queue q = (Queue) server.getPostOffice().getBinding(qName).getBindable();
   Assert.assertEquals(0, q.getDeliveringCount());
 }
  @Override
  public CompositeData[] browse(String filterStr) throws Exception {
    checkStarted();

    clearIO();
    try {
      int pageSize =
          addressSettingsRepository
              .getMatch(queue.getName().toString())
              .getManagementBrowsePageSize();
      int currentPageSize = 0;
      ArrayList<CompositeData> c = new ArrayList<>();
      Filter filter = FilterImpl.createFilter(filterStr);
      queue.flushExecutor();
      try (LinkedListIterator<MessageReference> iterator = queue.totalIterator()) {
        while (iterator.hasNext() && currentPageSize++ < pageSize) {
          MessageReference ref = iterator.next();
          if (filter == null || filter.match(ref.getMessage())) {
            c.add(OpenTypeSupport.convert(ref));
          }
        }
        CompositeData[] rc = new CompositeData[c.size()];
        c.toArray(rc);
        return rc;
      }
    } catch (ActiveMQException e) {
      throw new IllegalStateException(e.getMessage());
    } finally {
      blockOnIO();
    }
  }
  @Override
  public Map<String, Object>[] listMessages(final String filterStr) throws Exception {
    checkStarted();

    clearIO();
    try {
      Filter filter = FilterImpl.createFilter(filterStr);
      List<Map<String, Object>> messages = new ArrayList<>();
      queue.flushExecutor();
      try (LinkedListIterator<MessageReference> iterator = queue.totalIterator()) {
        while (iterator.hasNext()) {
          MessageReference ref = iterator.next();
          if (filter == null || filter.match(ref.getMessage())) {
            Message message = ref.getMessage();
            messages.add(message.toMap());
          }
        }
        return messages.toArray(new Map[messages.size()]);
      }
    } catch (ActiveMQException e) {
      throw new IllegalStateException(e.getMessage());
    } finally {
      blockOnIO();
    }
  }
  /**
   * 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;
  }
  @Override
  public void connectionFailed(
      final ActiveMQException me, boolean failedOver, String scaleDownTargetNodeID) {
    ActiveMQServerLogger.LOGGER.bridgeConnectionFailed(failedOver);

    synchronized (connectionGuard) {
      keepConnecting = true;
    }

    try {
      if (producer != null) {
        producer.close();
      }

      cleanUpSessionFactory(csf);
    } catch (Throwable dontCare) {
    }

    try {
      session.cleanUp(false);
    } catch (Throwable dontCare) {
    }

    if (scaleDownTargetNodeID != null && !scaleDownTargetNodeID.equals(nodeUUID.toString())) {
      synchronized (this) {
        try {
          logger.debug(
              "Moving "
                  + queue.getMessageCount()
                  + " messages from "
                  + queue.getName()
                  + " to "
                  + scaleDownTargetNodeID);
          ((QueueImpl) queue)
              .moveReferencesBetweenSnFQueues(SimpleString.toSimpleString(scaleDownTargetNodeID));

          // stop the bridge from trying to reconnect and clean up all the bindings
          fail(true);
        } catch (Exception e) {
          ActiveMQServerLogger.LOGGER.warn(e.getMessage(), e);
        }
      }
    } else if (scaleDownTargetNodeID != null) {
      // the disconnected node is scaling down to me, no need to reconnect to it
      logger.debug(
          "Received scaleDownTargetNodeID: " + scaleDownTargetNodeID + "; cancelling reconnect.");
      fail(true);
    } else {
      logger.debug("Received invalid scaleDownTargetNodeID: " + scaleDownTargetNodeID);

      fail(me.getType() == ActiveMQExceptionType.DISCONNECTED);
    }

    tryScheduleRetryReconnect(me.getType());
  }
 @Override
 public String toManagementString() {
   return this.getClass().getSimpleName()
       + " [name="
       + name
       + ", queue="
       + queue.getName()
       + "/"
       + queue.getID()
       + "]";
 }
  @Override
  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) {
          ActiveMQServerLogger.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);
        }
      }
    }
  }
  @Override
  public void postLoad(
      Journal messageJournal,
      ResourceManager resourceManager,
      Map<SimpleString, List<Pair<byte[], Long>>> duplicateIDMap)
      throws Exception {
    for (Queue queue : queues.values()) {
      queue.resume();
    }

    if (System.getProperty("org.apache.activemq.opt.directblast") != null) {
      messageJournal.runDirectJournalBlast();
    }
  }
  @Override
  public int moveMessages(
      final int flushLimit,
      final String filterStr,
      final String otherQueueName,
      final boolean rejectDuplicates)
      throws Exception {
    checkStarted();

    clearIO();
    try {
      Filter filter = FilterImpl.createFilter(filterStr);

      Binding binding = postOffice.getBinding(new SimpleString(otherQueueName));

      if (binding == null) {
        throw ActiveMQMessageBundle.BUNDLE.noQueueFound(otherQueueName);
      }

      int retValue =
          queue.moveReferences(flushLimit, filter, binding.getAddress(), rejectDuplicates);

      return retValue;
    } finally {
      blockOnIO();
    }
  }
  @Override
  public String listConsumersAsJSON() throws Exception {
    checkStarted();

    clearIO();
    try {
      Collection<Consumer> consumers = queue.getConsumers();

      JSONArray jsonArray = new JSONArray();

      for (Consumer consumer : consumers) {

        if (consumer instanceof ServerConsumer) {
          ServerConsumer serverConsumer = (ServerConsumer) consumer;

          JSONObject obj = new JSONObject();
          obj.put("consumerID", serverConsumer.getID());
          obj.put("connectionID", serverConsumer.getConnectionID().toString());
          obj.put("sessionID", serverConsumer.getSessionID());
          obj.put("browseOnly", serverConsumer.isBrowseOnly());
          obj.put("creationTime", serverConsumer.getCreationTime());

          jsonArray.put(obj);
        }
      }

      return jsonArray.toString();
    } finally {
      blockOnIO();
    }
  }
  private void internalCancelReferences() {
    cancelRefs();

    if (queue != null) {
      queue.deliverAsync();
    }
  }
  @Override
  public boolean retryMessage(final long messageID) throws Exception {

    checkStarted();
    clearIO();

    try {
      Filter singleMessageFilter =
          new Filter() {
            @Override
            public boolean match(ServerMessage message) {
              return message.getMessageID() == messageID;
            }

            @Override
            public SimpleString getFilterString() {
              return new SimpleString("custom filter for MESSAGEID= messageID");
            }
          };

      return queue.retryMessages(singleMessageFilter) > 0;
    } finally {
      blockOnIO();
    }
  }
  @Override
  public long countMessages(final String filterStr) throws Exception {
    checkStarted();

    clearIO();
    try {
      Filter filter = FilterImpl.createFilter(filterStr);
      if (filter == null) {
        return getMessageCount();
      } else {
        try (LinkedListIterator<MessageReference> iterator = queue.totalIterator()) {
          int count = 0;
          while (iterator.hasNext()) {
            MessageReference ref = iterator.next();
            if (filter.match(ref.getMessage())) {
              count++;
            }
          }
          return count;
        }
      }
    } finally {
      blockOnIO();
    }
  }
  @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) {
      ActiveMQServerLogger.LOGGER.journalErrorRemovingRef(messageID);
    } else {
      referencesToAck.add(removed);
    }
  }
 @Override
 public String getName() {
   clearIO();
   try {
     return queue.getName().toString();
   } finally {
     blockOnIO();
   }
 }
  @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 is done to prevent non tx to get out of sync in case of failures
   *
   * @param tx
   * @param page
   * @param ctx
   * @throws Exception
   */
  private void applyPageCounters(Transaction tx, Page page, RouteContextList ctx) throws Exception {
    List<org.apache.activemq.artemis.core.server.Queue> durableQueues = ctx.getDurableQueues();
    List<org.apache.activemq.artemis.core.server.Queue> nonDurableQueues =
        ctx.getNonDurableQueues();
    for (org.apache.activemq.artemis.core.server.Queue q : durableQueues) {
      if (tx == null) {
        // non transactional writes need an intermediate place
        // to avoid the counter getting out of sync
        q.getPageSubscription().getCounter().pendingCounter(page, 1);
      } else {
        // null tx is treated through pending counters
        q.getPageSubscription().getCounter().increment(tx, 1);
      }
    }

    for (org.apache.activemq.artemis.core.server.Queue q : nonDurableQueues) {
      q.getPageSubscription().getCounter().increment(tx, 1);
    }
  }
  protected void fail(final boolean permanently) {
    logger.debug(this + "\n\t::fail being called, permanently=" + permanently);

    if (queue != null) {
      try {
        if (logger.isTraceEnabled()) {
          logger.trace("Removing consumer on fail " + this + " from queue " + queue);
        }
        queue.removeConsumer(this);
      } catch (Exception dontcare) {
        logger.debug(dontcare);
      }
    }

    cancelRefs();
    if (queue != null) {
      queue.deliverAsync();
    }
  }
  @Override
  public boolean isPaused() throws Exception {
    checkStarted();

    clearIO();
    try {
      return queue.isPaused();
    } finally {
      blockOnIO();
    }
  }
  @Override
  public boolean expireMessage(final long messageID) throws Exception {
    checkStarted();

    clearIO();
    try {
      return queue.expireReference(messageID);
    } finally {
      blockOnIO();
    }
  }
  @Override
  public int retryMessages() throws Exception {
    checkStarted();
    clearIO();

    try {
      return queue.retryMessages(null);
    } finally {
      blockOnIO();
    }
  }
  /**
   * @param queue
   * @return
   * @throws Exception
   */
  private PageSubscriptionCounter locateCounter(Queue queue) throws Exception {
    PageSubscription subscription =
        server
            .getPagingManager()
            .getPageStore(new SimpleString("A1"))
            .getCursorProvider()
            .getSubscription(queue.getID());

    PageSubscriptionCounter counter = subscription.getCounter();
    return counter;
  }
  @Override
  public void resetMessagesAcknowledged() throws Exception {
    checkStarted();

    clearIO();
    try {
      queue.resetMessagesAcknowledged();
    } finally {
      blockOnIO();
    }
  }
  @Override
  public long getScheduledCount() {
    checkStarted();

    clearIO();
    try {
      return queue.getScheduledCount();
    } finally {
      blockOnIO();
    }
  }
  @Override
  public void flushExecutor() {
    checkStarted();

    clearIO();
    try {
      queue.flushExecutor();
    } finally {
      blockOnIO();
    }
  }
  @Override
  public boolean sendMessageToDeadLetterAddress(final long messageID) throws Exception {
    checkStarted();

    clearIO();
    try {
      return queue.sendMessageToDeadLetterAddress(messageID);
    } finally {
      blockOnIO();
    }
  }
  @Override
  public void resume() {
    checkStarted();

    clearIO();
    try {
      queue.resume();
    } finally {
      blockOnIO();
    }
  }
  @Override
  public long getMessagesAcknowledged() {
    checkStarted();

    clearIO();
    try {
      return queue.getMessagesAcknowledged();
    } finally {
      blockOnIO();
    }
  }
  protected Map<String, Object>[] getFirstMessage() throws Exception {
    checkStarted();

    clearIO();
    try {
      List<Map<String, Object>> messages = new ArrayList<>();
      queue.flushExecutor();
      try (LinkedListIterator<MessageReference> iterator = queue.totalIterator()) {
        // returns just the first, as it's the first only
        if (iterator.hasNext()) {
          MessageReference ref = iterator.next();
          Message message = ref.getMessage();
          messages.add(message.toMap());
        }
        return messages.toArray(new Map[1]);
      }
    } finally {
      blockOnIO();
    }
  }