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