final void removeAsyncMessage(
      ConnectionContext context, final MessageStore destination, final MessageAck ack)
      throws IOException {

    if (ack.isInTransaction()) {
      if (ack.getTransactionId().isXATransaction()
          || theStore.isConcurrentStoreAndDispatchTransactions() == false) {
        destination.removeAsyncMessage(context, ack);
      } else {
        Tx tx = getTx(ack.getTransactionId());
        tx.add(
            new RemoveMessageCommand(context) {
              @Override
              public MessageAck getMessageAck() {
                return ack;
              }

              @Override
              public Future<Object> run(ConnectionContext ctx) throws IOException {
                destination.removeMessage(ctx, ack);
                return AbstractMessageStore.FUTURE;
              }
            });
      }
    } else {
      destination.removeAsyncMessage(context, ack);
    }
  }
  final void acknowledge(
      ConnectionContext context,
      final TopicMessageStore destination,
      final String clientId,
      final String subscriptionName,
      final MessageId messageId,
      final MessageAck ack)
      throws IOException {

    if (ack.isInTransaction()) {
      if (ack.getTransactionId().isXATransaction()
          || theStore.isConcurrentStoreAndDispatchTransactions() == false) {
        destination.acknowledge(context, clientId, subscriptionName, messageId, ack);
      } else {
        Tx tx = getTx(ack.getTransactionId());
        tx.add(
            new RemoveMessageCommand(context) {
              public MessageAck getMessageAck() {
                return ack;
              }

              public Future<Object> run(ConnectionContext ctx) throws IOException {
                destination.acknowledge(ctx, clientId, subscriptionName, messageId, ack);
                return AbstractMessageStore.FUTURE;
              }
            });
      }
    } else {
      destination.acknowledge(context, clientId, subscriptionName, messageId, ack);
    }
  }
  public void acknowledge(MessageAck ack) throws Exception {
    MessageId first = ack.getFirstMessageId();
    MessageId lastm = ack.getLastMessageId();
    TransactionId tid = ack.getTransactionId();
    boolean isLocalTx = (tid != null) && tid.isLocalTransaction();
    boolean single = lastm.equals(first);

    MessageInfo mi = null;
    int n = 0;

    if (ack.isIndividualAck()) {
      Iterator<MessageInfo> iter = deliveringRefs.iterator();
      while (iter.hasNext()) {
        mi = iter.next();
        if (mi.amqId.equals(lastm)) {
          n++;
          iter.remove();
          session.getCoreSession().individualAcknowledge(nativeId, mi.nativeId);
          session.getCoreSession().commit();
          break;
        }
      }
    } else if (ack.isRedeliveredAck()) {
      // client tells that this message is for redlivery.
      // do nothing until poisoned.
      n = 1;
    } else if (ack.isPoisonAck()) {
      // send to dlq
      Iterator<MessageInfo> iter = deliveringRefs.iterator();
      boolean firstFound = false;
      while (iter.hasNext()) {
        mi = iter.next();
        if (mi.amqId.equals(first)) {
          n++;
          iter.remove();
          session
              .getCoreSession()
              .moveToDeadLetterAddress(nativeId, mi.nativeId, ack.getPoisonCause());
          session.getCoreSession().commit();
          if (single) {
            break;
          }
          firstFound = true;
        } else if (firstFound || first == null) {
          n++;
          iter.remove();
          session
              .getCoreSession()
              .moveToDeadLetterAddress(nativeId, mi.nativeId, ack.getPoisonCause());
          session.getCoreSession().commit();
          if (mi.amqId.equals(lastm)) {
            break;
          }
        }
      }
    } else if (ack.isDeliveredAck() || ack.isExpiredAck()) {
      // ToDo: implement with tests
      n = 1;
    } else {
      Iterator<MessageInfo> iter = deliveringRefs.iterator();
      boolean firstFound = false;
      while (iter.hasNext()) {
        MessageInfo ami = iter.next();
        if (ami.amqId.equals(first)) {
          n++;
          if (!isLocalTx) {
            iter.remove();
          } else {
            ami.setLocalAcked(true);
          }
          if (single) {
            mi = ami;
            break;
          }
          firstFound = true;
        } else if (firstFound || first == null) {
          n++;
          if (!isLocalTx) {
            iter.remove();
          } else {
            ami.setLocalAcked(true);
          }
          if (ami.amqId.equals(lastm)) {
            mi = ami;
            break;
          }
        }
      }
      if (mi != null && !isLocalTx) {
        session.getCoreSession().acknowledge(nativeId, mi.nativeId);
      }
    }

    acquireCredit(n);
  }