public void processPublish(ServerChannel session, PublishMessage msg) {
    LOG.trace("PUB --PUBLISH--> SRV executePublish invoked with {}", msg);
    String clientID = (String) session.getAttribute(NettyChannel.ATTR_KEY_CLIENTID);
    final String topic = msg.getTopicName();
    // check if the topic can be wrote
    String user = (String) session.getAttribute(NettyChannel.ATTR_KEY_USERNAME);
    if (!m_authorizator.canWrite(topic, user, clientID)) {
      LOG.debug("topic {} doesn't have write credentials", topic);
      return;
    }
    final AbstractMessage.QOSType qos = msg.getQos();
    final Integer messageID = msg.getMessageID();
    LOG.info("PUBLISH from clientID <{}> on topic <{}> with QoS {}", clientID, topic, qos);

    String guid = null;
    IMessagesStore.StoredMessage toStoreMsg = asStoredMessage(msg);
    toStoreMsg.setClientID(clientID);
    if (qos == AbstractMessage.QOSType.MOST_ONE) { // QoS0
      route2Subscribers(toStoreMsg);
    } else if (qos == AbstractMessage.QOSType.LEAST_ONE) { // QoS1
      route2Subscribers(toStoreMsg);
      sendPubAck(clientID, messageID);
      LOG.debug("replying with PubAck to MSG ID {}", messageID);
    } else if (qos == AbstractMessage.QOSType.EXACTLY_ONCE) { // QoS2
      guid = m_messagesStore.storePublishForFuture(toStoreMsg);
      sendPubRec(clientID, messageID);
      // Next the client will send us a pub rel
      // NB publish to subscribers for QoS 2 happen upon PUBREL from publisher
    }

    if (msg.isRetainFlag()) {
      if (qos == AbstractMessage.QOSType.MOST_ONE) {
        // QoS == 0 && retain => clean old retained
        m_messagesStore.cleanRetained(topic);
      } else {
        if (!msg.getPayload().hasRemaining()) {
          m_messagesStore.cleanRetained(topic);
        } else {
          if (guid == null) {
            // before wasn't stored
            guid = m_messagesStore.storePublishForFuture(toStoreMsg);
          }
          m_messagesStore.storeRetained(topic, guid);
        }
      }
    }
    m_interceptor.notifyTopicPublished(msg, clientID);
  }
  public void processSubscribe(ServerChannel session, SubscribeMessage msg) {
    String clientID = (String) session.getAttribute(NettyChannel.ATTR_KEY_CLIENTID);
    LOG.debug("SUBSCRIBE client <{}> packetID {}", clientID, msg.getMessageID());

    ClientSession clientSession = m_sessionsStore.sessionForClient(clientID);
    verifyToActivate(clientID, clientSession);
    // ack the client
    SubAckMessage ackMessage = new SubAckMessage();
    ackMessage.setMessageID(msg.getMessageID());

    List<Subscription> newSubscriptions = new ArrayList<>();
    for (SubscribeMessage.Couple req : msg.subscriptions()) {
      AbstractMessage.QOSType qos = AbstractMessage.QOSType.valueOf(req.getQos());
      Subscription newSubscription = new Subscription(clientID, req.getTopicFilter(), qos);
      // boolean valid = subscribeSingleTopic(newSubscription, req.getTopicFilter());
      boolean valid = clientSession.subscribe(req.getTopicFilter(), newSubscription);
      ackMessage.addType(valid ? qos : AbstractMessage.QOSType.FAILURE);
      if (valid) {
        newSubscriptions.add(newSubscription);
      }
    }

    // save session, persist subscriptions from session
    LOG.debug("SUBACK for packetID {}", msg.getMessageID());
    if (LOG.isTraceEnabled()) {
      LOG.trace("subscription tree {}", subscriptions.dumpTree());
    }
    session.write(ackMessage);

    // fire the publish
    for (Subscription subscription : newSubscriptions) {
      subscribeSingleTopic(subscription);
    }
  }
  /**
   * Remove the clientID from topic subscription, if not previously subscribed, doesn't reply any
   * error
   */
  public void processUnsubscribe(ServerChannel session, UnsubscribeMessage msg) {
    List<String> topics = msg.topicFilters();
    int messageID = msg.getMessageID();
    String clientID = (String) session.getAttribute(NettyChannel.ATTR_KEY_CLIENTID);
    LOG.debug("UNSUBSCRIBE subscription on topics {} for clientID <{}>", topics, clientID);

    ClientSession clientSession = m_sessionsStore.sessionForClient(clientID);
    verifyToActivate(clientID, clientSession);
    for (String topic : topics) {
      boolean validTopic = SubscriptionsStore.validate(topic);
      if (!validTopic) {
        // close the connection, not valid topicFilter is a protocol violation
        session.close(true);
        LOG.warn(
            "UNSUBSCRIBE found an invalid topic filter <{}> for clientID <{}>", topic, clientID);
        return;
      }

      subscriptions.removeSubscription(topic, clientID);
      clientSession.unsubscribeFrom(topic);
      m_interceptor.notifyTopicUnsubscribed(topic, clientID);
    }

    // ack the client
    UnsubAckMessage ackMessage = new UnsubAckMessage();
    ackMessage.setMessageID(messageID);

    LOG.info("replying with UnsubAck to MSG ID {}", messageID);
    session.write(ackMessage);
  }
  public void processDisconnect(ServerChannel session) throws InterruptedException {
    String clientID = (String) session.getAttribute(NettyChannel.ATTR_KEY_CLIENTID);
    boolean cleanSession = (Boolean) session.getAttribute(NettyChannel.ATTR_KEY_CLEANSESSION);
    LOG.info("DISCONNECT client <{}> with clean session {}", clientID, cleanSession);
    ClientSession clientSession = m_sessionsStore.sessionForClient(clientID);
    clientSession.disconnect();

    m_clientIDs.remove(clientID);
    session.close(true);

    // cleanup the will store
    m_willStore.remove(clientID);

    m_interceptor.notifyClientDisconnected(clientID);
    LOG.info("DISCONNECT client <{}> finished", clientID, cleanSession);
  }
  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);
  }
 public void processPubAck(ServerChannel session, PubAckMessage msg) {
   String clientID = (String) session.getAttribute(NettyChannel.ATTR_KEY_CLIENTID);
   int messageID = msg.getMessageID();
   // Remove the message from message store
   ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
   verifyToActivate(clientID, targetSession);
   targetSession.inFlightAcknowledged(messageID);
 }
 public void processPubComp(ServerChannel session, PubCompMessage msg) {
   String clientID = (String) session.getAttribute(NettyChannel.ATTR_KEY_CLIENTID);
   int messageID = msg.getMessageID();
   LOG.debug(
       "\t\tSRV <--PUBCOMP-- SUB processPubComp invoked for clientID {} ad messageID {}",
       clientID,
       messageID);
   // once received the PUBCOMP then remove the message from the temp memory
   ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
   verifyToActivate(clientID, targetSession);
   targetSession.secondPhaseAcknowledged(messageID);
 }
  public void processPubRec(ServerChannel session, PubRecMessage msg) {
    String clientID = (String) session.getAttribute(NettyChannel.ATTR_KEY_CLIENTID);
    int messageID = msg.getMessageID();
    ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
    verifyToActivate(clientID, targetSession);
    // remove from the inflight and move to the QoS2 second phase queue
    targetSession.inFlightAcknowledged(messageID);
    targetSession.secondPhaseAckWaiting(messageID);
    // once received a PUBREC reply with a PUBREL(messageID)
    LOG.debug(
        "\t\tSRV <--PUBREC-- SUB processPubRec invoked for clientID {} ad messageID {}",
        clientID,
        messageID);
    PubRelMessage pubRelMessage = new PubRelMessage();
    pubRelMessage.setMessageID(messageID);
    pubRelMessage.setQos(AbstractMessage.QOSType.LEAST_ONE);

    session.write(pubRelMessage);
  }
  /**
   * Second phase of a publish QoS2 protocol, sent by publisher to the broker. Search the stored
   * message and publish to all interested subscribers.
   */
  public void processPubRel(ServerChannel session, PubRelMessage msg) {
    String clientID = (String) session.getAttribute(NettyChannel.ATTR_KEY_CLIENTID);
    int messageID = msg.getMessageID();
    LOG.debug(
        "PUB --PUBREL--> SRV processPubRel invoked for clientID {} ad messageID {}",
        clientID,
        messageID);
    ClientSession targetSession = m_sessionsStore.sessionForClient(clientID);
    verifyToActivate(clientID, targetSession);
    IMessagesStore.StoredMessage evt = targetSession.storedMessage(messageID);
    route2Subscribers(evt);

    if (evt.isRetained()) {
      final String topic = evt.getTopic();
      if (!evt.getMessage().hasRemaining()) {
        m_messagesStore.cleanRetained(topic);
      } else {
        m_messagesStore.storeRetained(topic, evt.getGuid());
      }
    }

    sendPubComp(clientID, messageID);
  }