1
This commit is contained in:
54
mqtt/org/eclipse/paho/mqttv5/common/ExceptionHelper.java
Normal file
54
mqtt/org/eclipse/paho/mqttv5/common/ExceptionHelper.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common;
|
||||
|
||||
/**
|
||||
* Utility class to help create exceptions of the correct type.
|
||||
*/
|
||||
public class ExceptionHelper {
|
||||
public static MqttException createMqttException(int reasonCode) {
|
||||
|
||||
return new MqttException(reasonCode);
|
||||
}
|
||||
|
||||
public static MqttException createMqttException(Throwable cause) {
|
||||
|
||||
return new MqttException(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the specified class is available to the current
|
||||
* class loader. This is used to protect the code against using Java SE
|
||||
* APIs on Java ME.
|
||||
* @param className The ClassName
|
||||
* @return if the class is available
|
||||
*/
|
||||
public static boolean isClassAvailable(String className) {
|
||||
boolean result = false;
|
||||
try {
|
||||
Class.forName(className);
|
||||
result = true;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Utility classes should not have a public or default constructor.
|
||||
private ExceptionHelper() {
|
||||
}
|
||||
}
|
158
mqtt/org/eclipse/paho/mqttv5/common/MqttException.java
Normal file
158
mqtt/org/eclipse/paho/mqttv5/common/MqttException.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package org.eclipse.paho.mqttv5.common;
|
||||
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.packet.MqttDisconnect;
|
||||
|
||||
public class MqttException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Client encountered an exception. Use the {@link #getCause()} method to get
|
||||
* the underlying reason.
|
||||
*/
|
||||
public static final short REASON_CODE_CLIENT_EXCEPTION = 0x00;
|
||||
|
||||
// New MQTTv5 Packet Errors
|
||||
public static final int REASON_CODE_INVALID_IDENTIFIER = 50000; // Invalid Identifier in the IV fields
|
||||
public static final int REASON_CODE_INVALID_RETURN_CODE = 50001; // Invalid Return code
|
||||
public static final int REASON_CODE_MALFORMED_PACKET = 50002; // Packet was somehow malformed and did not comply to the MQTTv5 specification
|
||||
public static final int REASON_CODE_UNSUPPORTED_PROTOCOL_VERSION = 50003; // The CONNECT packet did not contain the correct protocol name or version
|
||||
|
||||
/**
|
||||
* The Server sent a publish message with an invalid topic alias.
|
||||
*/
|
||||
public static final int REASON_CODE_INVALID_TOPIC_ALAS = 50004;
|
||||
|
||||
/**
|
||||
* The client attempted to decode a property that had already been decoded, and can only be included once.
|
||||
*/
|
||||
public static final int REASON_CODE_DUPLICATE_PROPERTY = 50005;
|
||||
|
||||
private int reasonCode;
|
||||
private Throwable cause;
|
||||
private String disconnectReasonString;
|
||||
private int disconnectReasonCode = 0;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>MqttException</code> with the specified code as the
|
||||
* underlying reason.
|
||||
*
|
||||
* @param reasonCode
|
||||
* the reason code for the exception.
|
||||
*/
|
||||
public MqttException(int reasonCode) {
|
||||
super();
|
||||
this.reasonCode = reasonCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>MqttException</code> with the specified code as the
|
||||
* underlying reason, with the disconnect reason if available. This is only
|
||||
* meant as a hint for the developer, as the
|
||||
* <code>MqttCallback.disconnected</code> callback is the intended disconnect
|
||||
* notification mechanism.
|
||||
*
|
||||
* @param reasonCode the reason code for the exception.
|
||||
* @param disconnect diconnect flag
|
||||
*/
|
||||
public MqttException(int reasonCode, MqttDisconnect disconnect) {
|
||||
super();
|
||||
this.reasonCode = reasonCode;
|
||||
if (disconnect != null) {
|
||||
this.disconnectReasonCode = disconnect.getReturnCode();
|
||||
if (disconnect.getProperties() != null) {
|
||||
this.disconnectReasonString = disconnect.getProperties().getReasonString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>MqttException</code> with the specified
|
||||
* <code>Throwable</code> as the underlying reason.
|
||||
*
|
||||
* @param cause
|
||||
* the underlying cause of the exception.
|
||||
*/
|
||||
public MqttException(Throwable cause) {
|
||||
super();
|
||||
this.reasonCode = REASON_CODE_CLIENT_EXCEPTION;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>MqttException</code> with the specified
|
||||
* <code>Throwable</code> as the underlying reason.
|
||||
*
|
||||
* @param reason
|
||||
* the reason code for the exception.
|
||||
* @param cause
|
||||
* the underlying cause of the exception.
|
||||
*/
|
||||
public MqttException(int reason, Throwable cause) {
|
||||
super();
|
||||
this.reasonCode = reason;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reason code for this exception.
|
||||
*
|
||||
* @return the code representing the reason for this exception.
|
||||
*/
|
||||
public int getReasonCode() {
|
||||
return reasonCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying cause of this exception, if available.
|
||||
*
|
||||
* @return the Throwable that was the root cause of this exception, which may be
|
||||
* <code>null</code>.
|
||||
*/
|
||||
@Override
|
||||
public Throwable getCause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the detail message for this exception.
|
||||
*
|
||||
* @return the detail message, which may be <code>null</code>.
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
ResourceBundle bundle = ResourceBundle.getBundle("org.eclipse.paho.mqttv5.common.nls.messages");
|
||||
String message;
|
||||
try {
|
||||
message = bundle.getString(Integer.toString(reasonCode));
|
||||
} catch (MissingResourceException mre) {
|
||||
message = "Untranslated MqttException - RC: " + reasonCode;
|
||||
}
|
||||
if(this.disconnectReasonCode != 0) {
|
||||
message += " Disconnect RC: " + disconnectReasonCode;
|
||||
}
|
||||
if(this.disconnectReasonString != null) {
|
||||
message += " Disconnect Reason: " + disconnectReasonString;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <code>String</code> representation of this exception.
|
||||
*
|
||||
* @return a <code>String</code> representation of this exception.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
String result = getMessage() + " (" + reasonCode + ")";
|
||||
if (cause != null) {
|
||||
result = result + " - " + cause.toString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
298
mqtt/org/eclipse/paho/mqttv5/common/MqttMessage.java
Normal file
298
mqtt/org/eclipse/paho/mqttv5/common/MqttMessage.java
Normal file
@@ -0,0 +1,298 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.packet.MqttProperties;
|
||||
|
||||
/**
|
||||
* An MQTT message holds the application payload and options specifying how the
|
||||
* message is to be delivered The message includes a "payload" (the body of the
|
||||
* message) represented as a byte[].
|
||||
*/
|
||||
public class MqttMessage {
|
||||
|
||||
private boolean mutable = true;
|
||||
private byte[] payload;
|
||||
private int qos = 1;
|
||||
private boolean retained = false;
|
||||
private boolean dup = false;
|
||||
private int messageId;
|
||||
private MqttProperties properties;
|
||||
|
||||
/**
|
||||
* Utility method to validate the supplied QoS value.
|
||||
*
|
||||
* @param qos
|
||||
* The Quality Of Service Level to validate
|
||||
* @throws IllegalArgumentException
|
||||
* if value of QoS is not 0, 1 or 2.
|
||||
*
|
||||
*/
|
||||
public static void validateQos(int qos) {
|
||||
if ((qos < 0) || (qos > 2)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message with an empty payload, and all other values set to
|
||||
* defaults.
|
||||
*
|
||||
* The defaults are:
|
||||
* <ul>
|
||||
* <li>Message QoS set to 1</li>
|
||||
* <li>Message will not be "retained" by the server</li>
|
||||
* </ul>
|
||||
*/
|
||||
public MqttMessage() {
|
||||
setPayload(new byte[] {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message with the specified byte array as a payload, and all
|
||||
* other values set to defaults.
|
||||
*
|
||||
* @param payload
|
||||
* the payload
|
||||
*/
|
||||
public MqttMessage(byte[] payload) {
|
||||
setPayload(payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contructs an message with the specified payload, qos and retained flag.
|
||||
*
|
||||
* @param payload
|
||||
* The Message Payload.
|
||||
* @param qos
|
||||
* The Message QoS.
|
||||
* @param retained
|
||||
* If the message is retained.
|
||||
* @param properties
|
||||
* The Message {@link MqttProperties}
|
||||
*/
|
||||
public MqttMessage(byte[] payload, int qos, boolean retained, MqttProperties properties) {
|
||||
setPayload(payload);
|
||||
setQos(qos);
|
||||
setRetained(retained);
|
||||
setProperties(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the payload as a byte array.
|
||||
*
|
||||
* @return the payload as a byte array.
|
||||
*/
|
||||
public byte[] getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the payload, resetting it to be empty.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* if this message cannot be edited
|
||||
*/
|
||||
public void clearPayload() {
|
||||
checkMutable();
|
||||
this.payload = new byte[] {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the payload of this message to be the specified byte array.
|
||||
*
|
||||
* @param payload
|
||||
* the payload for this message.
|
||||
* @throws IllegalStateException
|
||||
* if this message cannot be edited
|
||||
* @throws NullPointerException
|
||||
* if no payload is provided
|
||||
*/
|
||||
public void setPayload(byte[] payload) {
|
||||
checkMutable();
|
||||
if (payload == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this message should be/was retained by the server. For
|
||||
* messages received from the server, this method returns whether or not the
|
||||
* message was from a current publisher, or was "retained" by the server as the
|
||||
* last message published on the topic.
|
||||
*
|
||||
* @return <code>true</code> if the message should be, or was, retained by the
|
||||
* server.
|
||||
* @see #setRetained(boolean)
|
||||
*/
|
||||
public boolean isRetained() {
|
||||
return retained;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the publish message should be retained by the messaging
|
||||
* engine. Sending a message with retained set to <code>true</code> and with an
|
||||
* empty byte array as the payload e.g. <code>new byte[0]</code> will clear the
|
||||
* retained message from the server. The default value is <code>false</code>
|
||||
*
|
||||
* @param retained
|
||||
* whether or not the messaging engine should retain the message.
|
||||
* @throws IllegalStateException
|
||||
* if this message cannot be edited
|
||||
*/
|
||||
public void setRetained(boolean retained) {
|
||||
checkMutable();
|
||||
this.retained = retained;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quality of service for this message.
|
||||
*
|
||||
* @return the quality of service to use, either 0, 1, or 2.
|
||||
* @see #setQos(int)
|
||||
*/
|
||||
public int getQos() {
|
||||
return qos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quality of service for this message.
|
||||
* <ul>
|
||||
* <li>Quality of Service 0 - indicates that a message should be delivered at
|
||||
* most once (zero or one times). The message will not be persisted to disk, and
|
||||
* will not be acknowledged across the network. This QoS is the fastest, but
|
||||
* should only be used for messages which are not valuable - note that if the
|
||||
* server cannot process the message (for example, there is an authorization
|
||||
* problem). Also known as "fire and forget".</li>
|
||||
*
|
||||
* <li>Quality of Service 1 - indicates that a message should be delivered at
|
||||
* least once (one or more times). The message can only be delivered safely if
|
||||
* it can be persisted, so the application must supply a means of persistence
|
||||
* using <code>MqttConnectOptions</code>. If a persistence mechanism is not
|
||||
* specified, the message will not be delivered in the event of a client
|
||||
* failure. The message will be acknowledged across the network. This is the
|
||||
* default QoS.</li>
|
||||
*
|
||||
* <li>Quality of Service 2 - indicates that a message should be delivered once.
|
||||
* The message will be persisted to disk, and will be subject to a two-phase
|
||||
* acknowledgement across the network. The message can only be delivered safely
|
||||
* if it can be persisted, so the application must supply a means of persistence
|
||||
* using <code>MqttConnectOptions</code>. If a persistence mechanism is not
|
||||
* specified, the message will not be delivered in the event of a client
|
||||
* failure.</li>
|
||||
* </ul>
|
||||
*
|
||||
* If persistence is not configured, QoS 1 and 2 messages will still be
|
||||
* delivered in the event of a network or server problem as the client will hold
|
||||
* state in memory. If the MQTT client is shutdown or fails and persistence is
|
||||
* not configured then delivery of QoS 1 and 2 messages can not be maintained as
|
||||
* client-side state will be lost.
|
||||
*
|
||||
* @param qos
|
||||
* the "quality of service" to use. Set to 0, 1, 2.
|
||||
* @throws IllegalArgumentException
|
||||
* if value of QoS is not 0, 1 or 2.
|
||||
* @throws IllegalStateException
|
||||
* if this message cannot be edited
|
||||
*/
|
||||
public void setQos(int qos) {
|
||||
checkMutable();
|
||||
validateQos(qos);
|
||||
this.qos = qos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mutability of this object (whether or not its values can be changed.
|
||||
*
|
||||
* @param mutable
|
||||
* <code>true</code> if the values can be changed, <code>false</code>
|
||||
* to prevent them from being changed.
|
||||
*/
|
||||
public void setMutable(boolean mutable) {
|
||||
this.mutable = mutable;
|
||||
}
|
||||
|
||||
protected void checkMutable() throws IllegalStateException {
|
||||
if (!mutable) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDuplicate(boolean dup) {
|
||||
this.dup = dup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this message might be a duplicate of one which has
|
||||
* already been received. This will only be set on messages received from the
|
||||
* server.
|
||||
*
|
||||
* @return <code>true</code> if the message might be a duplicate.
|
||||
*/
|
||||
public boolean isDuplicate() {
|
||||
return this.dup;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is only to be used internally to provide the MQTT id of a message
|
||||
* received from the server. Has no effect when publishing messages.
|
||||
*
|
||||
* @param messageId
|
||||
* the Message Identifier
|
||||
*/
|
||||
public void setId(int messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MQTT id of the message. This is only applicable to messages
|
||||
* received from the server.
|
||||
*
|
||||
* @return the MQTT id of the message
|
||||
*/
|
||||
public int getId() {
|
||||
return this.messageId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public MqttProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(MqttProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this message's payload. Makes an attempt
|
||||
* to return the payload as a string. As the MQTT client has no control over the
|
||||
* content of the payload it may fail.
|
||||
*
|
||||
* @return a string representation of this message.
|
||||
*/
|
||||
public String toString() {
|
||||
return new String(payload);
|
||||
}
|
||||
|
||||
public String toDebugString() {
|
||||
return "MqttMessage [mutable=" + mutable + ", payload=" + new String(payload) + ", qos=" + qos + ", retained="
|
||||
+ retained + ", dup=" + dup + ", messageId=" + messageId + "]";
|
||||
}
|
||||
|
||||
}
|
111
mqtt/org/eclipse/paho/mqttv5/common/MqttPersistable.java
Normal file
111
mqtt/org/eclipse/paho/mqttv5/common/MqttPersistable.java
Normal file
@@ -0,0 +1,111 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common;
|
||||
|
||||
/**
|
||||
* Represents an object used to pass data to be persisted across the
|
||||
* <code>MqttClientPersistence</code> interface.
|
||||
* <p>
|
||||
* When data is passed across the interface the header and payload are
|
||||
* separated, so that unnecessary message copies may be avoided. For example, if
|
||||
* a 10 MB payload was published it would be inefficient to create a byte array
|
||||
* a few bytes larger than 10 MB and copy the MQTT message header and payload
|
||||
* into a contiguous byte array.
|
||||
* </p>
|
||||
* <p>
|
||||
* When the request to persist data is made a separate byte array and offset is
|
||||
* passed for the header and payload. Only the data between offset and length
|
||||
* need be persisted. So for example, a message to be persisted consists of a
|
||||
* header byte array starting at offset 1 and length 4, plus a payload byte
|
||||
* array starting at offset 30 and length 40000. There are three ways in which
|
||||
* the persistence implementation may return data to the client on recovery:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>It could return the data as it was passed in originally, with the same
|
||||
* byte arrays and offsets.</li>
|
||||
* <li>It could safely just persist and return the bytes from the offset for the
|
||||
* specified length. For example, return a header byte array with offset 0 and
|
||||
* length 4, plus a payload byte array with offset 0 and length 40000</li>
|
||||
* <li>It could return the header and payload as a contiguous byte array with
|
||||
* the header bytes preceeding the payload. The contiguous byte array should be
|
||||
* set as the header byte array, with the payload byte array being null. For
|
||||
* example, return a single byte array with offset 0 and length 40004. This is
|
||||
* useful when recovering from a file where the header and payload could be
|
||||
* written as a contiguous stream of bytes.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public interface MqttPersistable {
|
||||
|
||||
/**
|
||||
* Returns the header bytes in an array. The bytes start at
|
||||
* {@link #getHeaderOffset()} and continue for {@link #getHeaderLength()}.
|
||||
*
|
||||
* @return the header bytes.
|
||||
* @throws MqttPersistenceException
|
||||
* if an error occurs getting the Header Bytes
|
||||
*/
|
||||
byte[] getHeaderBytes() throws MqttPersistenceException;
|
||||
|
||||
/**
|
||||
* Returns the length of the header.
|
||||
*
|
||||
* @return the header length
|
||||
* @throws MqttPersistenceException
|
||||
* if an error occurs getting the Header length
|
||||
*/
|
||||
int getHeaderLength() throws MqttPersistenceException;
|
||||
|
||||
/**
|
||||
* Returns the offset of the header within the byte array returned by
|
||||
* {@link #getHeaderBytes()}.
|
||||
*
|
||||
* @return the header offset.
|
||||
* @throws MqttPersistenceException
|
||||
* if an error occurs getting the Header offset
|
||||
*
|
||||
*/
|
||||
int getHeaderOffset() throws MqttPersistenceException;
|
||||
|
||||
/**
|
||||
* Returns the payload bytes in an array. The bytes start at
|
||||
* {@link #getPayloadOffset()} and continue for {@link #getPayloadLength()}.
|
||||
*
|
||||
* @return the payload bytes.
|
||||
* @throws MqttPersistenceException
|
||||
* if an error occurs getting the Payload Bytes
|
||||
*/
|
||||
byte[] getPayloadBytes() throws MqttPersistenceException;
|
||||
|
||||
/**
|
||||
* Returns the length of the payload.
|
||||
*
|
||||
* @return the payload length.
|
||||
* @throws MqttPersistenceException
|
||||
* if an error occurs getting the Payload length
|
||||
*/
|
||||
int getPayloadLength() throws MqttPersistenceException;
|
||||
|
||||
/**
|
||||
* Returns the offset of the payload within the byte array returned by
|
||||
* {@link #getPayloadBytes()}.
|
||||
*
|
||||
* @return the payload offset.
|
||||
* @throws MqttPersistenceException
|
||||
* if an error occurs getting the Payload Offset
|
||||
*
|
||||
*/
|
||||
int getPayloadOffset() throws MqttPersistenceException;
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package org.eclipse.paho.mqttv5.common;
|
||||
|
||||
/**
|
||||
* This exception is thrown by the implementor of the persistence
|
||||
* interface if there is a problem reading or writing persistent data.
|
||||
*/
|
||||
public class MqttPersistenceException extends MqttException {
|
||||
private static final long serialVersionUID = 300L;
|
||||
|
||||
/** Persistence is already being used by another client. */
|
||||
public static final short REASON_CODE_PERSISTENCE_IN_USE = 32200;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>MqttPersistenceException</code>
|
||||
*/
|
||||
public MqttPersistenceException() {
|
||||
super(REASON_CODE_CLIENT_EXCEPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>MqttPersistenceException</code> with the specified code
|
||||
* as the underlying reason.
|
||||
* @param reasonCode the reason code for the exception.
|
||||
*/
|
||||
public MqttPersistenceException(int reasonCode) {
|
||||
super(reasonCode);
|
||||
}
|
||||
/**
|
||||
* Constructs a new <code>MqttPersistenceException</code> with the specified
|
||||
* <code>Throwable</code> as the underlying reason.
|
||||
* @param cause the underlying cause of the exception.
|
||||
*/
|
||||
public MqttPersistenceException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
/**
|
||||
* Constructs a new <code>MqttPersistenceException</code> with the specified
|
||||
* <code>Throwable</code> as the underlying reason.
|
||||
* @param reason the reason code for the exception.
|
||||
* @param cause the underlying cause of the exception.
|
||||
*/
|
||||
public MqttPersistenceException(int reason, Throwable cause) {
|
||||
super(reason, cause);
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common;
|
||||
|
||||
/**
|
||||
* Thrown when a client is not authorized to perform an operation, or
|
||||
* if there is a problem with the security configuration.
|
||||
*/
|
||||
public class MqttSecurityException extends MqttException {
|
||||
private static final long serialVersionUID = 300L;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>MqttSecurityException</code> with the specified code
|
||||
* as the underlying reason.
|
||||
* @param reasonCode the reason code for the exception.
|
||||
*/
|
||||
public MqttSecurityException(int reasonCode) {
|
||||
super(reasonCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>MqttSecurityException</code> with the specified
|
||||
* <code>Throwable</code> as the underlying reason.
|
||||
* @param cause the underlying cause of the exception.
|
||||
*/
|
||||
public MqttSecurityException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
/**
|
||||
* Constructs a new <code>MqttSecurityException</code> with the specified
|
||||
* code and <code>Throwable</code> as the underlying reason.
|
||||
* @param reasonCode the reason code for the exception.
|
||||
* @param cause the underlying cause of the exception.
|
||||
*/
|
||||
public MqttSecurityException(int reasonCode, Throwable cause) {
|
||||
super(reasonCode, cause);
|
||||
}
|
||||
}
|
166
mqtt/org/eclipse/paho/mqttv5/common/MqttSubscription.java
Normal file
166
mqtt/org/eclipse/paho/mqttv5/common/MqttSubscription.java
Normal file
@@ -0,0 +1,166 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common;
|
||||
|
||||
public class MqttSubscription {
|
||||
|
||||
private boolean mutable = true;
|
||||
private String topic;
|
||||
private int qos = 1;
|
||||
private boolean noLocal = false;
|
||||
private boolean retainAsPublished = false;
|
||||
private int retainHandling = 0;
|
||||
private int messageId;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a subscription with the specified topic with
|
||||
* all other values set to defaults.
|
||||
*
|
||||
* The defaults are:
|
||||
* <ul>
|
||||
* <li>Subscription QoS is set to 1</li>
|
||||
* <li>Messages published to this topic by the same client will also be received.</li>
|
||||
* <li>Messages received by this subscription will keep the retain flag (if it is set).</li>
|
||||
* <li>Retained messages on this topic will be delivered once the subscription has been made.</li>
|
||||
* </ul>
|
||||
* @param topic The Topic
|
||||
*/
|
||||
public MqttSubscription(String topic){
|
||||
setTopic(topic);
|
||||
}
|
||||
|
||||
public MqttSubscription(String topic, int qos) {
|
||||
setTopic(topic);
|
||||
setQos(qos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to validate the supplied QoS value.
|
||||
* @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
|
||||
* @param qos The QoS level to validate.
|
||||
*/
|
||||
public static void validateQos(int qos) {
|
||||
if ((qos < 0) || (qos > 2)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to validate the supplied Retain handling value.
|
||||
* @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
|
||||
* @param retainHandling the retain value to validate.
|
||||
*/
|
||||
public static void validateRetainHandling(int retainHandling) {
|
||||
if ((retainHandling < 0) || (retainHandling > 2)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mutability of this object (whether or not its values can be
|
||||
* changed.
|
||||
* @param mutable <code>true</code> if the values can be changed,
|
||||
* <code>false</code> to prevent them from being changed.
|
||||
*/
|
||||
protected void setMutable(boolean mutable) {
|
||||
this.mutable = mutable;
|
||||
}
|
||||
|
||||
protected void checkMutable() throws IllegalStateException {
|
||||
if (!mutable) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
checkMutable();
|
||||
if(topic == null){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
public int getQos() {
|
||||
return qos;
|
||||
}
|
||||
|
||||
public void setQos(int qos) {
|
||||
checkMutable();
|
||||
validateQos(qos);
|
||||
this.qos = qos;
|
||||
}
|
||||
|
||||
public boolean isNoLocal() {
|
||||
return noLocal;
|
||||
}
|
||||
|
||||
public void setNoLocal(boolean noLocal) {
|
||||
checkMutable();
|
||||
this.noLocal = noLocal;
|
||||
}
|
||||
|
||||
public boolean isRetainAsPublished() {
|
||||
return retainAsPublished;
|
||||
}
|
||||
|
||||
public void setRetainAsPublished(boolean retainAsPublished) {
|
||||
checkMutable();
|
||||
this.retainAsPublished = retainAsPublished;
|
||||
}
|
||||
|
||||
public int getRetainHandling() {
|
||||
return retainHandling;
|
||||
}
|
||||
|
||||
public void setRetainHandling(int retainHandling) {
|
||||
checkMutable();
|
||||
validateRetainHandling(retainHandling);
|
||||
this.retainHandling = retainHandling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttSubscription [mutable=" + mutable + ", topic=" + topic + ", qos=" + qos + ", noLocal=" + noLocal
|
||||
+ ", retainAsPublished=" + retainAsPublished + ", retainHandling=" + retainHandling + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* This is only to be used internally to provide the MQTT id of a message
|
||||
* received from the server. Has no effect when publishing messages.
|
||||
* @param messageId The Message Identifier
|
||||
*/
|
||||
public void setId(int messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MQTT id of the message. This is only applicable to messages
|
||||
* received from the server.
|
||||
* @return the MQTT id of the message
|
||||
*/
|
||||
public int getId() {
|
||||
return this.messageId;
|
||||
}
|
||||
|
||||
|
||||
}
|
28
mqtt/org/eclipse/paho/mqttv5/common/Validators.java
Normal file
28
mqtt/org/eclipse/paho/mqttv5/common/Validators.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package org.eclipse.paho.mqttv5.common;
|
||||
|
||||
public class Validators {
|
||||
|
||||
public boolean validateClientId(String clientId) {
|
||||
// Count characters, surrogate pairs count as one character.
|
||||
int clientIdLength = 0;
|
||||
for (int i = 0; i < clientId.length() - 1; i++) {
|
||||
if (isCharacterHighSurrogate(clientId.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
clientIdLength++;
|
||||
}
|
||||
return clientIdLength < 65535;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ch
|
||||
* the character to check.
|
||||
* @return returns 'true' if the character is a high-surrogate code unit
|
||||
*/
|
||||
protected static boolean isCharacterHighSurrogate(char ch) {
|
||||
final char minHighSurrogate = '\uD800';
|
||||
final char maxHighSurrogate = '\uDBFF';
|
||||
return (ch >= minHighSurrogate) && (ch <= maxHighSurrogate);
|
||||
}
|
||||
|
||||
}
|
31
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttAck.java
Normal file
31
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttAck.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2019 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
/**
|
||||
* Abstract super-class of all acknowledgement messages.
|
||||
*/
|
||||
public abstract class MqttAck extends MqttPersistableWireMessage {
|
||||
public MqttAck(byte type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
return 0;
|
||||
}
|
||||
}
|
123
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttAuth.java
Normal file
123
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttAuth.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
|
||||
/**
|
||||
* An on-the-wire representation of an MQTT AUTH message. MQTTv5 - 3.15
|
||||
*/
|
||||
public class MqttAuth extends MqttWireMessage {
|
||||
|
||||
// Return codes
|
||||
private static final int[] validReturnCodes = { MqttReturnCode.RETURN_CODE_SUCCESS,
|
||||
MqttReturnCode.RETURN_CODE_CONTINUE_AUTHENTICATION, MqttReturnCode.RETURN_CODE_RE_AUTHENTICATE };
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.AUTH_METHOD_IDENTIFIER,
|
||||
MqttProperties.AUTH_DATA_IDENTIFIER, MqttProperties.REASON_STRING_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
// Fields
|
||||
private MqttProperties properties;
|
||||
|
||||
/**
|
||||
* Constructs an Auth message from a raw byte array
|
||||
*
|
||||
* @param data
|
||||
* - The variable header and payload bytes.
|
||||
* @throws IOException
|
||||
* - if an exception occurs when decoding an input stream
|
||||
* @throws MqttException
|
||||
* - If an exception occurs decoding this packet
|
||||
*/
|
||||
public MqttAuth(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_AUTH);
|
||||
properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
DataInputStream inputStream = new DataInputStream(bais);
|
||||
reasonCode = inputStream.readUnsignedByte();
|
||||
validateReturnCode(reasonCode, validReturnCodes);
|
||||
this.properties.decodeProperties(inputStream);
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an Auth message from the return code
|
||||
*
|
||||
* @param returnCode
|
||||
* - The Auth Return Code
|
||||
* @param properties
|
||||
* - The {@link MqttProperties} for the packet.
|
||||
* @throws MqttException
|
||||
* - If an exception occurs encoding this packet
|
||||
*/
|
||||
public MqttAuth(int returnCode, MqttProperties properties) throws MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_AUTH);
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
this.reasonCode = returnCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Return Code
|
||||
outputStream.writeByte(reasonCode);
|
||||
|
||||
// Write Identifier / Value Fields
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
return (byte) (1);
|
||||
}
|
||||
|
||||
public int getReturnCode() {
|
||||
return reasonCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttAuth [returnCode=" + reasonCode + ", properties=" + properties + "]";
|
||||
}
|
||||
}
|
154
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttConnAck.java
Normal file
154
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttConnAck.java
Normal file
@@ -0,0 +1,154 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
|
||||
/**
|
||||
* An on-the-wire representation of an MQTT CONNACK.
|
||||
*/
|
||||
public class MqttConnAck extends MqttAck {
|
||||
public static final String KEY = "Con";
|
||||
|
||||
private static final int[] validReturnCodes = { MqttReturnCode.RETURN_CODE_SUCCESS,
|
||||
MqttReturnCode.RETURN_CODE_UNSPECIFIED_ERROR, MqttReturnCode.RETURN_CODE_MALFORMED_CONTROL_PACKET,
|
||||
MqttReturnCode.RETURN_CODE_PROTOCOL_ERROR, MqttReturnCode.RETURN_CODE_IMPLEMENTATION_SPECIFIC_ERROR,
|
||||
MqttReturnCode.RETURN_CODE_UNSUPPORTED_PROTOCOL_VERSION, MqttReturnCode.RETURN_CODE_IDENTIFIER_NOT_VALID,
|
||||
MqttReturnCode.RETURN_CODE_BAD_USERNAME_OR_PASSWORD, MqttReturnCode.RETURN_CODE_NOT_AUTHORIZED,
|
||||
MqttReturnCode.RETURN_CODE_SERVER_UNAVAILABLE, MqttReturnCode.RETURN_CODE_SERVER_BUSY,
|
||||
MqttReturnCode.RETURN_CODE_BANNED, MqttReturnCode.RETURN_CODE_BAD_AUTHENTICATION,
|
||||
MqttReturnCode.RETURN_CODE_TOPIC_NAME_INVALID, MqttReturnCode.RETURN_CODE_PACKET_TOO_LARGE,
|
||||
MqttReturnCode.RETURN_CODE_QUOTA_EXCEEDED, MqttReturnCode.RETURN_CODE_RETAIN_NOT_SUPPORTED,
|
||||
MqttReturnCode.RETURN_CODE_USE_ANOTHER_SERVER, MqttReturnCode.RETURN_CODE_SERVER_MOVED,
|
||||
MqttReturnCode.RETURN_CODE_CONNECTION_RATE_EXCEEDED };
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.SESSION_EXPIRY_INTERVAL_IDENTIFIER,
|
||||
MqttProperties.RECEIVE_MAXIMUM_IDENTIFIER, MqttProperties.MAXIMUM_QOS_IDENTIFIER,
|
||||
MqttProperties.RETAIN_AVAILABLE_IDENTIFIER, MqttProperties.MAXIMUM_PACKET_SIZE_IDENTIFIER,
|
||||
MqttProperties.ASSIGNED_CLIENT_IDENTIFIER_IDENTIFIER, MqttProperties.TOPIC_ALIAS_MAXIMUM_IDENTIFIER,
|
||||
MqttProperties.WILDCARD_SUB_AVAILABLE_IDENTIFIER, MqttProperties.SUBSCRIPTION_AVAILABLE_IDENTIFIER,
|
||||
MqttProperties.SHARED_SUBSCRIPTION_AVAILABLE_IDENTIFIER, MqttProperties.SERVER_KEEP_ALIVE_IDENTIFIER,
|
||||
MqttProperties.RESPONSE_INFO_IDENTIFIER, MqttProperties.SERVER_REFERENCE_IDENTIFIER,
|
||||
MqttProperties.AUTH_METHOD_IDENTIFIER, MqttProperties.AUTH_DATA_IDENTIFIER,
|
||||
MqttProperties.REASON_STRING_IDENTIFIER, MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
private boolean sessionPresent;
|
||||
private MqttProperties properties;
|
||||
|
||||
public MqttConnAck(byte[] variableHeader) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_CONNACK);
|
||||
this.properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(variableHeader);
|
||||
DataInputStream dis = new DataInputStream(bais);
|
||||
sessionPresent = (dis.readUnsignedByte() & 0x01) == 0x01;
|
||||
reasonCode = dis.readUnsignedByte();
|
||||
validateReturnCode(reasonCode, validReturnCodes);
|
||||
this.properties.decodeProperties(dis);
|
||||
dis.close();
|
||||
}
|
||||
|
||||
public MqttConnAck(boolean sessionPresent, int returnCode, MqttProperties properties) throws MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_CONNACK);
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
this.sessionPresent = sessionPresent;
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
this.reasonCode = returnCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Session Present Flag
|
||||
byte connectAchnowledgeFlag = 0;
|
||||
if (sessionPresent) {
|
||||
connectAchnowledgeFlag |= 0x01;
|
||||
}
|
||||
dos.write(connectAchnowledgeFlag);
|
||||
|
||||
// Encode the Connect Return Code
|
||||
dos.write((byte) reasonCode);
|
||||
|
||||
// Write Identifier / Value Fields
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
dos.write(identifierValueFieldsByteArray);
|
||||
dos.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this message needs to include a message ID.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMessageIdRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
public boolean getSessionPresent() {
|
||||
return sessionPresent;
|
||||
}
|
||||
|
||||
public void setSessionPresent(boolean sessionPresent) {
|
||||
this.sessionPresent = sessionPresent;
|
||||
}
|
||||
|
||||
public int getReturnCode() {
|
||||
return reasonCode;
|
||||
}
|
||||
|
||||
public void setReturnCode(int returnCode) {
|
||||
this.reasonCode = returnCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static int[] getValidreturncodes() {
|
||||
return validReturnCodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttConnAck [returnCode=" + reasonCode + ", sessionPresent=" + sessionPresent + ", properties="
|
||||
+ properties + "]";
|
||||
}
|
||||
|
||||
}
|
337
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttConnect.java
Normal file
337
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttConnect.java
Normal file
@@ -0,0 +1,337 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.MqttMessage;
|
||||
|
||||
public class MqttConnect extends MqttWireMessage {
|
||||
|
||||
public static final String KEY = "Con";
|
||||
|
||||
private MqttProperties properties;
|
||||
|
||||
private MqttProperties willProperties;
|
||||
|
||||
// Fields
|
||||
private byte info;
|
||||
private String clientId;
|
||||
private boolean reservedByte;
|
||||
private boolean cleanStart;
|
||||
private MqttMessage willMessage;
|
||||
private String userName;
|
||||
private byte[] password;
|
||||
private int keepAliveInterval;
|
||||
private String willDestination;
|
||||
private int mqttVersion = DEFAULT_PROTOCOL_VERSION;
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.SESSION_EXPIRY_INTERVAL_IDENTIFIER,
|
||||
MqttProperties.WILL_DELAY_INTERVAL_IDENTIFIER, MqttProperties.RECEIVE_MAXIMUM_IDENTIFIER,
|
||||
MqttProperties.MAXIMUM_PACKET_SIZE_IDENTIFIER, MqttProperties.TOPIC_ALIAS_MAXIMUM_IDENTIFIER,
|
||||
MqttProperties.REQUEST_RESPONSE_INFO_IDENTIFIER, MqttProperties.REQUEST_PROBLEM_INFO_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER, MqttProperties.AUTH_METHOD_IDENTIFIER,
|
||||
MqttProperties.AUTH_DATA_IDENTIFIER };
|
||||
|
||||
private static final Byte[] validWillProperties = { MqttProperties.WILL_DELAY_INTERVAL_IDENTIFIER,
|
||||
MqttProperties.PAYLOAD_FORMAT_INDICATOR_IDENTIFIER, MqttProperties.MESSAGE_EXPIRY_INTERVAL_IDENTIFIER,
|
||||
MqttProperties.RESPONSE_TOPIC_IDENTIFIER, MqttProperties.CORRELATION_DATA_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER, MqttProperties.CONTENT_TYPE_IDENTIFIER };
|
||||
|
||||
/**
|
||||
* Constructor for an on the wire MQTT Connect message
|
||||
*
|
||||
* @param info
|
||||
* - Info Byte
|
||||
* @param data
|
||||
* - The variable header and payload bytes.
|
||||
* @throws IOException
|
||||
* - if an exception occurs when decoding an input stream
|
||||
* @throws MqttException
|
||||
* - If an exception occurs decoding this packet
|
||||
*/
|
||||
public MqttConnect(byte info, byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_CONNECT);
|
||||
this.info = info;
|
||||
this.properties = new MqttProperties(validProperties);
|
||||
this.willProperties = new MqttProperties(validWillProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
DataInputStream dis = new DataInputStream(bais);
|
||||
|
||||
// Verify the Protocol name and version
|
||||
String protocolName = MqttDataTypes.decodeUTF8(dis);
|
||||
if (!protocolName.equalsIgnoreCase(DEFAULT_PROTOCOL_NAME)) {
|
||||
throw new MqttPacketException(MqttPacketException.PACKET_CONNECT_ERROR_UNSUPPORTED_PROTOCOL_NAME);
|
||||
}
|
||||
mqttVersion = dis.readByte();
|
||||
if (mqttVersion != DEFAULT_PROTOCOL_VERSION) {
|
||||
throw new MqttPacketException(MqttPacketException.PACKET_CONNECT_ERROR_UNSUPPORTED_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
byte connectFlags = dis.readByte();
|
||||
reservedByte = (connectFlags & 0x01) != 0;
|
||||
cleanStart = (connectFlags & 0x02) != 0;
|
||||
boolean willFlag = (connectFlags & 0x04) != 0;
|
||||
int willQoS = (connectFlags >> 3) & 0x03;
|
||||
boolean willRetain = (connectFlags & 0x20) != 0;
|
||||
boolean passwordFlag = (connectFlags & 0x40) != 0;
|
||||
boolean usernameFlag = (connectFlags & 0x80) != 0;
|
||||
|
||||
if (reservedByte) {
|
||||
throw new MqttPacketException(MqttPacketException.PACKET_CONNECT_ERROR_INVALID_RESERVE_FLAG);
|
||||
}
|
||||
|
||||
keepAliveInterval = dis.readUnsignedShort();
|
||||
properties.decodeProperties(dis);
|
||||
clientId = MqttDataTypes.decodeUTF8(dis);
|
||||
|
||||
if (willFlag) {
|
||||
willProperties.decodeProperties(dis);
|
||||
if (willQoS == 3) {
|
||||
throw new MqttPacketException(MqttPacketException.PACKET_CONNECT_ERROR_INVALID_WILL_QOS);
|
||||
}
|
||||
willDestination = MqttDataTypes.decodeUTF8(dis);
|
||||
int willMessageLength = dis.readShort();
|
||||
byte[] willMessageBytes = new byte[willMessageLength];
|
||||
dis.read(willMessageBytes, 0, willMessageLength);
|
||||
willMessage = new MqttMessage(willMessageBytes);
|
||||
willMessage.setQos(willQoS);
|
||||
willMessage.setRetained(willRetain);
|
||||
}
|
||||
if (usernameFlag) {
|
||||
userName = MqttDataTypes.decodeUTF8(dis);
|
||||
}
|
||||
if (passwordFlag) {
|
||||
int passwordLength = dis.readShort();
|
||||
password = new byte[passwordLength];
|
||||
dis.read(password, 0, passwordLength);
|
||||
}
|
||||
|
||||
dis.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a new MQTT Connect Message
|
||||
*
|
||||
* @param clientId
|
||||
* - The Client Identifier
|
||||
* @param mqttVersion
|
||||
* - The MQTT Protocol version
|
||||
* @param cleanStart
|
||||
* - The Clean Session Identifier
|
||||
* @param keepAliveInterval
|
||||
* - The Keep Alive Interval
|
||||
* @param properties
|
||||
* - The {@link MqttProperties} for the packet.
|
||||
* @param willProperties
|
||||
* - The {@link MqttProperties} for the will message.
|
||||
*
|
||||
*/
|
||||
public MqttConnect(String clientId, int mqttVersion, boolean cleanStart, int keepAliveInterval,
|
||||
MqttProperties properties, MqttProperties willProperties) {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_CONNECT);
|
||||
this.clientId = clientId;
|
||||
this.mqttVersion = mqttVersion;
|
||||
this.cleanStart = cleanStart;
|
||||
this.keepAliveInterval = keepAliveInterval;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
this.willProperties = willProperties;
|
||||
this.willProperties.setValidProperties(validWillProperties);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
return (byte) 0;
|
||||
}
|
||||
|
||||
public boolean isCleanStart() {
|
||||
return cleanStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Protocol Name
|
||||
MqttDataTypes.encodeUTF8(dos, "MQTT");
|
||||
|
||||
// Encode the MQTT Version
|
||||
dos.write(mqttVersion);
|
||||
|
||||
byte connectFlags = 0;
|
||||
|
||||
if (cleanStart) {
|
||||
connectFlags |= 0x02;
|
||||
}
|
||||
|
||||
if (willMessage != null) {
|
||||
connectFlags |= 0x04;
|
||||
connectFlags |= (willMessage.getQos() << 3);
|
||||
if (willMessage.isRetained()) {
|
||||
connectFlags |= 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
if (userName != null) {
|
||||
connectFlags |= 0x80;
|
||||
}
|
||||
|
||||
if (password != null) {
|
||||
connectFlags |= 0x40;
|
||||
}
|
||||
|
||||
dos.write(connectFlags);
|
||||
dos.writeShort(keepAliveInterval);
|
||||
|
||||
// Write Identifier / Value Fields
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
dos.write(identifierValueFieldsByteArray);
|
||||
dos.flush();
|
||||
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
MqttDataTypes.encodeUTF8(dos, clientId);
|
||||
|
||||
|
||||
|
||||
if (willMessage != null) {
|
||||
// Encode Will properties here
|
||||
byte[] willIdentifierValueFieldsByteArray = willProperties.encodeProperties();
|
||||
dos.write(willIdentifierValueFieldsByteArray);
|
||||
|
||||
MqttDataTypes.encodeUTF8(dos, willDestination);
|
||||
dos.writeShort(willMessage.getPayload().length);
|
||||
dos.write(willMessage.getPayload());
|
||||
}
|
||||
|
||||
if (userName != null) {
|
||||
MqttDataTypes.encodeUTF8(dos, userName);
|
||||
}
|
||||
|
||||
if (password != null) {
|
||||
dos.writeShort(password.length);
|
||||
dos.write(password);
|
||||
}
|
||||
dos.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this message needs to include a message ID.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMessageIdRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
public void setWillMessage(MqttMessage willMessage) {
|
||||
this.willMessage = willMessage;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public void setPassword(byte[] password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setWillDestination(String willDestination) {
|
||||
this.willDestination = willDestination;
|
||||
}
|
||||
|
||||
public byte getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public MqttMessage getWillMessage() {
|
||||
return willMessage;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public byte[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public int getKeepAliveInterval() {
|
||||
return keepAliveInterval;
|
||||
}
|
||||
|
||||
public String getWillDestination() {
|
||||
return willDestination;
|
||||
}
|
||||
|
||||
public int getMqttVersion() {
|
||||
return mqttVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public MqttProperties getWillProperties() {
|
||||
return willProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttConnect [properties=" + properties + ", willProperties=" + willProperties + ", info=" + info
|
||||
+ ", clientId=" + clientId + ", reservedByte=" + reservedByte + ", cleanStart=" + cleanStart
|
||||
+ ", willMessage=" + willMessage + ", userName=" + userName + ", password=" + Arrays.toString(password)
|
||||
+ ", keepAliveInterval=" + keepAliveInterval + ", willDestination=" + willDestination + ", mqttVersion="
|
||||
+ mqttVersion + "]";
|
||||
}
|
||||
}
|
253
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttDataTypes.java
Normal file
253
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttDataTypes.java
Normal file
@@ -0,0 +1,253 @@
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.VariableByteInteger;
|
||||
|
||||
public class MqttDataTypes {
|
||||
|
||||
private static final int TWO_BYTE_INT_MAX = 65535;
|
||||
private static final long FOUR_BYTE_INT_MAX = 4294967295L;
|
||||
public static final int VARIABLE_BYTE_INT_MAX = 268435455;
|
||||
|
||||
public MqttDataTypes() throws IllegalAccessException {
|
||||
throw new IllegalAccessException("Utility Class");
|
||||
}
|
||||
|
||||
public static void validateTwoByteInt(Integer value) throws IllegalArgumentException {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
if (value >= 0 && value <= TWO_BYTE_INT_MAX) {
|
||||
return;
|
||||
} else {
|
||||
throw new IllegalArgumentException("This property must be a number between 0 and " + TWO_BYTE_INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateFourByteInt(Long value) throws IllegalArgumentException {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
if (value >= 0 && value <= FOUR_BYTE_INT_MAX) {
|
||||
return;
|
||||
} else {
|
||||
throw new IllegalArgumentException("This property must be a number between 0 and " + FOUR_BYTE_INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateVariableByteInt(int value) throws IllegalArgumentException {
|
||||
if (value >= 0 && value <= VARIABLE_BYTE_INT_MAX) {
|
||||
return;
|
||||
} else {
|
||||
throw new IllegalArgumentException("This property must be a number between 0 and " + VARIABLE_BYTE_INT_MAX);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void writeUnsignedFourByteInt(long value, DataOutputStream stream) throws IOException {
|
||||
stream.writeByte((byte) (value >>> 24));
|
||||
stream.writeByte((byte) (value >>> 16));
|
||||
stream.writeByte((byte) (value >>> 8));
|
||||
stream.writeByte((byte) (value >>> 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a Four Byte Integer, then converts it to a float. This is because Java
|
||||
* doesn't have Unsigned Integers.
|
||||
*
|
||||
* @param inputStream
|
||||
* The input stream to read from.
|
||||
* @return a {@link Long} containing the value of the Four Byte int (Between 0
|
||||
* and 4294967295)
|
||||
* @throws IOException
|
||||
* if an exception occurs whilst reading from the Input Stream
|
||||
*/
|
||||
public static Long readUnsignedFourByteInt(DataInputStream inputStream) throws IOException {
|
||||
byte[] readBuffer = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
inputStream.readFully(readBuffer, 4, 4);
|
||||
return (((long) readBuffer[0] << 56) + ((long) (readBuffer[1] & 255) << 48)
|
||||
+ ((long) (readBuffer[2] & 255) << 40) + ((long) (readBuffer[3] & 255) << 32)
|
||||
+ ((long) (readBuffer[4] & 255) << 24) + ((readBuffer[5] & 255) << 16) + ((readBuffer[6] & 255) << 8)
|
||||
+ ((readBuffer[7] & 255) << 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a Two Byte Integer, this is because Java does not have unsigned
|
||||
* integers.
|
||||
*
|
||||
* @param inputStream
|
||||
* The input stream to read from.
|
||||
* @return a {@link int} containing the value of the Two Byte int (Between 0 and
|
||||
* 65535)
|
||||
* @throws IOException
|
||||
* if an exception occurs whilst reading from the Input Stream
|
||||
*
|
||||
*/
|
||||
public static int readUnsignedTwoByteInt(DataInputStream inputStream) throws IOException {
|
||||
// byte readBuffer[] = {0,0}
|
||||
int ch1 = inputStream.read();
|
||||
int ch2 = inputStream.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new EOFException();
|
||||
return (int) ((ch1 << 8) + (ch2 << 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a String given into UTF-8, before writing this to the
|
||||
* {@link DataOutputStream} the length of the encoded string is encoded into two
|
||||
* bytes and then written to the {@link DataOutputStream}.
|
||||
* {@link DataOutputStream#writeUTF(String)} should be no longer used.
|
||||
* {@link DataOutputStream#writeUTF(String)} does not correctly encode UTF-16
|
||||
* surrogate characters.
|
||||
*
|
||||
* @param dos
|
||||
* The stream to write the encoded UTF-8 string to.
|
||||
* @param stringToEncode
|
||||
* The string to be encoded
|
||||
* @throws MqttException
|
||||
* Thrown when an error occurs with either the encoding or writing
|
||||
* the data to the stream.
|
||||
*/
|
||||
public static void encodeUTF8(DataOutputStream dos, String stringToEncode) throws MqttException {
|
||||
validateUTF8String(stringToEncode);
|
||||
try {
|
||||
byte[] encodedString = stringToEncode.getBytes(STRING_ENCODING);
|
||||
byte byte1 = (byte) ((encodedString.length >>> 8) & 0xFF);
|
||||
byte byte2 = (byte) ((encodedString.length >>> 0) & 0xFF);
|
||||
|
||||
dos.write(byte1);
|
||||
dos.write(byte2);
|
||||
dos.write(encodedString);
|
||||
} catch (IOException ex) {
|
||||
throw new MqttException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected static final Charset STRING_ENCODING = StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Decodes a UTF-8 string from the {@link DataInputStream} provided.
|
||||
* {@link DataInputStream#readUTF()} should be no longer used, because
|
||||
* {@link DataInputStream#readUTF()} does not decode UTF-16 surrogate characters
|
||||
* correctly.
|
||||
*
|
||||
* @param input
|
||||
* The input stream from which to read the encoded string.
|
||||
* @return a decoded String from the {@link DataInputStream}.
|
||||
* @throws MqttException
|
||||
* thrown when an error occurs with either reading from the stream
|
||||
* or decoding the encoding string.
|
||||
*/
|
||||
public static String decodeUTF8(DataInputStream input) throws MqttException {
|
||||
int encodedLength;
|
||||
try {
|
||||
encodedLength = input.readUnsignedShort();
|
||||
|
||||
byte[] encodedString = new byte[encodedLength];
|
||||
input.readFully(encodedString);
|
||||
String output = new String(encodedString, STRING_ENCODING);
|
||||
validateUTF8String(output);
|
||||
|
||||
return output;
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(MqttException.REASON_CODE_MALFORMED_PACKET, ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a UTF-8 String for suitability for MQTT.
|
||||
*
|
||||
* @param input
|
||||
* - The Input String
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private static void validateUTF8String(String input) throws IllegalArgumentException {
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
boolean isBad = false;
|
||||
char c = input.charAt(i);
|
||||
/* Check for mismatched surrogates */
|
||||
if (Character.isHighSurrogate(c)) {
|
||||
if (++i == input.length()) {
|
||||
isBad = true; /* Trailing high surrogate */
|
||||
} else {
|
||||
char c2 = input.charAt(i);
|
||||
if (!Character.isLowSurrogate(c2)) {
|
||||
isBad = true; /* No low surrogate */
|
||||
} else {
|
||||
int ch = ((((int) c) & 0x3ff) << 10) | (c2 & 0x3ff);
|
||||
if ((ch & 0xffff) == 0xffff || (ch & 0xffff) == 0xfffe) {
|
||||
isBad = true; /* Noncharacter in base plane */
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Character.isISOControl(c) || Character.isLowSurrogate(c)) {
|
||||
isBad = true; /* Control character or no high surrogate */
|
||||
} else if (c >= 0xfdd0 && (c <= 0xfddf || c >= 0xfffe)) {
|
||||
isBad = true; /* Noncharacter in other nonbase plane */
|
||||
}
|
||||
}
|
||||
if (isBad) {
|
||||
throw new IllegalArgumentException(String.format("Invalid UTF-8 char: [%04x]", (int) c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an MQTT Multi-Byte Integer from the given stream
|
||||
*
|
||||
* @param in
|
||||
* the DataInputStream to decode a Variable Byte Integer From
|
||||
* @return a new VariableByteInteger
|
||||
* @throws IOException
|
||||
* if an error occurred whilst decoding the VBI
|
||||
*/
|
||||
public static VariableByteInteger readVariableByteInteger(DataInputStream in) throws IOException {
|
||||
byte digit;
|
||||
int value = 0;
|
||||
int multiplier = 1;
|
||||
int count = 0;
|
||||
|
||||
do {
|
||||
digit = in.readByte();
|
||||
count++;
|
||||
value += ((digit & 0x7F) * multiplier);
|
||||
multiplier *= 128;
|
||||
} while ((digit & 0x80) != 0);
|
||||
|
||||
if (value < 0 || value > VARIABLE_BYTE_INT_MAX) {
|
||||
throw new IOException("This property must be a number between 0 and " + VARIABLE_BYTE_INT_MAX
|
||||
+ ". Read value was: " + value);
|
||||
}
|
||||
|
||||
return new VariableByteInteger(value, count);
|
||||
|
||||
}
|
||||
|
||||
public static byte[] encodeVariableByteInteger(int number) throws IllegalArgumentException {
|
||||
validateVariableByteInt(number);
|
||||
int numBytes = 0;
|
||||
long no = number;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
// Encode the remaining length fields in the four bytes
|
||||
do {
|
||||
byte digit = (byte) (no % 128);
|
||||
no = no / 128;
|
||||
if (no > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
baos.write(digit);
|
||||
numBytes++;
|
||||
} while ((no > 0) && (numBytes < 4));
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
}
|
129
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttDisconnect.java
Normal file
129
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttDisconnect.java
Normal file
@@ -0,0 +1,129 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
public class MqttDisconnect extends MqttWireMessage {
|
||||
|
||||
public static final String KEY = "Disc";
|
||||
|
||||
private static final int[] validReturnCodes = { MqttReturnCode.RETURN_CODE_SUCCESS,
|
||||
MqttReturnCode.RETURN_CODE_DISCONNECT_WITH_WILL_MESSAGE, MqttReturnCode.RETURN_CODE_UNSPECIFIED_ERROR,
|
||||
MqttReturnCode.RETURN_CODE_MALFORMED_CONTROL_PACKET, MqttReturnCode.RETURN_CODE_PROTOCOL_ERROR,
|
||||
MqttReturnCode.RETURN_CODE_IMPLEMENTATION_SPECIFIC_ERROR, MqttReturnCode.RETURN_CODE_NOT_AUTHORIZED,
|
||||
MqttReturnCode.RETURN_CODE_SERVER_BUSY, MqttReturnCode.RETURN_CODE_SERVER_SHUTTING_DOWN,
|
||||
MqttReturnCode.RETURN_CODE_KEEP_ALIVE_TIMEOUT, MqttReturnCode.RETURN_CODE_SESSION_TAKEN_OVER,
|
||||
MqttReturnCode.RETURN_CODE_TOPIC_FILTER_NOT_VALID, MqttReturnCode.RETURN_CODE_TOPIC_NAME_INVALID,
|
||||
MqttReturnCode.RETURN_CODE_RECEIVE_MAXIMUM_EXCEEDED, MqttReturnCode.RETURN_CODE_TOPIC_ALIAS_NOT_ACCEPTED,
|
||||
MqttReturnCode.RETURN_CODE_PACKET_TOO_LARGE, MqttReturnCode.RETURN_CODE_MESSAGE_RATE_TOO_HIGH,
|
||||
MqttReturnCode.RETURN_CODE_QUOTA_EXCEEDED, MqttReturnCode.RETURN_CODE_ADMINISTRITIVE_ACTION,
|
||||
MqttReturnCode.RETURN_CODE_PAYLOAD_FORMAT_INVALID, MqttReturnCode.RETURN_CODE_RETAIN_NOT_SUPPORTED,
|
||||
MqttReturnCode.RETURN_CODE_QOS_NOT_SUPPORTED, MqttReturnCode.RETURN_CODE_USE_ANOTHER_SERVER,
|
||||
MqttReturnCode.RETURN_CODE_SERVER_MOVED, MqttReturnCode.RETURN_CODE_SHARED_SUB_NOT_SUPPORTED,
|
||||
MqttReturnCode.RETURN_CODE_CONNECTION_RATE_EXCEEDED, MqttReturnCode.RETURN_CODE_MAXIMUM_CONNECT_TIME,
|
||||
MqttReturnCode.RETURN_CODE_SUB_IDENTIFIERS_NOT_SUPPORTED,
|
||||
MqttReturnCode.RETURN_CODE_WILDCARD_SUB_NOT_SUPPORTED };
|
||||
|
||||
// Fields
|
||||
private int returnCode = MqttReturnCode.RETURN_CODE_SUCCESS;
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.SESSION_EXPIRY_INTERVAL_IDENTIFIER,
|
||||
MqttProperties.SERVER_REFERENCE_IDENTIFIER, MqttProperties.REASON_STRING_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
private MqttProperties properties;
|
||||
|
||||
public MqttDisconnect(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_DISCONNECT);
|
||||
this.properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream inputStream = new DataInputStream(counter);
|
||||
if(data.length - counter.getCounter() >= 1) {
|
||||
returnCode = inputStream.readUnsignedByte();
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
}
|
||||
|
||||
long remainder = (long) data.length - counter.getCounter();
|
||||
if (remainder >= 2) {
|
||||
this.properties.decodeProperties(inputStream);
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
public MqttDisconnect(int returnCode, MqttProperties properties) throws MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_DISCONNECT);
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
this.returnCode = returnCode;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Return Code
|
||||
outputStream.writeByte(returnCode);
|
||||
|
||||
// Write Identifier / Value Fields
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
if (identifierValueFieldsByteArray.length != 0) {
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
outputStream.flush();
|
||||
}
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public int getReturnCode() {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
return (byte) 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttDisconnect [returnCode=" + returnCode + ", properties=" + properties + "]";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
|
||||
/**
|
||||
* This is a Class containing all of the low level packet exceptions that may be useful in identifying the cause of protocol errors.
|
||||
* @author jamessutton
|
||||
*/
|
||||
public class MqttPacketException extends MqttException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
/**
|
||||
* Protocol Exceptions for the CONNECT Packet
|
||||
*/
|
||||
public static final int PACKET_CONNECT_ERROR_UNSUPPORTED_PROTOCOL_NAME = 51000; // 3.1.2.1 - If Protocol name is not 'MQTT', return Unsupported Protocol Version Error.
|
||||
public static final int PACKET_CONNECT_ERROR_UNSUPPORTED_PROTOCOL_VERSION = 51001; // 3.1.2.2 - If Protocol version is not 5, return Unsupported Protocol Version Error.
|
||||
public static final int PACKET_CONNECT_ERROR_INVALID_RESERVE_FLAG = 51002; // 3.1.2.3 - If Reserved flag is not 0, return Malformed Packet Error.
|
||||
public static final int PACKET_CONNECT_ERROR_INVALID_WILL_QOS = 51003; // 3.1.2.6 - If Will QoS is 0x03, return Malformed Packet Error.
|
||||
|
||||
|
||||
public MqttPacketException(int reasonCode) {
|
||||
super(reasonCode);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.MqttPersistable;
|
||||
import org.eclipse.paho.mqttv5.common.MqttPersistenceException;
|
||||
|
||||
public abstract class MqttPersistableWireMessage extends MqttWireMessage
|
||||
implements MqttPersistable {
|
||||
|
||||
public MqttPersistableWireMessage(byte type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
public byte[] getHeaderBytes() throws MqttPersistenceException {
|
||||
byte[] headerBytes = null;
|
||||
try {
|
||||
if(this.getClass() == MqttPublish.class && this.getProperties().getTopicAlias() != null) {
|
||||
// Remove the Topic Alias temporarily.
|
||||
MqttProperties props = this.getProperties();
|
||||
Integer topicAlias = props.getTopicAlias();
|
||||
props.setTopicAlias(null);
|
||||
headerBytes = getHeader();
|
||||
// Re Set Topic Alias
|
||||
props.setTopicAlias(topicAlias);
|
||||
this.properties = props;
|
||||
} else {
|
||||
headerBytes = getHeader();
|
||||
}
|
||||
return headerBytes;
|
||||
}
|
||||
catch (MqttException ex) {
|
||||
throw new MqttPersistenceException(ex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public int getHeaderLength() throws MqttPersistenceException {
|
||||
return getHeaderBytes().length;
|
||||
}
|
||||
|
||||
public int getHeaderOffset() throws MqttPersistenceException{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public byte[] getPayloadBytes() throws MqttPersistenceException {
|
||||
try {
|
||||
return getPayload();
|
||||
}
|
||||
catch (MqttException ex) {
|
||||
throw new MqttPersistenceException(ex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public int getPayloadLength() throws MqttPersistenceException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getPayloadOffset() throws MqttPersistenceException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
52
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPingReq.java
Normal file
52
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPingReq.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
|
||||
public class MqttPingReq extends MqttWireMessage{
|
||||
|
||||
public static final String KEY = "Ping";
|
||||
|
||||
public MqttPingReq(){
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PINGREQ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>false</code> as message IDs are not required for MQTT
|
||||
* PINGREQ messages.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMessageIdRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return KEY;
|
||||
}
|
||||
}
|
52
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPingResp.java
Normal file
52
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPingResp.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
|
||||
public class MqttPingResp extends MqttAck {
|
||||
|
||||
public static final String KEY = "Ping";
|
||||
|
||||
public MqttPingResp() {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PINGRESP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>false</code> as message IDs are not required for MQTT PINGREQ
|
||||
* messages.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMessageIdRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return KEY;
|
||||
}
|
||||
}
|
1419
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttProperties.java
Normal file
1419
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttProperties.java
Normal file
File diff suppressed because it is too large
Load Diff
116
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPubAck.java
Normal file
116
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPubAck.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
public class MqttPubAck extends MqttAck {
|
||||
|
||||
private static final int[] validReturnCodes = { MqttReturnCode.RETURN_CODE_SUCCESS,
|
||||
MqttReturnCode.RETURN_CODE_NO_MATCHING_SUBSCRIBERS, MqttReturnCode.RETURN_CODE_UNSPECIFIED_ERROR,
|
||||
MqttReturnCode.RETURN_CODE_IMPLEMENTATION_SPECIFIC_ERROR, MqttReturnCode.RETURN_CODE_NOT_AUTHORIZED,
|
||||
MqttReturnCode.RETURN_CODE_TOPIC_NAME_INVALID, MqttReturnCode.RETURN_CODE_QUOTA_EXCEEDED,
|
||||
MqttReturnCode.RETURN_CODE_PAYLOAD_FORMAT_INVALID };
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.REASON_STRING_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
private MqttProperties properties;
|
||||
|
||||
public MqttPubAck(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBACK);
|
||||
properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream dis = new DataInputStream(counter);
|
||||
msgId = dis.readUnsignedShort();
|
||||
long remainder = (long) data.length - counter.getCounter();
|
||||
if (remainder >= 1) {
|
||||
reasonCode = dis.readUnsignedByte();
|
||||
validateReturnCode(reasonCode, validReturnCodes);
|
||||
} else {
|
||||
reasonCode = 0;
|
||||
}
|
||||
if (remainder >= 4) {
|
||||
this.properties.decodeProperties(dis);
|
||||
}
|
||||
dis.close();
|
||||
}
|
||||
|
||||
public MqttPubAck(int returnCode, int msgId, MqttProperties properties) throws MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBACK);
|
||||
this.reasonCode = returnCode;
|
||||
this.msgId = msgId;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Message ID
|
||||
outputStream.writeShort(msgId);
|
||||
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
|
||||
if (reasonCode != MqttReturnCode.RETURN_CODE_SUCCESS && identifierValueFieldsByteArray.length == 1) {
|
||||
// Encode the Return Code
|
||||
outputStream.write((byte) reasonCode);
|
||||
} else if (reasonCode != MqttReturnCode.RETURN_CODE_SUCCESS || identifierValueFieldsByteArray.length > 1) {
|
||||
// Encode the Return Code
|
||||
outputStream.write((byte) reasonCode);
|
||||
// Write Identifier / Value Fields
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public int getReturnCode() {
|
||||
return reasonCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttPubAck [returnCode=" + reasonCode + ", properties=" + properties + "]";
|
||||
}
|
||||
|
||||
}
|
118
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPubComp.java
Normal file
118
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPubComp.java
Normal file
@@ -0,0 +1,118 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
public class MqttPubComp extends MqttAck {
|
||||
|
||||
private static final int[] validReturnCodes = { MqttReturnCode.RETURN_CODE_SUCCESS,
|
||||
MqttReturnCode.RETURN_CODE_PACKET_ID_NOT_FOUND };
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.REASON_STRING_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
// Fields
|
||||
private MqttProperties properties;
|
||||
|
||||
public MqttPubComp(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP);
|
||||
properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream dis = new DataInputStream(counter);
|
||||
msgId = dis.readUnsignedShort();
|
||||
long remainder = (long) data.length - counter.getCounter();
|
||||
if (remainder >= 1) {
|
||||
reasonCode = dis.readUnsignedByte();
|
||||
validateReturnCode(reasonCode, validReturnCodes);
|
||||
} else {
|
||||
reasonCode = 0;
|
||||
}
|
||||
if (remainder >= 4) {
|
||||
this.properties.decodeProperties(dis);
|
||||
}
|
||||
dis.close();
|
||||
}
|
||||
|
||||
public MqttPubComp(int returnCode, int msgId, MqttProperties properties) throws MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP);
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
this.reasonCode = returnCode;
|
||||
this.msgId = msgId;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Message ID
|
||||
outputStream.writeShort(msgId);
|
||||
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
|
||||
if (reasonCode != MqttReturnCode.RETURN_CODE_SUCCESS && identifierValueFieldsByteArray.length == 1) {
|
||||
// Encode the Return Code
|
||||
outputStream.write((byte) reasonCode);
|
||||
} else if (reasonCode != MqttReturnCode.RETURN_CODE_SUCCESS || identifierValueFieldsByteArray.length > 1) {
|
||||
// Encode the Return Code
|
||||
outputStream.write((byte) reasonCode);
|
||||
// Write Identifier / Value Fields
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public int getReturnCode() {
|
||||
return reasonCode;
|
||||
}
|
||||
|
||||
public void setReturnCode(int returnCode) {
|
||||
this.reasonCode = returnCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttPubComp [returnCode=" + reasonCode + ", properties=" + properties + "]";
|
||||
}
|
||||
|
||||
}
|
121
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPubRec.java
Normal file
121
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPubRec.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
public class MqttPubRec extends MqttAck {
|
||||
|
||||
private static final int[] validReturnCodes = { MqttReturnCode.RETURN_CODE_SUCCESS,
|
||||
MqttReturnCode.RETURN_CODE_NO_MATCHING_SUBSCRIBERS, MqttReturnCode.RETURN_CODE_UNSPECIFIED_ERROR,
|
||||
MqttReturnCode.RETURN_CODE_IMPLEMENTATION_SPECIFIC_ERROR, MqttReturnCode.RETURN_CODE_NOT_AUTHORIZED,
|
||||
MqttReturnCode.RETURN_CODE_TOPIC_NAME_INVALID, MqttReturnCode.RETURN_CODE_PACKET_ID_IN_USE,
|
||||
MqttReturnCode.RETURN_CODE_QUOTA_EXCEEDED, MqttReturnCode.RETURN_CODE_PAYLOAD_FORMAT_INVALID };
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.REASON_STRING_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
// Fields
|
||||
private MqttProperties properties;
|
||||
|
||||
public MqttPubRec(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBREC);
|
||||
properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream dis = new DataInputStream(counter);
|
||||
msgId = dis.readUnsignedShort();
|
||||
long remainder = (long) data.length - counter.getCounter();
|
||||
if (remainder >= 1) {
|
||||
reasonCode = dis.readUnsignedByte();
|
||||
validateReturnCode(reasonCode, validReturnCodes);
|
||||
} else {
|
||||
reasonCode = 0;
|
||||
}
|
||||
if (remainder >= 4) {
|
||||
this.properties.decodeProperties(dis);
|
||||
}
|
||||
dis.close();
|
||||
}
|
||||
|
||||
public MqttPubRec(int returnCode, int msgId, MqttProperties properties) throws MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBREC);
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
this.reasonCode = returnCode;
|
||||
this.msgId = msgId;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Message ID
|
||||
outputStream.writeShort(msgId);
|
||||
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
|
||||
if (reasonCode != MqttReturnCode.RETURN_CODE_SUCCESS && identifierValueFieldsByteArray.length == 1) {
|
||||
// Encode the Return Code
|
||||
outputStream.write((byte) reasonCode);
|
||||
} else if (reasonCode != MqttReturnCode.RETURN_CODE_SUCCESS || identifierValueFieldsByteArray.length > 1) {
|
||||
// Encode the Return Code
|
||||
outputStream.write((byte) reasonCode);
|
||||
// Write Identifier / Value Fields
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public int getReturnCode() {
|
||||
return reasonCode;
|
||||
}
|
||||
|
||||
public void setReturnCode(int returnCode) {
|
||||
this.reasonCode = returnCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttPubRec [returnCode=" + reasonCode + ", properties=" + properties + "]";
|
||||
}
|
||||
|
||||
}
|
123
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPubRel.java
Normal file
123
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPubRel.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
public class MqttPubRel extends MqttPersistableWireMessage {
|
||||
|
||||
private static final int[] validReturnCodes = { MqttReturnCode.RETURN_CODE_SUCCESS,
|
||||
MqttReturnCode.RETURN_CODE_PACKET_ID_NOT_FOUND };
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.REASON_STRING_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
// Fields
|
||||
private MqttProperties properties;
|
||||
|
||||
public MqttPubRel(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBREL);
|
||||
properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream dis = new DataInputStream(counter);
|
||||
msgId = dis.readUnsignedShort();
|
||||
long remainder = (long) data.length - counter.getCounter();
|
||||
if (remainder >= 1) {
|
||||
reasonCode = dis.readUnsignedByte();
|
||||
validateReturnCode(reasonCode, validReturnCodes);
|
||||
} else {
|
||||
reasonCode = 0;
|
||||
}
|
||||
if (remainder >= 4) {
|
||||
this.properties.decodeProperties(dis);
|
||||
}
|
||||
dis.close();
|
||||
}
|
||||
|
||||
public MqttPubRel(int returnCode, int msgId, MqttProperties properties) throws MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBREL);
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
this.reasonCode = returnCode;
|
||||
this.msgId = msgId;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Message ID
|
||||
outputStream.writeShort(msgId);
|
||||
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
|
||||
if (reasonCode != MqttReturnCode.RETURN_CODE_SUCCESS && identifierValueFieldsByteArray.length == 1) {
|
||||
// Encode the Return Code
|
||||
outputStream.write((byte) reasonCode);
|
||||
} else if (reasonCode != MqttReturnCode.RETURN_CODE_SUCCESS || identifierValueFieldsByteArray.length > 1) {
|
||||
// Encode the Return Code
|
||||
outputStream.write((byte) reasonCode);
|
||||
// Write Identifier / Value Fields
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
return (byte) (2 | (this.duplicate ? 8 : 0));
|
||||
}
|
||||
|
||||
public int getReturnCode() {
|
||||
return reasonCode;
|
||||
}
|
||||
|
||||
public void setReturnCode(int returnCode) {
|
||||
this.reasonCode = returnCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttPubRel [returnCode=" + reasonCode + ", properties=" + properties + "]";
|
||||
}
|
||||
|
||||
}
|
242
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPublish.java
Normal file
242
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttPublish.java
Normal file
@@ -0,0 +1,242 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.MqttMessage;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
/**
|
||||
* An on-the-wire representation of an MQTT Publish message.
|
||||
*/
|
||||
public class MqttPublish extends MqttPersistableWireMessage {
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.PAYLOAD_FORMAT_INDICATOR_IDENTIFIER,
|
||||
MqttProperties.MESSAGE_EXPIRY_INTERVAL_IDENTIFIER, MqttProperties.TOPIC_ALIAS_IDENTIFIER,
|
||||
MqttProperties.RESPONSE_TOPIC_IDENTIFIER, MqttProperties.CORRELATION_DATA_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER, MqttProperties.CONTENT_TYPE_IDENTIFIER,
|
||||
MqttProperties.SUBSCRIPTION_IDENTIFIER_MULTI, MqttProperties.SUBSCRIPTION_IDENTIFIER };
|
||||
|
||||
private MqttProperties properties;
|
||||
|
||||
// Fields
|
||||
private byte[] payload;
|
||||
private int qos = 1;
|
||||
private boolean retained = false;
|
||||
private boolean dup = false;
|
||||
private String topicName;
|
||||
|
||||
/**
|
||||
* Constructs a new MqttPublish message
|
||||
*
|
||||
* @param topic
|
||||
* - The Destination Topic.
|
||||
* @param message
|
||||
* - The Message being sent.
|
||||
* @param properties
|
||||
* - The {@link MqttProperties} for the packet.
|
||||
*/
|
||||
public MqttPublish(String topic, MqttMessage message, MqttProperties properties) {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBLISH);
|
||||
this.topicName = topic;
|
||||
this.payload = message.getPayload();
|
||||
this.qos = message.getQos();
|
||||
this.dup = message.isDuplicate();
|
||||
this.retained = message.isRetained();
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new MqttPublish message from a byte array
|
||||
*
|
||||
* @param info
|
||||
* - Info Byte
|
||||
* @param data
|
||||
* - The variable header and payload bytes.
|
||||
* @throws IOException
|
||||
* - if an exception occurs when decoding an input stream
|
||||
* @throws MqttException
|
||||
* - If an exception occurs decoding this packet
|
||||
*/
|
||||
public MqttPublish(byte info, byte[] data) throws MqttException, IOException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_PUBLISH);
|
||||
this.properties = new MqttProperties(validProperties);
|
||||
this.qos = (info >> 1) & 0x03;
|
||||
if ((info & 0x01) == 0x01) {
|
||||
this.retained = true;
|
||||
}
|
||||
|
||||
if ((info & 0x08) == 0x08) {
|
||||
this.dup = true;
|
||||
}
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream dis = new DataInputStream(counter);
|
||||
|
||||
topicName = MqttDataTypes.decodeUTF8(dis);
|
||||
if (this.qos > 0) {
|
||||
msgId = dis.readUnsignedShort();
|
||||
}
|
||||
this.properties.decodeProperties(dis);
|
||||
this.payload = new byte[data.length - counter.getCounter()];
|
||||
dis.readFully(this.payload);
|
||||
dis.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
|
||||
// If we are using a Topic Alias, then the topic should be empty
|
||||
if (topicName != null) {
|
||||
MqttDataTypes.encodeUTF8(dos, topicName);
|
||||
} else {
|
||||
MqttDataTypes.encodeUTF8(dos, "");
|
||||
}
|
||||
|
||||
if (this.qos > 0) {
|
||||
dos.writeShort(msgId);
|
||||
}
|
||||
// Write Identifier / Value Fields
|
||||
byte[] identifierValueFieldsArray = this.properties.encodeProperties();
|
||||
dos.write(identifierValueFieldsArray);
|
||||
dos.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
byte info = (byte) (this.qos << 1);
|
||||
if (this.retained) {
|
||||
info |= 0x01;
|
||||
}
|
||||
if (this.dup || duplicate) {
|
||||
info |= 0x08;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return this.payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPayloadLength() {
|
||||
if (this.payload != null) {
|
||||
return this.payload.length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMessageIdRequired() {
|
||||
// all publishes require a message ID as it's used as the key to the
|
||||
// token store
|
||||
return true;
|
||||
}
|
||||
|
||||
public MqttMessage getMessage() {
|
||||
MqttMessage message = new MqttMessage(payload, qos, retained, properties);
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(MqttMessage message) {
|
||||
this.payload = message.getPayload();
|
||||
this.qos = message.getQos();
|
||||
this.dup = message.isDuplicate();
|
||||
this.retained = message.isRetained();
|
||||
}
|
||||
|
||||
public String getTopicName() {
|
||||
return topicName;
|
||||
}
|
||||
|
||||
public int getQoS() {
|
||||
return qos;
|
||||
}
|
||||
|
||||
public void setTopicName(String topicName) {
|
||||
this.topicName = topicName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Convert the first few bytes of the payload into a hex string
|
||||
StringBuilder hex = new StringBuilder();
|
||||
int limit = Math.min(payload.length, 20);
|
||||
for (int i = 0; i < limit; i++) {
|
||||
byte b = payload[i];
|
||||
String ch = Integer.toHexString(b);
|
||||
if (ch.length() == 1) {
|
||||
ch = "0" + ch;
|
||||
}
|
||||
hex.append(ch);
|
||||
}
|
||||
|
||||
// It will not always be possible to convert the binary payload into
|
||||
// characters, but never-the-less we attempt to do this as it is often
|
||||
// useful.
|
||||
String string = null;
|
||||
try {
|
||||
string = new String(payload, 0, limit, "UTF-8");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
string = "?";
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("MqttPublish [");
|
||||
sb.append(", qos=").append(this.qos);
|
||||
if (this.qos > 0) {
|
||||
sb.append(", messageId=").append(msgId);
|
||||
}
|
||||
sb.append(", retained=").append(this.retained);
|
||||
sb.append(", duplicate=").append(duplicate);
|
||||
sb.append(", topic=").append(topicName);
|
||||
sb.append(", payload=[hex=").append(hex);
|
||||
sb.append(", utf8=").append(string);
|
||||
sb.append(", length=").append(payload.length).append("]");
|
||||
sb.append(", properties=").append(this.properties.toString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttMessage;
|
||||
|
||||
public class MqttReceivedMessage extends MqttMessage {
|
||||
|
||||
public void setMessageId(int messageId){
|
||||
super.setId(messageId);
|
||||
}
|
||||
|
||||
public int getMessageId(){
|
||||
return super.getId();
|
||||
}
|
||||
|
||||
// This method exists here to get around the protected visibility of the
|
||||
// super class method.
|
||||
@Override
|
||||
public void setDuplicate(boolean value) {
|
||||
super.setDuplicate(value);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
/**
|
||||
* Reference of MQTT v5 Return Codes - 2.4
|
||||
*/
|
||||
public class MqttReturnCode {
|
||||
|
||||
/** Success and general Return Codes **/
|
||||
public static final int RETURN_CODE_SUCCESS = 0x00; // 0
|
||||
public static final int RETURN_CODE_MAX_QOS_0 = 0x00; // 0
|
||||
public static final int RETURN_CODE_MAX_QOS_1 = 0x01; // 1
|
||||
public static final int RETURN_CODE_MAX_QOS_2 = 0x02; // 2
|
||||
public static final int RETURN_CODE_DISCONNECT_WITH_WILL_MESSAGE = 0x04; // 4
|
||||
public static final int RETURN_CODE_NO_MATCHING_SUBSCRIBERS = 0x10; // 16
|
||||
public static final int RETURN_CODE_NO_SUBSCRIPTION_EXISTED = 0x11; // 17
|
||||
public static final int RETURN_CODE_CONTINUE_AUTHENTICATION = 0x18; // 24
|
||||
public static final int RETURN_CODE_RE_AUTHENTICATE = 0x19; // 25
|
||||
|
||||
/** Error Return Codes **/
|
||||
public static final int RETURN_CODE_UNSPECIFIED_ERROR = 0x80; // 128
|
||||
public static final int RETURN_CODE_MALFORMED_CONTROL_PACKET = 0x81; // 129
|
||||
public static final int RETURN_CODE_PROTOCOL_ERROR = 0x82; // 130
|
||||
public static final int RETURN_CODE_IMPLEMENTATION_SPECIFIC_ERROR = 0x83; // 131
|
||||
public static final int RETURN_CODE_UNSUPPORTED_PROTOCOL_VERSION = 0x84; // 132
|
||||
public static final int RETURN_CODE_IDENTIFIER_NOT_VALID = 0x85; // 133
|
||||
public static final int RETURN_CODE_BAD_USERNAME_OR_PASSWORD = 0x86; // 134
|
||||
public static final int RETURN_CODE_NOT_AUTHORIZED = 0x87; // 135
|
||||
public static final int RETURN_CODE_SERVER_UNAVAILABLE = 0x88; // 136
|
||||
public static final int RETURN_CODE_SERVER_BUSY = 0x89; // 137
|
||||
public static final int RETURN_CODE_BANNED = 0x8A; // 138
|
||||
public static final int RETURN_CODE_SERVER_SHUTTING_DOWN = 0x8B; // 139
|
||||
public static final int RETURN_CODE_BAD_AUTHENTICATION = 0x8C; // 140
|
||||
public static final int RETURN_CODE_KEEP_ALIVE_TIMEOUT = 0x8D; // 141
|
||||
public static final int RETURN_CODE_SESSION_TAKEN_OVER = 0x8E; // 142
|
||||
public static final int RETURN_CODE_TOPIC_FILTER_NOT_VALID = 0x8F; // 143
|
||||
public static final int RETURN_CODE_TOPIC_NAME_INVALID = 0x90; // 144
|
||||
public static final int RETURN_CODE_PACKET_ID_IN_USE = 0x91; // 145
|
||||
public static final int RETURN_CODE_PACKET_ID_NOT_FOUND = 0x92; // 146
|
||||
public static final int RETURN_CODE_RECEIVE_MAXIMUM_EXCEEDED = 0x93; // 147
|
||||
public static final int RETURN_CODE_TOPIC_ALIAS_NOT_ACCEPTED = 0x94; // 148
|
||||
public static final int RETURN_CODE_PACKET_TOO_LARGE = 0x95; // 149
|
||||
public static final int RETURN_CODE_MESSAGE_RATE_TOO_HIGH = 0x96; // 150
|
||||
public static final int RETURN_CODE_QUOTA_EXCEEDED = 0x97; // 151
|
||||
public static final int RETURN_CODE_ADMINISTRITIVE_ACTION = 0x98; // 152
|
||||
public static final int RETURN_CODE_PAYLOAD_FORMAT_INVALID = 0x99; // 153
|
||||
public static final int RETURN_CODE_RETAIN_NOT_SUPPORTED = 0x9A; // 154
|
||||
public static final int RETURN_CODE_QOS_NOT_SUPPORTED = 0x9B; // 155
|
||||
public static final int RETURN_CODE_USE_ANOTHER_SERVER = 0x9C; // 156
|
||||
public static final int RETURN_CODE_SERVER_MOVED = 0x9D; // 157
|
||||
public static final int RETURN_CODE_SHARED_SUB_NOT_SUPPORTED = 0x9E; // 158
|
||||
public static final int RETURN_CODE_CONNECTION_RATE_EXCEEDED = 0x9F; // 159
|
||||
public static final int RETURN_CODE_MAXIMUM_CONNECT_TIME = 0xA0; // 160
|
||||
public static final int RETURN_CODE_SUB_IDENTIFIERS_NOT_SUPPORTED = 0xA1; // 161
|
||||
public static final int RETURN_CODE_WILDCARD_SUB_NOT_SUPPORTED = 0xA2; // 162
|
||||
|
||||
|
||||
private MqttReturnCode() {
|
||||
throw new IllegalAccessError("Utility class");
|
||||
}
|
||||
|
||||
|
||||
}
|
134
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttSubAck.java
Normal file
134
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttSubAck.java
Normal file
@@ -0,0 +1,134 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
public class MqttSubAck extends MqttAck {
|
||||
|
||||
private static final int[] validReturnCodes = { MqttReturnCode.RETURN_CODE_MAX_QOS_0,
|
||||
MqttReturnCode.RETURN_CODE_MAX_QOS_1, MqttReturnCode.RETURN_CODE_MAX_QOS_2,
|
||||
MqttReturnCode.RETURN_CODE_UNSPECIFIED_ERROR, MqttReturnCode.RETURN_CODE_IMPLEMENTATION_SPECIFIC_ERROR,
|
||||
MqttReturnCode.RETURN_CODE_NOT_AUTHORIZED, MqttReturnCode.RETURN_CODE_TOPIC_FILTER_NOT_VALID,
|
||||
MqttReturnCode.RETURN_CODE_PACKET_ID_IN_USE, MqttReturnCode.RETURN_CODE_SHARED_SUB_NOT_SUPPORTED };
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.REASON_STRING_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
// Fields
|
||||
private MqttProperties properties;
|
||||
|
||||
public MqttSubAck(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_SUBACK);
|
||||
properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream inputStream = new DataInputStream(counter);
|
||||
msgId = inputStream.readUnsignedShort();
|
||||
this.properties.decodeProperties(inputStream);
|
||||
|
||||
int remainingLength = data.length - counter.getCounter();
|
||||
reasonCodes = new int[remainingLength];
|
||||
|
||||
for (int i = 0; i < remainingLength; i++) {
|
||||
int returnCode = inputStream.readUnsignedByte();
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
reasonCodes[i] = returnCode;
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
public MqttSubAck(int[] returnCodes, MqttProperties properties) throws MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_SUBACK);
|
||||
for (int returnCode : returnCodes) {
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
}
|
||||
this.reasonCodes = returnCodes;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Message ID
|
||||
outputStream.writeShort(msgId);
|
||||
|
||||
// Write Identifier / Value Fields
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
// Write Identifier / Value Fields
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
for (int returnCode : reasonCodes) {
|
||||
outputStream.writeByte(returnCode);
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getReturnCodes() {
|
||||
return reasonCodes;
|
||||
}
|
||||
|
||||
public void setReturnCodes(int[] returnCodes) {
|
||||
this.reasonCodes = returnCodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttSubAck [returnCodes=" + Arrays.toString(reasonCodes) + ", properties=" + properties + "]";
|
||||
}
|
||||
|
||||
}
|
217
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttSubscribe.java
Normal file
217
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttSubscribe.java
Normal file
@@ -0,0 +1,217 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2019 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.MqttSubscription;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
public class MqttSubscribe extends MqttPersistableWireMessage {
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.SUBSCRIPTION_IDENTIFIER,
|
||||
MqttProperties.SUBSCRIPTION_IDENTIFIER_SINGLE,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
// Fields
|
||||
private MqttProperties properties;
|
||||
private MqttSubscription[] subscriptions;
|
||||
|
||||
/**
|
||||
* Constructor for an on the Wire MQTT Subscribe message
|
||||
*
|
||||
* @param data
|
||||
* - The variable header and payload bytes.
|
||||
* @throws IOException
|
||||
* - if an exception occurs when decoding an input stream
|
||||
* @throws MqttException
|
||||
* - If an exception occurs decoding this packet
|
||||
*/
|
||||
public MqttSubscribe(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE);
|
||||
this.properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream inputStream = new DataInputStream(counter);
|
||||
msgId = inputStream.readUnsignedShort();
|
||||
|
||||
this.properties.decodeProperties(inputStream);
|
||||
|
||||
ArrayList<MqttSubscription> subscriptionList = new ArrayList<>();
|
||||
// Whilst we are reading data
|
||||
while (counter.getCounter() < data.length) {
|
||||
String topic = MqttDataTypes.decodeUTF8(inputStream);
|
||||
byte subscriptionOptions = inputStream.readByte();
|
||||
subscriptionList.add(decodeSubscription(topic, subscriptionOptions));
|
||||
}
|
||||
subscriptions = subscriptionList.toArray(new MqttSubscription[subscriptionList.size()]);
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for an on the Wire MQTT Subscribe message
|
||||
*
|
||||
* @param subscriptions
|
||||
* - An Array of {@link MqttSubscription} subscriptions.
|
||||
* @param properties
|
||||
* - The {@link MqttProperties} for the packet.
|
||||
*/
|
||||
public MqttSubscribe(MqttSubscription[] subscriptions, MqttProperties properties) {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE);
|
||||
this.subscriptions = subscriptions;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for an on the Wire MQTT Subscribe message
|
||||
*
|
||||
* @param subscription
|
||||
* - An {@link MqttSubscription}
|
||||
* @param properties
|
||||
* - The {@link MqttProperties} for the packet.
|
||||
*/
|
||||
public MqttSubscribe(MqttSubscription subscription, MqttProperties properties) {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE);
|
||||
this.subscriptions = new MqttSubscription[] { subscription };
|
||||
this.properties = properties;
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Message ID
|
||||
outputStream.writeShort(msgId);
|
||||
|
||||
// Write Identifier / Value Fields
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
for (MqttSubscription subscription : subscriptions) {
|
||||
outputStream.write(encodeSubscription(subscription));
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRetryable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes an {@link MqttSubscription} into it's on-the-wire representation.
|
||||
* Assumes that the Subscription topic is valid.
|
||||
*
|
||||
* @param subscription
|
||||
* - The {@link MqttSubscription} to encode.
|
||||
* @return A byte array containing the encoded subscription.
|
||||
* @throws MqttException
|
||||
*/
|
||||
private byte[] encodeSubscription(MqttSubscription subscription) throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
MqttDataTypes.encodeUTF8(outputStream, subscription.getTopic());
|
||||
|
||||
// Encode Subscription QoS
|
||||
byte subscriptionOptions = (byte) subscription.getQos();
|
||||
|
||||
// Encode NoLocal Option
|
||||
if (subscription.isNoLocal()) {
|
||||
subscriptionOptions |= 0x04;
|
||||
}
|
||||
|
||||
// Encode Retain As Published Option
|
||||
if (subscription.isRetainAsPublished()) {
|
||||
subscriptionOptions |= 0x08;
|
||||
}
|
||||
|
||||
// Encode Retain Handling Level
|
||||
subscriptionOptions |= (subscription.getRetainHandling() << 4);
|
||||
|
||||
outputStream.write(subscriptionOptions);
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private MqttSubscription decodeSubscription(String topic, byte subscriptionOptions) {
|
||||
MqttSubscription subscription = new MqttSubscription(topic);
|
||||
subscription.setQos(subscriptionOptions & 0x03);
|
||||
subscription.setNoLocal((subscriptionOptions & 0x04) != 0);
|
||||
subscription.setRetainAsPublished((subscriptionOptions & 0x08) != 0);
|
||||
subscription.setRetainHandling((subscriptionOptions >> 4) & 0x03);
|
||||
return subscription;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
return (byte) (2 | (duplicate ? 8 : 0));
|
||||
}
|
||||
|
||||
public MqttSubscription[] getSubscriptions() {
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttSubscribe [properties=" + properties + ", subscriptions=" + Arrays.toString(subscriptions) + "]";
|
||||
}
|
||||
|
||||
}
|
130
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttUnsubAck.java
Normal file
130
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttUnsubAck.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
public class MqttUnsubAck extends MqttAck {
|
||||
|
||||
private static final int[] validReturnCodes = { MqttReturnCode.RETURN_CODE_SUCCESS,
|
||||
MqttReturnCode.RETURN_CODE_NO_SUBSCRIPTION_EXISTED, MqttReturnCode.RETURN_CODE_UNSPECIFIED_ERROR,
|
||||
MqttReturnCode.RETURN_CODE_IMPLEMENTATION_SPECIFIC_ERROR, MqttReturnCode.RETURN_CODE_NOT_AUTHORIZED,
|
||||
MqttReturnCode.RETURN_CODE_TOPIC_FILTER_NOT_VALID, MqttReturnCode.RETURN_CODE_PACKET_ID_IN_USE };
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.REASON_STRING_IDENTIFIER,
|
||||
MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
// Fields
|
||||
private MqttProperties properties;
|
||||
|
||||
public MqttUnsubAck(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_UNSUBACK);
|
||||
properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream inputStream = new DataInputStream(counter);
|
||||
|
||||
msgId = inputStream.readUnsignedShort();
|
||||
|
||||
this.properties.decodeProperties(inputStream);
|
||||
|
||||
int remainingLengh = data.length - counter.getCounter();
|
||||
reasonCodes = new int[remainingLengh];
|
||||
|
||||
for (int i = 0; i < remainingLengh; i++) {
|
||||
reasonCodes[i] = inputStream.readUnsignedByte();
|
||||
validateReturnCode(reasonCodes[i], validReturnCodes);
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
public MqttUnsubAck(int[] returnCodes, MqttProperties properties) throws MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_UNSUBACK);
|
||||
for (int returnCode : returnCodes) {
|
||||
validateReturnCode(returnCode, validReturnCodes);
|
||||
}
|
||||
this.reasonCodes = returnCodes;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the msgId
|
||||
outputStream.writeShort(msgId);
|
||||
|
||||
// Write Identifier / Value Fields
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
// Write Identifier / Value Fields
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
for (int returnCode : reasonCodes) {
|
||||
outputStream.writeByte(returnCode);
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int[] getReturnCodes() {
|
||||
return reasonCodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttUnsubAck [returnCodes=" + Arrays.toString(reasonCodes) + ", properties=" + properties + "]";
|
||||
}
|
||||
|
||||
}
|
138
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttUnsubscribe.java
Normal file
138
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttUnsubscribe.java
Normal file
@@ -0,0 +1,138 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2019 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
|
||||
public class MqttUnsubscribe extends MqttPersistableWireMessage{
|
||||
|
||||
private static final Byte[] validProperties = { MqttProperties.USER_DEFINED_PAIR_IDENTIFIER };
|
||||
|
||||
|
||||
// Fields
|
||||
private String[] topics;
|
||||
private MqttProperties properties;
|
||||
|
||||
|
||||
|
||||
public MqttUnsubscribe(byte[] data) throws IOException, MqttException {
|
||||
super(MqttWireMessage.MESSAGE_TYPE_UNSUBSCRIBE);
|
||||
this.properties = new MqttProperties(validProperties);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||
CountingInputStream counter = new CountingInputStream(bais);
|
||||
DataInputStream inputStream = new DataInputStream(counter);
|
||||
msgId = inputStream.readUnsignedShort();
|
||||
|
||||
this.properties.decodeProperties(inputStream);
|
||||
|
||||
ArrayList<String> topicList = new ArrayList<>();
|
||||
// Whilst we are reading data
|
||||
while(counter.getCounter() < data.length){
|
||||
topicList.add( MqttDataTypes.decodeUTF8(inputStream));
|
||||
}
|
||||
topics = topicList.toArray(new String[topicList.size()]);
|
||||
|
||||
|
||||
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
public MqttUnsubscribe(String[] topics, MqttProperties properties){
|
||||
super(MqttWireMessage.MESSAGE_TYPE_UNSUBSCRIBE);
|
||||
this.topics = topics;
|
||||
if (properties != null) {
|
||||
this.properties = properties;
|
||||
} else {
|
||||
this.properties = new MqttProperties();
|
||||
}
|
||||
this.properties.setValidProperties(validProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getVariableHeader() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
// Encode the Message ID
|
||||
outputStream.writeShort(msgId);
|
||||
|
||||
|
||||
// Write Identifier / Value Fields
|
||||
byte[] identifierValueFieldsByteArray = this.properties.encodeProperties();
|
||||
outputStream.write(identifierValueFieldsByteArray);
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(baos);
|
||||
|
||||
for(String topic : topics){
|
||||
MqttDataTypes.encodeUTF8(outputStream, topic);
|
||||
}
|
||||
|
||||
outputStream.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe){
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected byte getMessageInfo() {
|
||||
return (byte)( 2 | (this.duplicate ? 8 : 0));
|
||||
}
|
||||
|
||||
public String[] getTopics() {
|
||||
return topics;
|
||||
}
|
||||
|
||||
public void setTopics(String[] topics) {
|
||||
this.topics = topics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MqttProperties getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MqttUnsubscribe [topics=" + Arrays.toString(topics) + ", properties=" + properties + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
417
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttWireMessage.java
Normal file
417
mqtt/org/eclipse/paho/mqttv5/common/packet/MqttWireMessage.java
Normal file
@@ -0,0 +1,417 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.eclipse.paho.mqttv5.common.ExceptionHelper;
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
import org.eclipse.paho.mqttv5.common.MqttPersistable;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.CountingInputStream;
|
||||
import org.eclipse.paho.mqttv5.common.packet.util.MultiByteArrayInputStream;
|
||||
|
||||
/**
|
||||
* An on-the wire representation of an MQTTv5 Message
|
||||
*/
|
||||
public abstract class MqttWireMessage {
|
||||
|
||||
public static final byte MESSAGE_TYPE_RESERVED = 0;
|
||||
public static final byte MESSAGE_TYPE_CONNECT = 1;
|
||||
public static final byte MESSAGE_TYPE_CONNACK = 2;
|
||||
public static final byte MESSAGE_TYPE_PUBLISH = 3;
|
||||
public static final byte MESSAGE_TYPE_PUBACK = 4;
|
||||
public static final byte MESSAGE_TYPE_PUBREC = 5;
|
||||
public static final byte MESSAGE_TYPE_PUBREL = 6;
|
||||
public static final byte MESSAGE_TYPE_PUBCOMP = 7;
|
||||
public static final byte MESSAGE_TYPE_SUBSCRIBE = 8;
|
||||
public static final byte MESSAGE_TYPE_SUBACK = 9;
|
||||
public static final byte MESSAGE_TYPE_UNSUBSCRIBE = 10;
|
||||
public static final byte MESSAGE_TYPE_UNSUBACK = 11;
|
||||
public static final byte MESSAGE_TYPE_PINGREQ = 12;
|
||||
public static final byte MESSAGE_TYPE_PINGRESP = 13;
|
||||
public static final byte MESSAGE_TYPE_DISCONNECT = 14;
|
||||
public static final byte MESSAGE_TYPE_AUTH = 15;
|
||||
|
||||
protected static final String STRING_ENCODING = "UTF-8";
|
||||
protected static final String DEFAULT_PROTOCOL_NAME = "MQTT";
|
||||
protected static final int DEFAULT_PROTOCOL_VERSION = 5;
|
||||
|
||||
private static final String[] PACKET_NAMES = { "reserved", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC",
|
||||
"PUBREL", "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", "PINGREQ", "PINGRESP", "DISCONNECT",
|
||||
"AUTH" };
|
||||
|
||||
private static final byte[] PACKET_RESERVED_MASKS = { 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0 };
|
||||
|
||||
// The type of the message (e.g CONNECT, PUBLISH, SUBSCRIBE)
|
||||
private byte type;
|
||||
|
||||
MqttProperties properties = new MqttProperties();
|
||||
|
||||
// The MQTT Message ID
|
||||
protected int msgId;
|
||||
protected int[] reasonCodes = null; // Multiple Reason Codes (SUBACK, UNSUBACK)
|
||||
protected int reasonCode = -1; // Single Reason Code, init with -1 as that's an invalid RC
|
||||
protected boolean duplicate = false;
|
||||
|
||||
public MqttWireMessage(byte type) {
|
||||
this.type = type;
|
||||
// Use zero as the default message ID. Can't use -1, as that is serialized
|
||||
// as 65535, which would be a valid ID.
|
||||
this.msgId = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-classes should override this to encode the message info. Only the
|
||||
* least-significant four bits will be used.
|
||||
*
|
||||
* @return The Message information byte.
|
||||
*/
|
||||
protected abstract byte getMessageInfo();
|
||||
|
||||
/**
|
||||
* Sub-classes should override this method to supply the payload bytes.
|
||||
*
|
||||
* @return The payload byte array
|
||||
* @throws MqttException
|
||||
* if an exception occurs whilst getting the payload.
|
||||
*/
|
||||
public byte[] getPayload() throws MqttException {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the type of the message
|
||||
*/
|
||||
public byte getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the MQTT message ID
|
||||
*/
|
||||
public int getMessageId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the MQTT message ID.
|
||||
*
|
||||
* @param msgId
|
||||
* the MQTT message ID
|
||||
*/
|
||||
public void setMessageId(int msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key associated with the message. For most message types this will
|
||||
* be unique. For connect, disconnect and ping only one message of this type is
|
||||
* allowed so a fixed key will be returned.
|
||||
*
|
||||
* @return The key associated with the message
|
||||
*/
|
||||
public String getKey() {
|
||||
return Integer.toString(getMessageId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array containing the MQTT header for the message.
|
||||
*
|
||||
* @return The MQTT Message Header
|
||||
* @throws MqttException
|
||||
* if there was an issue encoding the header
|
||||
*/
|
||||
public byte[] getHeader() throws MqttException {
|
||||
try {
|
||||
int first = ((getType() & 0x0f) << 4) ^ (getMessageInfo() & 0x0f);
|
||||
byte[] varHeader = getVariableHeader();
|
||||
int remLen = varHeader.length + getPayload().length;
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
dos.writeByte(first);
|
||||
dos.write(encodeVariableByteInteger(remLen));
|
||||
dos.write(varHeader);
|
||||
dos.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract byte[] getVariableHeader() throws MqttException;
|
||||
|
||||
/**
|
||||
* @return whether or not this message needs to include a message ID.
|
||||
*/
|
||||
public boolean isMessageIdRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an MQTT Wire Message
|
||||
*
|
||||
* @throws MqttException
|
||||
* if an error occurred whilst creating the WireMessage
|
||||
* @param data
|
||||
* the MqttPersistable to create the message from
|
||||
* @return MqttWireMessage the de-persisted message
|
||||
*/
|
||||
public static MqttWireMessage createWireMessage(MqttPersistable data) throws MqttException {
|
||||
byte[] payload = data.getPayloadBytes();
|
||||
|
||||
// The persistable interface allows a message to be restored entirely in the
|
||||
// header array.
|
||||
// We need to treat these two arrays as a single array of bytes and use the
|
||||
// decoding
|
||||
// logic to identify the true header / payload split.
|
||||
|
||||
if (payload == null) {
|
||||
payload = new byte[0];
|
||||
}
|
||||
MultiByteArrayInputStream mbais = new MultiByteArrayInputStream(data.getHeaderBytes(), data.getHeaderOffset(),
|
||||
data.getHeaderLength(), payload, data.getPayloadOffset(), data.getPayloadLength());
|
||||
return createWireMessage(mbais);
|
||||
}
|
||||
|
||||
public static MqttWireMessage createWireMessage(byte[] bytes) throws MqttException {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||
return createWireMessage(bais);
|
||||
}
|
||||
|
||||
private static MqttWireMessage createWireMessage(InputStream inputStream) throws MqttException {
|
||||
try {
|
||||
CountingInputStream counter = new CountingInputStream(inputStream);
|
||||
DataInputStream in = new DataInputStream(counter);
|
||||
int first = in.readUnsignedByte();
|
||||
byte type = (byte) (first >> 4);
|
||||
byte info = (byte) (first &= 0x0f);
|
||||
long remLen = MqttDataTypes.readVariableByteInteger(in).getValue();
|
||||
long totalToRead = counter.getCounter() + remLen;
|
||||
|
||||
MqttWireMessage result;
|
||||
long remainder = totalToRead - counter.getCounter();
|
||||
byte[] data = new byte[0];
|
||||
|
||||
// The remaining bytes must be the payload
|
||||
if (remainder > 0) {
|
||||
data = new byte[(int) remainder];
|
||||
in.readFully(data, 0, data.length);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MqttWireMessage.MESSAGE_TYPE_CONNECT:
|
||||
result = new MqttConnect(info, data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_CONNACK:
|
||||
result = new MqttConnAck(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_PUBLISH:
|
||||
result = new MqttPublish(info, data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_PUBACK:
|
||||
result = new MqttPubAck(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_PUBREC:
|
||||
result = new MqttPubRec(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_PUBREL:
|
||||
result = new MqttPubRel(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_PUBCOMP:
|
||||
result = new MqttPubComp(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE:
|
||||
result = new MqttSubscribe(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_SUBACK:
|
||||
result = new MqttSubAck(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_UNSUBSCRIBE:
|
||||
result = new MqttUnsubscribe(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_UNSUBACK:
|
||||
result = new MqttUnsubAck(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_PINGREQ:
|
||||
result = new MqttPingReq();
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_PINGRESP:
|
||||
result = new MqttPingResp();
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_DISCONNECT:
|
||||
result = new MqttDisconnect(data);
|
||||
break;
|
||||
case MqttWireMessage.MESSAGE_TYPE_AUTH:
|
||||
result = new MqttAuth(data);
|
||||
break;
|
||||
default:
|
||||
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_MALFORMED_PACKET);
|
||||
}
|
||||
return result;
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] encodeVariableByteInteger(int number) {
|
||||
int numBytes = 0;
|
||||
long no = number;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
// Encode the remaining length fields in the four bytes
|
||||
do {
|
||||
byte digit = (byte) (no % 128);
|
||||
no = no / 128;
|
||||
if (no > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
baos.write(digit);
|
||||
numBytes++;
|
||||
} while ((no > 0) && (numBytes < 4));
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the reserved bits set on an MQTT packet conform to the MQTT
|
||||
* specification.
|
||||
*
|
||||
* @param type
|
||||
* - The Message Type
|
||||
* @param reserved
|
||||
* - The Reserved Bits
|
||||
* @throws MqttException
|
||||
* If the set reserved bits do not match the specification.
|
||||
* @throws IllegalArgumentException
|
||||
* If the message type does not exist.
|
||||
*/
|
||||
public static void validateReservedBits(byte type, byte reserved) throws MqttException, IllegalArgumentException {
|
||||
if (type == MESSAGE_TYPE_PUBLISH) {
|
||||
// Publish can vary, but will be parsed separately.
|
||||
return;
|
||||
}
|
||||
if (type > MESSAGE_TYPE_AUTH) {
|
||||
throw new IllegalArgumentException("Unrecognised Message Type.");
|
||||
}
|
||||
if (reserved != PACKET_RESERVED_MASKS[type]) {
|
||||
throw new MqttException(MqttException.REASON_CODE_MALFORMED_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] encodeMessageId() throws MqttException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
dos.writeShort(msgId);
|
||||
dos.flush();
|
||||
return baos.toByteArray();
|
||||
} catch (IOException ioe) {
|
||||
throw new MqttException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRetryable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setDuplicate(boolean duplicate) {
|
||||
this.duplicate = duplicate;
|
||||
}
|
||||
|
||||
public boolean isDuplicate() {
|
||||
return this.duplicate;
|
||||
}
|
||||
|
||||
public MqttProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(MqttProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return PACKET_NAMES[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a return code is valid for this Packet
|
||||
*
|
||||
* @param returnCode
|
||||
* - The return code to validate
|
||||
* @param validReturnCodes
|
||||
* - The list of valid return codes
|
||||
* @throws MqttException
|
||||
* - Thrown if the return code is not valid
|
||||
*/
|
||||
protected void validateReturnCode(int returnCode, int[] validReturnCodes) throws MqttException {
|
||||
for (int validReturnCode : validReturnCodes) {
|
||||
if (returnCode == validReturnCode) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new MqttException(MqttException.REASON_CODE_INVALID_RETURN_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the reason codes from the MqttWireMessage. These will be present if
|
||||
* the messages is of the following types:
|
||||
* <ul>
|
||||
* <li>CONNACK - 1 Reason Code Max.</li>
|
||||
* <li>PUBACK - 1 Reason Code Max.</li>
|
||||
* <li>PUBREC - 1 Reason Code Max.</li>
|
||||
* <li>PUBCOMP - 1 Reason Code Max.</li>
|
||||
* <li>PUBREL - 1 Reason Code Max.</li>
|
||||
* <li>SUBACK - 1 or more Reason Codes.</li>
|
||||
* <li>UNSUBACK - 1 or more Reason Codes.</li>
|
||||
* <li>AUTH - 1 Reason Code Max.</li>
|
||||
* </ul>
|
||||
*
|
||||
* Warning: This method may be removed in favour of Token.getReasonCodes()
|
||||
*
|
||||
* May be null if this message does not contain any Reason Codes.
|
||||
*
|
||||
* @return An array of return codes, or null.
|
||||
*/
|
||||
public int[] getReasonCodes() {
|
||||
if (this.reasonCodes != null) {
|
||||
return this.reasonCodes;
|
||||
} else if (this.reasonCode != -1) {
|
||||
return new int[] { this.reasonCode };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] serialize() throws MqttException {
|
||||
byte[] a = getHeader();
|
||||
byte[] b = getPayload();
|
||||
|
||||
byte[] c = new byte[a.length + b.length];
|
||||
System.arraycopy(a, 0, c, 0, a.length);
|
||||
System.arraycopy(b, 0, c, a.length, b.length);
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
40
mqtt/org/eclipse/paho/mqttv5/common/packet/UserProperty.java
Normal file
40
mqtt/org/eclipse/paho/mqttv5/common/packet/UserProperty.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package org.eclipse.paho.mqttv5.common.packet;
|
||||
|
||||
public class UserProperty {
|
||||
private final String key;
|
||||
private final String value;
|
||||
|
||||
public UserProperty(String key, String value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return key.hashCode() ^ value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof UserProperty) {
|
||||
UserProperty property = (UserProperty) o;
|
||||
return this.key.equals(property.getKey()) && this.value.equals(property.getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserProperty [key=" + key + ", value=" + value + "]";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class CountingInputStream extends InputStream{
|
||||
private InputStream inputStream;
|
||||
private int counter;
|
||||
|
||||
|
||||
/**Constructs a new <code>CountingInputStream</code> wrapping the supplied
|
||||
* input stream.
|
||||
* @param inputStream The inputStream to count and provide
|
||||
*/
|
||||
public CountingInputStream(InputStream inputStream){
|
||||
this.inputStream = inputStream;
|
||||
this.counter = 0;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
int i = inputStream.read();
|
||||
if (i != -1){
|
||||
counter++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes read since last reset
|
||||
* @return the counter
|
||||
*/
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
/**
|
||||
* Resets the counter to zero
|
||||
*/
|
||||
public void resetCounter() {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class MultiByteArrayInputStream extends InputStream {
|
||||
|
||||
private byte[] bytesA;
|
||||
private int offsetA;
|
||||
private int lengthA;
|
||||
private byte[] bytesB;
|
||||
private int offsetB;
|
||||
private int lengthB;
|
||||
|
||||
private int pos = 0;
|
||||
|
||||
public MultiByteArrayInputStream(byte[] bytesA, int offsetA, int lengthA, byte[] bytesB, int offsetB, int lengthB) {
|
||||
this.bytesA = bytesA;
|
||||
this.bytesB = bytesB;
|
||||
this.offsetA = offsetA;
|
||||
this.offsetB = offsetB;
|
||||
this.lengthA = lengthA;
|
||||
this.lengthB = lengthB;
|
||||
}
|
||||
public int read() throws IOException {
|
||||
int result = -1;
|
||||
if (pos<lengthA) {
|
||||
result = bytesA[offsetA+pos];
|
||||
} else if (pos<lengthA+lengthB) {
|
||||
result = bytesB[offsetB+pos-lengthA];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
if (result < 0) {
|
||||
result += 256;
|
||||
}
|
||||
pos++;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Dave Locke - Original MQTTv3 implementation
|
||||
* James Sutton - Initial MQTTv5 implementation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.packet.util;
|
||||
|
||||
/**
|
||||
* Represents a Variable Byte Integer (VBI), as defined by the MQTT v5 (1.5.5)
|
||||
* specification.
|
||||
*/
|
||||
public class VariableByteInteger {
|
||||
private int value;
|
||||
private int length;
|
||||
|
||||
public VariableByteInteger(int value) {
|
||||
this(value, -1);
|
||||
}
|
||||
|
||||
public VariableByteInteger(int value, int length) {
|
||||
this.value = value;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes read when decoding this MBI
|
||||
*
|
||||
* @return The Encoded Length of the VBI.
|
||||
*/
|
||||
public int getEncodedLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this MBI.
|
||||
*
|
||||
* @return The value of the VBI.
|
||||
*/
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
225
mqtt/org/eclipse/paho/mqttv5/common/util/MqttTopicValidator.java
Normal file
225
mqtt/org/eclipse/paho/mqttv5/common/util/MqttTopicValidator.java
Normal file
@@ -0,0 +1,225 @@
|
||||
package org.eclipse.paho.mqttv5.common.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public class MqttTopicValidator {
|
||||
|
||||
/**
|
||||
* The forward slash (/) is used to separate each level within a topic tree and provide a hierarchical structure to
|
||||
* the topic space. The use of the topic level separator is significant when the two wildcard characters are
|
||||
* encountered in topics specified by subscribers.
|
||||
*/
|
||||
public static final String TOPIC_LEVEL_SEPARATOR = "/";
|
||||
|
||||
/**
|
||||
* Multi-level wildcard The number sign (#) is a wildcard character that matches any number of levels within a topic.
|
||||
*/
|
||||
public static final String MULTI_LEVEL_WILDCARD = "#";
|
||||
|
||||
/**
|
||||
* Single-level wildcard The plus sign (+) is a wildcard character that matches only one topic level.
|
||||
*/
|
||||
public static final String SINGLE_LEVEL_WILDCARD = "+";
|
||||
|
||||
/**
|
||||
* Multi-level wildcard pattern(/#)
|
||||
*/
|
||||
public static final String MULTI_LEVEL_WILDCARD_PATTERN = TOPIC_LEVEL_SEPARATOR + MULTI_LEVEL_WILDCARD;
|
||||
|
||||
/**
|
||||
* Topic wildcards (#+)
|
||||
*/
|
||||
public static final String TOPIC_WILDCARDS = MULTI_LEVEL_WILDCARD + SINGLE_LEVEL_WILDCARD;
|
||||
|
||||
// topic name and topic filter length range defined in the spec
|
||||
private static final int MIN_TOPIC_LEN = 1;
|
||||
private static final int MAX_TOPIC_LEN = 65535;
|
||||
private static final char NUL = '\u0000';
|
||||
|
||||
/**
|
||||
* Validate the topic name or topic filter
|
||||
*
|
||||
* @param topicString
|
||||
* topic name or filter
|
||||
* @param wildcardAllowed
|
||||
* true if validate topic filter, false otherwise
|
||||
* @param sharedSubAllowed
|
||||
* true if shared subscription is allowed, false otherwise
|
||||
* @throws IllegalArgumentException
|
||||
* if the topic is invalid
|
||||
*/
|
||||
public static void validate(String topicString, boolean wildcardAllowed, boolean sharedSubAllowed)
|
||||
throws IllegalArgumentException {
|
||||
int topicLen = 0;
|
||||
try {
|
||||
topicLen = topicString.getBytes("UTF-8").length;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException(e.getMessage());
|
||||
}
|
||||
|
||||
// Spec: length check
|
||||
// - All Topic Names and Topic Filters MUST be at least one character
|
||||
// long
|
||||
// - Topic Names and Topic Filters are UTF-8 encoded strings, they MUST
|
||||
// NOT encode to more than 65535 bytes
|
||||
if (topicLen < MIN_TOPIC_LEN || topicLen > MAX_TOPIC_LEN) {
|
||||
throw new IllegalArgumentException(String.format("Invalid topic length, should be in range[%d, %d]!",
|
||||
new Object[] { Integer.valueOf(MIN_TOPIC_LEN), Integer.valueOf(MAX_TOPIC_LEN) }));
|
||||
}
|
||||
|
||||
// *******************************************************************************
|
||||
// 1) This is a topic filter string that can contain wildcard characters
|
||||
// *******************************************************************************
|
||||
if (wildcardAllowed) {
|
||||
// Only # or +
|
||||
if (Strings.equalsAny(topicString, new String[] { MULTI_LEVEL_WILDCARD, SINGLE_LEVEL_WILDCARD })) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1) Check multi-level wildcard
|
||||
// Rule:
|
||||
// The multi-level wildcard can be specified only on its own or next
|
||||
// to the topic level separator character.
|
||||
|
||||
// - Can only contains one multi-level wildcard character
|
||||
// - The multi-level wildcard must be the last character used within
|
||||
// the topic tree
|
||||
if (Strings.countMatches(topicString, MULTI_LEVEL_WILDCARD) > 1
|
||||
|| (topicString.contains(MULTI_LEVEL_WILDCARD) && !topicString.endsWith(MULTI_LEVEL_WILDCARD_PATTERN))) {
|
||||
throw new IllegalArgumentException("Invalid usage of multi-level wildcard in topic string: " + topicString);
|
||||
}
|
||||
|
||||
// 2) Check single-level wildcard
|
||||
// Rule:
|
||||
// The single-level wildcard can be used at any level in the topic
|
||||
// tree, and in conjunction with the
|
||||
// multilevel wildcard. It must be used next to the topic level
|
||||
// separator, except when it is specified on
|
||||
// its own.
|
||||
validateSingleLevelWildcard(topicString);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate Shared Subscriptions
|
||||
if (!sharedSubAllowed && topicString.startsWith("$share/")) {
|
||||
throw new IllegalArgumentException("Shared Subscriptions are not allowed.");
|
||||
}
|
||||
|
||||
// *******************************************************************************
|
||||
// 2) This is a topic name string that MUST NOT contains any wildcard characters
|
||||
// *******************************************************************************
|
||||
if (Strings.containsAny(topicString, TOPIC_WILDCARDS)) {
|
||||
throw new IllegalArgumentException("The topic name MUST NOT contain any wildcard characters (#+)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void validateSingleLevelWildcard(String topicString) {
|
||||
char singleLevelWildcardChar = SINGLE_LEVEL_WILDCARD.charAt(0);
|
||||
char topicLevelSeparatorChar = TOPIC_LEVEL_SEPARATOR.charAt(0);
|
||||
|
||||
char[] chars = topicString.toCharArray();
|
||||
int length = chars.length;
|
||||
char prev = NUL, next = NUL;
|
||||
for (int i = 0; i < length; i++) {
|
||||
prev = (i - 1 >= 0) ? chars[i - 1] : NUL;
|
||||
next = (i + 1 < length) ? chars[i + 1] : NUL;
|
||||
|
||||
if (chars[i] == singleLevelWildcardChar) {
|
||||
// prev and next can be only '/' or none
|
||||
if (prev != topicLevelSeparatorChar && prev != NUL || next != topicLevelSeparatorChar && next != NUL) {
|
||||
throw new IllegalArgumentException(String
|
||||
.format("Invalid usage of single-level wildcard in topic string '%s'!", new Object[] { topicString }));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the supplied topic name and filter match
|
||||
*
|
||||
* @param topicFilter
|
||||
* topic filter: wildcards allowed
|
||||
* @param topicName
|
||||
* topic name: wildcards not allowed
|
||||
* @return true if the topic matches the filter
|
||||
* @throws IllegalArgumentException
|
||||
* if the topic name or filter is invalid
|
||||
*/
|
||||
public static boolean isMatched(String topicFilter, String topicName) throws IllegalArgumentException {
|
||||
int topicPos = 0;
|
||||
int filterPos = 0;
|
||||
int topicLen = topicName.length();
|
||||
int filterLen = topicFilter.length();
|
||||
|
||||
MqttTopicValidator.validate(topicFilter, true, true);
|
||||
MqttTopicValidator.validate(topicName, false, true);
|
||||
|
||||
if (topicFilter.equals(topicName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while (filterPos < filterLen && topicPos < topicLen) {
|
||||
if (topicFilter.charAt(filterPos) == '#') {
|
||||
/*
|
||||
* next 'if' will break when topicFilter = topic/# and topicName topic/A/, but they are matched
|
||||
*/
|
||||
topicPos = topicLen;
|
||||
filterPos = filterLen;
|
||||
break;
|
||||
}
|
||||
if (topicName.charAt(topicPos) == '/' && topicFilter.charAt(filterPos) != '/')
|
||||
break;
|
||||
if (topicFilter.charAt(filterPos) != '+' && topicFilter.charAt(filterPos) != '#'
|
||||
&& topicFilter.charAt(filterPos) != topicName.charAt(topicPos))
|
||||
break;
|
||||
if (topicFilter.charAt(filterPos) == '+') { // skip until we meet the next separator, or end of string
|
||||
int nextpos = topicPos + 1;
|
||||
while (nextpos < topicLen && topicName.charAt(nextpos) != '/')
|
||||
nextpos = ++topicPos + 1;
|
||||
} else if (topicFilter.charAt(filterPos) == '#')
|
||||
topicPos = topicLen - 1; // skip until end of string
|
||||
filterPos++;
|
||||
topicPos++;
|
||||
}
|
||||
|
||||
if ((topicPos == topicLen) && (filterPos == filterLen)) {
|
||||
return true;
|
||||
} else {
|
||||
/*
|
||||
* https://github.com/eclipse/paho.mqtt.java/issues/418 Covers edge case to match sport/# to sport
|
||||
*/
|
||||
if ((topicFilter.length() - filterPos > 0) && (topicPos == topicLen)) {
|
||||
if (topicName.charAt(topicPos - 1) == '/' && topicFilter.charAt(filterPos) == '#')
|
||||
return true;
|
||||
if (topicFilter.length() - filterPos > 1 && topicFilter.substring(filterPos, filterPos + 2).equals("/#")) {
|
||||
if ((topicFilter.length() - topicName.length()) == 2
|
||||
&& topicFilter.substring(topicFilter.length() - 2, topicFilter.length()).equals("/#")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* https://github.com/eclipse/paho.mqtt.java/issues/918
|
||||
* covers cases that include more then one wildcard
|
||||
* sport/+/tennis/#
|
||||
*/
|
||||
String[] topicFilterParts = topicFilter.split(TOPIC_LEVEL_SEPARATOR);
|
||||
String[] topicParts = topicName.split(TOPIC_LEVEL_SEPARATOR);
|
||||
if(topicFilterParts.length -1 == topicParts.length &&
|
||||
topicFilterParts[topicFilterParts.length-1].equals( MULTI_LEVEL_WILDCARD)) {
|
||||
for (int i = 0; i<topicParts.length;i++) {
|
||||
if(!topicParts[i].equals(topicFilterParts[i]) && !topicFilterParts[i].equals(SINGLE_LEVEL_WILDCARD)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
173
mqtt/org/eclipse/paho/mqttv5/common/util/Strings.java
Normal file
173
mqtt/org/eclipse/paho/mqttv5/common/util/Strings.java
Normal file
@@ -0,0 +1,173 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* Bin Zhang - initial API and implementation and/or initial documentation
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.common.util;
|
||||
|
||||
/**
|
||||
* String helper
|
||||
*/
|
||||
public final class Strings {
|
||||
// Represents a failed index search.
|
||||
private static final int INDEX_NOT_FOUND = -1;
|
||||
|
||||
/**
|
||||
* Checks if the CharSequence equals any character in the given set of characters.
|
||||
*
|
||||
* @param cs the CharSequence to check
|
||||
* @param strs the set of characters to check against
|
||||
* @return true if equals any
|
||||
*/
|
||||
public static boolean equalsAny(CharSequence cs, CharSequence[] strs) {
|
||||
boolean eq = false;
|
||||
if (cs == null) {
|
||||
eq = strs == null;
|
||||
}
|
||||
|
||||
if (strs != null) {
|
||||
for (CharSequence str : strs) {
|
||||
eq = eq || str.equals(cs);
|
||||
}
|
||||
}
|
||||
|
||||
return eq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the CharSequence contains any character in the given set of characters.
|
||||
*
|
||||
* @param cs the CharSequence to check, may be null
|
||||
* @param searchChars the chars to search for, may be null
|
||||
* @return the {@code true} if any of the chars are found, {@code false} if no match or null input
|
||||
*/
|
||||
public static boolean containsAny(CharSequence cs, CharSequence searchChars) {
|
||||
if (searchChars == null) {
|
||||
return false;
|
||||
}
|
||||
return containsAny(cs, toCharArray(searchChars));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the CharSequence contains any character in the given set of characters.
|
||||
*
|
||||
* @param cs the CharSequence to check, may be null
|
||||
* @param searchChars the chars to search for, may be null
|
||||
* @return the {@code true} if any of the chars are found, {@code false} if no match or null input
|
||||
*/
|
||||
public static boolean containsAny(CharSequence cs, char[] searchChars) {
|
||||
if (isEmpty(cs) || isEmpty(searchChars)) {
|
||||
return false;
|
||||
}
|
||||
int csLength = cs.length();
|
||||
int searchLength = searchChars.length;
|
||||
int csLast = csLength - 1;
|
||||
int searchLast = searchLength - 1;
|
||||
for (int i = 0; i < csLength; i++) {
|
||||
char ch = cs.charAt(i);
|
||||
for (int j = 0; j < searchLength; j++) {
|
||||
if (searchChars[j] == ch) {
|
||||
if (Character.isHighSurrogate(ch)) {
|
||||
if (j == searchLast) {
|
||||
// missing low surrogate, fine, like String.indexOf(String)
|
||||
return true;
|
||||
}
|
||||
if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// ch is in the Basic Multilingual Plane
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a CharSequence is empty ("") or null.
|
||||
*
|
||||
* @param cs the CharSequence to check, may be null
|
||||
* @return {@code true} if the CharSequence is empty or null
|
||||
*/
|
||||
public static boolean isEmpty(CharSequence cs) {
|
||||
return cs == null || cs.length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array
|
||||
*/
|
||||
private static boolean isEmpty(char[] array) {
|
||||
return array == null || array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Green implementation of toCharArray.
|
||||
*
|
||||
* @param cs the {@code CharSequence} to be processed
|
||||
* @return the resulting char array
|
||||
*/
|
||||
private static char[] toCharArray(CharSequence cs) {
|
||||
if (cs instanceof String) {
|
||||
return ((String) cs).toCharArray();
|
||||
}
|
||||
else {
|
||||
int sz = cs.length();
|
||||
char[] array = new char[cs.length()];
|
||||
for (int i = 0; i < sz; i++) {
|
||||
array[i] = cs.charAt(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts how many times the substring appears in the larger string.
|
||||
*
|
||||
* @param str the CharSequence to check, may be null
|
||||
* @param sub the substring to count, may be null
|
||||
* @return the number of occurrences, 0 if either CharSequence is {@code null}
|
||||
*/
|
||||
public static int countMatches(CharSequence str, CharSequence sub) {
|
||||
if (isEmpty(str) || isEmpty(sub)) {
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
int idx = 0;
|
||||
while ((idx = indexOf(str, sub, idx)) != INDEX_NOT_FOUND) {
|
||||
count++;
|
||||
idx += sub.length();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
|
||||
*
|
||||
* @param cs the {@code CharSequence} to be processed
|
||||
* @param searchChar the {@code CharSequence} to be searched for
|
||||
* @param start the start index
|
||||
* @return the index where the search sequence was found
|
||||
*/
|
||||
private static int indexOf(CharSequence cs, CharSequence searchChar, int start) {
|
||||
return cs.toString().indexOf(searchChar.toString(), start);
|
||||
}
|
||||
|
||||
private Strings() {
|
||||
// prevented from constructing objects
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user