Example #1
0
  /**
   * A PUBLISH Control Packet is sent from a Client to a Server or from Server to a Client to
   * transport an Application Message.
   *
   * @throws RetriableException
   * @throws UnRetriableException
   */
  @Override
  public void handle(PublishMessage publishMessage)
      throws RetriableException, UnRetriableException {

    log.debug(" handle : client attempting to publish a message.");

    /**
     * During an attempt to publish a message.
     *
     * <p>All Topic Names and Topic Filters MUST be at least one character long [MQTT-4.7.3-1]
     *
     * <p>The wildcard characters can be used in Topic Filters, but MUST NOT be used within a Topic
     * Name [MQTT-4.7.1-1].
     *
     * <p>Topic Names and Topic Filters MUST NOT include the null character (Unicode U+0000)
     * [Unicode] [MQTT-4.7.3-2]
     *
     * <p>Topic Names and Topic Filters are UTF-8 encoded strings, they MUST NOT encode to more than
     * 65535 bytes [MQTT-4.7.3-3]. See Section 1.5.3
     */
    String topic = publishMessage.getTopic();
    if (null == topic
        || topic.isEmpty()
        || topic.contains(Constant.MULTI_LEVEL_WILDCARD)
        || topic.contains(Constant.SINGLE_LEVEL_WILDCARD)
        || topic.contains(Constant.SYS_PREFIX)) {
      log.info(" handle : Invalid topic " + publishMessage.getTopic());
      throw new ShutdownException(" Invalid topic name");
    }

    /** Before publishing we should get the current session and validate it. */
    Observable<IOTClient> permissionObservable =
        checkPermission(
            publishMessage.getSessionId(),
            publishMessage.getAuthKey(),
            AuthorityRole.PUBLISH,
            topic);

    permissionObservable.subscribe(
        (iotSession) -> {
          try {

            publishMessage.setPartitionId(iotSession.getPartitionId());
            publishMessage.setClientId(iotSession.getSessionId());
            publishMessage.setId(-1);

            /** Message processing is based on 4.3 Quality of Service levels and protocol flows */

            /**
             * 4.3.1 QoS 0: At most once delivery Accepts ownership of the message when it receives
             * the PUBLISH packet.
             */
            if (MqttQoS.AT_MOST_ONCE.value() == publishMessage.getQos()) {

              getMessenger().publish(iotSession.getPartitionId(), publishMessage);
            }

            /**
             * 4.3.2 QoS 1: At least once delivery
             *
             * <p>MUST respond with a PUBACK Packet containing the Packet Identifier from the
             * incoming PUBLISH Packet, having accepted ownership of the Application Message After
             * it has sent a PUBACK Packet the Receiver MUST treat any incoming PUBLISH packet that
             * contains the same Packet Identifier as being a new publication, irrespective of the
             * setting of its DUP flag.
             */
            if (MqttQoS.AT_LEAST_ONCE.value() == publishMessage.getQos()) {

              getMessenger().publish(iotSession.getPartitionId(), publishMessage);

              // We need to generate a puback message to close this conversation.

              AcknowledgeMessage acknowledgeMessage =
                  AcknowledgeMessage.from(publishMessage.getMessageId());
              acknowledgeMessage.copyTransmissionData(publishMessage);

              pushToServer(acknowledgeMessage);
            }

            /**
             * 4.3.3 QoS 2: Exactly once delivery
             *
             * <p>MUST respond with a PUBREC containing the Packet Identifier from the incoming
             * PUBLISH Packet, having accepted ownership of the Application Message. Until it has
             * received the corresponding PUBREL packet, the Receiver MUST acknowledge any
             * subsequent PUBLISH packet with the same Packet Identifier by sending a PUBREC. It
             * MUST NOT cause duplicate messages to be delivered to any onward recipients in this
             * case.
             */
            if (MqttQoS.EXACTLY_ONCE.value() == publishMessage.getQos()) {

              queueQos2Message(publishMessage);
            }

          } catch (UnRetriableException | RetriableException e) {
            disconnectDueToError(e, publishMessage);
          }
        },
        throwable -> disconnectDueToError(throwable, publishMessage));
  }
Example #2
0
  /**
   * Provides the Observer with a new item to observe.
   *
   * <p>The {@link com.caricah.iotracah.core.modules.Server} may call this method 0 or more times.
   *
   * <p>The {@code Observable} will not call this method again after it calls either {@link
   * #onCompleted} or {@link #onError}.
   *
   * @param iotMessage the item emitted by the Observable
   */
  @Override
  public void onNext(IOTMessage iotMessage) {

    getExecutorService()
        .submit(
            () -> {
              log.info(" onNext : received {}", iotMessage);
              try {

                IOTMessage response = null;

                switch (iotMessage.getMessageType()) {
                  case ConnectMessage.MESSAGE_TYPE:
                    ConnectMessage connectMessage = (ConnectMessage) iotMessage;
                    response =
                        ConnectAcknowledgeMessage.from(
                            connectMessage.isDup(),
                            connectMessage.getQos(),
                            connectMessage.isRetain(),
                            connectMessage.getKeepAliveTime(),
                            MqttConnectReturnCode.CONNECTION_ACCEPTED);

                    break;
                  case SubscribeMessage.MESSAGE_TYPE:
                    SubscribeMessage subscribeMessage = (SubscribeMessage) iotMessage;

                    List<Integer> grantedQos = new ArrayList<>();
                    subscribeMessage
                        .getTopicFilterList()
                        .forEach(
                            topic -> {
                              String topicKey =
                                  quickCheckIdKey(
                                      "",
                                      Arrays.asList(topic.getKey().split(Constant.PATH_SEPARATOR)));

                              Set<String> channelIds = subscriptions.get(topicKey);

                              if (Objects.isNull(channelIds)) {
                                channelIds = new HashSet<>();
                              }

                              channelIds.add(subscribeMessage.getConnectionId());
                              subscriptions.put(topicKey, channelIds);

                              grantedQos.add(topic.getValue());
                            });

                    response =
                        SubscribeAcknowledgeMessage.from(
                            subscribeMessage.getMessageId(), grantedQos);

                    break;
                  case UnSubscribeMessage.MESSAGE_TYPE:
                    UnSubscribeMessage unSubscribeMessage = (UnSubscribeMessage) iotMessage;
                    response =
                        UnSubscribeAcknowledgeMessage.from(unSubscribeMessage.getMessageId());

                    break;
                  case Ping.MESSAGE_TYPE:
                    response = iotMessage;
                    break;
                  case PublishMessage.MESSAGE_TYPE:
                    PublishMessage publishMessage = (PublishMessage) iotMessage;

                    Set<String> matchingTopics =
                        getMatchingSubscriptions("", publishMessage.getTopic());

                    for (String match : matchingTopics) {
                      Set<String> channelIds = subscriptions.get(match);

                      if (Objects.nonNull(channelIds)) {

                        channelIds.forEach(
                            id -> {
                              PublishMessage clonePublishMessage = publishMessage.cloneMessage();
                              clonePublishMessage.copyTransmissionData(iotMessage);
                              clonePublishMessage.setConnectionId(id);
                              pushToServer(clonePublishMessage);
                            });
                      }
                    }

                    if (MqttQoS.AT_MOST_ONCE.value() == publishMessage.getQos()) {

                      break;

                    } else if (MqttQoS.AT_LEAST_ONCE.value() == publishMessage.getQos()) {

                      response = AcknowledgeMessage.from(publishMessage.getMessageId());
                      break;
                    }

                  case PublishReceivedMessage.MESSAGE_TYPE:
                  case ReleaseMessage.MESSAGE_TYPE:
                  case CompleteMessage.MESSAGE_TYPE:
                  case DisconnectMessage.MESSAGE_TYPE:
                  case AcknowledgeMessage.MESSAGE_TYPE:
                  default:
                    DisconnectMessage disconnectMessage = DisconnectMessage.from(true);
                    disconnectMessage.copyTransmissionData(iotMessage);

                    throw new ShutdownException(disconnectMessage);
                }

                if (Objects.nonNull(response)) {

                  response.copyTransmissionData(iotMessage);
                  pushToServer(response);
                }

              } catch (ShutdownException e) {

                IOTMessage response = e.getResponse();
                if (Objects.nonNull(response)) {
                  pushToServer(response);
                }

              } catch (Exception e) {
                log.error(" onNext : Serious error that requires attention ", e);
              }
            });
  }