1 /** 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * SPDX-License-Identifier: Apache-2.0. 4 */ 5 package software.amazon.awssdk.crt.mqtt5.packets; 6 7 import java.util.List; 8 import java.util.Map; 9 import java.util.function.Function; 10 import java.util.stream.Collectors; 11 import java.util.stream.Stream; 12 13 /** 14 * Data model of an <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205">MQTT5 DISCONNECT</a> packet. 15 */ 16 public class DisconnectPacket { 17 18 private DisconnectReasonCode reasonCode = DisconnectReasonCode.NORMAL_DISCONNECTION; 19 private Long sessionExpiryIntervalSeconds; 20 private String reasonString; 21 private List<UserProperty> userProperties; 22 private String serverReference; 23 24 /** 25 * Returns a value indicating the reason that the sender is closing the connection 26 * 27 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208">MQTT5 Disconnect Reason Code</a> 28 * 29 * @return Value indicating the reason that the sender is closing the connection 30 */ getReasonCode()31 public DisconnectReasonCode getReasonCode() { 32 return this.reasonCode; 33 } 34 35 /** 36 * Returns a change to the session expiry interval negotiated at connection time as part of the disconnect. Only 37 * valid for DisconnectPackets sent from client to server. It is not valid to attempt to change session expiry 38 * from zero to a non-zero value. 39 * 40 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901211">MQTT5 Session Expiry Interval</a>. 41 * 42 * @return A change to the session expiry interval negotiated at connection time as part of the disconnect. 43 */ getSessionExpiryIntervalSeconds()44 public Long getSessionExpiryIntervalSeconds() { 45 return this.sessionExpiryIntervalSeconds; 46 } 47 48 /** 49 * Returns additional diagnostic information about the reason that the sender is closing the connection 50 * 51 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901212">MQTT5 Reason String</a> 52 * 53 * @return Additional diagnostic information about the reason that the sender is closing the connection 54 */ getReasonString()55 public String getReasonString() { 56 return this.reasonString; 57 } 58 59 /** 60 * Returns a list of MQTT5 user properties included with the packet. 61 * 62 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901213">MQTT5 User Property</a> 63 * 64 * @return List of MQTT5 user properties included with the packet. 65 */ getUserProperties()66 public List<UserProperty> getUserProperties() { 67 return this.userProperties; 68 } 69 70 /** 71 * Returns a property indicating an alternate server that the client may temporarily or permanently attempt 72 * to connect to instead of the configured endpoint. Will only be set if the reason code indicates another 73 * server may be used (ServerMoved, UseAnotherServer). 74 * 75 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901214">MQTT5 Server Reference</a> 76 * 77 * @return Property indicating an alternate server that the client may temporarily or permanently attempt 78 * to connect to instead of the configured endpoint. 79 */ getServerReference()80 public String getServerReference() { 81 return this.serverReference; 82 } 83 84 /** 85 * Creates a DisconnectPacket instance using the provided DisconnectPacketBuilder. 86 */ DisconnectPacket(DisconnectPacketBuilder builder)87 private DisconnectPacket(DisconnectPacketBuilder builder) { 88 this.reasonCode = builder.reasonCode; 89 this.sessionExpiryIntervalSeconds = builder.sessionExpiryIntervalSeconds; 90 this.reasonString = builder.reasonString; 91 this.userProperties = builder.userProperties; 92 this.serverReference = builder.serverReference; 93 } 94 DisconnectPacket()95 private DisconnectPacket() {} 96 97 /** 98 * A native, JNI-only helper function for more easily setting the reason code 99 * @param reasonCode A int representing the reason code 100 */ nativeAddDisconnectReasonCode(int reasonCode)101 private void nativeAddDisconnectReasonCode(int reasonCode) { 102 this.reasonCode = DisconnectReasonCode.getEnumValueFromInteger(reasonCode); 103 } 104 105 /******************************************************************************* 106 * builder 107 ******************************************************************************/ 108 109 /** 110 * Reason code inside DisconnectPackets. Helps determine why a connection was terminated. 111 * 112 * Enum values match <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208">MQTT5 spec</a> encoding values. 113 */ 114 public enum DisconnectReasonCode { 115 116 /** 117 * Returned when the remote endpoint wishes to disconnect normally. Will not trigger the publish of a Will message if a 118 * Will message was configured on the connection. 119 * 120 * May be sent by the client or server. 121 */ 122 NORMAL_DISCONNECTION(0), 123 124 /** 125 * Returns when the client wants to disconnect but requires that the server publish the Will message configured 126 * on the connection. 127 * 128 * May only be sent by the client. 129 */ 130 DISCONNECT_WITH_WILL_MESSAGE(4), 131 132 /** 133 * Returned when the connection was closed but the sender does not want to specify a reason or none 134 * of the other reason codes apply. 135 * 136 * May be sent by the client or the server. 137 */ 138 UNSPECIFIED_ERROR(128), 139 140 /** 141 * Indicates the remote endpoint received a packet that does not conform to the MQTT specification. 142 * 143 * May be sent by the client or the server. 144 */ 145 MALFORMED_PACKET(129), 146 147 /** 148 * Returned when an unexpected or out-of-order packet was received by the remote endpoint. 149 * 150 * May be sent by the client or the server. 151 */ 152 PROTOCOL_ERROR(130), 153 154 /** 155 * Returned when a valid packet was received by the remote endpoint, but could not be processed by the current implementation. 156 * 157 * May be sent by the client or the server. 158 */ 159 IMPLEMENTATION_SPECIFIC_ERROR(131), 160 161 /** 162 * Returned when the remote endpoint received a packet that represented an operation that was not authorized within 163 * the current connection. 164 * 165 * May only be sent by the server. 166 */ 167 NOT_AUTHORIZED(135), 168 169 /** 170 * Returned when the server is busy and cannot continue processing packets from the client. 171 * 172 * May only be sent by the server. 173 */ 174 SERVER_BUSY(137), 175 176 /** 177 * Returned when the server is shutting down. 178 * 179 * May only be sent by the server. 180 */ 181 SERVER_SHUTTING_DOWN(139), 182 183 /** 184 * Returned when the server closes the connection because no packet from the client has been received in 185 * 1.5 times the KeepAlive time set when the connection was established. 186 * 187 * May only be sent by the server. 188 */ 189 KEEP_ALIVE_TIMEOUT(141), 190 191 /** 192 * Returned when the server has established another connection with the same client ID as a client's current 193 * connection, causing the current client to become disconnected. 194 * 195 * May only be sent by the server. 196 */ 197 SESSION_TAKEN_OVER(142), 198 199 /** 200 * Returned when the topic filter name is correctly formed but not accepted by the server. 201 * 202 * May only be sent by the server. 203 */ 204 TOPIC_FILTER_INVALID(143), 205 206 /** 207 * Returned when topic name is correctly formed, but is not accepted. 208 * 209 * May be sent by the client or the server. 210 */ 211 TOPIC_NAME_INVALID(144), 212 213 /** 214 * Returned when the remote endpoint reached a state where there were more in-progress QoS1+ publishes then the 215 * limit it established for itself when the connection was opened. 216 * 217 * May be sent by the client or the server. 218 */ 219 RECEIVE_MAXIMUM_EXCEEDED(147), 220 221 /** 222 * Returned when the remote endpoint receives a PublishPacket that contained a topic alias greater than the 223 * maximum topic alias limit that it established for itself when the connection was opened. 224 * 225 * May be sent by the client or the server. 226 */ 227 TOPIC_ALIAS_INVALID(148), 228 229 /** 230 * Returned when the remote endpoint received a packet whose size was greater than the maximum packet size limit 231 * it established for itself when the connection was opened. 232 * 233 * May be sent by the client or the server. 234 */ 235 PACKET_TOO_LARGE(149), 236 237 /** 238 * Returned when the remote endpoint's incoming data rate was too high. 239 * 240 * May be sent by the client or the server. 241 */ 242 MESSAGE_RATE_TOO_HIGH(150), 243 244 /** 245 * Returned when an internal quota of the remote endpoint was exceeded. 246 * 247 * May be sent by the client or the server. 248 */ 249 QUOTA_EXCEEDED(151), 250 251 /** 252 * Returned when the connection was closed due to an administrative action. 253 * 254 * May be sent by the client or the server. 255 */ 256 ADMINISTRATIVE_ACTION(152), 257 258 /** 259 * Returned when the remote endpoint received a packet where payload format did not match the format specified 260 * by the payload format indicator. 261 * 262 * May be sent by the client or the server. 263 */ 264 PAYLOAD_FORMAT_INVALID(153), 265 266 /** 267 * Returned when the server does not support retained messages. 268 * 269 * May only be sent by the server. 270 */ 271 RETAIN_NOT_SUPPORTED(154), 272 273 /** 274 * Returned when the client sends a QoS that is greater than the maximum QOS established when the connection was 275 * opened. 276 * 277 * May only be sent by the server. 278 */ 279 QOS_NOT_SUPPORTED(155), 280 281 /** 282 * Returned by the server to tell the client to temporarily use a different server. 283 * 284 * May only be sent by the server. 285 */ 286 USE_ANOTHER_SERVER(156), 287 288 /** 289 * Returned by the server to tell the client to permanently use a different server. 290 * 291 * May only be sent by the server. 292 */ 293 SERVER_MOVED(157), 294 295 /** 296 * Returned by the server to tell the client that shared subscriptions are not supported on the server. 297 * 298 * May only be sent by the server. 299 */ 300 SHARED_SUBSCRIPTIONS_NOT_SUPPORTED(158), 301 302 /** 303 * Returned when the server disconnects the client due to the connection rate being too high. 304 * 305 * May only be sent by the server. 306 */ 307 CONNECTION_RATE_EXCEEDED(159), 308 309 /** 310 * Returned by the server when the maximum connection time authorized for the connection was exceeded. 311 * 312 * May only be sent by the server. 313 */ 314 MAXIMUM_CONNECT_TIME(160), 315 316 /** 317 * Returned by the server when it received a SubscribePacket with a subscription identifier, but the server does 318 * not support subscription identifiers. 319 * 320 * May only be sent by the server. 321 */ 322 SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED (161), 323 324 /** 325 * Returned by the server when it received a SubscribePacket with a wildcard topic filter, but the server does 326 * not support wildcard topic filters. 327 * 328 * May only be sent by the server. 329 */ 330 WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED(162); 331 332 private int reasonCode; 333 DisconnectReasonCode(int code)334 private DisconnectReasonCode(int code) { 335 reasonCode = code; 336 } 337 338 /** 339 * @return The native enum integer value associated with this Java enum value 340 */ getValue()341 public int getValue() { 342 return reasonCode; 343 } 344 345 /** 346 * Creates a Java DisconnectReasonCode enum value from a native integer value. 347 * 348 * @param value native integer value for DisconnectReasonCode 349 * @return a new DisconnectReasonCode value 350 */ getEnumValueFromInteger(int value)351 public static DisconnectReasonCode getEnumValueFromInteger(int value) { 352 DisconnectReasonCode enumValue = enumMapping.get(value); 353 if (enumValue != null) { 354 return enumValue; 355 } 356 throw new RuntimeException("Illegal DisconnectReasonCode"); 357 } 358 buildEnumMapping()359 private static Map<Integer, DisconnectReasonCode> buildEnumMapping() { 360 return Stream.of(DisconnectReasonCode.values()) 361 .collect(Collectors.toMap(DisconnectReasonCode::getValue, Function.identity())); 362 } 363 364 private static Map<Integer, DisconnectReasonCode> enumMapping = buildEnumMapping(); 365 } 366 367 /** 368 * A class to that allows for the creation of a DisconnectPacket. Set all of the settings you want in the 369 * packet and then use the build() function to get a DisconnectPacket populated with the settings 370 * defined in the builder. 371 */ 372 static final public class DisconnectPacketBuilder { 373 374 private DisconnectReasonCode reasonCode = DisconnectReasonCode.NORMAL_DISCONNECTION; 375 private Long sessionExpiryIntervalSeconds; 376 private String reasonString; 377 private List<UserProperty> userProperties; 378 private String serverReference; 379 380 /** 381 * Sets the value indicating the reason that the sender is closing the connection 382 * 383 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208">MQTT5 Disconnect Reason Code</a> 384 * 385 * @param reasonCode Value indicating the reason that the sender is closing the connection 386 * @return The DisconnectPacketBuilder after setting the reason code. 387 */ withReasonCode(DisconnectReasonCode reasonCode)388 public DisconnectPacketBuilder withReasonCode(DisconnectReasonCode reasonCode) { 389 this.reasonCode = reasonCode; 390 return this; 391 } 392 393 /** 394 * Sets the change to the session expiry interval negotiated at connection time as part of the disconnect. Only 395 * valid for DisconnectPackets sent from client to server. It is not valid to attempt to change session expiry 396 * from zero to a non-zero value. 397 * 398 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901211">MQTT5 Session Expiry Interval</a> 399 * 400 * @param sessionExpiryIntervalSeconds the session expiry interval negotiated at connection time as part of the disconnect 401 * @return The DisconnectPacketBuilder after setting the session expiry interval. 402 */ withSessionExpiryIntervalSeconds(long sessionExpiryIntervalSeconds)403 public DisconnectPacketBuilder withSessionExpiryIntervalSeconds(long sessionExpiryIntervalSeconds) { 404 this.sessionExpiryIntervalSeconds = sessionExpiryIntervalSeconds; 405 return this; 406 } 407 408 /** 409 * Sets the additional diagnostic information about the reason that the sender is closing the connection 410 * 411 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901212">MQTT5 Reason String</a> 412 * 413 * @param reasonString Additional diagnostic information about the reason that the sender is closing the connection 414 * @return The DisconnectPacketBuilder after setting the reason string. 415 */ withReasonString(String reasonString)416 public DisconnectPacketBuilder withReasonString(String reasonString) { 417 this.reasonString = reasonString; 418 return this; 419 } 420 421 /** 422 * Sets the list of MQTT5 user properties included with the packet. 423 * 424 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901213">MQTT5 User Property</a> 425 * 426 * @param userProperties List of MQTT5 user properties included with the packet. 427 * @return The DisconnectPacketBuilder after setting the user properties. 428 */ withUserProperties(List<UserProperty> userProperties)429 public DisconnectPacketBuilder withUserProperties(List<UserProperty> userProperties) { 430 this.userProperties = userProperties; 431 return this; 432 } 433 434 /** 435 * Sets the property indicating an alternate server that the client may temporarily or permanently attempt 436 * to connect to instead of the configured endpoint. Will only be set if the reason code indicates another 437 * server may be used (ServerMoved, UseAnotherServer). 438 * 439 * See <a href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901214">MQTT5 Server Reference</a> 440 * 441 * @param serverReference Property indicating an alternate server that the client may temporarily or permanently 442 * attempt to connect to instead of the configured endpoint. 443 * @return The DisconnectPacketBuilder after setting the server reference. 444 */ withServerReference(String serverReference)445 public DisconnectPacketBuilder withServerReference(String serverReference) { 446 this.serverReference = serverReference; 447 return this; 448 } 449 450 /** 451 * Creates a new DisconnectPacketBuilder so a DisconnectPacket can be created. 452 */ DisconnectPacketBuilder()453 public DisconnectPacketBuilder() {} 454 455 /** 456 * Creates a new DisconnectPacket using the settings set in the builder. 457 * @return The DisconnectPacket created from the builder 458 */ build()459 public DisconnectPacket build() 460 { 461 return new DisconnectPacket(this); 462 } 463 } 464 465 } 466