public void removeMessage(MessageId msgId) throws IOException {
   synchronized (messageTable) {
     Message removed = messageTable.remove(msgId);
     if (removed != null) {
       removed.decrementReferenceCount();
     }
     if ((lastBatchId != null && lastBatchId.equals(msgId)) || messageTable.isEmpty()) {
       lastBatchId = null;
     }
   }
 }
  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);
  }