final Authorizator authorizator = new Authorizator(authorizatorPolicy); SessionRegistry sessions = new SessionRegistry(subscriptions, queueRepository, authorizator); dispatcher = new PostOffice(subscriptions, retainedRepository, sessions, interceptor, authorizator); final BrokerConfiguration brokerConfig = new BrokerConfiguration(config); MQTTConnectionFactory connectionFactory = new MQTTConnectionFactory(brokerConfig, authenticator, sessions,
void handleConnectionLost() { String clientID = NettyUtils.clientID(channel); if (clientID == null || clientID.isEmpty()) { return; } LOG.info("Notifying connection lost event. CId: {}, channel: {}", clientID, channel); Session session = sessionRegistry.retrieve(clientID); if (session.hasWill()) { postOffice.fireWill(session.getWill()); } if (session.isClean()) { sessionRegistry.remove(clientID); } else { sessionRegistry.disconnect(clientID); } connected = false; //dispatch connection lost to intercept. String userName = NettyUtils.userName(channel); postOffice.dispatchConnectionLost(clientID,userName); LOG.trace("dispatch disconnection: clientId={}, userName={}", clientID, userName); }
/** * Use the broker to publish a message. It's intended for embedding applications. It can be used * only after the integration is correctly started with startServer. * * @param msg the message to forward. * @param clientId the id of the sending integration. * @throws IllegalStateException if the integration is not yet started */ public void internalPublish(MqttPublishMessage msg, final String clientId) { final int messageID = msg.variableHeader().packetId(); if (!initialized) { LOG.error("Moquette is not started, internal message cannot be published. CId: {}, messageId: {}", clientId, messageID); throw new IllegalStateException("Can't publish on a integration is not yet started"); } LOG.trace("Internal publishing message CId: {}, messageId: {}", clientId, messageID); dispatcher.internalPublish(msg); }
postOffice.receivedPublishQos0(topic, username, clientId, payload, retain, msg); break; case AT_LEAST_ONCE: { final int messageID = msg.variableHeader().packetId(); postOffice.receivedPublishQos1(this, topic, username, payload, messageID, retain, msg); break; final Session session = sessionRegistry.retrieve(clientId); session.receivedPublishQos2(messageID, msg); postOffice.receivedPublishQos2(this, msg, username);
void processSubscribe(MqttSubscribeMessage msg) { final String clientID = NettyUtils.clientID(channel); if (!connected) { LOG.warn("SUBSCRIBE received on already closed connection, CId={}, channel: {}", clientID, channel); dropConnection(); return; } postOffice.subscribeClientToTopics(msg, clientID, NettyUtils.userName(channel), this); }
@Test public void testDontNotifyClientSubscribedToTopicAfterDisconnectedAndReconnectOnSameChannel() { connect(this.connection, FAKE_CLIENT_ID); subscribe(connection, NEWS_TOPIC, AT_MOST_ONCE); // publish on /news final ByteBuf payload = Unpooled.copiedBuffer("Hello world!", Charset.defaultCharset()); sut.receivedPublishQos0(new Topic(NEWS_TOPIC), TEST_USER, TEST_PWD, payload, false, MqttMessageBuilders.publish() .payload(payload.retainedDuplicate()) .qos(MqttQoS.AT_MOST_ONCE) .retained(false) .topicName(NEWS_TOPIC).build()); ConnectionTestUtils.verifyPublishIsReceived(channel, AT_MOST_ONCE, "Hello world!"); unsubscribeAndVerifyAck(NEWS_TOPIC); // publish on /news final ByteBuf payload2 = Unpooled.copiedBuffer("Hello world!", Charset.defaultCharset()); sut.receivedPublishQos0(new Topic(NEWS_TOPIC), TEST_USER, TEST_PWD, payload2, false, MqttMessageBuilders.publish() .payload(payload) .qos(MqttQoS.AT_MOST_ONCE) .retained(false) .topicName(NEWS_TOPIC).build()); ConnectionTestUtils.verifyNoPublishIsReceived(channel); }
private void publishQos1(MQTTConnection publisher, String topic, String payload, int messageID) { final ByteBuf bytePayload = Unpooled.copiedBuffer(payload, Charset.defaultCharset()); sut.receivedPublishQos1(publisher, new Topic(topic), TEST_USER, bytePayload, messageID, false, MqttMessageBuilders.publish() .payload(Unpooled.copiedBuffer(payload, Charset.defaultCharset())) .qos(MqttQoS.AT_LEAST_ONCE) .retained(false) .topicName(NEWS_TOPIC).build()); }
@Test public void testSubscribedToNotAuthorizedTopic() { NettyUtils.userName(channel, FAKE_USER_NAME); IAuthorizatorPolicy prohibitReadOnNewsTopic = mock(IAuthorizatorPolicy.class); when(prohibitReadOnNewsTopic.canRead(eq(new Topic(NEWS_TOPIC)), eq(FAKE_USER_NAME), eq(FAKE_CLIENT_ID))) .thenReturn(false); sut = new PostOffice(subscriptions, new MemoryRetainedRepository(), sessionRegistry, ConnectionTestUtils.NO_OBSERVERS_INTERCEPTOR, new Authorizator(prohibitReadOnNewsTopic)); connection.processConnect(connectMessage); ConnectionTestUtils.assertConnectAccepted(channel); //Exercise MqttSubscribeMessage subscribe = MqttMessageBuilders.subscribe() .addSubscription(AT_MOST_ONCE, NEWS_TOPIC) .messageId(1) .build(); sut.subscribeClientToTopics(subscribe, FAKE_CLIENT_ID, FAKE_USER_NAME, connection); // Verify MqttSubAckMessage subAckMsg = channel.readOutbound(); verifyFailureQos(subAckMsg); }
@Test public void cleanRetainedMessageStoreWhenPublishWithRetainedQos0IsReceived() { connection.processConnect(connectMessage); ConnectionTestUtils.assertConnectAccepted(channel); // publish a QoS1 retained message final ByteBuf anyPayload = Unpooled.copiedBuffer("Any payload", Charset.defaultCharset()); final MqttPublishMessage publishMsg = MqttMessageBuilders.publish() .payload(Unpooled.copiedBuffer("Any payload", Charset.defaultCharset())) .qos(MqttQoS.AT_LEAST_ONCE) .retained(true) .topicName(NEWS_TOPIC) .build(); sut.receivedPublishQos1(connection, new Topic(NEWS_TOPIC), TEST_USER, anyPayload, 1, true, publishMsg); assertMessageIsRetained(NEWS_TOPIC, anyPayload); // publish a QoS0 retained message // Exercise final ByteBuf qos0Payload = Unpooled.copiedBuffer("QoS0 payload", Charset.defaultCharset()); sut.receivedPublishQos0(new Topic(NEWS_TOPIC), TEST_USER, connection.getClientId(), qos0Payload, true, MqttMessageBuilders.publish() .payload(qos0Payload) .qos(MqttQoS.AT_MOST_ONCE) .retained(false) .topicName(NEWS_TOPIC).build()); // Verify assertTrue("Retained message for topic /news must be cleared", retainedRepository.isEmpty()); }
private void processUnsubscribe(MqttUnsubscribeMessage msg) { List<String> topics = msg.payload().topics(); String clientID = NettyUtils.clientID(channel); LOG.trace("Processing UNSUBSCRIBE message. CId={}, topics: {}", clientID, topics); postOffice.unsubscribe(topics, this, msg.variableHeader().messageId()); }
private void publishQos2(MQTTConnection connection, String topic, String payload) { final ByteBuf bytePayload = Unpooled.copiedBuffer(payload, Charset.defaultCharset()); sut.receivedPublishQos2(connection, MqttMessageBuilders.publish() .payload(bytePayload) .qos(MqttQoS.EXACTLY_ONCE) .retained(true) .topicName(NEWS_TOPIC).build(), "username"); }
private void publishRetainedMessagesForSubscriptions(String clientID, List<Subscription> newSubscriptions) { Session targetSession = this.sessionRegistry.retrieve(clientID); for (Subscription subscription : newSubscriptions) { final String topicFilter = subscription.getTopicFilter().toString(); final List<RetainedMessage> retainedMsgs = retainedRepository.retainedOnTopic(topicFilter); if (retainedMsgs.isEmpty()) { // not found continue; } for (RetainedMessage retainedMsg : retainedMsgs) { final MqttQoS retainedQos = retainedMsg.qosLevel(); MqttQoS qos = lowerQosToTheSubscriptionDesired(subscription, retainedQos); // final ByteBuf origPayload = retainedMsg.getPayload(); final ByteBuf payloadBuf = Unpooled.wrappedBuffer(retainedMsg.getPayload()); // ByteBuf payload = origPayload.retainedDuplicate(); targetSession.sendRetainedPublishOnSessionAtQos(subscription.getTopicFilter(), qos, payloadBuf); } } }
@Test public void testWillIsFired() { final PostOffice postOfficeMock = mock(PostOffice.class); sut = createMQTTConnectionWithPostOffice(CONFIG, postOfficeMock); channel = (EmbeddedChannel) sut.channel; MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID).willFlag(true) .willTopic("topic").willMessage("Topic message").build(); sut.processConnect(msg); // Exercise sut.handleConnectionLost(); // Verify verify(postOfficeMock).fireWill(any(Session.Will.class)); assertFalse("Connection MUST be disconnected", sut.isConnected()); }
postOffice.dispatchConnection(msg); LOG.trace("dispatch connection: {}", msg.toString()); } catch (SessionCorruptedException scex) {
private void subscribe(MqttQoS topic, String newsTopic, MQTTConnection connection) { MqttSubscribeMessage subscribe = MqttMessageBuilders.subscribe() .addSubscription(topic, newsTopic) .messageId(1) .build(); sut.subscribeClientToTopics(subscribe, connection.getClientId(), null, this.connection); MqttSubAckMessage subAck = ((EmbeddedChannel) this.connection.channel).readOutbound(); assertEquals(topic.value(), (int) subAck.payload().grantedQoSLevels().get(0)); }
@Test public void testPublishQoS0ToItself() { connection.processConnect(connectMessage); ConnectionTestUtils.assertConnectAccepted(channel); // subscribe subscribe(AT_MOST_ONCE, NEWS_TOPIC, connection); // Exercise final ByteBuf payload = Unpooled.copiedBuffer("Hello world!", Charset.defaultCharset()); sut.receivedPublishQos0(new Topic(NEWS_TOPIC), TEST_USER, FAKE_CLIENT_ID, payload, false, MqttMessageBuilders.publish() .payload(payload.retainedDuplicate()) .qos(MqttQoS.AT_MOST_ONCE) .retained(false) .topicName(NEWS_TOPIC).build()); // Verify ConnectionTestUtils.verifyReceivePublish(channel, NEWS_TOPIC, "Hello world!"); }
private void connectPublishDisconnectFromAnotherClient(String firstPayload, String topic) { MQTTConnection anotherConnection = connectNotCleanAs(PUBLISHER_ID); // publish from another channel final ByteBuf anyPayload = Unpooled.copiedBuffer(firstPayload, Charset.defaultCharset()); sut.receivedPublishQos1(anotherConnection, new Topic(topic), TEST_USER, anyPayload, 1, false, MqttMessageBuilders.publish() .payload(Unpooled.copiedBuffer(firstPayload, Charset.defaultCharset())) .qos(MqttQoS.AT_LEAST_ONCE) .retained(false) .topicName(topic).build()); // disconnect the other channel anotherConnection.processDisconnect(null); }
@Test public void testUnsubscribeWithBadFormattedTopic() { connect(this.connection, FAKE_CLIENT_ID); // Exercise sut.unsubscribe(singletonList(BAD_FORMATTED_TOPIC), connection, 1); // Verify assertFalse("Unsubscribe with bad topic MUST close drop the connection, (issue 68)", channel.isOpen()); }
@Test public void testPublishWithQoS2() { connection.processConnect(connectMessage); ConnectionTestUtils.assertConnectAccepted(channel); subscribe(connection, NEWS_TOPIC, EXACTLY_ONCE); // Exercise final ByteBuf anyPayload = Unpooled.copiedBuffer("Any payload", Charset.defaultCharset()); sut.receivedPublishQos2(connection, MqttMessageBuilders.publish() .payload(anyPayload) .qos(MqttQoS.EXACTLY_ONCE) .retained(true) .topicName(NEWS_TOPIC).build(), "username"); // Verify ConnectionTestUtils.verifyPublishIsReceived(channel, EXACTLY_ONCE, "Any payload"); }
private void publish2Subscribers(ByteBuf origPayload, Topic topic, MqttQoS publishingQos) { Set<Subscription> topicMatchingSubscriptions = subscriptions.matchQosSharpening(topic); for (final Subscription sub : topicMatchingSubscriptions) { MqttQoS qos = lowerQosToTheSubscriptionDesired(sub, publishingQos); Session targetSession = this.sessionRegistry.retrieve(sub.getClientId()); boolean isSessionPresent = targetSession != null; if (isSessionPresent) { LOG.debug("Sending PUBLISH message to active subscriber CId: {}, topicFilter: {}, qos: {}", sub.getClientId(), sub.getTopicFilter(), qos); // final String login = targetSession.login(); // if (!authorizator.canRead(topic, login, sub.getClientId())) { // LOG.debug("Authorizator prohibit Client {} to be notified on {}", sub.getClientId(), topic); // return; // } // we need to retain because duplicate only copy r/w indexes and don't retain() causing refCnt = 0 ByteBuf payload = origPayload.retainedDuplicate(); targetSession.sendPublishOnSessionAtQos(topic, qos, payload); } else { // If we are, the subscriber disconnected after the subscriptions tree selected that session as a // destination. LOG.debug("PUBLISH to not yet present session. CId: {}, topicFilter: {}, qos: {}", sub.getClientId(), sub.getTopicFilter(), qos); } } }