/**
   * Starts the consumer which will register a queue or topic listener with Hazelcast and enable
   * message consumption (push or pull). If the consumer is already active, this method does
   * nothing.
   */
  void start() {
    if (active) {
      return;
    }

    receiveLock.lock();
    try {
      // Start listening for events. We currently always listen for events even
      // if we don't have a message listener. If this has a performance impact
      // on Hazelcast we may want to only listen if there is a registered
      // message listener that we need to notify.
      IQueue<byte[]> queue = hazelcastMQContext.resolveQueue(destination);
      if (queue != null) {
        // Get the raw queue outside of any transactional context so we can add
        // an item listener.
        queue = config.getHazelcastInstance().getQueue(queue.getName());
        queueListener = new HzQueueListener(queue);
      }

      // If we are a consumer on a topic, immediately start listening for events
      // so we can buffer them for (a)synchronous consumption.
      ITopic<byte[]> topic = hazelcastMQContext.resolveTopic(destination);
      if (topic != null) {
        topicListener = new HzTopicListener(topic);
      }

      active = true;

      if (messageListener != null) {
        // We have a message listener, so tell the context to drain the dispatch
        // ready queues.
        hazelcastMQContext.onConsumerDispatchReady(id);
      } else {
        // Signal that any receive requests can continue.
        receiveCondition.signalAll();
      }
    } finally {
      receiveLock.unlock();
    }
  }