protected void directSend(
      ClientSession clientsession,
      String topic,
      AbstractMessage.QOSType qos,
      ByteBuffer message,
      boolean retained,
      Integer messageID) {
    String clientId = clientsession.clientID;
    LOG.debug(
        "directSend invoked clientId <{}> on topic <{}> QoS {} retained {} messageID {}",
        clientId,
        topic,
        qos,
        retained,
        messageID);
    PublishMessage pubMessage = new PublishMessage();
    pubMessage.setRetainFlag(retained);
    pubMessage.setTopicName(topic);
    pubMessage.setQos(qos);
    pubMessage.setPayload(message);

    LOG.info("send publish message to <{}> on topic <{}>", clientId, topic);
    if (LOG.isDebugEnabled()) {
      LOG.debug("content <{}>", DebugUtils.payload2Str(message));
    }
    // set the PacketIdentifier only for QoS > 0
    if (pubMessage.getQos() != AbstractMessage.QOSType.MOST_ONE) {
      pubMessage.setMessageID(messageID);
    } else {
      if (messageID != null) {
        throw new RuntimeException(
            "Internal bad error, trying to forwardPublish a QoS 0 message with PacketIdentifier: "
                + messageID);
      }
    }

    if (m_clientIDs == null) {
      throw new RuntimeException(
          "Internal bad error, found m_clientIDs to null while it should be initialized, somewhere it's overwritten!!");
    }
    LOG.debug("clientIDs are {}", m_clientIDs);
    if (m_clientIDs.get(clientId) == null) {
      // TODO while we were publishing to the target client, that client disconnected,
      // could happen is not an error HANDLE IT
      throw new RuntimeException(
          String.format(
              "Can't find a ConnectionDescriptor for client <%s> in cache <%s>",
              clientId, m_clientIDs));
    }
    ServerChannel session = m_clientIDs.get(clientId).session;
    LOG.debug("Session for clientId {} is {}", clientId, session);

    String user = (String) session.getAttribute(NettyChannel.ATTR_KEY_USERNAME);
    if (!m_authorizator.canRead(topic, user, clientId)) {
      LOG.debug("topic {} doesn't have read credentials", topic);
      return;
    }
    session.write(pubMessage);
  }
  /**
   * Flood the subscribers with the message to notify. MessageID is optional and should only used
   * for QoS 1 and 2
   */
  void route2Subscribers(IMessagesStore.StoredMessage pubMsg) {
    final String topic = pubMsg.getTopic();
    final AbstractMessage.QOSType publishingQos = pubMsg.getQos();
    final ByteBuffer origMessage = pubMsg.getMessage();
    LOG.debug(
        "route2Subscribers republishing to existing subscribers that matches the topic {}", topic);
    if (LOG.isTraceEnabled()) {
      LOG.trace("content <{}>", DebugUtils.payload2Str(origMessage));
      LOG.trace("subscription tree {}", subscriptions.dumpTree());
    }
    // if QoS 1 or 2 store the message
    String guid = null;
    if (publishingQos == QOSType.EXACTLY_ONCE || publishingQos == QOSType.LEAST_ONE) {
      guid = m_messagesStore.storePublishForFuture(pubMsg);
    }

    for (final Subscription sub : subscriptions.matches(topic)) {
      AbstractMessage.QOSType qos = publishingQos;
      if (qos.byteValue() > sub.getRequestedQos().byteValue()) {
        qos = sub.getRequestedQos();
      }
      ClientSession targetSession = m_sessionsStore.sessionForClient(sub.getClientId());
      verifyToActivate(sub.getClientId(), targetSession);

      LOG.debug(
          "Broker republishing to client <{}> topic <{}> qos <{}>, active {}",
          sub.getClientId(),
          sub.getTopicFilter(),
          qos,
          targetSession.isActive());
      ByteBuffer message = origMessage.duplicate();
      if (qos == AbstractMessage.QOSType.MOST_ONE && targetSession.isActive()) {
        // QoS 0
        directSend(targetSession, topic, qos, message, false, null);
      } else {
        // QoS 1 or 2
        // if the target subscription is not clean session and is not connected => store it
        if (!targetSession.isCleanSession() && !targetSession.isActive()) {
          // store the message in targetSession queue to deliver
          targetSession.enqueueToDeliver(guid);
        } else {
          // publish
          if (targetSession.isActive()) {
            int messageId = targetSession.nextPacketId();
            targetSession.inFlightAckWaiting(guid, messageId);
            directSend(targetSession, topic, qos, message, false, messageId);
          }
        }
      }
    }
  }