From 9480be5b39bbd3901102e82ba047bd4e91260999 Mon Sep 17 00:00:00 2001 From: Marcello Rinaldo Martina <39562568+marcellorinaldo@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:39:45 +0100 Subject: [PATCH] feat(sparkplug): added Sparkplug cloud connection boilerplate (#5059) * feat(sparkplug): added Sparkplug cloud connection factory Signed-off-by: Marcello Martina * build: fixed coverage report path Signed-off-by: Marcello Martina * feat(CloudEndpoint): added default implementation for CloudConnectionListener, CloudDeliveryListener, and DataServiceListener Signed-off-by: Marcello Martina * chore: added missing copyright header Signed-off-by: Marcello Martina * build: added DP in distrib Signed-off-by: Marcello Martina --------- Signed-off-by: Marcello Martina --- kura/distrib/config/kura.build.properties | 2 + kura/distrib/pom.xml | 6 + .../build.properties | 15 ++ .../feature.properties | 26 ++ .../feature.xml | 29 +++ .../pom.xml | 49 ++++ kura/features/pom.xml | 11 +- .../META-INF/MANIFEST.MF | 27 ++ .../SparkplugCloudConnectionFactory.xml | 34 +++ .../OSGI-INF/SparkplugCloudEndpoint.xml | 48 ++++ .../OSGI-INF/SparkplugDataTransport.xml | 35 +++ ...g.mqtt.endpoint.SparkplugCloudEndpoint.xml | 28 ++ ....mqtt.transport.SparkplugDataTransport.xml | 28 ++ .../about.html | 36 +++ .../build.properties | 6 + .../pom.xml | 35 +++ .../mqtt/endpoint/SparkplugCloudEndpoint.java | 239 ++++++++++++++++++ .../SparkplugCloudConnectionFactory.java | 142 +++++++++++ .../transport/SparkplugDataTransport.java | 122 +++++++++ kura/pom.xml | 1 + .../META-INF/MANIFEST.MF | 13 + .../about.html | 36 +++ .../build.properties | 20 ++ .../pom.xml | 66 +++++ .../test/CloudDeliveryListenerTest.java | 57 +++++ .../test/ConnectionStatusCallbackTest.java | 125 +++++++++ .../mqtt/endpoint/test/DataServiceTest.java | 90 +++++++ .../mqtt/endpoint/test/StepsCollection.java | 118 +++++++++ .../SparkplugCloudConnectionFactoryTest.java | 236 +++++++++++++++++ kura/test/pom.xml | 3 +- 30 files changed, 1676 insertions(+), 7 deletions(-) create mode 100644 kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/build.properties create mode 100644 kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/feature.properties create mode 100644 kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/feature.xml create mode 100644 kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/pom.xml create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/META-INF/MANIFEST.MF create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugCloudConnectionFactory.xml create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugCloudEndpoint.xml create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugDataTransport.xml create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/metatype/org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint.xml create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/metatype/org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport.xml create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/about.html create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/build.properties create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/pom.xml create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/SparkplugCloudEndpoint.java create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/factory/SparkplugCloudConnectionFactory.java create mode 100644 kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/transport/SparkplugDataTransport.java create mode 100644 kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/META-INF/MANIFEST.MF create mode 100644 kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/about.html create mode 100644 kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/build.properties create mode 100644 kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/pom.xml create mode 100644 kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/CloudDeliveryListenerTest.java create mode 100644 kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/ConnectionStatusCallbackTest.java create mode 100644 kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/DataServiceTest.java create mode 100644 kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/StepsCollection.java create mode 100644 kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/factory/test/SparkplugCloudConnectionFactoryTest.java diff --git a/kura/distrib/config/kura.build.properties b/kura/distrib/config/kura.build.properties index 21033db22d0..066aba56679 100644 --- a/kura/distrib/config/kura.build.properties +++ b/kura/distrib/config/kura.build.properties @@ -133,6 +133,7 @@ org.eclipse.kura.event.publisher.version=1.3.0-SNAPSHOT org.eclipse.kura.db.sqlite.provider.version=1.2.0-SNAPSHOT org.eclipse.kura.rest.network.status.provider.version=1.2.0-SNAPSHOT org.eclipse.kura.wire.script.tools.version=1.2.0-SNAPSHOT +org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.version=1.0.0-SNAPSHOT ## features versions org.eclipse.kura.driver.opcua.version=1.5.0-SNAPSHOT @@ -145,3 +146,4 @@ org.eclipse.kura.driver.ble.xdk.version=1.5.0-SNAPSHOT org.eclipse.kura.wire.script.filter.version=1.5.0-SNAPSHOT org.eclipse.kura.wire.script.tools.feature.version=1.2.0-SNAPSHOT org.eclipse.kura.db.sqlite.feature.version=1.2.0-SNAPSHOT +org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.feature.version=1.0.0-SNAPSHOT diff --git a/kura/distrib/pom.xml b/kura/distrib/pom.xml index 330be5a4655..dbe9f16433a 100644 --- a/kura/distrib/pom.xml +++ b/kura/distrib/pom.xml @@ -2918,6 +2918,12 @@ ${org.eclipse.kura.db.sqlite.feature.version} dp + + org.eclipse.kura.feature + org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider + ${org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.feature.version} + dp + ${project.build.directory} diff --git a/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/build.properties b/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/build.properties new file mode 100644 index 00000000000..e45b07b05d5 --- /dev/null +++ b/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/build.properties @@ -0,0 +1,15 @@ +############################################################################### +# Copyright (c) 2023 Eurotech and/or its affiliates and others +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Eurotech +############################################################################### + +bin.includes = feature.xml,\ + feature.properties diff --git a/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/feature.properties b/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/feature.properties new file mode 100644 index 00000000000..1f052f3a6f2 --- /dev/null +++ b/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/feature.properties @@ -0,0 +1,26 @@ +############################################################################### +# Copyright (c) 2023 Eurotech and/or its affiliates and others +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Eurotech +############################################################################### + +featureName=Eclipse Kura - Sparkplug MQTT Cloud Connection Provider +providerName=Eclipse Kura +description=Eclipse Kura Cloud Connection compliant with the Sparkplug v3.0.0 specification + +copyright=\ +Copyright (c) 2023 Eurotech and/or its affiliates and others\n\ +\n\ +This program and the accompanying materials are made\n\ +available under the terms of the Eclipse Public License 2.0\n\ +which accompanies this distribution, and is available at\n\ +which is available at https://www.eclipse.org/legal/epl-2.0/\n\ +\n\ +SPDX-License-Identifier: EPL-2.0\n diff --git a/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/feature.xml b/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/feature.xml new file mode 100644 index 00000000000..235e1d04691 --- /dev/null +++ b/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/feature.xml @@ -0,0 +1,29 @@ + + + + + %description + + + + %copyright + + + + %license + + + + + diff --git a/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/pom.xml b/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/pom.xml new file mode 100644 index 00000000000..2fd2236ef3e --- /dev/null +++ b/kura/features/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/pom.xml @@ -0,0 +1,49 @@ + + + + + 4.0.0 + + + org.eclipse.kura.feature + features + 5.5.0-SNAPSHOT + .. + + + org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider + 1.0.0-SNAPSHOT + eclipse-feature + + + + + de.dentrassi.maven + osgi-dp + ${osgi-dp-plugin-version} + + + + build + + + + + + + diff --git a/kura/features/pom.xml b/kura/features/pom.xml index 6e1f4b1c552..074894f9352 100644 --- a/kura/features/pom.xml +++ b/kura/features/pom.xml @@ -6,11 +6,11 @@ This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at https://www.eclipse.org/legal/epl-2.0/ - - SPDX-License-Identifier: EPL-2.0 - - Contributors: - Eurotech + + SPDX-License-Identifier: EPL-2.0 + + Contributors: + Eurotech Red Hat Inc --> @@ -65,6 +65,7 @@ org.eclipse.kura.ai.triton.server org.eclipse.kura.wire.script.tools org.eclipse.kura.db.sqlite.provider + org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/META-INF/MANIFEST.MF b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..82e0c7fe795 --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/META-INF/MANIFEST.MF @@ -0,0 +1,27 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Sparkplug MQTT Cloud Connection Provider +Bundle-SymbolicName: org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider +Bundle-Version: 1.0.0.qualifier +Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))" +Import-Package: org.eclipse.kura;version="[1.0,2.0)", + org.eclipse.kura.configuration;version="[1.1,2.0)", + org.eclipse.kura.cloud;version="[1.1,2.0)", + org.eclipse.kura.cloudconnection;version="[1.0,1.1)", + org.eclipse.kura.cloudconnection.factory;version="[1.0,1.1)", + org.eclipse.kura.cloudconnection.listener;version="[1.0,2.0)", + org.eclipse.kura.cloudconnection.message;version="[1.0,2.0)", + org.eclipse.kura.cloudconnection.publisher;version="[1.0,1.1)", + org.eclipse.kura.cloudconnection.subscriber;version="[1.0,1.1)", + org.eclipse.kura.cloudconnection.subscriber.listener;version="[1.0,2.0)", + org.eclipse.kura.data;version="[1.0,2.0)", + org.eclipse.kura.data.listener;version="[1.0,2.0)", + org.eclipse.kura.data.transport.listener;version="[1.0,2.0)", + org.osgi.framework;version="1.8.0", + org.osgi.service.component;version="1.2.0", + org.osgi.service.event;version="1.3.1", + org.slf4j;version="[1.7,2.0]" +Bundle-ActivationPolicy: lazy +Service-Component: OSGI-INF/*.xml +Bundle-Vendor: Eclipse Kura +Bundle-License: Eclipse Public License v2.0 diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugCloudConnectionFactory.xml b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugCloudConnectionFactory.xml new file mode 100644 index 00000000000..8cbf792ee60 --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugCloudConnectionFactory.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugCloudEndpoint.xml b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugCloudEndpoint.xml new file mode 100644 index 00000000000..d9aaeb1623f --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugCloudEndpoint.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugDataTransport.xml b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugDataTransport.xml new file mode 100644 index 00000000000..6bb17d543dc --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/SparkplugDataTransport.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/metatype/org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint.xml b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/metatype/org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint.xml new file mode 100644 index 00000000000..dfa8775271c --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/metatype/org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/metatype/org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport.xml b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/metatype/org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport.xml new file mode 100644 index 00000000000..2308f695852 --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/OSGI-INF/metatype/org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/about.html b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/about.html new file mode 100644 index 00000000000..ec5809fefb9 --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/about.html @@ -0,0 +1,36 @@ + + + + + About + + +

About This Content

+ +

November 30, 2017

+

License

+ +

+ The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content + is provided to you under the terms and conditions of the Eclipse + Public License Version 2.0 ("EPL"). A copy of the EPL is + available at http://www.eclipse.org/legal/epl-2.0. + For purposes of the EPL, "Program" will mean the Content. +

+ +

+ If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may + apply to your use of any object code in the Content. Check the + Redistributor's license that was provided with the Content. If no such + license exists, contact the Redistributor. Unless otherwise indicated + below, the terms and conditions of the EPL still apply to any source + code in the Content and such source code may be obtained at http://www.eclipse.org. +

+ + + \ No newline at end of file diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/build.properties b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/build.properties new file mode 100644 index 00000000000..bd9c2862852 --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/build.properties @@ -0,0 +1,6 @@ +source.. = src/main/java +output.. = target/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/,\ + about.html, diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/pom.xml b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/pom.xml new file mode 100644 index 00000000000..1fd93645075 --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + + org.eclipse.kura + kura + 5.5.0-SNAPSHOT + + + org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider + 1.0.0-SNAPSHOT + eclipse-plugin + + + ${project.basedir}/.. + + ${project.basedir}/../test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/target/site/jacoco-aggregate/jacoco.xml + + + + diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/SparkplugCloudEndpoint.java b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/SparkplugCloudEndpoint.java new file mode 100644 index 00000000000..ed2e60bb17f --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/SparkplugCloudEndpoint.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2023 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + *******************************************************************************/ +package org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +import org.eclipse.kura.KuraConnectException; +import org.eclipse.kura.KuraDisconnectException; +import org.eclipse.kura.KuraException; +import org.eclipse.kura.cloud.CloudConnectionEstablishedEvent; +import org.eclipse.kura.cloud.CloudConnectionLostEvent; +import org.eclipse.kura.cloudconnection.CloudConnectionManager; +import org.eclipse.kura.cloudconnection.CloudEndpoint; +import org.eclipse.kura.cloudconnection.listener.CloudConnectionListener; +import org.eclipse.kura.cloudconnection.listener.CloudDeliveryListener; +import org.eclipse.kura.cloudconnection.message.KuraMessage; +import org.eclipse.kura.cloudconnection.subscriber.listener.CloudSubscriberListener; +import org.eclipse.kura.configuration.ConfigurableComponent; +import org.eclipse.kura.configuration.ConfigurationService; +import org.eclipse.kura.data.DataService; +import org.eclipse.kura.data.listener.DataServiceListener; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SparkplugCloudEndpoint + implements ConfigurableComponent, CloudEndpoint, CloudConnectionManager, DataServiceListener { + + private static final Logger logger = LoggerFactory.getLogger(SparkplugCloudEndpoint.class); + + private Set cloudConnectionListeners = new HashSet<>(); + private Set cloudDeliveryListeners = new HashSet<>(); + private String kuraServicePid; + + /* + * Activation APIs + */ + + private DataService dataService; + private EventAdmin eventAdmin; + + public void setDataService(DataService dataService) { + this.dataService = dataService; + } + + public void setEventAdmin(EventAdmin eventAdmin) { + this.eventAdmin = eventAdmin; + } + + public void activate(Map properties) { + this.kuraServicePid = (String) properties.get(ConfigurationService.KURA_SERVICE_PID); + logger.info("{} - Activating", this.kuraServicePid); + + this.dataService.addDataServiceListener(this); + update(properties); + + logger.info("{} - Activated", this.kuraServicePid); + } + + public void update(Map properties) { + logger.info("{} - Updating", this.kuraServicePid); + + logger.info("{} - Updated", this.kuraServicePid); + } + + public void deactivate() { + logger.info("{} - Deactivating", this.kuraServicePid); + + logger.info("{} - Deactivated", this.kuraServicePid); + } + + /* + * CloudEndpoint APIs + */ + + @Override + public String publish(KuraMessage message) throws KuraException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void registerSubscriber(Map subscriptionProperties, + CloudSubscriberListener cloudSubscriberListener) { + // TODO Auto-generated method stub + + } + + @Override + public void unregisterSubscriber(CloudSubscriberListener cloudSubscriberListener) { + // TODO Auto-generated method stub + + } + + @Override + public void registerCloudDeliveryListener(CloudDeliveryListener cloudDeliveryListener) { + logger.debug("{} - Adding CloudDeliveryListener {}", this.kuraServicePid, + cloudDeliveryListener.getClass().getName()); + this.cloudDeliveryListeners.add(cloudDeliveryListener); + } + + @Override + public void unregisterCloudDeliveryListener(CloudDeliveryListener cloudDeliveryListener) { + logger.debug("{} - Removing CloudDeliveryListener {}", this.kuraServicePid, + cloudDeliveryListener.getClass().getName()); + this.cloudDeliveryListeners.remove(cloudDeliveryListener); + } + + /* + * CloudConnectionManager APIs + */ + + @Override + public void connect() throws KuraConnectException { + this.dataService.connect(); + } + + @Override + public void disconnect() throws KuraDisconnectException { + this.dataService.disconnect(0); + } + + @Override + public boolean isConnected() { + return this.dataService.isConnected(); + } + + @Override + public void registerCloudConnectionListener(CloudConnectionListener cloudConnectionListener) { + logger.debug("{} - Adding CloudConnectionListener {}", this.kuraServicePid, + cloudConnectionListener.getClass().getName()); + this.cloudConnectionListeners.add(cloudConnectionListener); + } + + @Override + public void unregisterCloudConnectionListener(CloudConnectionListener cloudConnectionListener) { + logger.debug("{} - Removing CloudConnectionListener {}", this.kuraServicePid, + cloudConnectionListener.getClass().getName()); + this.cloudConnectionListeners.remove(cloudConnectionListener); + } + + /* + * DataServiceListener APIs + */ + + @Override + public void onConnectionEstablished() { + logger.debug("{} - Connection estabilished", this.kuraServicePid); + this.cloudConnectionListeners.forEach(listener -> callSafely(listener::onConnectionEstablished)); + postConnectionChangeEvent(true); + + // TO DO: init subscriptions + } + + @Override + public void onDisconnecting() { + // nothing to do + } + + @Override + public void onDisconnected() { + logger.debug("{} - Disconnected", this.kuraServicePid); + this.cloudConnectionListeners.forEach(listener -> callSafely(listener::onDisconnected)); + postConnectionChangeEvent(false); + } + + @Override + public void onConnectionLost(Throwable cause) { + logger.debug("{} - Connection lost", this.kuraServicePid); + this.cloudConnectionListeners.forEach(listener -> callSafely(listener::onConnectionLost)); + postConnectionChangeEvent(false); + } + + @Override + public void onMessageArrived(String topic, byte[] payload, int qos, boolean retained) { + logger.debug("{} - Message arrived, forwarding to registered subscribers", this.kuraServicePid); + // TODO + } + + @Override + public void onMessagePublished(int messageId, String topic) { + // nothing to do + } + + @Override + public void onMessageConfirmed(int messageId, String topic) { + logger.debug("{} - Message with ID {} confirmed", this.kuraServicePid, messageId); + this.cloudDeliveryListeners + .forEach(listener -> callSafely(listener::onMessageConfirmed, String.valueOf(messageId))); + } + + /* + * Utilities + */ + + private void postConnectionChangeEvent(final boolean isConnected) { + logger.debug("{} - Posting connection changed event", this.kuraServicePid); + + Map eventProperties = new HashMap<>(); + eventProperties.put("cloud.service.pid", this.kuraServicePid); + + Event event = isConnected ? new CloudConnectionEstablishedEvent(eventProperties) + : new CloudConnectionLostEvent(eventProperties); + + this.eventAdmin.postEvent(event); + } + + private void callSafely(Runnable f) { + try { + f.run(); + } catch (Exception e) { + logger.warn("{} - An error occured in listener {}", this.kuraServicePid, f.getClass().getName(), e); + } + } + + private void callSafely(Consumer f, T argument) { + try { + f.accept(argument); + } catch (Exception e) { + logger.error("{} - An error occured in listener {}", this.kuraServicePid, f.getClass().getName(), e); + } + } + +} diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/factory/SparkplugCloudConnectionFactory.java b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/factory/SparkplugCloudConnectionFactory.java new file mode 100644 index 00000000000..1d0232e638e --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/factory/SparkplugCloudConnectionFactory.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2023 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + *******************************************************************************/ +package org.eclipse.kura.cloudconnection.sparkplug.mqtt.factory; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.kura.KuraErrorCode; +import org.eclipse.kura.KuraException; +import org.eclipse.kura.cloudconnection.CloudEndpoint; +import org.eclipse.kura.cloudconnection.factory.CloudConnectionFactory; +import org.eclipse.kura.configuration.ConfigurationService; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.component.ComponentConstants; + +public class SparkplugCloudConnectionFactory implements CloudConnectionFactory { + + private static final String FACTORY_PID = "org.eclipse.kura.cloudconnection.sparkplug.mqtt.factory.SparkplugCloudConnectionFactory"; + + private static final String CLOUD_ENDPOINT_FACTORY_PID = "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint"; + private static final String DATA_SERVICE_FACTORY_PID = "org.eclipse.kura.data.DataService"; + private static final String DATA_TRANSPORT_SERVICE_FACTORY_PID = + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport"; + + private static final String CLOUD_ENDPOINT_PID = "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint"; + private static final String DATA_SERVICE_PID = "org.eclipse.kura.cloudconnection.sparkplug.mqtt.data.SparkplugDataService"; + private static final String DATA_TRANSPORT_SERVICE_PID = "org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport"; + + private static final String DATA_SERVICE_REFERENCE_NAME = "DataService" + + ComponentConstants.REFERENCE_TARGET_SUFFIX; + private static final String DATA_TRANSPORT_SERVICE_REFERENCE_NAME = "DataTransportService" + + ComponentConstants.REFERENCE_TARGET_SUFFIX; + + private static final String REFERENCE_TARGET_VALUE_FORMAT = "(" + ConfigurationService.KURA_SERVICE_PID + "=%s)"; + + private ConfigurationService configurationService; + + public void setConfigurationService(ConfigurationService configurationService) { + this.configurationService = configurationService; + } + + @Override + public String getFactoryPid() { + return CLOUD_ENDPOINT_FACTORY_PID; + } + + @Override + public void createConfiguration(String pid) throws KuraException { + String dataServicePid = getStackPidWithSuffix(pid, DATA_SERVICE_PID); + String dataTransportServicePid = getStackPidWithSuffix(pid, DATA_TRANSPORT_SERVICE_PID); + + // CloudEndpoint + Map cloudEndpointProperties = new HashMap<>(); + cloudEndpointProperties.put(DATA_SERVICE_REFERENCE_NAME, + String.format(REFERENCE_TARGET_VALUE_FORMAT, dataServicePid)); + cloudEndpointProperties.put(KURA_CLOUD_CONNECTION_FACTORY_PID, FACTORY_PID); + + this.configurationService.createFactoryConfiguration(CLOUD_ENDPOINT_FACTORY_PID, pid, cloudEndpointProperties, + false); + + // DataService + Map dataServiceProperties = new HashMap<>(); + dataServiceProperties.put(DATA_TRANSPORT_SERVICE_REFERENCE_NAME, + String.format(REFERENCE_TARGET_VALUE_FORMAT, dataTransportServicePid)); + + this.configurationService.createFactoryConfiguration(DATA_SERVICE_FACTORY_PID, dataServicePid, + dataServiceProperties, false); + + // DataTransportService + this.configurationService.createFactoryConfiguration(DATA_TRANSPORT_SERVICE_FACTORY_PID, + dataTransportServicePid, null, true); + } + + @Override + public List getStackComponentsPids(String pid) throws KuraException { + String dataServicePid = getStackPidWithSuffix(pid, DATA_SERVICE_PID); + String dataTransportServicePid = getStackPidWithSuffix(pid, DATA_TRANSPORT_SERVICE_PID); + + List stackComponentPids = new LinkedList<>(); + stackComponentPids.add(pid); + stackComponentPids.add(dataServicePid); + stackComponentPids.add(dataTransportServicePid); + + return stackComponentPids; + } + + @Override + public void deleteConfiguration(String pid) throws KuraException { + String dataServicePid = getStackPidWithSuffix(pid, DATA_SERVICE_PID); + String dataTransportServicePid = getStackPidWithSuffix(pid, DATA_TRANSPORT_SERVICE_PID); + + this.configurationService.deleteFactoryConfiguration(pid, false); + this.configurationService.deleteFactoryConfiguration(dataServicePid, false); + this.configurationService.deleteFactoryConfiguration(dataTransportServicePid, true); + } + + @Override + public Set getManagedCloudConnectionPids() throws KuraException { + final BundleContext context = FrameworkUtil.getBundle(SparkplugCloudConnectionFactory.class).getBundleContext(); + + try { + return context + .getServiceReferences(CloudEndpoint.class, + "(service.factoryPid=" + CLOUD_ENDPOINT_FACTORY_PID + ")") + .stream().map(ref -> (String) ref.getProperty(ConfigurationService.KURA_SERVICE_PID)) + .collect(Collectors.toSet()); + } catch (InvalidSyntaxException e) { + throw new KuraException(KuraErrorCode.CONFIGURATION_ATTRIBUTE_INVALID, e); + } + } + + private String getStackPidWithSuffix(String userPid, String componentPid) throws KuraException { + if (!userPid.startsWith(CLOUD_ENDPOINT_PID)) { + throw new KuraException(KuraErrorCode.INVALID_PARAMETER, "Invalid PID '{}'", userPid); + } + + String[] parts = userPid.split("-"); + + if (parts.length > 1) { + return componentPid + "-" + parts[1]; + } else { + return componentPid; + } + } + +} diff --git a/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/transport/SparkplugDataTransport.java b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/transport/SparkplugDataTransport.java new file mode 100644 index 00000000000..43bd5e21d0a --- /dev/null +++ b/kura/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider/src/main/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/transport/SparkplugDataTransport.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2023 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + *******************************************************************************/ +package org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport; + +import java.util.Map; + +import org.eclipse.kura.KuraConnectException; +import org.eclipse.kura.KuraException; +import org.eclipse.kura.KuraNotConnectedException; +import org.eclipse.kura.KuraTimeoutException; +import org.eclipse.kura.KuraTooManyInflightMessagesException; +import org.eclipse.kura.configuration.ConfigurableComponent; +import org.eclipse.kura.data.DataTransportService; +import org.eclipse.kura.data.DataTransportToken; +import org.eclipse.kura.data.transport.listener.DataTransportListener; + +public class SparkplugDataTransport implements ConfigurableComponent, DataTransportService { + + /* + * Activation APIs + */ + + public void activate(Map properties) { + + } + + public void update(Map properties) { + + } + + public void deactivate() { + + } + + /* + * DataTransportService APIs + */ + + @Override + public void connect() throws KuraConnectException { + // TODO Auto-generated method stub + + } + + @Override + public boolean isConnected() { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getBrokerUrl() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getAccountName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getUsername() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getClientId() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void disconnect(long quiesceTimeout) { + // TODO Auto-generated method stub + + } + + @Override + public void subscribe(String topic, int qos) throws KuraTimeoutException, KuraException, KuraNotConnectedException { + // TODO Auto-generated method stub + + } + + @Override + public void unsubscribe(String topic) throws KuraTimeoutException, KuraException, KuraNotConnectedException { + // TODO Auto-generated method stub + + } + + @Override + public DataTransportToken publish(String topic, byte[] payload, int qos, boolean retain) + throws KuraTooManyInflightMessagesException, KuraException, KuraNotConnectedException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void addDataTransportListener(DataTransportListener listener) { + // TODO Auto-generated method stub + + } + + @Override + public void removeDataTransportListener(DataTransportListener listener) { + // TODO Auto-generated method stub + + } + +} diff --git a/kura/pom.xml b/kura/pom.xml index 70d9b68fd39..a3762391ef7 100644 --- a/kura/pom.xml +++ b/kura/pom.xml @@ -129,6 +129,7 @@ org.eclipse.kura.wire.script.tools org.eclipse.kura.db.sqlite.provider org.eclipse.kura.rest.network.status.provider + org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider emulator test-util diff --git a/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/META-INF/MANIFEST.MF b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..e2582bee1e7 --- /dev/null +++ b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/META-INF/MANIFEST.MF @@ -0,0 +1,13 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test +Bundle-SymbolicName: org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test;singleton:=true +Bundle-Version: 5.5.0.qualifier +Bundle-Vendor: Eclipse Kura +Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))" +Bundle-ActivationPolicy: lazy +Fragment-Host: org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider +Import-Package: org.junit;version="[4.0.0,5.0.0)", + org.junit.runners;version="[4.12,5.0)", + org.mockito;version="[4.0.0,5.0.0)", + org.mockito.stubbing;version="[4.0.0,5.0.0)" diff --git a/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/about.html b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/about.html new file mode 100644 index 00000000000..ec5809fefb9 --- /dev/null +++ b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/about.html @@ -0,0 +1,36 @@ + + + + + About + + +

About This Content

+ +

November 30, 2017

+

License

+ +

+ The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content + is provided to you under the terms and conditions of the Eclipse + Public License Version 2.0 ("EPL"). A copy of the EPL is + available at http://www.eclipse.org/legal/epl-2.0. + For purposes of the EPL, "Program" will mean the Content. +

+ +

+ If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may + apply to your use of any object code in the Content. Check the + Redistributor's license that was provided with the Content. If no such + license exists, contact the Redistributor. Unless otherwise indicated + below, the terms and conditions of the EPL still apply to any source + code in the Content and such source code may be obtained at http://www.eclipse.org. +

+ + + \ No newline at end of file diff --git a/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/build.properties b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/build.properties new file mode 100644 index 00000000000..269bf94d7cd --- /dev/null +++ b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/build.properties @@ -0,0 +1,20 @@ +# +# Copyright (c) 2023 Eurotech and/or its affiliates and others +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Eurotech +# + +bin.includes = .,\ + META-INF/,\ + about.html +source.. = src/test/java/ +additional.bundles = org.eclipse.kura.api,\ + slf4j.api,\ + org.apache.logging.log4j.api diff --git a/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/pom.xml b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/pom.xml new file mode 100644 index 00000000000..ce25beb530a --- /dev/null +++ b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/pom.xml @@ -0,0 +1,66 @@ + + + + 4.0.0 + + + org.eclipse.kura + test + 5.5.0-SNAPSHOT + + + org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test + eclipse-test-plugin + + + ${project.basedir}/../.. + ${project.build.directory}/site/jacoco-aggregate/jacoco.xml + + + + + + org.jacoco + jacoco-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + compiletests + test-compile + + testCompile + + + + + + org.eclipse.tycho + tycho-surefire-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.eclipse.tycho + target-platform-configuration + + + + diff --git a/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/CloudDeliveryListenerTest.java b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/CloudDeliveryListenerTest.java new file mode 100644 index 00000000000..0b560ac6af1 --- /dev/null +++ b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/CloudDeliveryListenerTest.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2023 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + *******************************************************************************/ +package org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.eclipse.kura.cloudconnection.listener.CloudDeliveryListener; +import org.junit.Test; + +public class CloudDeliveryListenerTest extends StepsCollection { + + private CloudDeliveryListener cloudDeliveryListener = mock(CloudDeliveryListener.class); + + /* + * Scenarios + */ + + @Test + public void shouldNotifyOnMessageConfirmed() { + givenCloudDeliveryListener(this.cloudDeliveryListener); + + whenOnMessageConfirmed(121, "test"); + + thenCloudDeliveryListenerNotifiedOnMessageConfirmed("121", 1); + } + + @Test + public void shouldNotNotifyOnMessageConfirmed() { + givenCloudDeliveryListener(this.cloudDeliveryListener); + givenUnregisterCloudDeliveryListener(this.cloudDeliveryListener); + + whenOnMessageConfirmed(121, "test"); + + thenCloudDeliveryListenerNotifiedOnMessageConfirmed("121", 0); + } + + /* + * Steps + */ + + private void thenCloudDeliveryListenerNotifiedOnMessageConfirmed(String expectedMessageId, int expectedTimes) { + verify(this.cloudDeliveryListener, times(expectedTimes)).onMessageConfirmed(expectedMessageId); + } + +} diff --git a/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/ConnectionStatusCallbackTest.java b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/ConnectionStatusCallbackTest.java new file mode 100644 index 00000000000..b7263c35c7c --- /dev/null +++ b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/ConnectionStatusCallbackTest.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2023 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + *******************************************************************************/ +package org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.test; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.eclipse.kura.cloud.CloudConnectionEstablishedEvent; +import org.eclipse.kura.cloud.CloudConnectionLostEvent; +import org.eclipse.kura.cloudconnection.listener.CloudConnectionListener; +import org.junit.Test; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; + +public class ConnectionStatusCallbackTest extends StepsCollection { + + private CloudConnectionListener listener = mock(CloudConnectionListener.class); + private EventAdmin eventAdmin = mock(EventAdmin.class); + + /* + * Scenarios + */ + + @Test + public void shouldNotifyOnConnectionEstabilishedAndPostEvent() { + givenEventAdmin(this.eventAdmin); + givenCloudConnectionListener(this.listener); + + whenOnConnectionEstabilished(); + + thenCloudConnectionListenerNotifiedOnConnectionEstabilished(1); + thenEventAdminPostedEvent(CloudConnectionEstablishedEvent.class); + } + + @Test + public void shouldNotNotifyOnConnectionEstabilishedButShouldPostEvent() { + givenEventAdmin(this.eventAdmin); + givenCloudConnectionListener(this.listener); + givenUnregisterCloudConnectionListener(this.listener); + + whenOnConnectionEstabilished(); + + thenCloudConnectionListenerNotifiedOnConnectionEstabilished(0); + thenEventAdminPostedEvent(CloudConnectionEstablishedEvent.class); + } + + @Test + public void shouldNotifyOnDisconnectedAndPostEvent() { + givenEventAdmin(this.eventAdmin); + givenCloudConnectionListener(this.listener); + + whenOnDisconnected(); + + thenCloudConnectionListenerNotifiedOnDisconnected(1); + thenEventAdminPostedEvent(CloudConnectionLostEvent.class); + } + + @Test + public void shouldNotNotifyOnDisconnectedButShouldPostEvent() { + givenEventAdmin(this.eventAdmin); + givenCloudConnectionListener(this.listener); + givenUnregisterCloudConnectionListener(this.listener); + + whenOnDisconnected(); + + thenCloudConnectionListenerNotifiedOnDisconnected(0); + thenEventAdminPostedEvent(CloudConnectionLostEvent.class); + } + + @Test + public void shouldNotifyOnConnectionLostAndPostEvent() { + givenEventAdmin(this.eventAdmin); + givenCloudConnectionListener(this.listener); + + whenOnConnectionLost(); + + thenCloudConnectionListenerNotifiedOnConnectionLost(1); + thenEventAdminPostedEvent(CloudConnectionLostEvent.class); + } + + @Test + public void shouldNotNotifyOnConnectionLostButShouldPostEvent() { + givenEventAdmin(this.eventAdmin); + givenCloudConnectionListener(this.listener); + givenUnregisterCloudConnectionListener(this.listener); + + whenOnConnectionLost(); + + thenCloudConnectionListenerNotifiedOnConnectionLost(0); + thenEventAdminPostedEvent(CloudConnectionLostEvent.class); + } + + /* + * Steps + */ + + private void thenCloudConnectionListenerNotifiedOnConnectionEstabilished(int expectedTimes) { + verify(this.listener, times(expectedTimes)).onConnectionEstablished(); + } + + private void thenCloudConnectionListenerNotifiedOnDisconnected(int expectedTimes) { + verify(this.listener, times(expectedTimes)).onDisconnected(); + } + + private void thenCloudConnectionListenerNotifiedOnConnectionLost(int expectedTimes) { + verify(this.listener, times(expectedTimes)).onConnectionLost(); + } + + private void thenEventAdminPostedEvent(Class eventType) { + verify(this.eventAdmin, times(1)).postEvent(any(eventType)); + } + +} diff --git a/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/DataServiceTest.java b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/DataServiceTest.java new file mode 100644 index 00000000000..f590a75a1a9 --- /dev/null +++ b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/DataServiceTest.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2023 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + *******************************************************************************/ +package org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.eclipse.kura.KuraConnectException; +import org.eclipse.kura.KuraDisconnectException; +import org.eclipse.kura.configuration.ConfigurationService; +import org.eclipse.kura.data.DataService; +import org.eclipse.kura.data.listener.DataServiceListener; +import org.junit.Test; + +public class DataServiceTest extends StepsCollection { + + private DataService dataService = mock(DataService.class); + + /* + * Scenarios + */ + + @Test + public void shouldRegisterAsDataServiceListenerToDataService() { + givenDataService(this.dataService); + + whenActivate(new PropertiesBuilder().add(ConfigurationService.KURA_SERVICE_PID, "test-pid").build()); + + thenDataServiceAddedDataServiceListener(this.endpoint); + } + + @Test + public void shouldCallDataServiceIsConnected() { + givenDataService(this.dataService); + + whenIsConnected(); + + thenDataServiceCalledIsConnected(); + } + + @Test + public void shouldCallDataServiceDisconnect() throws KuraDisconnectException { + givenDataService(this.dataService); + + whenDisconnect(); + + thenDataServiceCalledDisconnect(0); + } + + @Test + public void shouldCallDataServiceConnect() throws KuraConnectException { + givenDataService(this.dataService); + + whenConnect(); + + thenDataServiceCalledConnect(); + } + + /* + * Steps + */ + + private void thenDataServiceAddedDataServiceListener(DataServiceListener listener) { + verify(this.dataService, times(1)).addDataServiceListener(listener); + } + + private void thenDataServiceCalledIsConnected() { + verify(this.dataService, times(1)).isConnected(); + } + + private void thenDataServiceCalledDisconnect(long expectedQuiesceTimeout) { + verify(this.dataService, times(1)).disconnect(expectedQuiesceTimeout); + } + + private void thenDataServiceCalledConnect() throws KuraConnectException { + verify(this.dataService, times(1)).connect(); + } + +} diff --git a/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/StepsCollection.java b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/StepsCollection.java new file mode 100644 index 00000000000..38825a1bfad --- /dev/null +++ b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/endpoint/test/StepsCollection.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2023 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + *******************************************************************************/ +package org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.test; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.kura.KuraConnectException; +import org.eclipse.kura.KuraDisconnectException; +import org.eclipse.kura.cloudconnection.listener.CloudConnectionListener; +import org.eclipse.kura.cloudconnection.listener.CloudDeliveryListener; +import org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint; +import org.eclipse.kura.data.DataService; +import org.osgi.service.event.EventAdmin; + +public class StepsCollection { + + SparkplugCloudEndpoint endpoint = new SparkplugCloudEndpoint(); + + /* + * Given + */ + + void givenDataService(DataService dataService) { + this.endpoint.setDataService(dataService); + } + + void givenEventAdmin(EventAdmin eventAdmin) { + this.endpoint.setEventAdmin(eventAdmin); + } + + void givenCloudConnectionListener(CloudConnectionListener listener) { + this.endpoint.registerCloudConnectionListener(listener); + } + + void givenUnregisterCloudConnectionListener(CloudConnectionListener listener) { + this.endpoint.unregisterCloudConnectionListener(listener); + } + + void givenCloudDeliveryListener(CloudDeliveryListener listener) { + this.endpoint.registerCloudDeliveryListener(listener); + } + + void givenUnregisterCloudDeliveryListener(CloudDeliveryListener listener) { + this.endpoint.unregisterCloudDeliveryListener(listener); + } + + /* + * When + */ + + void whenOnConnectionEstabilished() { + this.endpoint.onConnectionEstablished(); + } + + void whenOnDisconnected() { + this.endpoint.onDisconnected(); + } + + void whenOnConnectionLost() { + this.endpoint.onConnectionLost(new Throwable()); + } + + void whenActivate(Map properties) { + this.endpoint.activate(properties); + } + + void whenIsConnected() { + this.endpoint.isConnected(); + } + + void whenDisconnect() throws KuraDisconnectException { + this.endpoint.disconnect(); + } + + void whenConnect() throws KuraConnectException { + this.endpoint.connect(); + } + + void whenOnMessageConfirmed(int messageId, String topic) { + this.endpoint.onMessageConfirmed(messageId, topic); + } + + /* + * Then + */ + + /* + * Utilities + */ + + class PropertiesBuilder { + + private Map properties = new HashMap<>(); + + public PropertiesBuilder add(String key, Object value) { + this.properties.put(key, value); + return this; + } + + public Map build() { + return this.properties; + } + + } + + +} diff --git a/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/factory/test/SparkplugCloudConnectionFactoryTest.java b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/factory/test/SparkplugCloudConnectionFactoryTest.java new file mode 100644 index 00000000000..09d155ab22d --- /dev/null +++ b/kura/test/org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test/src/test/java/org/eclipse/kura/cloudconnection/sparkplug/mqtt/factory/test/SparkplugCloudConnectionFactoryTest.java @@ -0,0 +1,236 @@ +/******************************************************************************* + * Copyright (c) 2023 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + *******************************************************************************/ +package org.eclipse.kura.cloudconnection.sparkplug.mqtt.factory.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.kura.KuraException; +import org.eclipse.kura.cloudconnection.factory.CloudConnectionFactory; +import org.eclipse.kura.cloudconnection.sparkplug.mqtt.factory.SparkplugCloudConnectionFactory; +import org.eclipse.kura.configuration.ConfigurationService; +import org.junit.Before; +import org.junit.Test; + +public class SparkplugCloudConnectionFactoryTest { + + private SparkplugCloudConnectionFactory factory = new SparkplugCloudConnectionFactory(); + private ConfigurationService configuratioServiceMock = mock(ConfigurationService.class); + private String returnedFactoryPid; + private Exception occurredException; + private List returnedStackComponentPids; + + /* + * Scenarios + */ + + @Test + public void shouldReturnCorrectFactoryPid() { + whenGetFactoryPid(); + thenReturnedFactoryPidEquals("org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint"); + } + + @Test + public void shouldFailWithOtherComponentPid() { + whenCreateConfiguration("org.eclipse.kura.cloudconnection.nonsparkplug.mqtt.endpoint.Example"); + + thenExceptionOccurred(KuraException.class); + } + + @Test + public void shouldCreateCloudStackComponentsWithSuffix() throws KuraException { + whenCreateConfiguration("org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint-test"); + + thenFactoryComponentIsCreated( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint", + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint-test", + new ExpectedPropertiesBuilder() + .withProperty("DataService.target", + "(kura.service.pid=org.eclipse.kura.cloudconnection.sparkplug.mqtt.data.SparkplugDataService-test)") + .withProperty(CloudConnectionFactory.KURA_CLOUD_CONNECTION_FACTORY_PID, + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.factory.SparkplugCloudConnectionFactory") + .build(), + false); + thenFactoryComponentIsCreated( + "org.eclipse.kura.data.DataService", + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.data.SparkplugDataService-test", + new ExpectedPropertiesBuilder() + .withProperty("DataTransportService.target", + "(kura.service.pid=org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport-test)") + .build(), + false); + thenFactoryComponentIsCreated( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport", + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport-test", + null, + true); + } + + @Test + public void shouldCreateCloudStackComponentsWithoutSuffix() throws KuraException { + whenCreateConfiguration("org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint"); + + thenFactoryComponentIsCreated( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint", + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint", + new ExpectedPropertiesBuilder() + .withProperty("DataService.target", + "(kura.service.pid=org.eclipse.kura.cloudconnection.sparkplug.mqtt.data.SparkplugDataService)") + .withProperty(CloudConnectionFactory.KURA_CLOUD_CONNECTION_FACTORY_PID, + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.factory.SparkplugCloudConnectionFactory") + .build(), + false); + thenFactoryComponentIsCreated( + "org.eclipse.kura.data.DataService", + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.data.SparkplugDataService", + new ExpectedPropertiesBuilder() + .withProperty("DataTransportService.target", + "(kura.service.pid=org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport)") + .build(), + false); + thenFactoryComponentIsCreated( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport", + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport", + null, + true); + } + + @Test + public void shouldReturnCorrectStackComponentPidsWithSuffix() throws KuraException { + whenGetStackComponentsPids( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint-test"); + + thenReturnedStackComponentPidsContain( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint-test"); + thenReturnedStackComponentPidsContain( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.data.SparkplugDataService-test"); + thenReturnedStackComponentPidsContain( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport-test"); + } + + @Test + public void shouldReturnCorrectStackComponentPidsWithoutSuffix() throws KuraException { + whenGetStackComponentsPids("org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint"); + + thenReturnedStackComponentPidsContain( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint"); + thenReturnedStackComponentPidsContain( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.data.SparkplugDataService"); + thenReturnedStackComponentPidsContain( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport"); + } + + @Test + public void shouldDeleteCloudStackComponentsWithSuffix() throws KuraException { + whenDeleteConfiguration("org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint-test"); + + thenFactoryComponentIsDeleted( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.endpoint.SparkplugCloudEndpoint-test", false); + thenFactoryComponentIsDeleted("org.eclipse.kura.cloudconnection.sparkplug.mqtt.data.SparkplugDataService-test", + false); + thenFactoryComponentIsDeleted( + "org.eclipse.kura.cloudconnection.sparkplug.mqtt.transport.SparkplugDataTransport-test", true); + } + + /* + * Steps + */ + + /* + * Given + */ + + /* + * When + */ + + private void whenGetFactoryPid() { + this.returnedFactoryPid = this.factory.getFactoryPid(); + } + + private void whenCreateConfiguration(String userPid) { + try { + this.factory.createConfiguration(userPid); + } catch (Exception e) { + this.occurredException = e; + } + } + + private void whenGetStackComponentsPids(String userPid) throws KuraException { + this.returnedStackComponentPids = this.factory.getStackComponentsPids(userPid); + } + + private void whenDeleteConfiguration(String userPid) throws KuraException { + this.factory.deleteConfiguration(userPid); + } + + /* + * Then + */ + + private void thenReturnedFactoryPidEquals(String expectedFactoryPid) { + assertEquals(expectedFactoryPid, this.returnedFactoryPid); + } + + private void thenExceptionOccurred(Class expectedException) { + assertNotNull(this.occurredException); + assertEquals(expectedException.getName(), this.occurredException.getClass().getName()); + } + + private void thenFactoryComponentIsCreated(String expectedFactoryPid, String expectedPid, + Map expectedProperties, boolean expectedTakeSnapshot) throws KuraException { + verify(this.configuratioServiceMock, times(1)).createFactoryConfiguration(expectedFactoryPid, expectedPid, + expectedProperties, expectedTakeSnapshot); + } + + private void thenReturnedStackComponentPidsContain(String expectedPid) { + assertTrue(this.returnedStackComponentPids.contains(expectedPid)); + } + + private void thenFactoryComponentIsDeleted(String expectedPid, boolean expectedTakeSnapshot) throws KuraException { + verify(this.configuratioServiceMock, times(1)).deleteFactoryConfiguration(expectedPid, expectedTakeSnapshot); + } + + /* + * Utilities + */ + + @Before + public void setup() { + this.factory.setConfigurationService(this.configuratioServiceMock); + } + + private class ExpectedPropertiesBuilder { + + private Map properties = new HashMap<>(); + + public ExpectedPropertiesBuilder withProperty(String key, Object value) { + this.properties.put(key, value); + return this; + } + + public Map build() { + return this.properties; + } + + } + +} diff --git a/kura/test/pom.xml b/kura/test/pom.xml index 8c7ebde463d..9e1b16c7555 100644 --- a/kura/test/pom.xml +++ b/kura/test/pom.xml @@ -925,7 +925,6 @@ org.eclipse.kura.useradmin.store.test org.eclipse.kura.core.keystore.test org.eclipse.kura.log.filesystem.provider.test - org.eclipse.kura.rest.provider.test org.eclipse.kura.rest.asset.provider.test org.eclipse.kura.rest.configuration.provider.test @@ -941,13 +940,13 @@ org.eclipse.kura.rest.service.listing.provider.test org.eclipse.kura.rest.system.provider.test org.eclipse.kura.rest.wire.provider.test - org.eclipse.kura.util.test.driver org.eclipse.kura.container.orchestration.provider.test org.eclipse.kura.container.provider.test org.eclipse.kura.ai.triton.server.test org.eclipse.kura.db.sqlite.provider.test org.eclipse.kura.message.store.provider.test + org.eclipse.kura.cloudconnection.sparkplug.mqtt.provider.test