| <?xml version="1.0" encoding="UTF-8"?> |
| <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> |
| <channel> |
| <title>Eclipse Ditto Blog</title> |
| <description>Announcements, tutorials and examples around Eclipse Ditto and Digital Twins</description> |
| <link>https://www.eclipse.org/ditto/</link> |
| <atom:link href="https://www.eclipse.org/ditto/feed.xml" rel="self" type="application/rss+xml"/> |
| <pubDate>Mon, 22 Mar 2021 13:07:19 +0000</pubDate> |
| <lastBuildDate>Mon, 22 Mar 2021 13:07:19 +0000</lastBuildDate> |
| <generator>Jekyll v3.6.2</generator> |
| |
| <item> |
| <title>Use Eclipse Ditto with Azure IoT Hub as message broker</title> |
| <description><p>This blogpost is based upon Eclipse Ditto Version <strong>1.5.0</strong>, the Azure IoT Suite as of |
| <strong>2021-03-19</strong> and the azure-iot-device-client version <strong>1.29.2</strong>.</p> |
| |
| <h1 id="connecting-devices-to-eclipse-ditto-via-azure-iot-hub">Connecting devices to Eclipse Ditto via Azure IoT Hub</h1> |
| <p>This blog post elaborates on connecting and managing devices in Eclipse Ditto by using the Azure IoT Hub |
| as a message broker.</p> |
| |
| <p><img src="images/blog/2021-03-22-azure-iot-hub-integration-overview.png" alt="Connection Overview" /></p> |
| |
| <p>The basic functionality that can be used at the time of creating this blogpost are:</p> |
| |
| <ul> |
| <li>[D2C] Sending telemetry data from the device to update its Ditto digital-twin representation.</li> |
| <li>[D2C] Same ID enforcement based on the Azure IoT Hub device-id to prevent spoofing other digital-twins.</li> |
| <li>[C2D] Sending live-messages to the device.</li> |
| <li>[D2C] Sending feedback to live messages to the service.</li> |
| </ul> |
| |
| <h2 id="setting-up-connections-in-ditto">Setting up connections in Ditto</h2> |
| <p>The features described above will work with an “out-of-the-box” Azure IoT Hub subscription, |
| so no additional configuration is needed in the IoT Hub. In order to connect Ditto to the IoT Hub you have to set up |
| two AMQP 1.0 connections. One for receiving telemetry data, the other for sending live-messages and receiving |
| live-message feedback.</p> |
| |
| <h3 id="telemetry-connection">Telemetry Connection</h3> |
| |
| <p>This connection subscribes to telemetry messages, published by the Azure IoT Hub built-in “Event Hub like” endpoint.</p> |
| |
| <p>Adding an enforcement for the <code class="highlighter-rouge">{{ thing:id }}</code> based on the <code class="highlighter-rouge">{{ header:iothub-connection-device-id }}</code> prevents |
| applying a digital-twin update to the twin of another device (Device Spoofing).</p> |
| |
| <p>To establish this connection the placeholders below have to be substituted by:</p> |
| |
| <ul> |
| <li> |
| <p><code class="highlighter-rouge">{{userName}}</code>: The <code class="highlighter-rouge">SharedAccessKeyName</code> in your Event Hub-compatible endpoint (i.e. service).</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{password}}</code>: The <code class="highlighter-rouge">SharedAccessKey</code> in your Event Hub-compatible endpoint.</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{endpoint}}</code>: The <code class="highlighter-rouge">Endpoint</code> in your Event Hub-compatible endpoint (Cut leading “sb://” and trailing slash, |
| e.g.. ihsuprodblres055dednamespace.servicebus.windows.net).</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{entityPath}}</code>: The <code class="highlighter-rouge">EntitiyPath</code> in your Event Hub-compatible endpoint (e.g.. hubname-8584619-2e72252706).</p> |
| </li> |
| </ul> |
| |
| <p><em>Note: You can use the “service” IoT Hub policy instead of the “iothubowner” policy, since this is more restricitve, |
| and represents the actual use of Ditto as a northbound service.</em></p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"azure-example-connection-telemetry"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqp-10"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionStatus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"open"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"failoverEnabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqps://{{userName}}:{{password}}@{{endpoint}}:5671"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"source"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"addresses"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="s2">"{{entityPath}}/ConsumerGroups/$Default/Partitions/0"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"{{entityPath}}/ConsumerGroups/$Default/Partitions/1"</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"authorizationContext"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"ditto"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"enforcement"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"input"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{ header:iothub-connection-device-id }}"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="s2">"{{ thing:id }}"</span><span class="w"> |
| </span><span class="p">]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| <ul> |
| <li> |
| <p><a href="https://www.eclipse.org/ditto/connectivity-manage-connections.html">Further information on creating connections</a></p> |
| </li> |
| <li> |
| <p><a href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-messages-d2c">Further information on D2C messaging capabilities of Azure IoT Hub</a></p> |
| </li> |
| <li> |
| <p><a href="https://docs.microsoft.com/de-de/azure/iot-hub/iot-hub-devguide-messages-read-builtin">Further information on the built-in “event-hub like” endpoint of Azure IoT Hub</a></p> |
| </li> |
| </ul> |
| |
| <h3 id="message-connection">Message connection</h3> |
| |
| <p>This connection enables forwarding live messages to the Azure IoT Hub (which forwards it to the device) |
| and receiving feedback to these live-messages from the device.</p> |
| |
| <p>Adding the header-mapping <code class="highlighter-rouge">"message_id": "{{header:correlation-id}}"</code> enables Azure IoT Hub to correlate messages. |
| Adding the header-mapping <code class="highlighter-rouge">"to": "/devices/{{ header:ditto-message-thing-id }}/messages/deviceInbound"</code> is |
| necessary for correct message routing by Azure IoT Hub. The header <code class="highlighter-rouge">ditto-message-thing-id</code> will be set as a |
| default header by Ditto.</p> |
| |
| <p>To establish this connection the placeholders below have to be substituted:</p> |
| |
| <ul> |
| <li> |
| <p><code class="highlighter-rouge">{{userName}}</code>: The name of the chosen IoT Hub policy + “@sas.root.” + the name of your IoT Hub |
| (i.e. service@sas.root.my-hub).</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{hostName}}</code>: The Hostname of your IoT Hub (i.e. my-hub.azure-devices.net).</p> |
| </li> |
| <li> |
| <p><code class="highlighter-rouge">{{encodedSasToken}}</code>: An URL encoded SAS token. Information on how to generate a token can be found at |
| <a href="https://docs.microsoft.com/en-us/cli/azure/ext/azure-iot/iot/hub?view=azure-cli-latest#ext_azure_iot_az_iot_hub_generate_sas_token">az iot hub generate-sas-token.</a> |
| The generated token has to be additionally URL encoded (browser console -&gt; <code class="highlighter-rouge">encodeURI('{{generatedToken}}')</code>).</p> |
| </li> |
| </ul> |
| |
| <p><em>Note: The generated SAS token has a maximum TTL of 365 days, so the token has to be changed to a newly generated before expiry. |
| Otherwise, the connection tries to reconnect or closes automatically, when <code class="highlighter-rouge">failoverEnabled</code> is set to <code class="highlighter-rouge">false</code>.</em></p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"azure-example-connection-messages"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqp-10"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"connectionStatus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"open"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"failoverEnabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"amqps://{{userName}}:{{encodedSasToken}}@{{hostName}}:5671"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"target"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="p">{</span><span class="s2">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/messages/devicebound"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"topics"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> |
| </span><span class="s2">"_/_/things/live/messages"</span><span class="w"> |
| </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"authorizationContext"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"ditto"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"headerMapping"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"message_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{{header:correlation-id}}"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"to"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/devices/{{ header:ditto-message-thing-id }}/messages/deviceInbound"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p><strong>The java azure-iot-device-client currently can not be used to receive messages with JSON as body. |
| Thus, the messages’ payload has to be byte-encoded.</strong></p> |
| |
| <p>This can be achieved by configuring an outgoing JavaScript payload mapper in the message connection:</p> |
| <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">mapFromDittoProtocolMsg</span><span class="p">(</span> |
| <span class="nx">namespace</span><span class="p">,</span> |
| <span class="nx">id</span><span class="p">,</span> |
| <span class="nx">group</span><span class="p">,</span> |
| <span class="nx">channel</span><span class="p">,</span> |
| <span class="nx">criterion</span><span class="p">,</span> |
| <span class="nx">action</span><span class="p">,</span> |
| <span class="nx">path</span><span class="p">,</span> |
| <span class="nx">dittoHeaders</span><span class="p">,</span> |
| <span class="nx">value</span><span class="p">,</span> |
| <span class="nx">status</span><span class="p">,</span> |
| <span class="nx">extra</span> |
| <span class="p">)</span> <span class="p">{</span> |
| |
| <span class="kd">let</span> <span class="nx">headers</span> <span class="o">=</span> <span class="nx">dittoHeaders</span><span class="p">;</span> |
| <span class="kd">let</span> <span class="nx">textPayload</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> |
| <span class="kd">let</span> <span class="nx">bytePayload</span> <span class="o">=</span> <span class="nx">Ditto</span><span class="p">.</span><span class="nx">stringToArrayBuffer</span><span class="p">(</span><span class="nx">Ditto</span><span class="p">.</span><span class="nx">buildDittoProtocolMsg</span><span class="p">(</span><span class="nx">namespace</span><span class="p">,</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">group</span><span class="p">,</span> <span class="nx">channel</span><span class="p">,</span> <span class="nx">criterion</span><span class="p">,</span> <span class="nx">action</span><span class="p">,</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">dittoHeaders</span><span class="p">,</span> <span class="nx">value</span><span class="p">).</span><span class="nx">toString</span><span class="p">());</span> |
| <span class="kd">let</span> <span class="nx">contentType</span> <span class="o">=</span> <span class="s1">'application/octet-stream'</span><span class="p">;</span> |
| |
| <span class="k">return</span> <span class="nx">Ditto</span><span class="p">.</span><span class="nx">buildExternalMsg</span><span class="p">(</span> |
| <span class="nx">headers</span><span class="p">,</span> |
| <span class="nx">textPayload</span><span class="p">,</span> |
| <span class="nx">bytePayload</span><span class="p">,</span> |
| <span class="nx">contentType</span> |
| <span class="p">);</span> |
| <span class="p">}</span> |
| </code></pre></div></div> |
| |
| <ul> |
| <li> |
| <p><a href="https://www.eclipse.org/ditto/connectivity-manage-connections.html">Further information on creating connections</a></p> |
| </li> |
| <li> |
| <p><a href="https://www.eclipse.org/ditto/connectivity-mapping.html">Further information on payload-mapping</a></p> |
| </li> |
| <li> |
| <p><a href="https://docs.microsoft.com/de-de/azure/iot-hub/iot-hub-devguide-messages-c2d">Further information on C2D messaging capabilities of Azure IoT Hub</a></p> |
| </li> |
| </ul> |
| |
| <h2 id="possible-improvements">Possible improvements</h2> |
| |
| <p>Some features of Ditto could be used in combination with Azure IoT Hub with some adjustments. These include:</p> |
| |
| <ul> |
| <li>Using the <code class="highlighter-rouge">ImplicitThingCreationMapper</code> to implicitly create a new thing when a new device is registered in Azure IoT Hub.</li> |
| <li>Using the ConnectionStatusMapper to update the ConnectionStatus of things, when their devices disconnect from Azure IoT Hub.</li> |
| <li>[C2D] Directly invoke methods on the device (Direct Method Invocation).</li> |
| </ul> |
| |
| <h3 id="using-the-implicitthingcreation-and-connectionstatus-features-based-on-azure-iot-hub-events">Using the ImplicitThingCreation and ConnectionStatus features based on Azure IoT Hub events</h3> |
| |
| <p>Azure IoT Hub has the possibility to publish events for status changes of device connections and the creation/removal of new devices. |
| These events are published via an Azure EventGrid to another chosen Azure application. |
| By publishing these events to an Azure Event Hub, a Ditto AMQP connection can subscribe for them.</p> |
| |
| <p>The payload-mappers for <code class="highlighter-rouge">ImplicitThingCreation</code> and <code class="highlighter-rouge">ConnectionStatus</code> could be adjusted to handle such event messages and |
| create new things or update the <code class="highlighter-rouge">ConnectionStatus</code> feature depending on the received messages.</p> |
| |
| <ul> |
| <li><a href="https://docs.microsoft.com/de-de/azure/event-grid/event-schema-iot-hub?tabs=event-grid-event-schema">Further information on the events published by Azure IoT Hub</a></li> |
| </ul> |
| |
| <h3 id="using-direct-method-invocation">Using Direct Method Invocation</h3> |
| |
| <p>Azure IoT Hub provides an endpoint for directly invoking methods on a device. This can be compared to live-commands. |
| Direct Method Invocation can only be done via HTTP. For authentication SAS has to be used. |
| This authentication mechanism, however, is not yet implemented for HTTP Push of Eclipse Ditto |
| connections.</p> |
| |
| <ul> |
| <li><a href="https://docs.microsoft.com/de-de/azure/iot-hub/iot-hub-devguide-direct-methods">Further information on direct method invocations</a></li> |
| </ul> |
| |
| <h3 id="implementing-an-automatic-refresh-mechanism-for-sasl-tokens">Implementing an automatic refresh mechanism for SASL tokens</h3> |
| |
| <p>The <code class="highlighter-rouge">connectionString</code> provided by an Azure IoT Hub device’s policy could be used to generate and refresh a SASL token. |
| This would require a new connection setting, which could store such a <code class="highlighter-rouge">connectionString</code>, and an algorithm, which can |
| generate a SAS token out of that string.</p> |
| |
| <h2 id="getting-started">Getting started</h2> |
| |
| <p>To get started using Azure IoT Hub as a message broker for Eclipse Ditto, the |
| <a href="https://github.com/eclipse/ditto-examples/tree/master/azure/azure-iot-hub-device-simulator">Azure IoT Hub Device Simulator Example</a> |
| is a good entry point.</p> |
| </description> |
| <pubDate>Mon, 22 Mar 2021 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2021-03-22-azure-iot-hub-integration.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2021-03-22-azure-iot-hub-integration.html</guid> |
| |
| <category>blog</category> |
| |
| <category>architecture</category> |
| |
| <category>connection</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Support merge functionality for things resources</title> |
| <description><p>With the upcoming release of Eclipse Ditto <strong>version 2.0.0</strong> it will be possible to merge existing things and their subresources.</p> |
| |
| <h2 id="merge-functionality-for-things-resources">Merge functionality for things resources</h2> |
| <p>Ditto now supports merging of existing things and all of its subresources with the provided payload in the request. |
| This can be done by using the HTTP API with the PATCH method, via the Ditto protocol, and also by using the Ditto Java Client. |
| For all three ways there is an example provided in this blog post.</p> |
| |
| <p>In contrast to the already existing PUT resource, this new functionality <strong>allows partial updates</strong> on a thing and all its subresources. |
| To get more into detail, from now on it is possible to add or update attributes, and a feature property at the same time, |
| without overwriting the complete thing. Another use case might be to update several feature properties within a single request |
| and let all other parts of the thing untouched.</p> |
| |
| <p>Ditto uses the <a href="https://tools.ietf.org/html/rfc7396">JSON Merge Patch</a> semantics to merge the request body |
| with the existing thing. In short, a JSON merge patch resembles the original JSON structure of a thing, and |
| the fields provided in the patch are added, updated, or deleted in the existing thing.</p> |
| |
| <p>Please be aware that <code class="highlighter-rouge">null</code> values have a special meaning when applying a merge patch. A <code class="highlighter-rouge">null</code> value indicates |
| the removal of existing fields in the updated thing. |
| For more details and examples, please refer to <a href="https://tools.ietf.org/html/rfc7396">RFC-7396</a>.</p> |
| |
| <h3 id="permissions-to-merge-things-and-things-subresources">Permissions to merge things and things subresources</h3> |
| <p>In order to execute such a merge operation, the authorized subject needs to have WRITE permission at all resources |
| that should change by the merge. Consequently, if the permission is missing for some part of the merge, |
| the merge is rejected and <strong>not</strong> applied at all.</p> |
| |
| <h2 id="examples">Examples</h2> |
| |
| <p>To demonstrate the new merge feature, we assume that the following thing already exists:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thingId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"policyId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer-policy"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer:0.1.0"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"manufacturer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ACME demo corp."</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Berlin, main floor"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"serialno"</span><span class="p">:</span><span class="w"> </span><span class="s2">"42"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"model"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Speaking coffee machine"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"coffee-brewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"com.acme:coffeebrewer:0.1.0"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewed-coffees"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"brewingTemp"</span><span class="p">:</span><span class="w"> </span><span class="mi">87</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">44</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"timeoutSeconds"</span><span class="p">:</span><span class="w"> </span><span class="mi">6000</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"waterAmount"</span><span class="p">:</span><span class="w"> </span><span class="mi">731</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"temperature"</span><span class="p">:</span><span class="w"> </span><span class="mi">44</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h3 id="permissions-to-execute-the-example">Permissions to execute the example</h3> |
| <p>For this example, the authorized subject needs to have unrestricted WRITE permissions on all affected paths |
| of the JSON merge patch: <em>attributes/manufacturingYear</em>, <em>features/water-tank/properties/configuration/smartMode</em>, and |
| <em>features/water-tank/properties/configuration/tempToHold</em>. |
| The WRITE permission must not be revoked on any level further down the hierarchy. |
| Consequently, it is also sufficient for the authorized subject to have unrestricted WRITE permission at root level or |
| unrestricted WRITE permission at /attributes and /features etc.</p> |
| |
| <p>The following subparts will show how to use the merge feature via the HTTP API, the Ditto protocol |
| and the Ditto Java Client.</p> |
| |
| <h3 id="merge-via-http-api">Merge via HTTP API</h3> |
| <p>An existing thing can be merged via the HTTP API using the <em>PATCH</em> method with the following request body. |
| Notice that this request will add the “manufacturingYear” to the attributes, update the “tempToHold” to 50 and |
| delete the “smartMode” key from the feature property “water-tank”.</p> |
| |
| <p>The <code class="highlighter-rouge">Content-Type</code> header for this request must be <em>application/merge-patch+json</em>.</p> |
| |
| <p>PATCH /things/com.acme:coffeebrewer</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"manufacturingYear"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>After the request was successfully performed the thing will look like this:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thingId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"policyId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer-policy"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer:0.1.0"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"manufacturer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ACME demo corp."</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"manufacturingYear"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Berlin, main floor"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"serialno"</span><span class="p">:</span><span class="w"> </span><span class="s2">"42"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"model"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Speaking coffee machine"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"coffee-brewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"com.acme:coffeebrewer:0.1.0"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewed-coffees"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewingTemp"</span><span class="p">:</span><span class="w"> </span><span class="mi">87</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"timeoutSeconds"</span><span class="p">:</span><span class="w"> </span><span class="mi">6000</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"waterAmount"</span><span class="p">:</span><span class="w"> </span><span class="mi">731</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"temperature"</span><span class="p">:</span><span class="w"> </span><span class="mi">44</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>It is also possible to apply the <em>PATCH</em> method to all subresources of a thing, e.g. merging only the attributes of a thing. <br /> |
| Check out the newly added <em>PATCH</em> resources in our <a href="http-api-doc.html">HTTP API</a>.</p> |
| |
| <h3 id="merge-via-ditto-protocol">Merge via Ditto protocol</h3> |
| <p>It is also possible to merge the existing thing via the Ditto protocol. |
| Applying the following Ditto merge command to the existing thing will lead to the same result as in the above HTTP example.</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme/coffeebrewer/things/twin/commands/merge"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application/merge-patch+json"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thingId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme:coffeebrewer"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"manufacturingYear"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>Another Ditto protocol example to merge a feature property:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme/coffeebrewer/things/twin/commands/merge"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application/merge-patch+json"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/features/coffee-brewer/properties/brewed-coffees"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h3 id="using-the-ditto-client-to-merge-things">Using the ditto-client to merge things</h3> |
| <p>The merge functionality is also supported via the <a href="client-sdk-java.html">Ditto Java Client</a> |
| with the upcoming (<strong>Ditto Java Client version 2.0.0</strong>).</p> |
| |
| <p>Example for merging a thing with the Ditto Java Client:</p> |
| |
| <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="n">String</span> <span class="n">THING_ID</span> <span class="o">=</span> <span class="s">"com.acme:coffeebrewer"</span><span class="o">;</span> |
| <span class="kd">final</span> <span class="n">String</span> <span class="n">FEATURE_ID</span> <span class="o">=</span> <span class="s">"water-tank"</span><span class="o">;</span> |
| <span class="kd">final</span> <span class="n">JsonPointer</span> <span class="n">ATTRIBUTE_KEY</span> <span class="o">=</span> <span class="n">JsonFactory</span><span class="o">.</span><span class="na">newPointer</span><span class="o">(</span><span class="s">"manufacturingYear"</span><span class="o">);</span> |
| <span class="kd">final</span> <span class="n">String</span> <span class="n">ATTRIBUTE_VALUE</span> <span class="o">=</span> <span class="s">"2020"</span><span class="o">;</span> |
| <span class="kd">final</span> <span class="n">Feature</span> <span class="n">FEATURE</span> <span class="o">=</span> <span class="n">ThingsModelFactory</span><span class="o">.</span><span class="na">newFeatureBuilder</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">withId</span><span class="o">(</span><span class="n">FEATURE_ID</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">properties</span><span class="o">(</span><span class="n">ThingsModelFactory</span><span class="o">.</span><span class="na">newFeaturePropertiesBuilder</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="s">"smartMode"</span><span class="o">,</span> <span class="kc">false</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="s">"tempToHold"</span><span class="o">,</span> <span class="mi">50</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">())</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">();</span> |
| |
| <span class="kd">final</span> <span class="n">Thing</span> <span class="n">THING</span> <span class="o">=</span> <span class="n">ThingsModelFactory</span><span class="o">.</span><span class="na">newThingBuilder</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">setId</span><span class="o">(</span><span class="n">THING_ID</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">setAttribute</span><span class="o">(</span><span class="n">ATTRIBUTE_KEY_NEW</span><span class="o">,</span> <span class="n">JsonFactory</span><span class="o">.</span><span class="na">newValue</span><span class="o">(</span><span class="n">ATTRIBUTE_VALUE</span><span class="o">))</span> |
| <span class="o">.</span><span class="na">setFeature</span><span class="o">(</span><span class="n">FEATURE</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">();</span> |
| |
| <span class="c1">// initialize the ditto-client</span> |
| <span class="kd">final</span> <span class="n">DittoClient</span> <span class="n">dittoClient</span> <span class="o">=</span> <span class="o">...</span> <span class="o">;</span> |
| |
| <span class="n">dittoClient</span><span class="o">.</span><span class="na">twin</span><span class="o">().</span><span class="na">merge</span><span class="o">(</span><span class="n">THING_ID</span><span class="o">,</span> <span class="n">THING</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">whenComplete</span><span class="o">(((</span><span class="n">adaptable</span><span class="o">,</span> <span class="n">throwable</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> |
| <span class="k">if</span> <span class="o">(</span><span class="n">throwable</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> |
| <span class="n">LOGGER</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"Received error while sending MergeThing: '{}' "</span><span class="o">,</span> <span class="n">throwable</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span> |
| <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> |
| <span class="n">LOGGER</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Received response for MergeThing: '{}'"</span><span class="o">,</span> <span class="n">adaptable</span><span class="o">);</span> |
| <span class="o">}</span> |
| <span class="o">}));</span> |
| </code></pre></div></div> |
| |
| <p>After running this code snippet, the existing thing should look like the above result for the HTTP example.</p> |
| |
| <p>More examples for merging an attribute, all attributes and a feature property via Ditto Java Client.</p> |
| <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// initialize the ditto-client</span> |
| <span class="kd">final</span> <span class="n">DittoClient</span> <span class="n">dittoClient</span> <span class="o">=</span> <span class="o">...</span> <span class="o">;</span> |
| |
| <span class="c1">// merge attribute</span> |
| <span class="n">dittoClient</span><span class="o">.</span><span class="na">twin</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">forId</span><span class="o">(</span><span class="s">"com.acme:coffeebrewer"</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">mergeAttribute</span><span class="o">(</span><span class="s">"manufacturingYear"</span><span class="o">,</span> <span class="s">"2021"</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">whenComplete</span><span class="o">(...);</span> |
| |
| <span class="c1">// merge attributes</span> |
| <span class="n">dittoClient</span><span class="o">.</span><span class="na">twin</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">forId</span><span class="o">(</span><span class="s">"com.acme:coffeebrewer"</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">mergeAttributes</span><span class="o">(</span><span class="n">JsonObject</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">().</span><span class="na">set</span><span class="o">(</span><span class="s">"manufacturingYear"</span><span class="o">,</span> <span class="s">"2021"</span><span class="o">).</span><span class="na">build</span><span class="o">())</span> |
| <span class="o">.</span><span class="na">whenComplete</span><span class="o">(...);</span> |
| |
| <span class="c1">// merge feature property</span> |
| <span class="n">dittoClient</span><span class="o">.</span><span class="na">twin</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">forFeature</span><span class="o">(</span><span class="s">"com.acme:coffeebrewer"</span><span class="o">,</span> <span class="s">"water-tank"</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">mergeProperty</span><span class="o">(</span><span class="s">"configuration/smartMode"</span><span class="o">,</span> <span class="kc">false</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">whenComplete</span><span class="o">(...);</span> |
| </code></pre></div></div> |
| |
| <h2 id="merge-events">Merge events</h2> |
| <p>In this section we want to cover the new <code class="highlighter-rouge">ThingMerged</code> event which will be emitted after successfully applying an <code class="highlighter-rouge">MergeThing</code> command. |
| For every HTTP request or Ditto protocol message which performs a merge operation on a thing there will be sent out |
| exactly one <code class="highlighter-rouge">ThingMerged</code> event. This event contains the <strong>path</strong> and the <strong>value</strong> of the merge operation. |
| The <strong>path</strong> describes on which level of the thing the <strong>value</strong> was merged.</p> |
| |
| <h3 id="merge-event-example">Merge event example</h3> |
| <p>Let’s assume we want to patch/merge multiple feature properties at once. |
| PATCH /things/com.acme:coffeebrewer/features</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"coffee-brewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewed-coffees"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">30</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>The following <code class="highlighter-rouge">ThingMerged</code> event is emitted:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme/coffeebrewer/things/twin/events/merged"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application/merge-patch+json"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/features"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"coffee-brewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"brewed-coffees"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"water-tank"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"smartMode"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"tempToHold"</span><span class="p">:</span><span class="w"> </span><span class="mi">30</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"revision"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-02-04T09:42:39Z"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions towards this new functionality.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| |
| </description> |
| <pubDate>Thu, 04 Feb 2021 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2021-02-04-merge-feature.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2021-02-04-merge-feature.html</guid> |
| |
| <category>blog</category> |
| |
| <category>http</category> |
| |
| <category>protocol</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Policy actions: token based subject activation</title> |
| <description><p>The upcoming version of Eclipse Ditto <strong>2.0.0</strong> will be enhanced with the ability to |
| <a href="basic-policy.html#actions">alter policies based on policy actions</a>.</p> |
| |
| <h2 id="policy-actions">Policy actions</h2> |
| |
| <p>This new concept of <a href="basic-policy.html#actions">Policy actions</a> allows upfront defined modifications to policies without |
| the need for the one invoking the action to have “WRITE” permissions granted on the policy.</p> |
| |
| <h2 id="token-based-activation-of-subject">Token based activation of subject</h2> |
| |
| <p>Together with the concept of actions, a first action named |
| <a href="basic-policy.html#action-activatetokenintegration"><code class="highlighter-rouge">activateTokenIntegration</code></a> is added.<br /> |
| This action</p> |
| <ul> |
| <li>only works when using <a href="#" data-toggle="tooltip" data-original-title="JSON Web Token (JWT)">JWT</a> |
| based authentication issued by Google or other OpenID Connect providers as |
| <a href="installation-operating.html#openid-connect">documented in the installation/operation guide</a></li> |
| <li>checks whether the <a href="basic-auth.html#authenticated-subjects">authenticated subjects</a> which invoked the action have the |
| permission to <code class="highlighter-rouge">EXECUTE</code> the action on a policy entry</li> |
| <li>checks whether the <a href="basic-auth.html#authenticated-subjects">authenticated subjects</a> which invoked the action have at |
| least some kind of <code class="highlighter-rouge">READ</code> permission to any <code class="highlighter-rouge">thing:/</code> resource in a policy entry</li> |
| </ul> |
| |
| <p>When all the conditions were met for a policy entry, the action will inject a new <a href="basic-policy.html#subjects">subject</a> |
| into the matched policy entry which by default (the |
| <a href="basic-policy.html#action-activatetokenintegration">pattern is configurable</a>) is the following. |
| This syntax uses <a href="basic-placeholders.html">placeholders</a> in order to extract information from the authenticated JWT and |
| the policy entry:</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |
| integration:{{policy-entry:label}}:{{jwt:aud}} |
| |
| </code></pre></div></div> |
| |
| <p>The value of the injected subject will contain the <a href="basic-policy.html#expiring-policy-subjects">expiry</a> timestamp |
| copied from the JWT <code class="highlighter-rouge">"exp"</code> (the expiration time of the token) claim.</p> |
| |
| <h2 id="example-use-case">Example use case</h2> |
| |
| <p>Assuming that you have configured a custom OpenID Connect provider <code class="highlighter-rouge">some-openid-connect-provider</code> as |
| <a href="installation-operating.html#openid-connect">documented in the installation/operation guide</a>:</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ditto.gateway.authentication { |
| oauth { |
| openid-connect-issuers = { |
| some-openid-connect-provider = "https://some-openid-connect-provider.com" |
| } |
| } |
| } |
| </code></pre></div></div> |
| |
| <p>Let’s describe our scenario:</p> |
| <ul> |
| <li>It is required to enable that a Ditto <a href="basic-connections.html">connection</a> (e.g. an |
| <a href="connectivity-protocol-bindings-http.html">HTTP connection</a> invoking an HTTP webhook) shall receive events whenever |
| the temperature of a twin is modified</li> |
| <li>For security reasons however, the webhook shall not receive events longer than the expiration time of the JWT which |
| was used in order to activate the webhook</li> |
| <li>The webhook can be extended by invoking the action again before the “expiry” time was reached</li> |
| </ul> |
| |
| <p>The underlying <a href="basic-policy.html">policy</a> shall be the following one:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"policyId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my.namespace:policy-a"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"entries"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"owner"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"subjects"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"some-openid-connect-provider:some-admin-id"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"authenticated via OpenID connect provider &lt;some-openid-connect-provider&gt;"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"resources"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thing:/"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"READ"</span><span class="p">,</span><span class="w"> </span><span class="s2">"WRITE"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"policy:/"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"READ"</span><span class="p">,</span><span class="w"> </span><span class="s2">"WRITE"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"temperature-observer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"subjects"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"some-openid-connect-provider:some-user-id"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"authenticated via OpenID connect provider &lt;some-openid-connect-provider&gt;"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"resources"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thing:/features/temperature"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"READ"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"policy:/entries/temperature-observer/actions/activateTokenIntegration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"EXECUTE"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>The policy entry <code class="highlighter-rouge">"temperature-observer"</code> above describes that:</p> |
| <ul> |
| <li>the user “some-user-id” may <code class="highlighter-rouge">READ</code> the <code class="highlighter-rouge">"temperature"</code> feature of things using this policy</li> |
| <li>is allowed to <code class="highlighter-rouge">EXECUTE</code> the <code class="highlighter-rouge">activateTokenIntegration</code> action in order to inject a subject derived from his provided |
| JWT</li> |
| </ul> |
| |
| <p>Let’s assume that the authenticated JWT used for executing the action contained the following claims:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"iss"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://some-openid-connect-provider.com"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"sub"</span><span class="p">:</span><span class="w"> </span><span class="s2">"some-user-id"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"exp"</span><span class="p">:</span><span class="w"> </span><span class="mi">1622802633</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"aud"</span><span class="p">:</span><span class="w"> </span><span class="s2">"some-specific-audience-0815"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>The “exp” field contains the token expiry timestamp (seconds since epoch) and resolves to: |
| <code class="highlighter-rouge">Friday, June 4, 2021 10:30:33 AM</code>.</p> |
| |
| <p>Once the HTTP API |
| <a href="/http-api-doc.html#/Policies/post_policies__policyId__entries__label__actions_activateTokenIntegration">POST /api/2/policies/{policyId}/entries/{label}/actions/activateTokenIntegration</a>, with <code class="highlighter-rouge">policyId=my.namespace:policy-a</code> and <code class="highlighter-rouge">label=temperature-observer</code>,<br /> |
| is invoked (without any payload), a new subject will be injected when the |
| <a href="basic-policy.html#action-activatetokenintegration">described prerequisites</a> were enforced successfully.</p> |
| |
| <p>As a simplification, all possible policy entries may be injected with the subject by invoking the top level action<br /> |
| <a href="/http-api-doc.html#/Policies/post_policies__policyId__actions_activateTokenIntegration">POST /api/2/policies/{policyId}/actions/activateTokenIntegration</a>, with <code class="highlighter-rouge">policyId=my.namespace:policy-a</code>.</p> |
| |
| <p>The value of the injected subject will contain the expiration timestamp from the JWT, so the injected policy subject |
| <code class="highlighter-rouge">integration:temperature-observer:some-specific-audience-0815</code> will result in a modified policy:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"policyId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"my.namespace:policy-a"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"entries"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"owner"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">unchanged</span><span class="w"> </span><span class="err">...</span><span class="w"> </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"temperature-observer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"subjects"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"some-openid-connect-provider:some-user-id"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"authenticated via OpenID connect provider &lt;some-openid-connect-provider&gt;"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"integration:temperature-observer:some-specific-audience-0815"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"added via action &lt;activateTokenIntegration&gt;"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"expiry"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2021-06-04T10:30:33Z"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"resources"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"thing:/features/temperature"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"READ"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"policy:/entries/temperature-observer/actions/activateTokenIntegration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"grant"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"EXECUTE"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"revoke"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>When we now have a |
| managed HTTP connection which <a href="basic-connections.html#authorization">configures the <code class="highlighter-rouge">authorizationContext</code></a> to include |
| the subject <code class="highlighter-rouge">integration:temperature-observer:some-specific-audience-0815</code> for a |
| <a href="basic-connections.html#targets">connection target</a>, this connection is allowed to publish changes to the temperature of |
| all things using the above policy until the <code class="highlighter-rouge">"expiry"</code> timestamp was reached.<br /> |
| Afterwards, publishing changes automatically stops, unless the action is invoked again with a JWT having a longer “exp” |
| time prolonging the injected policy subject.</p> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions towards this new token based subject activation |
| for policies.<br /> |
| Or do you have other use cases in mind you might be able to solve with this feature? Please let us know.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Fri, 22 Jan 2021 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2021-01-22-policy-subject-activate-token-integration.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2021-01-22-policy-subject-activate-token-integration.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Announcing Eclipse Ditto Release 1.5.0</title> |
| <description><p>Wrapping up this crazy year, the Ditto team is happy to announce the next feature update of Ditto <code class="highlighter-rouge">1.x</code>: |
| <strong>Eclipse Ditto 1.5.0</strong></p> |
| |
| <p>1.5.0 focuses on:</p> |
| |
| <ul> |
| <li>Desired properties management (CRUD)</li> |
| <li>Addition of “cloudevents” HTTP endpoint</li> |
| <li>Ditto internal pub/sub supports using a “grouping” concept which improves Ditto’s scalability capabilities</li> |
| <li>Issuing “weak Acknowledgements” when a command requesting acks was filtered out by Ditto (improvement of “at least once” delivery scenarios)</li> |
| <li>Feature ID may be used in header mappings of connections</li> |
| </ul> |
| |
| <p>Please have a look at the <a href="release_notes_150.html">1.5.0 release notes</a> for a more detailed information on the release.</p> |
| |
| <h2 id="artifacts">Artifacts</h2> |
| |
| <p>The new Java artifacts have been published at the <a href="https://repo.eclipse.org/content/repositories/ditto/">Eclipse Maven repository</a> |
| as well as <a href="https://repo1.maven.org/maven2/org/eclipse/ditto/">Maven central</a>.</p> |
| |
| <p>Also the <a href="client-sdk-java.html">Ditto Java client</a>’s artifacts were published to Maven central.</p> |
| |
| <p>The Docker images have been pushed to Docker Hub:</p> |
| <ul> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-policies/">eclipse/ditto-policies</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things/">eclipse/ditto-things</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things-search/">eclipse/ditto-things-search</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-gateway/">eclipse/ditto-gateway</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-connectivity/">eclipse/ditto-connectivity</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-concierge/">eclipse/ditto-concierge</a></li> |
| </ul> |
| |
| <h2 id="kubernetes-ready-helm-chart">Kubernetes ready: Helm chart</h2> |
| |
| <p>In order to run Eclipse Ditto in a Kubernetes environment, best rely on the official |
| <a href="https://hub.helm.sh/charts/eclipse-iot/ditto">Helm chart</a> and deploy Ditto via the Helm package manager.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Thu, 10 Dec 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-12-10-release-announcement-150.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-12-10-release-announcement-150.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Weak acknowledgments to decouple signal publishers and subscribers</title> |
| <description><h2 id="motivation">Motivation</h2> |
| |
| <p><a href="2020-08-31-release-announcement-120.html">Ditto 1.2.0</a> introduced at-least-once delivery via |
| <a href="basic-acknowledgements.html">acknowledgement requests</a>.<br /> |
| It increased coupling between the publisher and the subscriber of signals in that the subscriber is no longer at the |
| liberty to filter for signals it is interested in. Instead, the subscriber must consume all signals in order to |
| fulfill acknowledgement requests and prevent endless redelivery.</p> |
| |
| <p>To combat the problem, |
| <a href="2020-10-28-release-announcement-140.html">Ditto 1.4.0</a> made acknowledgement labels unique and introduced the requirement |
| to manage <a href="basic-acknowledgements.html#issuing-acknowledgements"><em>declared acknowledgements</em></a>, identifying of each |
| subscriber.<br /> |
| It is now possible for Ditto to issue |
| <a href="basic-acknowledgements.html#weak-acknowledgements-wacks"><em>weak acknowledgements</em></a> on behalf of the subscriber |
| whenever it decides to not consume a signal. That allows subscribers to configure RQL and namespace filters freely |
| without causing any futile redelivery.</p> |
| |
| <div class="alert alert-info" role="alert"><i class="fa fa-info-circle"></i> <b>Note:</b> Weak acknowledgements are available since Ditto 1.5.0.</div> |
| |
| <h2 id="what-it-is">What it is</h2> |
| |
| <p>A <a href="basic-acknowledgements.html#weak-acknowledgements-wacks"><em>weak acknowledgement</em></a> is issued by Ditto for any |
| <a href="basic-acknowledgements.html#requesting-acks">acknowledgement request</a> that will not be fulfilled now or ever without |
| configuration change.<br /> |
| A weak acknowledgement is identified by the header <code class="highlighter-rouge">ditto-weak-ack: true</code>.</p> |
| |
| <p>The status code of weak acknowledgements is <code class="highlighter-rouge">200 OK</code>; it signifies that any redelivery is not to be made on their |
| account.</p> |
| |
| <p>A weak acknowledgement may look like this in Ditto protocol:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.acme/xdk_53/things/twin/acks/my-mqtt-connection:my-mqtt-topic"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"ditto-weak-ack"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Acknowledgement was issued automatically, because the subscriber is not authorized to receive the signal."</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">200</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h2 id="how-it-works">How it works</h2> |
| |
| <p>Since Ditto 1.4.0, subscribers of <em>twin events</em> or <em>live signals</em> are required to declare unique acknowledgement labels |
| they are allowed to send. The labels of acknowledgement requests are then identifying the intended subscribers.<br /> |
| If the intended subscriber exists but does not receive the signal for non-transient reasons, Ditto issues |
| a weak acknowledgement for that subscriber.<br /> |
| Such reasons may be:</p> |
| <ul> |
| <li>The intended subscriber <strong>is not authorized</strong> to receive the signal by policy;</li> |
| <li>The intended subscriber did not subscribe for the signal type (<em>twin event, live command, live event or live message</em>);</li> |
| <li>The intended subscriber filtered the signal out by its <a href="basic-changenotifications.html#filtering">namespace or RQL filter</a>;</li> |
| <li>The intended subscriber dropped the signal because its <a href="connectivity-mapping.html">payload mapper</a> produced nothing.</li> |
| </ul> |
| |
| <h2 id="limitation">Limitation</h2> |
| |
| <p>The distributed nature of cluster pub/sub means that weak acknowledgements are not always issued correctly.<br /> |
| They are only <em>eventually correct</em> in the sense that some time after a change to the publisher-subscriber pair, |
| the issued weak acknowledgements will reflect the change.<br /> |
| Such changes include:</p> |
| <ul> |
| <li>Opening and closing of Websocket or other connections acting as the subscriber;</li> |
| <li>Subscribing and unsubscribing for different signal types via Websocket;</li> |
| <li>Modification of connections via the <a href="connectivity-manage-connections.html">connectivity API</a>;</li> |
| <li>Migration of a connection from one Ditto cluster member to another due to load balancing.</li> |
| </ul> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions towards this new concept of weak |
| acknowledgements.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Mon, 16 Nov 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-11-16-weak-acknowledgements.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-11-16-weak-acknowledgements.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Desired Feature Properties</title> |
| <description><h2 id="desired-feature-properties-added-to-things-model">Desired feature properties added to things model</h2> |
| <p>With the upcoming release of Eclipse Ditto <strong>version 1.5.0</strong> |
| <a href="basic-feature.html#feature-desired-properties">desired feature properties</a> are introduced to the things model for |
| <strong>API versions later than 1</strong>. The <em>desired properties</em> for features are added on the same level of the model as |
| the feature properties and can reflect for example feature property updates ,which are intended, but not yet applied.</p> |
| |
| <div class="alert alert-info" role="alert"><i class="fa fa-info-circle"></i> <b>Note:</b> Further logics for desired feature properties might be implemented in future Ditto |
| versions.</div> |
| |
| <p>A fully-fledged JSON representation of a feature with desired properties is shown below:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"lamp"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"definition"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"com.mycompany.fb:Lamp:1.0.0"</span><span class="w"> </span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"on"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"location"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"longitude"</span><span class="p">:</span><span class="w"> </span><span class="mf">34.052235</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"latitude"</span><span class="p">:</span><span class="w"> </span><span class="mf">-118.243683</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"on"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"color"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"red"</span><span class="p">:</span><span class="w"> </span><span class="mi">128</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"green"</span><span class="p">:</span><span class="w"> </span><span class="mi">255</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"blue"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"desiredProperties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"on"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h2 id="operations-on-desired-feature-properties">Operations on desired feature properties</h2> |
| |
| <ul> |
| <li><strong>CRUD operations</strong> |
| <ul> |
| <li>You can create multiple desired properties of a feature or just single ones.</li> |
| <li>You can retrieve all desired properties of a feature or just single ones.</li> |
| <li>You can modify all desired properties of a feature or just single ones.</li> |
| <li>You can delete all desired properties of a feature or just single ones.</li> |
| </ul> |
| </li> |
| <li><strong>Search</strong> |
| <ul> |
| <li>You can <a href="httpapi-search.html">search</a> for things with specific desired properties with <a href="basic-rql.html">RQL-functions</a>.</li> |
| <li>You can search for things, which have <a href="basic-rql.html#exists">existent</a> desired properties for a feature.</li> |
| </ul> |
| </li> |
| <li><strong>Get notified on changes</strong> |
| <ul> |
| <li>You can <a href="basic-signals-event.html">receive events</a> for changes done to the desired properties of things |
| you’re authorized to read.</li> |
| <li>You can <a href="basic-enrichment.html">enrich</a> and <a href="basic-changenotifications.html#filtering">filter</a> the |
| events you want to receive, for changes done to the desired properties.</li> |
| </ul> |
| </li> |
| </ul> |
| |
| <h3 id="executing-crud-operations-on-desired-feature-properties">Executing CRUD operations on desired feature properties</h3> |
| <p>CRUD operations can be executed either via the <a href="httpapi-concepts.html">Ditto HTTP API</a> <strong>versions later than 1</strong> or via |
| <a href="protocol-overview.html">ditto-protocol</a> messages.</p> |
| |
| <p><em>Possible CRUD operations for desired feature properties via ditto-protocol</em>:</p> |
| |
| <ul> |
| <li><a href="protocol-examples-retrievedesiredproperties.html">Retrieve all desired properties of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-retrievedesiredproperty.html">Retrieve a single desired property of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-modifydesiredproperties.html">Create/Modify all desired properties of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-modifydesiredproperty.html">Create/Modify a single desired property of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-deletedesiredproperties.html">Delete all desired properties of a feature via ditto-protocol</a></li> |
| <li><a href="protocol-examples-deletedesiredproperty.html">Delete a single desired property of a feature via ditto-protocol</a></li> |
| </ul> |
| |
| <h3 id="using-the-ditto-client-to-manage-desired-feature-properties">Using the ditto-client to manage desired feature properties</h3> |
| <p>The desired feature properties can also be retrieved, modified and deleted via the <a href="client-sdk-java.html">Ditto Java Client</a>. |
| With the upcoming (<strong>Ditto Java Client version 1.5.0</strong>), no special CRUD operations for |
| desired feature properties are implemented in the client. Thus, the operations have to be executed via creating |
| <strong>ditto-protocol messages</strong> manually in the client.</p> |
| |
| <p>Example for creating/modifying desired feature properties of a thing via the ditto-client:</p> |
| |
| <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="n">Adaptable</span> <span class="n">modifyFeatureDesiredProperties</span> <span class="o">=</span> |
| <span class="n">Adaptable</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">(</span><span class="n">TopicPath</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">(</span><span class="n">ThingId</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"com.mycompany.fb:Car:1.0.0"</span><span class="o">))</span> |
| <span class="o">.</span><span class="na">things</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">twin</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">commands</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">modify</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">())</span> |
| <span class="o">.</span><span class="na">withPayload</span><span class="o">(</span><span class="n">Payload</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">(</span> |
| <span class="n">JsonPointer</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"/features/lamp/desiredProperties"</span><span class="o">))</span> |
| <span class="o">.</span><span class="na">withValue</span><span class="o">(</span><span class="n">JsonObject</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">().</span><span class="na">set</span><span class="o">(</span><span class="s">"on"</span><span class="o">,</span> <span class="kc">false</span><span class="o">).</span><span class="na">build</span><span class="o">())</span> |
| <span class="o">.</span><span class="na">build</span><span class="o">()).</span><span class="na">build</span><span class="o">();</span> |
| |
| <span class="n">client</span><span class="o">.</span><span class="na">sendDittoProtocol</span><span class="o">(</span><span class="n">modifyFeatureDesiredProperties</span><span class="o">).</span><span class="na">whenComplete</span><span class="o">(((</span><span class="n">adaptable</span><span class="o">,</span> <span class="n">throwable</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> |
| <span class="k">if</span> <span class="o">(</span><span class="n">throwable</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> |
| <span class="n">LOGGER</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"Received error while sending ModifyFeatureDesiredProperties: '{}' "</span><span class="o">,</span> |
| <span class="n">throwable</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span> |
| <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> |
| <span class="n">LOGGER</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Received response for ModifyFeatureDesiredProperties: '{}'"</span><span class="o">,</span> <span class="n">adaptable</span><span class="o">);</span> |
| <span class="o">}</span> |
| <span class="o">}));</span> |
| </code></pre></div></div> |
| |
| <h2 id="feedback">Feedback?</h2> |
| |
| <p>Please <a href="feedback.html">get in touch</a> if you have feedback or questions towards this new concept of desired properties.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Wed, 11 Nov 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-11-11-desired-properties.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-11-11-desired-properties.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Announcing Eclipse Ditto Release 1.4.0</title> |
| <description><p>Today, the Ditto team is happy to announce the next feature update of Ditto <code class="highlighter-rouge">1.x</code>: <strong>Eclipse Ditto 1.4.0</strong></p> |
| |
| <p>1.4.0 focuses on:</p> |
| |
| <ul> |
| <li>Declaration of acknowledgement labels unique to each subscriber</li> |
| </ul> |
| |
| <p>Please have a look at the <a href="release_notes_140.html">1.4.0 release notes</a> for a more detailed information on the release.</p> |
| |
| <p>Also, some bugs were fixed which are not backported to Ditto 1.3.0 - it is recommended to update to Ditto 1.4.0 right |
| away and skip 1.3.0.</p> |
| |
| <h2 id="artifacts">Artifacts</h2> |
| |
| <p>The new Java artifacts have been published at the <a href="https://repo.eclipse.org/content/repositories/ditto/">Eclipse Maven repository</a> |
| as well as <a href="https://repo1.maven.org/maven2/org/eclipse/ditto/">Maven central</a>.</p> |
| |
| <p>Also the <a href="client-sdk-java.html">Ditto Java client</a>’s artifacts were published to Maven central.</p> |
| |
| <p>The Docker images have been pushed to Docker Hub:</p> |
| <ul> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-policies/">eclipse/ditto-policies</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things/">eclipse/ditto-things</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things-search/">eclipse/ditto-things-search</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-gateway/">eclipse/ditto-gateway</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-connectivity/">eclipse/ditto-connectivity</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-concierge/">eclipse/ditto-concierge</a></li> |
| </ul> |
| |
| <h2 id="kubernetes-ready-helm-chart">Kubernetes ready: Helm chart</h2> |
| |
| <p>In order to run Eclipse Ditto in a Kubernetes environment, best rely on the official |
| <a href="https://hub.helm.sh/charts/eclipse-iot/ditto">Helm chart</a> and deploy Ditto via the Helm package manager.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Wed, 28 Oct 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-10-28-release-announcement-140.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-10-28-release-announcement-140.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>E2E acknowledgment</title> |
| <description><h2 id="e2e-acknowledgement-using-eclipse-ditto">E2E acknowledgement using Eclipse Ditto</h2> |
| |
| <p>By adding the new <a href="basic-acknowledgements.html">acknowledgements feature</a> to Ditto, it is now possible to provide an end to end QoS (quality of service) with level 1. |
| Previously the <a href="architecture-services-connectivity.html">connectivity service</a> of Ditto did accept all incoming messages immediately as soon as it received the message.</p> |
| |
| <p>This behaviour can now be controlled by a Ditto header called <a href="basic-acknowledgements.html#requesting-acks"><code class="highlighter-rouge">requested-acks</code></a>.</p> |
| |
| <p>In this blog post I will provide a few examples for how to use this new feature in the following scenarios:</p> |
| |
| <ol> |
| <li>Device publishes a change of its state and doesn’t care about whether the event regarding this change will be received by any subscriber or not.<br /> |
| This is the <a href="#scenario-1---fire-and-forget">fire and forget</a> scenario.</li> |
| <li>Device publishes a change of its state and wants to be sure it is persisted in Ditto.<br /> |
| This is the <a href="#scenario-2---processed-in-ditto">processed in Ditto</a> scenario.</li> |
| <li>Device publishes a change of its state and wants to be sure an application connected to Eclipse Ditto will receive the event regarding this change of the state.<br /> |
| This is the <a href="#scenario-3---notification-scenario">notification</a> scenario.</li> |
| <li>Device publishes a change of its state and wants to be notified when a subscriber was not able to receive the event regarding this change.<br /> |
| This is the <a href="#scenario-4---tracked-notification-scenario">tracked notification</a> scenario.</li> |
| <li>Device sends a live message and wants to be sure that it will eventually receive a response.<br /> |
| This is the <a href="#scenario-5---long-running-live-message-scenario">long running live message</a> scenario.</li> |
| <li>Device sends a live message and wants to either receive the response within a given timeout or never.<br /> |
| This is the <a href="#scenario-6---asking-for-required-information-scenario">asking for required information</a> scenario.</li> |
| </ol> |
| |
| <h2 id="scenarios">Scenarios</h2> |
| |
| <p>The following scenarios all share the same context:</p> |
| <ul> |
| <li>a device sends data/messages via a device connectivity layer (e.g. <a href="https://eclipse.org/hono/">Eclipse Hono</a> or an MQTT broker) to Ditto</li> |
| <li>Ditto’s <a href="architecture-services-connectivity.html">connectivity service</a> |
| <ul> |
| <li>consumes the <a href="protocol-specification.html">Ditto Protocol</a> message</li> |
| <li>forwards the message into the Ditto cluster to be processed</li> |
| <li>is responsible for technically acknowledging/settling the consumed message at the device connectivity layer / broker |
| <ul> |
| <li>the strategy of when this is done and with which outcome is handled by the Ditto headers mentioned in the scenarios</li> |
| </ul> |
| </li> |
| </ul> |
| </li> |
| </ul> |
| |
| <p>Although all scenarios are based on the device sending something (e.g. telemetry data or a message), the |
| E2E acknowledgement can also be used the other way around when e.g. a backend application sends something to a device.</p> |
| |
| <h3 id="scenario-1---fire-and-forget">Scenario 1 - Fire and Forget</h3> |
| |
| <p>This is the simplest scenario of all, since the change can be published in a fire and forget semantics.</p> |
| |
| <p>In this scenario the device will send the modification command containing the headers:</p> |
| <ul> |
| <li><code class="highlighter-rouge">response-required=false</code></li> |
| <li><code class="highlighter-rouge">requested-acks=[]</code></li> |
| </ul> |
| |
| <p>Example <a href="protocol-specification.html">Ditto Protocol</a> message:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"org.eclipse.ditto/my-thing/things/twin/commands/modify"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"response-required"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"requested-acks"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/features/lightSwitch/properties/status"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"on"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>For this case the connectivity service will immediately acknowledge the incoming message at the messaging system and then continues to process the command.</p> |
| |
| <p>It doesn’t matter if the command could be processed successfully or if any subscriber received an event for this change.</p> |
| |
| <h3 id="scenario-2---processed-in-ditto">Scenario 2 - Processed in Ditto</h3> |
| |
| <p>For this scenario the device wants to be sure its change will be properly persisted in Ditto.</p> |
| |
| <p>The command needs to define the following headers:</p> |
| <ul> |
| <li><code class="highlighter-rouge">response-required=false</code></li> |
| <li><code class="highlighter-rouge">requested-acks=["twin-persisted"]</code></li> |
| </ul> |
| |
| <p>Example <a href="protocol-specification.html">Ditto Protocol</a> message:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"org.eclipse.ditto/my-thing/things/twin/commands/modify"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"response-required"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"requested-acks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"twin-persisted"</span><span class="p">]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/features/lightSwitch/properties/status"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"on"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>For this case the connectivity service will wait until the modification will be properly persisted in Ditto before acknowledging the incoming message at the messaging system.</p> |
| |
| <p>If the device published this command for example via an AMQP broker (with a QoS 1 “at least once” semantic), |
| this will cause the broker to redeliver the command to Ditto if the acknowledgment fails.<br /> |
| Please be aware that a redelivery will only be requested for the following error status codes:</p> |
| <ul> |
| <li><code class="highlighter-rouge">408</code> (Request timed out)</li> |
| <li><code class="highlighter-rouge">424</code> (Dependency Failure)</li> |
| <li>All kinds of <code class="highlighter-rouge">5xx</code> status codes</li> |
| </ul> |
| |
| <p>In this scenario it does matter if the command could be processed successfully, but it’s still not relevant if any subscriber received an event for this change.</p> |
| |
| <h3 id="scenario-3---notification-scenario">Scenario 3 - Notification scenario</h3> |
| |
| <p>For this scenario the device wants to be sure another system will be notified about the change of its state.</p> |
| |
| <p>This could be for example an alarming system which wants to be sure a backend application receives the information that the alarm was triggered.</p> |
| |
| <p>A prerequisite for this is that any kind of connection exists that publishes the event to the backend application and declares a user defined acknowledgement label. |
| This can be a <a href="httpapi-protocol-bindings-websocket.html">WebSocket session</a> or any kind of Ditto Connection types which can be found <a href="connectivity-overview.html">here</a>.</p> |
| |
| <p>For this example we expect the event to be forwarded by an <a href="connectivity-protocol-bindings-http.html">HTTP connection</a> |
| which declared the following acknowledgement label as |
| <a href="basic-connections.html#target-issued-acknowledgement-label">issued acknowledgement of the target</a>: <code class="highlighter-rouge">d45d4522-142e-4057-ae87-8969343a3ddc:backend-processed</code>.</p> |
| |
| <p>The UUID prefix in this case is the ID of the HTTP connection and the<code class="highlighter-rouge">backend-process</code> part is a custom label, defined by the user.</p> |
| |
| <p>The command needs to define the following headers:</p> |
| <ul> |
| <li><code class="highlighter-rouge">response-required=false</code></li> |
| <li><code class="highlighter-rouge">requested-acks=["d45d4522-142e-4057-ae87-8969343a3ddc:backend-processed"]</code></li> |
| <li><code class="highlighter-rouge">timeout=30s</code> (optional. Default is 10s.)</li> |
| </ul> |
| |
| <p>Example <a href="protocol-specification.html">Ditto Protocol</a> message:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"org.eclipse.ditto/my-thing/things/twin/commands/modify"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"response-required"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"requested-acks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"d45d4522-142e-4057-ae87-8969343a3ddc:backend-processed"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"timeout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"30s"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/features/alarm/properties/status"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"on"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>For this case the connectivity service will wait until the HTTP request, which forwards the event regarding the thing change, |
| will be finished before acknowledging the incoming message at the messaging system.</p> |
| |
| <p>The status code of the HTTP response will in this case determine if the message will be acknowledged successfully or not and if a redelivery will be requested or not.<br /> |
| All kinds of <code class="highlighter-rouge">2xx</code> status codes will lead to a successful acknowledgement at the messaging system.<br /> |
| All other status codes will lead to a failed acknowledgement at the messaging system and for the following status codes a redelivery will be requested:</p> |
| <ul> |
| <li><code class="highlighter-rouge">408</code> (Request timed out)</li> |
| <li><code class="highlighter-rouge">424</code> (Dependency Failure)</li> |
| <li>All kinds of <code class="highlighter-rouge">5xx</code> status codes</li> |
| </ul> |
| |
| <p>In this scenario it is ensured a specified subscriber will receive an event for this change.</p> |
| |
| <h3 id="scenario-4---tracked-notification-scenario">Scenario 4 - Tracked notification scenario</h3> |
| |
| <p>For this scenario the device wants to know when a system could not be notified about the change of its state.</p> |
| |
| <p>This could be for example an alarming system which wants to be sure a backend application receives the information, |
| or if not: tries to send an SMS as notification.</p> |
| |
| <p>This scenario is mostly like scenario 3, but needs to set the <code class="highlighter-rouge">response-required</code> header to <code class="highlighter-rouge">true</code> and it’s required |
| to <a href="basic-connections.html#source-reply-target">configure the reply-target of the source</a> to also expect “nack” responses.</p> |
| |
| <p>Example <a href="protocol-specification.html">Ditto Protocol</a> message:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"org.eclipse.ditto/my-thing/things/twin/commands/modify"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"response-required"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"requested-acks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"d45d4522-142e-4057-ae87-8969343a3ddc:backend-processed"</span><span class="p">]</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/features/alarm/properties/status"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"on"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>In this case the device will receive an acknowledgement response containing the status code and payload of the response of the backend application.<br /> |
| Based on this the device can decide how to handle the situation.<br /> |
| It is suggested to publish the modification command with QoS 0 (“at most once” semantics) in this case because the |
| device handles the result of the E2E acknowledgement. With QoS 1 (“at least once” semantics) brokers would usually redeliver the message to Ditto.</p> |
| |
| <p>If the HTTP endpoint of the backend application responds with the following response:</p> |
| |
| <p>headers:</p> |
| <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"content-type": "application/json" |
| </code></pre></div></div> |
| |
| <p>body:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"errorCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"notification.smartphone.failed"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Could not notify smartphone."</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>status code: <code class="highlighter-rouge">424</code></p> |
| |
| <p>The response received at the device would look like this:</p> |
| |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"org.eclipse.ditto/my-thing/things/twin/acks/d45d4522-142e-4057-ae87-8969343a3ddc:backend-processed"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"response-required"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"requested-acks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"d45d4522-142e-4057-ae87-8969343a3ddc:backend-processed"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"timeout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"30s"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application/json"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"errorCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"notification.smartphone.failed"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Could not notify smartphone."</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">424</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <h3 id="scenario-5---long-running-live-message-scenario">Scenario 5 - Long running live message scenario</h3> |
| |
| <p>For this scenario the device is going to ask for an information which it needs eventually.</p> |
| |
| <p>Let’s say the device asks for the endpoint where it should download the new firmware from.<br /> |
| It’s not required that this response arrives within a given time. It’s just required to eventually arrive at the device |
| and after it was received the device can signal the user, that it is ready to download the firmware.</p> |
| |
| <p>The headers of the live message should have the following values:</p> |
| <ul> |
| <li><code class="highlighter-rouge">response-required=true</code></li> |
| <li><code class="highlighter-rouge">requested-acks=["live-response"]</code></li> |
| </ul> |
| |
| <p>Example <a href="protocol-specification.html">Ditto Protocol</a> message:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"org.eclipse.ditto/my-thing/things/live/messages/firmware"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"response-required"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"requested-acks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"live-response"</span><span class="p">],</span><span class="w"> |
| </span><span class="s2">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"text/plain"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"timeout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5s"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/outbox/messages/firmware"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"firmware.url.query"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>By requesting the acknowledgement <code class="highlighter-rouge">live-response</code> the connectivity service will wait until the response for the live message arrived |
| for 5s before acknowledging the incoming message at the broker and will request a redelivery if the response did not arrive within this timeout.</p> |
| |
| <p>This will repeat until either the broker discards the message or the response arrives in the specified timeout. |
| That way it is guaranteed that the device will eventually receive the response.</p> |
| |
| <h3 id="scenario-6---asking-for-required-information-scenario">Scenario 6 - Asking for required information scenario</h3> |
| |
| <p>For this scenario the device is going to ask for an information which it needs right now to proceed with its current task.</p> |
| |
| <p>Let’s say the device asks if it should allow a car with a license plate it detected to drive on the property by opening the barrier. |
| It could be possible to ask for that information, so the barrier opens automatically, but providing a fallback mechanism |
| like entering a code directly at the device if this response does not arrive within time.</p> |
| |
| <p>The headers of the live message should have the following values:</p> |
| <ul> |
| <li><code class="highlighter-rouge">response-required=true</code></li> |
| <li><code class="highlighter-rouge">requested-acks=[]</code></li> |
| </ul> |
| |
| <p>Example <a href="protocol-specification.html">Ditto Protocol</a> message:</p> |
| <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> |
| </span><span class="s2">"topic"</span><span class="p">:</span><span class="w"> </span><span class="s2">"org.eclipse.ditto/my-thing/things/live/messages/car-enter"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> |
| </span><span class="s2">"response-required"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"requested-acks"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w"> |
| </span><span class="s2">"content-type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"text/plain"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"timeout"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5s"</span><span class="w"> |
| </span><span class="p">},</span><span class="w"> |
| </span><span class="s2">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/outbox/messages/car-enter"</span><span class="p">,</span><span class="w"> |
| </span><span class="s2">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FN IB 1337"</span><span class="w"> |
| </span><span class="p">}</span><span class="w"> |
| </span></code></pre></div></div> |
| |
| <p>By requesting explicitly not requesting any acknowledgement but still requiring a response, the connectivity service will |
| immediately acknowledge the incoming message at the broker. The device will then either receive the response within the specified timeout or never. |
| So the device can provide its alternative options to open the barrier after 5 seconds.</p> |
| |
| <h2 id="we-embrace-your-feedback">We embrace your feedback</h2> |
| |
| <p>I hope I could demonstrate the power of the new acknowledgement feature properly and could make it clear how it can be used. |
| Maybe you did recognize some of your use cases in the given examples or maybe you have another use case which can or cannot be solved by this feature.</p> |
| |
| <p>We would love to get your <a href="feedback.html">feedback</a>.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Fri, 23 Oct 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-10-23-end-2-end-acknowledgment.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-10-23-end-2-end-acknowledgment.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Asynchronous Client Creation in Ditto Java Client 1.3.0</title> |
| <description><p>Before <a href="https://github.com/eclipse/ditto-clients/tree/master/java">Ditto Java Client</a> 1.3.0, |
| a client object connects to a configured Ditto back-end during its creation.</p> |
| |
| <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// create a client object and block until it connects to the Ditto back-end.</span> |
| <span class="kd">final</span> <span class="n">DittoClient</span> <span class="n">client</span> <span class="o">=</span> <span class="n">DittoClients</span><span class="o">.</span><span class="na">newInstance</span><span class="o">(</span><span class="n">messagingProvider</span><span class="o">);</span> |
| </code></pre></div></div> |
| |
| <p>There are several problems with the approach.</p> |
| <ol> |
| <li>The calling thread blocks waiting for IO, namely the authentication process |
| and establishment of a websocket.</li> |
| <li>If the client is configured to reconnect, then an incorrect end-ponit configuration |
| makes the factory method block forever.</li> |
| <li>If the client is not configured to reconnect, then the factory method will throw |
| an exception. But it is not possible to give the client reference to the exception |
| handler, since the client creation did not complete. Consequently the exception handler |
| has no simple way to free all resources allocated for the client.</li> |
| </ol> |
| |
| <p>1.3.0 addresses these problems by introducing an asynchronous client creation interface.</p> |
| |
| <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">DittoClients</span> <span class="o">{</span> |
| |
| <span class="kd">public</span> <span class="kd">static</span> <span class="n">DisconnectedDittoClient</span> <span class="nf">newDisconnectedInstance</span><span class="o">(</span><span class="n">MessagingProvider</span> <span class="n">mp</span><span class="o">);</span> |
| <span class="o">}</span> |
| |
| <span class="kd">public</span> <span class="kd">interface</span> <span class="nc">DisconnectedDittoClient</span> <span class="o">{</span> |
| |
| <span class="n">CompletionStage</span><span class="o">&lt;</span><span class="n">DittoClient</span><span class="o">&gt;</span> <span class="nf">connect</span><span class="o">();</span> |
| |
| <span class="kt">void</span> <span class="nf">destroy</span><span class="o">();</span> |
| <span class="o">}</span> |
| </code></pre></div></div> |
| |
| <p>The method <code class="highlighter-rouge">DittoClients.newDisconnectedInstance(MessagingProvider)</code> creates a <code class="highlighter-rouge">DisconnectedDittoClient</code> |
| object. The <code class="highlighter-rouge">DisconnectedDittoClient</code> has references to all resources allocated for the client and |
| can free them via the <code class="highlighter-rouge">destroy()</code> method. The <code class="highlighter-rouge">DisconnectedDittoClient</code> object offers no method to |
| interact with the Ditto API. By calling <code class="highlighter-rouge">connect()</code>, one obtains a future that yields a familiar |
| <code class="highlighter-rouge">DittoClient</code> object upon completion. One might use the asynchronous client creation interface thus:</p> |
| |
| <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="n">DisconnectedDittoClient</span> <span class="n">disconnectedClient</span> <span class="o">=</span> |
| <span class="n">DittoClients</span><span class="o">.</span><span class="na">newDisconnectedInstance</span><span class="o">(</span><span class="n">messagingProvider</span><span class="o">);</span> |
| |
| <span class="n">disconnectedClient</span><span class="o">.</span><span class="na">connect</span><span class="o">()</span> |
| <span class="o">.</span><span class="na">thenAccept</span><span class="o">(</span><span class="k">this</span><span class="o">::</span><span class="n">startUsingDittoClient</span><span class="o">)</span> |
| <span class="o">.</span><span class="na">exceptionally</span><span class="o">(</span><span class="n">exception</span> <span class="o">-&gt;</span> <span class="o">{</span> |
| <span class="k">this</span><span class="o">.</span><span class="na">handleConnectionFailure</span><span class="o">(</span><span class="n">exception</span><span class="o">);</span> |
| <span class="n">disconnectedClient</span><span class="o">.</span><span class="na">destroy</span><span class="o">();</span> |
| <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> |
| <span class="o">});</span> |
| </code></pre></div></div> |
| |
| <p>The asynchronous client creation interface has the following advantages.</p> |
| |
| <ol> |
| <li> |
| <p>The calling thread does not block.</p> |
| </li> |
| <li> |
| <p>Even if configured to reconnect, the user can receive connection errors via |
| the connection error handler in <code class="highlighter-rouge">MessagingConfiguration</code> and shut down the client |
| at will.</p> |
| </li> |
| <li> |
| <p>When initial reconnection is disabled, the method <code class="highlighter-rouge">DisconnectedDittoClient.connect()</code> |
| returns a failed future on connection error. It is possible to reference the |
| <code class="highlighter-rouge">DittoDisconnectedClient</code> object in the future’s error handler, where the client can |
| be destroyed.</p> |
| </li> |
| </ol> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Thu, 08 Oct 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-10-08-asynchronous-client-creation.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-10-08-asynchronous-client-creation.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| <item> |
| <title>Announcing Eclipse Ditto Release 1.3.0</title> |
| <description><p>Today, the Ditto team is happy to announce the next feature update of Ditto <code class="highlighter-rouge">1.x</code>: <strong>Eclipse Ditto 1.3.0</strong></p> |
| |
| <p>1.3.0 focuses on the following areas:</p> |
| |
| <ul> |
| <li>Implicit/automatic creation of digital twins (things)</li> |
| <li>Use response of HTTP push connections as live message response</li> |
| <li>“Raw” payload mapper for “pass-through” connectivity scenarios not affecting the twin</li> |
| </ul> |
| |
| <p>Please have a look at the <a href="release_notes_130.html">1.3.0 release notes</a> for a more detailed information on the release.</p> |
| |
| <p>Also, some bugs were fixed which are not backported to Ditto 1.2.x - it is recommended to update to Ditto 1.3.0 right |
| away and skip 1.2.x.</p> |
| |
| <h2 id="artifacts">Artifacts</h2> |
| |
| <p>The new Java artifacts have been published at the <a href="https://repo.eclipse.org/content/repositories/ditto/">Eclipse Maven repository</a> |
| as well as <a href="https://repo1.maven.org/maven2/org/eclipse/ditto/">Maven central</a>.</p> |
| |
| <p>Also the <a href="client-sdk-java.html">Ditto Java client</a>’s artifacts were published to Maven central.</p> |
| |
| <p>The Docker images have been pushed to Docker Hub:</p> |
| <ul> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-policies/">eclipse/ditto-policies</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things/">eclipse/ditto-things</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-things-search/">eclipse/ditto-things-search</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-gateway/">eclipse/ditto-gateway</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-connectivity/">eclipse/ditto-connectivity</a></li> |
| <li><a href="https://hub.docker.com/r/eclipse/ditto-concierge/">eclipse/ditto-concierge</a></li> |
| </ul> |
| |
| <h2 id="kubernetes-ready-helm-chart">Kubernetes ready: Helm chart</h2> |
| |
| <p>In order to run Eclipse Ditto in a Kubernetes environment, best rely on the official |
| <a href="https://hub.helm.sh/charts/eclipse-iot/ditto">Helm chart</a> and deploy Ditto via the Helm package manager.</p> |
| |
| <p><br /> |
| <br /></p> |
| <figure><img class="docimage" src="images/ditto.svg" alt="Ditto" style="max-width: 500px" /></figure> |
| |
| <p>–<br /> |
| The Eclipse Ditto team</p> |
| </description> |
| <pubDate>Wed, 30 Sep 2020 00:00:00 +0000</pubDate> |
| <link>https://www.eclipse.org/ditto/2020-09-30-release-announcement-130.html</link> |
| <guid isPermaLink="true">https://www.eclipse.org/ditto/2020-09-30-release-announcement-130.html</guid> |
| |
| <category>blog</category> |
| |
| |
| </item> |
| |
| </channel> |
| </rss> |