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 internalPublish(PublishMessage msg) {
    final AbstractMessage.QOSType qos = msg.getQos();
    final String topic = msg.getTopicName();
    LOG.info("embedded PUBLISH on topic <{}> with QoS {}", topic, qos);

    String guid = null;
    IMessagesStore.StoredMessage toStoreMsg = asStoredMessage(msg);
    toStoreMsg.setClientID("BROKER_SELF");
    toStoreMsg.setMessageID(1);
    if (qos == AbstractMessage.QOSType.EXACTLY_ONCE) { // QoS2
      guid = m_messagesStore.storePublishForFuture(toStoreMsg);
    }
    route2Subscribers(toStoreMsg);

    if (!msg.isRetainFlag()) {
      return;
    }
    if (qos == AbstractMessage.QOSType.MOST_ONE || !msg.getPayload().hasRemaining()) {
      // QoS == 0 && retain => clean old retained
      m_messagesStore.cleanRetained(topic);
      return;
    }
    if (guid == null) {
      // before wasn't stored
      guid = m_messagesStore.storePublishForFuture(toStoreMsg);
    }
    m_messagesStore.storeRetained(topic, guid);
  }
  /**
   * 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);
  }