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);
  }
 private static IMessagesStore.StoredMessage asStoredMessage(PublishMessage msg) {
   IMessagesStore.StoredMessage stored =
       new IMessagesStore.StoredMessage(
           msg.getPayload().array(), msg.getQos(), msg.getTopicName());
   stored.setRetained(msg.isRetainFlag());
   stored.setMessageID(msg.getMessageID());
   return stored;
 }
  /** Specialized version to publish will testament message. */
  private void forwardPublishWill(WillMessage will, String clientID) {
    // it has just to publish the message downstream to the subscribers
    // NB it's a will publish, it needs a PacketIdentifier for this conn, default to 1
    Integer messageId = null;
    if (will.getQos() != AbstractMessage.QOSType.MOST_ONE) {
      messageId = m_messagesStore.nextPacketID(clientID);
    }

    IMessagesStore.StoredMessage tobeStored = asStoredMessage(will);
    tobeStored.setClientID(clientID);
    tobeStored.setMessageID(messageId);
    route2Subscribers(tobeStored);
  }
  /**
   * 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);
          }
        }
      }
    }
  }
  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);
  }
  /**
   * 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);
  }
  private boolean subscribeSingleTopic(final Subscription newSubscription) {
    subscriptions.add(newSubscription.asClientTopicCouple());

    // scans retained messages to be published to the new subscription
    // TODO this is ugly, it does a linear scan on potential big dataset
    Collection<IMessagesStore.StoredMessage> messages =
        m_messagesStore.searchMatching(
            new IMatchingCondition() {
              public boolean match(String key) {
                return SubscriptionsStore.matchTopics(key, newSubscription.getTopicFilter());
              }
            });

    ClientSession targetSession = m_sessionsStore.sessionForClient(newSubscription.getClientId());
    verifyToActivate(newSubscription.getClientId(), targetSession);
    for (IMessagesStore.StoredMessage storedMsg : messages) {
      // fire the as retained the message
      LOG.debug("send publish message for topic {}", newSubscription.getTopicFilter());
      // forwardPublishQoS0(newSubscription.getClientId(), storedMsg.getTopic(), storedMsg.getQos(),
      // storedMsg.getPayload(), true);
      Integer packetID =
          storedMsg.getQos() == QOSType.MOST_ONE
              ? null
              : m_messagesStore.nextPacketID(newSubscription.getClientId());
      directSend(
          targetSession,
          storedMsg.getTopic(),
          storedMsg.getQos(),
          storedMsg.getPayload(),
          true,
          packetID);
    }

    // notify the Observables
    m_interceptor.notifyTopicSubscribed(newSubscription);
    return true;
  }
  /** Republish QoS1 and QoS2 messages stored into the session for the clientID. */
  private void republishStoredInSession(ClientSession clientSession) {
    LOG.trace("republishStoredInSession for client <{}>", clientSession);
    List<IMessagesStore.StoredMessage> publishedEvents = clientSession.storedMessages();
    if (publishedEvents.isEmpty()) {
      LOG.info("No stored messages for client <{}>", clientSession.clientID);
      return;
    }

    LOG.info("republishing stored messages to client <{}>", clientSession.clientID);
    for (IMessagesStore.StoredMessage pubEvt : publishedEvents) {
      // TODO put in flight zone
      directSend(
          clientSession,
          pubEvt.getTopic(),
          pubEvt.getQos(),
          pubEvt.getMessage(),
          false,
          pubEvt.getMessageID());
      clientSession.removeEnqueued(pubEvt.getGuid());
    }
  }
 private static IMessagesStore.StoredMessage asStoredMessage(WillMessage will) {
   IMessagesStore.StoredMessage pub =
       new IMessagesStore.StoredMessage(will.getPayload().array(), will.getQos(), will.getTopic());
   pub.setRetained(will.isRetained());
   return pub;
 }