// Must be synchronized since messages may be arriving while handler is being set and might
  // otherwise end
  // up not queueing enough executors - so messages get stranded
  public synchronized ClientConsumerImpl setMessageHandler(final MessageHandler theHandler)
      throws ActiveMQException {
    checkClosed();

    if (receiverThread != null) {
      throw ActiveMQClientMessageBundle.BUNDLE.inReceive();
    }

    boolean noPreviousHandler = handler == null;

    if (handler != theHandler && clientWindowSize == 0) {
      startSlowConsumer();
    }

    handler = theHandler;

    // if no previous handler existed queue up messages for delivery
    if (handler != null && noPreviousHandler) {
      requeueExecutors();
    }
    // if unsetting a previous handler may be in onMessage so wait for completion
    else if (handler == null && !noPreviousHandler) {
      waitForOnMessageToComplete(true);
    }

    return this;
  }
  /**
   * Decodes this TransportConfiguration from a buffer.
   *
   * <p>Note this is only used internally by ActiveMQ
   *
   * @param buffer the buffer to decode from
   */
  public void decode(final ActiveMQBuffer buffer) {
    name = buffer.readString();
    factoryClassName = buffer.readString();

    int num = buffer.readInt();

    if (params == null) {
      if (num > 0) {
        params = new HashMap<>();
      }
    } else {
      params.clear();
    }

    for (int i = 0; i < num; i++) {
      String key = buffer.readString();

      byte type = buffer.readByte();

      Object val;

      switch (type) {
        case TYPE_BOOLEAN:
          {
            val = buffer.readBoolean();

            break;
          }
        case TYPE_INT:
          {
            val = buffer.readInt();

            break;
          }
        case TYPE_LONG:
          {
            val = buffer.readLong();

            break;
          }
        case TYPE_STRING:
          {
            val = buffer.readString();

            break;
          }
        default:
          {
            throw ActiveMQClientMessageBundle.BUNDLE.invalidType(type);
          }
      }

      params.put(key, val);
    }
  }
  @Override
  public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause)
      throws Exception {
    if (!active) {
      return;
    }
    // We don't want to log this - since it is normal for this to happen during failover/reconnect
    // and we don't want to spew out stack traces in that event
    // The user has access to this exeception anyway via the ActiveMQException initial cause

    ActiveMQException me = ActiveMQClientMessageBundle.BUNDLE.nettyError();
    me.initCause(cause);

    synchronized (listener) {
      try {
        listener.connectionException(channelId(ctx.channel()), me);
        active = false;
      } catch (Exception ex) {
        ActiveMQClientLogger.LOGGER.errorCallingLifeCycleListener(ex);
      }
    }
  }
  private void encodeMap(final ActiveMQBuffer buffer, final Map<String, Object> map) {
    for (Map.Entry<String, Object> entry : map.entrySet()) {
      buffer.writeString(entry.getKey());

      Object val = entry.getValue();

      if (val instanceof Boolean) {
        buffer.writeByte(TransportConfiguration.TYPE_BOOLEAN);
        buffer.writeBoolean((Boolean) val);
      } else if (val instanceof Integer) {
        buffer.writeByte(TransportConfiguration.TYPE_INT);
        buffer.writeInt((Integer) val);
      } else if (val instanceof Long) {
        buffer.writeByte(TransportConfiguration.TYPE_LONG);
        buffer.writeLong((Long) val);
      } else if (val instanceof String) {
        buffer.writeByte(TransportConfiguration.TYPE_STRING);
        buffer.writeString((String) val);
      } else {
        throw ActiveMQClientMessageBundle.BUNDLE.invalidEncodeType(val);
      }
    }
  }
 private void checkClosed() throws ActiveMQException {
   if (closed) {
     throw ActiveMQClientMessageBundle.BUNDLE.consumerClosed();
   }
 }
  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;
    }
  }