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); }
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); }
/** 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); } } } } }
/** * 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; }