Skip to content

Commit

Permalink
New LastWillAndTestament Builder (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
pglombardo authored Jan 15, 2024
1 parent d05e435 commit 9fc832f
Show file tree
Hide file tree
Showing 7 changed files with 677 additions and 10 deletions.
37 changes: 36 additions & 1 deletion Documentation/docs/how-to/set-lwt.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ The Last Will and Testament support of MQTT can be used to notify subscribers th

For a more in-depth explanation, see [What is MQTT Last Will and Testament (LWT)? – MQTT Essentials: Part 9](https://www.hivemq.com/blog/mqtt-essentials-part-9-last-will-and-testament/).


# LastWillAndTestament

This example instantiates the `LastWillAndTestament` in the `HiveMQClientOption` class. This is then sent to the broker in the `connect` operation.

```csharp
// Specify the Last Will and Testament specifics in HiveMQClientOptions
var options = new HiveMQClientOptions
Expand All @@ -28,6 +33,36 @@ connectResult = await client.ConnectAsync().ConfigureAwait(false);
// unexpectedly disconnected or alternatively, if your client disconnects with `DisconnectWithWillMessage`
var disconnectOptions = new DisconnectOptions { ReasonCode = DisconnectReasonCode.DisconnectWithWillMessage };
var disconnectResult = await client.DisconnectAsync(disconnectOptions).ConfigureAwait(false);
``````
```

Because the client above disconnected with `DisconnectReasonCode.DisconnectWithWillMessage`, subscribers to the `last/will` topic will receive the Last Will and Testament message as specified above.

# LastWillAndTestament Builder Class

As an ease-of-use alternative, the HiveMQtt client offers a `LastWillAndTestamentBuilder` class to more easily define a last will and testament class.

```csharp

var lwt = new LastWillAndTestamentBuilder()
.WithTopic("last/will")
.WithPayload("last will message")
.WithQualityOfServiceLevel(QualityOfService.AtLeastOnceDelivery)
.WithContentType("application/text")
.WithResponseTopic("response/topic")
.WithCorrelationData(new byte[] { 1, 2, 3, 4, 5 })
.WithPayloadFormatIndicator(MQTT5PayloadFormatIndicator.UTF8Encoded)
.WithMessageExpiryInterval(100)
.WithUserProperty("userPropertyKey", "userPropertyValue")
.WithWillDelayInterval(1)
.Build();

// Setup & Connect the client with LWT
var options = new HiveMQClientOptions
{
LastWillAndTestament = lwt,
};

var client = new HiveMQClient(options);
connectResult = await client.ConnectAsync().ConfigureAwait(false);
```

3 changes: 0 additions & 3 deletions Source/HiveMQtt/Client/HiveMQClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ public HiveMQClient(HiveMQClientOptions? options = null)
/// <inheritdoc />
public List<Subscription> Subscriptions { get; } = new();

/// <inheritdoc />
internal MQTT5Properties? ConnectionProperties { get; }

/// <inheritdoc />
public bool IsConnected() => this.connectState == ConnectState.Connected;

Expand Down
2 changes: 1 addition & 1 deletion Source/HiveMQtt/Client/HiveMQClientOptionsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ namespace HiveMQtt.Client;
/// </summary>
public class HiveMQClientOptionsBuilder
{
private readonly HiveMQClientOptions options = new HiveMQClientOptions();
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private readonly HiveMQClientOptions options = new HiveMQClientOptions();

/// <summary>
/// Sets the address of the broker to connect to.
Expand Down
108 changes: 103 additions & 5 deletions Source/HiveMQtt/Client/LastWillAndTestament.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,97 @@
namespace HiveMQtt.Client;

using System.Text;
using HiveMQtt.Client.Exceptions;
using HiveMQtt.Client.Internal;
using HiveMQtt.MQTT5.Types;

/// <summary>
/// Represents a Last Will and Testament message.
/// </summary>
public class LastWillAndTestament
{
/// <summary>
/// Initializes a new instance of the <see cref="LastWillAndTestament"/> class.
/// <para>
/// This constructor is obsolete. Use the constructor that uses QualityOfService with a default value instead.
/// </para>
/// </summary>
/// <param name="topic">The topic of the Last Will and Testament.</param>
/// <param name="qos">The Quality of Service level for the Last Will and Testament.</param>
/// <param name="payload">The UTF-8 encoded payload of the Last Will and Testament.</param>
/// <param name="retain">A value indicating whether the Last Will and Testament should be retained by the MQTT broker when published.</param>
[Obsolete("Use the LastWillAndTestament constructor that uses QualityOfService with a default value instead.")]
public LastWillAndTestament(string topic, QualityOfService? qos, string payload, bool retain = false)
{
this.Topic = topic;
this.QoS = qos;

if (qos is null)
{
this.QoS = QualityOfService.AtMostOnceDelivery;
}
else
{
this.QoS = (QualityOfService)qos;
}

this.PayloadAsString = payload;
this.Retain = retain;
this.UserProperties = new Dictionary<string, string>();
}

/// <summary>
/// Initializes a new instance of the <see cref="LastWillAndTestament"/> class.
/// <para>
/// This constructor is obsolete. Use the constructor that uses QualityOfService with a default value instead.
/// </para>
/// </summary>
/// <param name="topic">The topic of the Last Will and Testament.</param>
/// <param name="qos">The Quality of Service level for the Last Will and Testament.</param>
/// <param name="payload">The byte payload of the Last Will and Testament.</param>
/// <param name="retain">A value indicating whether the Last Will and Testament should be retained by the MQTT broker when published.</param>
[Obsolete("Use the LastWillAndTestament constructor that uses QualityOfService with a default value instead.")]
public LastWillAndTestament(string topic, QualityOfService? qos, byte[] payload, bool retain = false)
{
this.Topic = topic;

if (qos is null)
{
this.QoS = QualityOfService.AtMostOnceDelivery;
}
else
{
this.QoS = (QualityOfService)qos;
}

this.Payload = payload;
this.Retain = retain;
this.UserProperties = new Dictionary<string, string>();
}

/// <summary>
/// Initializes a new instance of the <see cref="LastWillAndTestament"/> class.
/// </summary>
/// <param name="topic">The topic of the Last Will and Testament.</param>
/// <param name="payload">The UTF-8 encoded payload of the Last Will and Testament.</param>
/// <param name="qos">The Quality of Service level for the Last Will and Testament.</param>
/// <param name="retain">A value indicating whether the Last Will and Testament should be retained by the MQTT broker when published.</param>
public LastWillAndTestament(string topic, string payload, QualityOfService qos = QualityOfService.AtMostOnceDelivery, bool retain = false)
{
this.Topic = topic;
this.QoS = qos;
this.PayloadAsString = payload;
this.Retain = retain;
this.UserProperties = new Dictionary<string, string>();
}

/// <summary>
/// Initializes a new instance of the <see cref="LastWillAndTestament"/> class.
/// </summary>
/// <param name="topic">The topic of the Last Will and Testament.</param>
/// <param name="payload">The byte payload of the Last Will and Testament.</param>
/// <param name="qos">The Quality of Service level for the Last Will and Testament.</param>
/// <param name="retain">A value indicating whether the Last Will and Testament should be retained by the MQTT broker when published.</param>
public LastWillAndTestament(string topic, byte[] payload, QualityOfService qos = QualityOfService.AtMostOnceDelivery, bool retain = false)
{
this.Topic = topic;
this.QoS = qos;
Expand All @@ -44,12 +118,12 @@ public LastWillAndTestament(string topic, QualityOfService? qos, byte[] payload,
/// <summary>
/// Gets or sets the topic of this Publish.
/// </summary>
public string? Topic { get; set; }
public string Topic { get; set; }

/// <summary>
/// Gets or sets the Quality of Service level for this publish.
/// </summary>
public QualityOfService? QoS { get; set; }
public QualityOfService QoS { get; set; }

/// <summary>
/// Gets or sets the UTF-8 encoded payload of this Publish.
Expand Down Expand Up @@ -98,7 +172,7 @@ public string PayloadAsString
/// See <seealso href="https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901062">Will Delay Interval</seealso>.
/// </para>
/// </summary>
public Int64? WillDelayInterval { get; set; }
public long? WillDelayInterval { get; set; }

/// <summary>
/// Gets or sets a value indicating the format of the Payload.
Expand All @@ -108,7 +182,7 @@ public string PayloadAsString
/// <summary>
/// Gets or sets a value indicating the lifetime of the message in seconds.
/// </summary>
public Int64? MessageExpiryInterval { get; set; }
public long? MessageExpiryInterval { get; set; }

/// <summary>
/// Gets or sets a value indicating the content type of the Payload.
Expand All @@ -129,4 +203,28 @@ public string PayloadAsString
/// Gets or sets a Dictionary containing the User Properties to be sent with the Last Will and Testament message.
/// </summary>
public Dictionary<string, string> UserProperties { get; set; }

/// <summary>
/// Validates the LastWillAndTestament.
/// </summary>
/// <returns>A value indicating whether the LastWillAndTestament is valid.</returns>
/// <exception cref="HiveMQttClientException">Thrown if the LastWillAndTestament is not valid.</exception>
public bool Validate()
{
if (this.Topic is null)
{
throw new HiveMQttClientException("LastWillAndTestament requires a Topic: Topic must not be null");
}
else
{
Validator.ValidateTopicName(this.Topic);
}

if (this.Payload is null)
{
throw new HiveMQttClientException("LastWillAndTestament requires a Payload: Payload must not be null");
}

return true;
}
}
Loading

0 comments on commit 9fc832f

Please sign in to comment.