Ejemplo n.º 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));
  }