private void doCleanUp(final boolean sendCloseMessage) throws ActiveMQException {
    try {
      if (closed) {
        return;
      }

      // We need an extra flag closing, since we need to prevent any more messages getting queued to
      // execute
      // after this and we can't just set the closed flag to true here, since after/in onmessage the
      // message
      // might be acked and if the consumer is already closed, the ack will be ignored
      closing = true;

      // Now we wait for any current handler runners to run.
      waitForOnMessageToComplete(true);

      resetLargeMessageController();

      closed = true;

      synchronized (this) {
        if (receiverThread != null) {
          // Wake up any receive() thread that might be waiting
          notify();
        }

        handler = null;

        receiverThread = null;
      }

      flushAcks();

      clearBuffer();

      if (sendCloseMessage) {
        sessionContext.closeConsumer(this);
      }
    } catch (Throwable t) {
      // Consumer close should always return without exception
    }

    session.removeConsumer(this);
  }
  private ClientMessage receive(final long timeout, final boolean forcingDelivery)
      throws ActiveMQException {
    checkClosed();

    if (largeMessageReceived != null) {
      // Check if there are pending packets to be received
      largeMessageReceived.discardBody();
      largeMessageReceived = null;
    }

    if (rateLimiter != null) {
      rateLimiter.limit();
    }

    if (handler != null) {
      throw ActiveMQClientMessageBundle.BUNDLE.messageHandlerSet();
    }

    if (clientWindowSize == 0) {
      startSlowConsumer();
    }

    receiverThread = Thread.currentThread();

    // To verify if deliveryForced was already call
    boolean deliveryForced = false;
    // To control when to call deliveryForce
    boolean callForceDelivery = false;

    long start = -1;

    long toWait = timeout == 0 ? Long.MAX_VALUE : timeout;

    try {
      while (true) {
        ClientMessageInternal m = null;

        synchronized (this) {
          while ((stopped || (m = buffer.poll()) == null) && !closed && toWait > 0) {
            if (start == -1) {
              start = System.currentTimeMillis();
            }

            if (m == null && forcingDelivery) {
              if (stopped) {
                break;
              }

              // we only force delivery once per call to receive
              if (!deliveryForced) {
                callForceDelivery = true;
                break;
              }
            }

            try {
              wait(toWait);
            } catch (InterruptedException e) {
              throw new ActiveMQInterruptedException(e);
            }

            if (m != null || closed) {
              break;
            }

            long now = System.currentTimeMillis();

            toWait -= now - start;

            start = now;
          }
        }

        if (failedOver) {
          if (m == null) {
            // if failed over and the buffer is null, we reset the state and try it again
            failedOver = false;
            deliveryForced = false;
            toWait = timeout == 0 ? Long.MAX_VALUE : timeout;
            continue;
          } else {
            failedOver = false;
          }
        }

        if (callForceDelivery) {
          if (isTrace) {
            ActiveMQClientLogger.LOGGER.trace("Forcing delivery");
          }
          // JBPAPP-6030 - Calling forceDelivery outside of the lock to avoid distributed dead locks
          sessionContext.forceDelivery(this, forceDeliveryCount++);
          callForceDelivery = false;
          deliveryForced = true;
          continue;
        }

        if (m != null) {
          session.workDone();

          if (m.containsProperty(ClientConsumerImpl.FORCED_DELIVERY_MESSAGE)) {
            long seq = m.getLongProperty(ClientConsumerImpl.FORCED_DELIVERY_MESSAGE);

            // Need to check if forceDelivery was called at this call
            // As we could be receiving a message that came from a previous call
            if (forcingDelivery && deliveryForced && seq == forceDeliveryCount - 1) {
              // forced delivery messages are discarded, nothing has been delivered by the queue
              resetIfSlowConsumer();

              if (isTrace) {
                ActiveMQClientLogger.LOGGER.trace(
                    "There was nothing on the queue, leaving it now:: returning null");
              }

              return null;
            } else {
              if (isTrace) {
                ActiveMQClientLogger.LOGGER.trace(
                    "Ignored force delivery answer as it belonged to another call");
              }
              // Ignore the message
              continue;
            }
          }
          // if we have already pre acked we can't expire
          boolean expired = m.isExpired();

          flowControlBeforeConsumption(m);

          if (expired) {
            m.discardBody();

            session.expire(this, m);

            if (clientWindowSize == 0) {
              startSlowConsumer();
            }

            if (toWait > 0) {
              continue;
            } else {
              return null;
            }
          }

          if (m.isLargeMessage()) {
            largeMessageReceived = m;
          }

          if (isTrace) {
            ActiveMQClientLogger.LOGGER.trace("Returning " + m);
          }

          return m;
        } else {
          if (isTrace) {
            ActiveMQClientLogger.LOGGER.trace("Returning null");
          }
          resetIfSlowConsumer();
          return null;
        }
      }
    } finally {
      receiverThread = null;
    }
  }