This is an automated email from the ASF dual-hosted git repository.

bossenti pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 585e63fa69 feat: provide basic support for IoTDB as time series 
storage (#2835)
585e63fa69 is described below

commit 585e63fa69c61edbcd737b9c8d40db9e23974bd5
Author: Tim <[email protected]>
AuthorDate: Wed May 15 08:58:56 2024 +0200

    feat: provide basic support for IoTDB as time series storage (#2835)
    
    * feat: provide basic support for IotDB as time series storage
    
    * dep: align iotdb versions
    
    * update iotdb source
    
    * fix import order
    
    * improve dependency management
    
    * align module name
    
    * remove singletons
    
    * improve writing of values
---
 pom.xml                                            |   8 +-
 .../influx/DataExplorerManagerInflux.java          |   4 +-
 .../pom.xml                                        |  29 +++-
 .../iotdb/DataExplorerManagerIotDb.java            |  33 ++--
 .../iotdb/IotDbMeasurementRecord.java              |   2 +-
 .../iotdb/IotDbPropertyConverter.java              |   2 +-
 .../dataexplorer}/iotdb/IotDbSessionProvider.java  |   2 +-
 .../dataexplorer/iotdb/TimeSeriesStorageIotDb.java | 175 +++++++++++++++++++++
 .../DataLakeMeasurementSanitizerIotDb.java         |  54 +++++++
 .../iotdb/sanitize}/IotDbNameSanitizer.java        |   2 +-
 .../iotdb/sanitize}/IotDbReservedKeywords.java     |   2 +-
 .../iotdb/sanitize}/MeasureNameSanitizerIotDb.java |   2 +-
 .../iotdb/IotDbPropertyConverterTest.java          |   4 +-
 .../DataLakeMeasurementSanitizerIotDbTest.java     |  75 +++++++++
 .../iotdb/sanitize}/IotDbNameSanitizerTest.java    |   2 +-
 .../sanitize/MeasureNameSanitizerIotDbTest.java    |   4 +-
 streampipes-data-explorer-management/pom.xml       |   5 +
 .../management/DataExplorerDispatcher.java         |   5 +-
 .../management/SupportedDataExplorerStorages.java  |   1 +
 .../streampipes-sinks-databases-jvm/pom.xml        |  15 +-
 .../sinks/databases/jvm/iotdb/IotDbSink.java       |   7 +-
 21 files changed, 382 insertions(+), 51 deletions(-)

diff --git a/pom.xml b/pom.xml
index 72bd0ae237..f631832ea6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,7 @@
         <httpcore.version>4.4.9</httpcore.version>
         <influxdb.version>2.24</influxdb.version>
         <inlong.version>1.12.0</inlong.version>
+        <iotdb.version>1.3.0</iotdb.version>
         <jackson.version>2.17.0</jackson.version>
         <jackson.databind.version>2.17.0</jackson.databind.version>
         <jakarta-annotation.version>3.0.0</jakarta-annotation.version>
@@ -389,6 +390,11 @@
                 <artifactId>httpcore</artifactId>
                 <version>${httpcore.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.iotdb</groupId>
+                <artifactId>iotdb-session</artifactId>
+                <version>${iotdb.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.apache.kafka</groupId>
                 <artifactId>kafka-clients</artifactId>
@@ -825,6 +831,7 @@
         <module>streampipes-data-explorer-api</module>
         <module>streampipes-data-explorer-export</module>
         <module>streampipes-data-explorer-influx</module>
+        <module>streampipes-data-explorer-iotdb</module>
         <module>streampipes-data-explorer-management</module>
         <module>streampipes-data-export</module>
         <module>streampipes-dataformat</module>
@@ -868,7 +875,6 @@
         <module>streampipes-storage-management</module>
         <module>streampipes-test-utils</module>
         <module>streampipes-test-utils-executors</module>
-        <module>streampipes-ts-store-iotdb</module>
         <module>streampipes-user-management</module>
         <module>streampipes-vocabulary</module>
         <module>streampipes-wrapper</module>
diff --git 
a/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
 
b/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
index e79cb8d2bc..1c25d5fbc5 100644
--- 
a/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
+++ 
b/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
@@ -34,9 +34,7 @@ import 
org.apache.streampipes.storage.management.StorageDispatcher;
 
 import java.util.List;
 
-public enum DataExplorerManagerInflux implements IDataExplorerManager {
-
-  INSTANCE;
+public class DataExplorerManagerInflux implements IDataExplorerManager {
 
   @Override
   public IDataLakeMeasurementCounter getMeasurementCounter(
diff --git a/streampipes-ts-store-iotdb/pom.xml 
b/streampipes-data-explorer-iotdb/pom.xml
similarity index 74%
rename from streampipes-ts-store-iotdb/pom.xml
rename to streampipes-data-explorer-iotdb/pom.xml
index ff294b445d..763c932a36 100644
--- a/streampipes-ts-store-iotdb/pom.xml
+++ b/streampipes-data-explorer-iotdb/pom.xml
@@ -24,7 +24,7 @@
         <version>0.97.0-SNAPSHOT</version>
     </parent>
 
-    <artifactId>streampipes-ts-store-iotdb</artifactId>
+    <artifactId>streampipes-data-explorer-iotdb</artifactId>
 
     <properties>
         <maven.compiler.source>17</maven.compiler.source>
@@ -44,6 +44,11 @@
             <artifactId>streampipes-commons</artifactId>
             <version>0.97.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.streampipes</groupId>
+            <artifactId>streampipes-data-explorer-api</artifactId>
+            <version>0.97.0-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>org.apache.streampipes</groupId>
             <artifactId>streampipes-data-explorer</artifactId>
@@ -64,7 +69,16 @@
         <dependency>
             <groupId>org.apache.iotdb</groupId>
             <artifactId>iotdb-session</artifactId>
-            <version>1.3.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.xerial.snappy</groupId>
+                    <artifactId>snappy-java</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.github.luben</groupId>
+                    <artifactId>zstd-jni</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -72,11 +86,22 @@
         </dependency>
 
         <!--Test dependencies -->
+        <dependency>
+            <groupId>org.apache.streampipes</groupId>
+            <artifactId>streampipes-test-utils</artifactId>
+            <version>0.97.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-api</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git 
a/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/DataExplorerManagerIotDb.java
similarity index 54%
copy from 
streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
copy to 
streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/DataExplorerManagerIotDb.java
index e79cb8d2bc..5457ec166d 100644
--- 
a/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/DataExplorerManagerIotDb.java
@@ -16,40 +16,27 @@
  *
  */
 
-package org.apache.streampipes.dataexplorer.influx;
+package org.apache.streampipes.dataexplorer.iotdb;
 
 import org.apache.streampipes.client.api.IStreamPipesClient;
-import org.apache.streampipes.commons.environment.Environments;
 import org.apache.streampipes.dataexplorer.DataExplorerSchemaManagement;
-import org.apache.streampipes.dataexplorer.api.IDataExplorerQueryManagement;
-import org.apache.streampipes.dataexplorer.api.IDataExplorerSchemaManagement;
-import org.apache.streampipes.dataexplorer.api.IDataExplorerManager;
-import org.apache.streampipes.dataexplorer.api.IDataLakeMeasurementCounter;
-import org.apache.streampipes.dataexplorer.api.IDataLakeMeasurementSanitizer;
-import org.apache.streampipes.dataexplorer.api.ITimeSeriesStorage;
-import org.apache.streampipes.dataexplorer.influx.client.InfluxClientProvider;
-import 
org.apache.streampipes.dataexplorer.influx.sanitize.DataLakeMeasurementSanitizerInflux;
+import org.apache.streampipes.dataexplorer.api.*;
+import 
org.apache.streampipes.dataexplorer.iotdb.sanitize.DataLakeMeasurementSanitizerIotDb;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 
 import java.util.List;
 
-public enum DataExplorerManagerInflux implements IDataExplorerManager {
-
-  INSTANCE;
+public class DataExplorerManagerIotDb implements IDataExplorerManager {
 
   @Override
-  public IDataLakeMeasurementCounter getMeasurementCounter(
-    List<DataLakeMeasure> allMeasurements,
-    List<String> measurementsToCount) {
-    return new DataLakeMeasurementCounterInflux(allMeasurements, 
measurementsToCount);
+  public IDataLakeMeasurementCounter 
getMeasurementCounter(List<DataLakeMeasure> allMeasurements, List<String> 
measurementsToCount) {
+    return null;
   }
 
   @Override
-  public IDataExplorerQueryManagement getQueryManagement(
-    IDataExplorerSchemaManagement dataExplorerSchemaManagement
-    ) {
-    return new DataExplorerQueryManagementInflux(dataExplorerSchemaManagement);
+  public IDataExplorerQueryManagement 
getQueryManagement(IDataExplorerSchemaManagement dataExplorerSchemaManagement) {
+    return null;
   }
 
   @Override
@@ -61,11 +48,11 @@ public enum DataExplorerManagerInflux implements 
IDataExplorerManager {
 
   @Override
   public ITimeSeriesStorage getTimeseriesStorage(DataLakeMeasure measure) {
-    return new TimeSeriesStorageInflux(measure, Environments.getEnvironment(), 
new InfluxClientProvider());
+    return new TimeSeriesStorageIotDb(measure, new IotDbPropertyConverter(), 
new IotDbSessionProvider());
   }
 
   @Override
   public IDataLakeMeasurementSanitizer 
getMeasurementSanitizer(IStreamPipesClient client, DataLakeMeasure measure) {
-    return new DataLakeMeasurementSanitizerInflux(client, measure);
+    return new DataLakeMeasurementSanitizerIotDb(client, measure);
   }
 }
diff --git 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbMeasurementRecord.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/IotDbMeasurementRecord.java
similarity index 96%
rename from 
streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbMeasurementRecord.java
rename to 
streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/IotDbMeasurementRecord.java
index 63cb18f4b4..24591da3bd 100644
--- 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbMeasurementRecord.java
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/IotDbMeasurementRecord.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.streampipes.ts.store.iotdb;
+package org.apache.streampipes.dataexplorer.iotdb;
 
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 
diff --git 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbPropertyConverter.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/IotDbPropertyConverter.java
similarity index 98%
rename from 
streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbPropertyConverter.java
rename to 
streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/IotDbPropertyConverter.java
index c0793ba9eb..9772c39c44 100644
--- 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbPropertyConverter.java
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/IotDbPropertyConverter.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.streampipes.ts.store.iotdb;
+package org.apache.streampipes.dataexplorer.iotdb;
 
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.streampipes.commons.exceptions.SpRuntimeException;
diff --git 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbSessionProvider.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/IotDbSessionProvider.java
similarity index 97%
rename from 
streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbSessionProvider.java
rename to 
streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/IotDbSessionProvider.java
index 4a46ede043..6ff4bc38ca 100644
--- 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbSessionProvider.java
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/IotDbSessionProvider.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.streampipes.ts.store.iotdb;
+package org.apache.streampipes.dataexplorer.iotdb;
 
 import org.apache.iotdb.session.pool.SessionPool;
 import org.apache.streampipes.commons.environment.Environment;
diff --git 
a/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/TimeSeriesStorageIotDb.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/TimeSeriesStorageIotDb.java
new file mode 100644
index 0000000000..c233580d1f
--- /dev/null
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/TimeSeriesStorageIotDb.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.dataexplorer.iotdb;
+
+import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.rpc.StatementExecutionException;
+import org.apache.iotdb.session.pool.SessionPool;
+import org.apache.streampipes.commons.environment.Environments;
+import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+import org.apache.streampipes.dataexplorer.TimeSeriesStorage;
+import org.apache.streampipes.dataexplorer.iotdb.sanitize.IotDbNameSanitizer;
+import org.apache.streampipes.model.datalake.DataLakeMeasure;
+import org.apache.streampipes.model.runtime.Event;
+import org.apache.streampipes.model.schema.EventPropertyPrimitive;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TimeSeriesStorageIotDb extends TimeSeriesStorage {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(TimeSeriesStorageIotDb.class);
+
+  private final IotDbPropertyConverter propertyConverter;
+  private final SessionPool sessionPool;
+
+  public TimeSeriesStorageIotDb(DataLakeMeasure measure,
+                                IotDbPropertyConverter propertyConverter,
+                                IotDbSessionProvider iotDbSessionProvider) {
+    super(measure);
+    this.propertyConverter = propertyConverter;
+    this.sessionPool = 
iotDbSessionProvider.getSessionPool(Environments.getEnvironment());
+  }
+
+  @Override
+  protected void storeSanitizedRuntimeNames() {
+    measure.getEventSchema()
+        .getEventProperties()
+        .forEach(ep -> sanitizedRuntimeNames.put(
+            ep.getRuntimeName(),
+            new 
IotDbNameSanitizer().renameReservedKeywords(ep.getRuntimeName())
+        ));
+  }
+
+  @Override
+  protected void sanitizeRuntimeNamesInEvent(Event event) {
+    event.getRaw()
+        .keySet()
+        .forEach(runtimeName -> {
+              // timestamp field does not need to be renamed since it is not 
written as measurement
+              // and taken care of separately
+              if (!runtimeName.equals(measure.getTimestampFieldName())) {
+                event.renameFieldByRuntimeName(
+                    runtimeName,
+                    new 
IotDbNameSanitizer().renameReservedKeywords(runtimeName)
+                );
+              }
+            }
+        );
+  }
+
+  @Override
+  protected void writeToTimeSeriesStorage(Event event) throws 
SpRuntimeException {
+    if (event == null) {
+      LOG.warn("Input event is null - skipping event");
+      return;
+    }
+
+    var timestampValue = event.getFieldBySelector(measure.getTimestampField())
+        .getAsPrimitive()
+        .getAsLong();
+
+    if (timestampValue == null) {
+      LOG.warn("Timestamp of input event is null - skipping event");
+      return;
+    }
+
+    if (event.getRaw().size() <= 1) {
+      LOG.warn("Event only consists of timestamp and does not include any 
measurement - skipping event");
+      return;
+    }
+
+    var iotDbRecords = extractMeasurementRecords(event);
+
+    if (iotDbRecords.isEmpty()) {
+      return;
+    }
+
+    insertIntoIotDb(timestampValue, iotDbRecords);
+  }
+
+  @Override
+  public void close() throws SpRuntimeException {
+    this.sessionPool.close();
+  }
+
+  /**
+   * Extracts all relevant information for the IotDb from the incoming event.
+   *
+   * @param event The event from which measurement records are extracted.
+   * @return An ArrayList of IotDbMeasurementRecord objects containing the 
extracted measurement records.
+   */
+  private ArrayList<IotDbMeasurementRecord> extractMeasurementRecords(Event 
event) {
+    var iotDbRecords = new ArrayList<IotDbMeasurementRecord>();
+    allEventProperties.forEach(ep -> {
+      try {
+        // timestamp is already known and is not part of the measurement thus 
we ignore it here
+        if (!ep.getRuntimeName().equals(measure.getTimestampFieldName())) {
+
+          try {
+            var eventField = 
event.getFieldByRuntimeName(ep.getRuntimeName()).getAsPrimitive();
+
+            if (ep instanceof EventPropertyPrimitive) {
+              iotDbRecords.add(propertyConverter.convertPrimitiveProperty(
+                  (EventPropertyPrimitive) ep,
+                  
event.getFieldByRuntimeName(ep.getRuntimeName()).getAsPrimitive(),
+                  sanitizedRuntimeNames.get(ep.getRuntimeName())
+              ));
+            } else {
+              iotDbRecords.add(propertyConverter.convertNonPrimitiveProperty(
+                  ep,
+                  sanitizedRuntimeNames.get(ep.getRuntimeName())
+              ));
+            }
+          } catch (SpRuntimeException e) {
+            LOG.debug("Value and metadata for event field '{}' could not be 
extracted - "
+                    + "property will not be written to storage",
+                ep.getRuntimeName()
+            );
+          }
+        }
+      } catch (SpRuntimeException e) {
+        LOG.error("Event could not be converted to the IotDB representation: 
{}", e.getMessage());
+      }
+    });
+    return iotDbRecords;
+  }
+
+  /**
+   * Inserts measurement records into an IoTDB database as an aligned time 
series of one device.
+   *
+   * @param timestampValue The timestamp value associated with the measurement 
records.
+   * @param iotDbRecords   An ArrayList of IotDbMeasurementRecord objects 
containing the measurement records to be inserted.
+   */
+  private void insertIntoIotDb(long timestampValue, 
ArrayList<IotDbMeasurementRecord> iotDbRecords) {
+    try {
+      sessionPool.insertAlignedRecordsOfOneDevice(
+          "root.streampipes.%s".formatted(measure.getMeasureName()),
+          List.of(timestampValue),
+          
List.of(iotDbRecords.stream().map(IotDbMeasurementRecord::measurementName).toList()),
+          
List.of(iotDbRecords.stream().map(IotDbMeasurementRecord::dataType).toList()),
+          
List.of(iotDbRecords.stream().map(IotDbMeasurementRecord::value).toList())
+      );
+    } catch (IoTDBConnectionException | StatementExecutionException e) {
+      LOG.error("Failed to write event to IoTDB  - {}", e.getMessage());
+    }
+  }
+}
diff --git 
a/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/DataLakeMeasurementSanitizerIotDb.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/DataLakeMeasurementSanitizerIotDb.java
new file mode 100644
index 0000000000..bd6637be27
--- /dev/null
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/DataLakeMeasurementSanitizerIotDb.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.dataexplorer.iotdb.sanitize;
+
+import org.apache.streampipes.client.api.IStreamPipesClient;
+import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+import org.apache.streampipes.dataexplorer.DataLakeMeasurementSanitizer;
+import org.apache.streampipes.model.datalake.DataLakeMeasure;
+
+/**
+ * Ensures proper sanitization of data lake measurements with respect to 
Apache IoTDB specifics.
+ * <p>
+ * This includes both sanitizing the measurement name according to IoTDB's 
path specifications,
+ * because the measurement name is used as part of the path in IoTDB,
+ * and sanitizing the runtime names of the event properties so that they do 
not match any reserved keywords.
+ *
+ * @see <a 
href="https://iotdb.apache.org/UserGuide/latest/Basic-Concept/Data-Model-and-Terminology.html#path";>IotDB
 Path Spec</a>
+ */
+public class DataLakeMeasurementSanitizerIotDb extends 
DataLakeMeasurementSanitizer {
+  public DataLakeMeasurementSanitizerIotDb(IStreamPipesClient client, 
DataLakeMeasure measure) {
+    super(client, measure);
+  }
+
+  @Override
+  protected void cleanDataLakeMeasure() throws SpRuntimeException {
+      measure.setMeasureName(new 
MeasureNameSanitizerIotDb().sanitize(measure.getMeasureName()));
+
+      measure.getEventSchema()
+             .getEventProperties()
+             .forEach(eventProperty -> eventProperty.setRuntimeName(
+                 new 
IotDbNameSanitizer().renameReservedKeywords(eventProperty.getRuntimeName()))
+             );
+  }
+
+  protected DataLakeMeasure getMeasure() {
+    return measure;
+  }
+}
diff --git 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbNameSanitizer.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/IotDbNameSanitizer.java
similarity index 96%
rename from 
streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbNameSanitizer.java
rename to 
streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/IotDbNameSanitizer.java
index 8d66978dc9..0f9c4cfe36 100644
--- 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbNameSanitizer.java
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/IotDbNameSanitizer.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.streampipes.ts.store.iotdb;
+package org.apache.streampipes.dataexplorer.iotdb.sanitize;
 
 public class IotDbNameSanitizer {
 
diff --git 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbReservedKeywords.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/IotDbReservedKeywords.java
similarity index 98%
rename from 
streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbReservedKeywords.java
rename to 
streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/IotDbReservedKeywords.java
index 5813cffce1..11c2530b2a 100644
--- 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/IotDbReservedKeywords.java
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/IotDbReservedKeywords.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.streampipes.ts.store.iotdb;
+package org.apache.streampipes.dataexplorer.iotdb.sanitize;
 
 import java.util.Arrays;
 import java.util.List;
diff --git 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/MeasureNameSanitizerIotDb.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/MeasureNameSanitizerIotDb.java
similarity index 97%
rename from 
streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/MeasureNameSanitizerIotDb.java
rename to 
streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/MeasureNameSanitizerIotDb.java
index bc30a4ed2d..0cd93a45f9 100644
--- 
a/streampipes-ts-store-iotdb/src/main/java/org/apache/streampipes/ts/store/iotdb/MeasureNameSanitizerIotDb.java
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/MeasureNameSanitizerIotDb.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.streampipes.ts.store.iotdb;
+package org.apache.streampipes.dataexplorer.iotdb.sanitize;
 
 /**
  * Ensures that measurement names comply with IoTDB path specifications.
diff --git 
a/streampipes-ts-store-iotdb/src/test/java/org/apache/streampipes/ts/store/iotdb/IotDbPropertyConverterTest.java
 
b/streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/IotDbPropertyConverterTest.java
similarity index 96%
rename from 
streampipes-ts-store-iotdb/src/test/java/org/apache/streampipes/ts/store/iotdb/IotDbPropertyConverterTest.java
rename to 
streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/IotDbPropertyConverterTest.java
index 29d0aab2ae..5b7085784e 100644
--- 
a/streampipes-ts-store-iotdb/src/test/java/org/apache/streampipes/ts/store/iotdb/IotDbPropertyConverterTest.java
+++ 
b/streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/IotDbPropertyConverterTest.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.streampipes.ts.store.iotdb;
+package org.apache.streampipes.dataexplorer.iotdb;
 
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.streampipes.commons.exceptions.SpRuntimeException;
@@ -81,7 +81,7 @@ public class IotDbPropertyConverterTest {
   @Test
   public void convertPrimitivePropertyNumber() {
 
-    var property = new EventPropertyPrimitive(SO.NUMBER.toString(), "test", 
null, null);
+    var property = new EventPropertyPrimitive(SO.NUMBER, "test", null, null);
     var field = new PrimitiveField("test", "test", 5.24);
 
     var result = new 
IotDbPropertyConverter().convertPrimitiveProperty(property, field, 
"sanitizedTest");
diff --git 
a/streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/DataLakeMeasurementSanitizerIotDbTest.java
 
b/streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/DataLakeMeasurementSanitizerIotDbTest.java
new file mode 100644
index 0000000000..12e491637d
--- /dev/null
+++ 
b/streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/DataLakeMeasurementSanitizerIotDbTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.dataexplorer.iotdb.sanitize;
+
+import org.apache.streampipes.client.api.IStreamPipesClient;
+import org.apache.streampipes.model.datalake.DataLakeMeasure;
+import org.apache.streampipes.test.generator.EventPropertyPrimitiveTestBuilder;
+import org.apache.streampipes.test.generator.EventSchemaTestBuilder;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class DataLakeMeasurementSanitizerIotDbTest {
+
+  private IStreamPipesClient clientMock;
+
+  @BeforeEach
+  public void setUp() {
+    this.clientMock = mock(IStreamPipesClient.class);
+  }
+
+  @Test
+  public void cleanDataLakeMeasure() {
+    var eventSchema = EventSchemaTestBuilder.create()
+        .withEventProperty(
+            EventPropertyPrimitiveTestBuilder
+                .create()
+                .withRuntimeName("timestamp")
+                .build())
+        .withEventProperty(
+            EventPropertyPrimitiveTestBuilder
+                .create()
+                .withRuntimeName("all")
+                .build())
+        .withEventProperty(
+            EventPropertyPrimitiveTestBuilder
+                .create()
+                .withRuntimeName("pressure")
+                .build())
+        .build();
+    var measure = new DataLakeMeasure(
+        "invalid.Measure",
+        "s0::%s".formatted("timestamp"),
+        eventSchema
+    );
+    var sanitizer = new DataLakeMeasurementSanitizerIotDb(clientMock, measure);
+
+    sanitizer.cleanDataLakeMeasure();
+    var result = sanitizer.getMeasure();
+
+    assertEquals("invalid_Measure", result.getMeasureName());
+    assertEquals(3, result.getEventSchema().getEventProperties().size());
+    assertEquals("timestamp_", 
result.getEventSchema().getEventProperties().get(0).getRuntimeName());
+    assertEquals("all_", 
result.getEventSchema().getEventProperties().get(1).getRuntimeName());
+    assertEquals("pressure", 
result.getEventSchema().getEventProperties().get(2).getRuntimeName());
+  }
+}
diff --git 
a/streampipes-ts-store-iotdb/src/test/java/org/apache/streampipes/ts/store/iotdb/IotDbNameSanitizerTest.java
 
b/streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/IotDbNameSanitizerTest.java
similarity index 95%
rename from 
streampipes-ts-store-iotdb/src/test/java/org/apache/streampipes/ts/store/iotdb/IotDbNameSanitizerTest.java
rename to 
streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/IotDbNameSanitizerTest.java
index 16cbf865b2..5cc0a4ccdc 100644
--- 
a/streampipes-ts-store-iotdb/src/test/java/org/apache/streampipes/ts/store/iotdb/IotDbNameSanitizerTest.java
+++ 
b/streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/IotDbNameSanitizerTest.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.streampipes.ts.store.iotdb;
+package org.apache.streampipes.dataexplorer.iotdb.sanitize;
 
 import org.junit.jupiter.api.Test;
 
diff --git 
a/streampipes-ts-store-iotdb/src/test/java/org/apache/streampipes/ts/store/iotdb/sanitize/MeasureNameSanitizerIotDbTest.java
 
b/streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/MeasureNameSanitizerIotDbTest.java
similarity index 91%
rename from 
streampipes-ts-store-iotdb/src/test/java/org/apache/streampipes/ts/store/iotdb/sanitize/MeasureNameSanitizerIotDbTest.java
rename to 
streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/MeasureNameSanitizerIotDbTest.java
index 6a11f4dee6..886e924391 100644
--- 
a/streampipes-ts-store-iotdb/src/test/java/org/apache/streampipes/ts/store/iotdb/sanitize/MeasureNameSanitizerIotDbTest.java
+++ 
b/streampipes-data-explorer-iotdb/src/test/java/org/apache/streampipes/dataexplorer/iotdb/sanitize/MeasureNameSanitizerIotDbTest.java
@@ -16,9 +16,8 @@
  *
  */
 
-package org.apache.streampipes.ts.store.iotdb.sanitize;
+package org.apache.streampipes.dataexplorer.iotdb.sanitize;
 
-import org.apache.streampipes.ts.store.iotdb.MeasureNameSanitizerIotDb;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -30,6 +29,7 @@ public class MeasureNameSanitizerIotDbTest {
     var sanitizer = new MeasureNameSanitizerIotDb();
 
     assertEquals("myMeasure", sanitizer.sanitize("myMeasure"));
+    assertEquals("my_Measure", sanitizer.sanitize("my.Measure"));
     assertEquals("我的措施", sanitizer.sanitize("我的措施"));
     assertEquals("my_Measure", sanitizer.sanitize("my-Measure"));
     assertEquals("my_Measure_", sanitizer.sanitize("my-Measure?"));
diff --git a/streampipes-data-explorer-management/pom.xml 
b/streampipes-data-explorer-management/pom.xml
index 9e1d6a9600..e82a032919 100644
--- a/streampipes-data-explorer-management/pom.xml
+++ b/streampipes-data-explorer-management/pom.xml
@@ -47,6 +47,11 @@
             <artifactId>streampipes-data-explorer-influx</artifactId>
             <version>0.97.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.streampipes</groupId>
+            <artifactId>streampipes-data-explorer-iotdb</artifactId>
+            <version>0.97.0-SNAPSHOT</version>
+        </dependency>
     </dependencies>
 
     <properties>
diff --git 
a/streampipes-data-explorer-management/src/main/java/org/apache/streampipes/dataexplorer/management/DataExplorerDispatcher.java
 
b/streampipes-data-explorer-management/src/main/java/org/apache/streampipes/dataexplorer/management/DataExplorerDispatcher.java
index 96ba5b70ae..8614c15b57 100644
--- 
a/streampipes-data-explorer-management/src/main/java/org/apache/streampipes/dataexplorer/management/DataExplorerDispatcher.java
+++ 
b/streampipes-data-explorer-management/src/main/java/org/apache/streampipes/dataexplorer/management/DataExplorerDispatcher.java
@@ -21,6 +21,7 @@ package org.apache.streampipes.dataexplorer.management;
 import org.apache.streampipes.commons.environment.Environments;
 import org.apache.streampipes.dataexplorer.api.IDataExplorerManager;
 import org.apache.streampipes.dataexplorer.influx.DataExplorerManagerInflux;
+import org.apache.streampipes.dataexplorer.iotdb.DataExplorerManagerIotDb;
 
 public class DataExplorerDispatcher {
 
@@ -31,8 +32,8 @@ public class DataExplorerDispatcher {
     return switch (Environments.getEnvironment()
                                .getTsStorage()
                                .getValueOrDefault()) {
-      case SupportedDataExplorerStorages.INFLUX_DB -> 
DataExplorerManagerInflux.INSTANCE;
-      default -> DataExplorerManagerInflux.INSTANCE;
+      case SupportedDataExplorerStorages.IOT_DB -> new 
DataExplorerManagerIotDb();
+      default -> new DataExplorerManagerInflux();
     };
   }
 }
diff --git 
a/streampipes-data-explorer-management/src/main/java/org/apache/streampipes/dataexplorer/management/SupportedDataExplorerStorages.java
 
b/streampipes-data-explorer-management/src/main/java/org/apache/streampipes/dataexplorer/management/SupportedDataExplorerStorages.java
index 70af654be8..1ee48e2acc 100644
--- 
a/streampipes-data-explorer-management/src/main/java/org/apache/streampipes/dataexplorer/management/SupportedDataExplorerStorages.java
+++ 
b/streampipes-data-explorer-management/src/main/java/org/apache/streampipes/dataexplorer/management/SupportedDataExplorerStorages.java
@@ -28,4 +28,5 @@ package org.apache.streampipes.dataexplorer.management;
  */
 public class SupportedDataExplorerStorages {
   public static final String INFLUX_DB = "influxdb";
+  public static final String IOT_DB = "iotdb";
 }
diff --git a/streampipes-extensions/streampipes-sinks-databases-jvm/pom.xml 
b/streampipes-extensions/streampipes-sinks-databases-jvm/pom.xml
index b4f2449ac4..e948bf1e28 100644
--- a/streampipes-extensions/streampipes-sinks-databases-jvm/pom.xml
+++ b/streampipes-extensions/streampipes-sinks-databases-jvm/pom.xml
@@ -52,13 +52,18 @@
         <dependency>
             <groupId>org.apache.iotdb</groupId>
             <artifactId>iotdb-session</artifactId>
-            <!-- 0.13.3 is the only recommended production ready version for 
edge side (standalone) deployment -->
-            <!-- Should be worked for IoTDB version >= 0.13.0. -->
-            <version>0.13.3</version>
             <exclusions>
                 <exclusion>
-                    <groupId>ch.qos.logback</groupId>
-                    <artifactId>logback-classic</artifactId>
+                    <groupId>org.xerial.snappy</groupId>
+                    <artifactId>snappy-java</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.antlr</groupId>
+                    <artifactId>antlr4-runtime</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.github.luben</groupId>
+                    <artifactId>zstd-jni</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
diff --git 
a/streampipes-extensions/streampipes-sinks-databases-jvm/src/main/java/org/apache/streampipes/sinks/databases/jvm/iotdb/IotDbSink.java
 
b/streampipes-extensions/streampipes-sinks-databases-jvm/src/main/java/org/apache/streampipes/sinks/databases/jvm/iotdb/IotDbSink.java
index d326376c1a..44dd0dda0e 100644
--- 
a/streampipes-extensions/streampipes-sinks-databases-jvm/src/main/java/org/apache/streampipes/sinks/databases/jvm/iotdb/IotDbSink.java
+++ 
b/streampipes-extensions/streampipes-sinks-databases-jvm/src/main/java/org/apache/streampipes/sinks/databases/jvm/iotdb/IotDbSink.java
@@ -21,6 +21,7 @@ package org.apache.streampipes.sinks.databases.jvm.iotdb;
 import org.apache.streampipes.commons.exceptions.SpRuntimeException;
 import 
org.apache.streampipes.extensions.api.pe.context.EventSinkRuntimeContext;
 import org.apache.streampipes.model.DataSinkType;
+import org.apache.streampipes.model.extensions.ExtensionAssetType;
 import org.apache.streampipes.model.graph.DataSinkDescription;
 import org.apache.streampipes.model.runtime.Event;
 import org.apache.streampipes.model.runtime.field.AbstractField;
@@ -30,7 +31,6 @@ import 
org.apache.streampipes.sdk.builder.StreamRequirementsBuilder;
 import org.apache.streampipes.sdk.helpers.EpRequirements;
 import org.apache.streampipes.sdk.helpers.Labels;
 import org.apache.streampipes.sdk.helpers.Locales;
-import org.apache.streampipes.sdk.utils.Assets;
 import org.apache.streampipes.wrapper.params.compat.SinkParams;
 import org.apache.streampipes.wrapper.standalone.StreamPipesDataSink;
 
@@ -38,7 +38,6 @@ import org.apache.iotdb.rpc.IoTDBConnectionException;
 import org.apache.iotdb.rpc.StatementExecutionException;
 import org.apache.iotdb.session.pool.SessionPool;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
-import org.apache.iotdb.tsfile.utils.Binary;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -77,7 +76,7 @@ public class IotDbSink extends StreamPipesDataSink {
     return DataSinkBuilder
         .create("org.apache.streampipes.sinks.databases.jvm.iotdb", 0)
         .withLocales(Locales.EN)
-        .withAssets(Assets.DOCUMENTATION, Assets.ICON).
+        .withAssets(ExtensionAssetType.DOCUMENTATION, ExtensionAssetType.ICON).
         category(DataSinkType.DATABASE)
         .requiredStream(
             StreamRequirementsBuilder.create()
@@ -169,7 +168,7 @@ public class IotDbSink extends StreamPipesDataSink {
         values.add(value);
       } else {
         types.add(TSDataType.TEXT);
-        values.add(Binary.valueOf(value.toString()));
+        values.add(value);
       }
     }
 


Reply via email to