Repository: apache/streampipes
Pull request #2291 (2023-12-07 07:20:51)

refactor(#2285): Remove image labeling component
<!--
  ~ 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.
  ~
  -->
  
  <!--
Thanks for contributing! Here are some tips you can follow to help us incorporate your contribution quickly and easily:
1. If this is your first time, please read our contributor guidelines:
    - https://streampipes.apache.org/getinvolved.html
    - https://cwiki.apache.org/confluence/display/STREAMPIPES/Getting+Started
2. Make sure the PR title is formatted like: `[#<GitHub issue id>] PR title ...`
3. If the PR is unfinished, add '[WIP]' in your PR title, e.g., `[WIP][#<GitHub issue id>] PR title ...`.
4. Please write your PR title to summarize what this PR proposes/fixes.
5. Link the PR to the corresponding GitHub issue (if present) in the `Development` section in the right menu bar. 
6. Be sure to keep the PR description updated to reflect all changes.
7. If possible, provide a concise example to reproduce the issue for a faster review.
8. Make sure tests pass via `mvn clean install`.
9. (Optional) If the contribution is large, please file an Apache ICLA
    - http://apache.org/licenses/icla.pdf
-->

### Purpose
<!--
Please clarify what changes you are proposing and describe how those changes will address the issue.
Furthermore, describe potential consequences the changes might have.
-->
Implement #2285 

### Remarks
<!--
Is there anything left we need to pay attention on?
Are there some references that might be important? E.g. links to Confluence, or discussions
on the mailing list or GitHub.
-->
PR introduces (a) breaking change(s): <yes/no>

PR introduces (a) deprecation(s): <yes/no>



Patch:
diff --git a/installer/cli/environments/lite-monitoring b/installer/cli/environments/lite-monitoring
index d99f12c3e4..ffc232a38e 100644
--- a/installer/cli/environments/lite-monitoring
+++ b/installer/cli/environments/lite-monitoring
@@ -16,7 +16,6 @@
 [environment:lite-monitoring]
 ui
 backend
-consul
 couchdb
 kafka
 zookeeper
diff --git a/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java b/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
index 63dece4bb0..9866f10e82 100644
--- a/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
+++ b/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
@@ -26,5 +26,6 @@ public class HttpConstants {
   public static final String APPLICATION_JSON_TYPE = "application/json";
   public static final String X_API_USER = "X-API-USER";
   public static final String X_API_KEY = "X-API-KEY";
+  public static final String BASIC = "Basic ";
 
 }
diff --git a/streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/StreamPipesCollectorRegistry.java b/streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/StreamPipesCollectorRegistry.java
index 1795ea03d7..242344b1f2 100644
--- a/streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/StreamPipesCollectorRegistry.java
+++ b/streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/StreamPipesCollectorRegistry.java
@@ -43,6 +43,14 @@ public class StreamPipesCollectorRegistry {
       .register(collectorRegistry);
   }
 
+  public static Gauge registerGauge(String name, String help, String... labelNames) {
+    return Gauge.build()
+                .name(name)
+                .help(help)
+                .labelNames(labelNames)
+                .register(collectorRegistry);
+  }
+
   public static Counter registerCounter(String name, String help) {
     return Counter.build()
       .name(name)
diff --git a/streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/adapter/AdapterMetrics.java b/streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/adapter/AdapterMetrics.java
new file mode 100644
index 0000000000..174531fb6e
--- /dev/null
+++ b/streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/adapter/AdapterMetrics.java
@@ -0,0 +1,126 @@
+/*
+ * 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.commons.prometheus.adapter;
+
+import org.apache.streampipes.commons.prometheus.StreamPipesCollectorRegistry;
+
+import io.prometheus.client.Gauge;
+
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+
+/**
+ * AdapterMetrics class manages monitoring metrics related to StreamPipes adapters.
+ * It allows register and remove adapter instances as well as updating the values of metrics.
+ * Currently, the following metrics are supported:
+ * <ul>
+ *   <li><strong>adapter_events_published_total:</strong> The total number of events published per adapter</li>
+ * </ul>
+ * <p>Usage Example:
+ * <pre>{@code
+ * AdapterMetrics adapterMetrics = new AdapterMetrics();
+ * adapterMetrics.register("adapter-1", "Sample Adapter");
+ * adapterMetrics.updateTotalEventsPublished("adapter-1", "Sample Adapter", 100);
+ * adapterMetrics.remove("adapter-1", "Sample Adapter");
+ * }</pre>
+ */
+public class AdapterMetrics {
+
+  // Map to keep book about the registered adapters and to serve as mapping between adapter id and adapter name
+  // Both are used as dimensions for the metric
+  private final HashMap<String, String> registeredAdapters;
+
+  private static final String ELEMENT_NOT_FOUND_TEMPLATE = "No entry for adapter '%s' found. Please register it first.";
+
+  private final Gauge totalAdapterEventsPublishedMetric;
+
+  public AdapterMetrics() {
+    this.totalAdapterEventsPublishedMetric = StreamPipesCollectorRegistry.registerGauge(
+        "adapter_events_published_total",
+        "Total amount of events published per adapter",
+        "adapterId", "adapterName"
+    );
+    this.registeredAdapters = new HashMap<>();
+  }
+
+  /**
+   * Registers a new adapter with the given adapterId and adapterName.
+   *
+   * @param adapterId   The unique identifier for the adapter
+   * @param adapterName The name of the adapter
+   */
+  public void register(String adapterId, String adapterName) {
+    if (!contains(adapterId)) {
+      this.registeredAdapters.put(adapterId, adapterName);
+    }
+  }
+
+  /**
+   * Checks if an adapter with the specified adapterId is registered.
+   *
+   * @param adapterId The unique identifier for the adapter
+   * @return True if the adapter is registered, false otherwise
+   */
+  public boolean contains(String adapterId) {
+    return this.registeredAdapters.containsKey(adapterId);
+  }
+
+  /**
+   * Updates the total number of events published for a specific adapter.
+   *
+   * @param adapterId            The unique identifier for the adapter
+   * @param adapterName          The name of the adapter
+   * @param totalEventsPublished The total number of events published by the adapter
+   * @throws NoSuchElementException If the adapter is not registered yet
+   */
+  public void updateTotalEventsPublished(String adapterId, String adapterName, long totalEventsPublished) {
+    if (contains(adapterId)) {
+      // Order of labels needs to be preserved and match the one in AdapterMetrics#register
+      totalAdapterEventsPublishedMetric.labels(adapterId, adapterName)
+                                       .set(totalEventsPublished);
+    } else {
+      throw new NoSuchElementException(ELEMENT_NOT_FOUND_TEMPLATE.formatted(adapterId));
+    }
+  }
+
+  /**
+   * Removes the specified adapter from the registry.
+   * This results in no more metrics being published for this adapter.
+   *
+   * @param adapterId   The unique identifier for the adapter
+   * @param adapterName The name of the adapter
+   * @throws NoSuchElementException If the adapter is not found in the registry
+   */
+  public void remove(String adapterId, String adapterName) {
+    if (contains(adapterId)) {
+      this.totalAdapterEventsPublishedMetric.remove(adapterId, adapterName);
+      this.registeredAdapters.remove(adapterId);
+    } else {
+      throw new NoSuchElementException(ELEMENT_NOT_FOUND_TEMPLATE.formatted(adapterId));
+    }
+  }
+
+  /**
+   * Returns the number of registered adapters in the registry.
+   *
+   * @return The number of registered adapters
+   */
+  public int size() {
+    return registeredAdapters.size();
+  }
+}
diff --git a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.css b/streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/adapter/AdapterMetricsManager.java
similarity index 55%
rename from ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.css
rename to streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/adapter/AdapterMetricsManager.java
index 860b1e858b..8e211ac7ee 100644
--- a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.css
+++ b/streampipes-commons/src/main/java/org/apache/streampipes/commons/prometheus/adapter/AdapterMetricsManager.java
@@ -16,18 +16,28 @@
  *
  */
 
-.mat-list-item {
-    height: 5px;
-}
+package org.apache.streampipes.commons.prometheus.adapter;
 
-::ng-deep .mat-form-field-underline {
-    background-color: rgba(255, 255, 255, 0.8);
-}
+/**
+ * The `AdapterMetricsManager` enum provides a single access point for retrieving the instance of
+ * the `AdapterMetrics` class and ensures that only one instance of the metrics manager exists throughout the
+ * application's lifecycle.
+ */
+public enum AdapterMetricsManager {
 
-::ng-deep .mat-optgroup-label {
-    background-color: #ffffff !important;
-}
+  INSTANCE;
+
+  private final AdapterMetrics adapterMetrics;
+
+  AdapterMetricsManager() {
+    this.adapterMetrics = new AdapterMetrics();
+  }
+
+  public static AdapterMetricsManager getInstance() {
+    return INSTANCE;
+  }
 
-::ng-deep .mat-option {
-    background-color: #ffffff !important;
+  public AdapterMetrics getAdapterMetrics() {
+    return adapterMetrics;
+  }
 }
diff --git a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/health/AdapterHealthCheck.java b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/health/AdapterHealthCheck.java
index fabf477af5..8653ea0803 100644
--- a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/health/AdapterHealthCheck.java
+++ b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/health/AdapterHealthCheck.java
@@ -19,9 +19,12 @@
 package org.apache.streampipes.connect.management.health;
 
 import org.apache.streampipes.commons.exceptions.connect.AdapterException;
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetrics;
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetricsManager;
 import org.apache.streampipes.connect.management.management.AdapterMasterManagement;
 import org.apache.streampipes.connect.management.management.WorkerRestClient;
 import org.apache.streampipes.connect.management.util.WorkerPaths;
+import org.apache.streampipes.manager.monitoring.pipeline.ExtensionsLogProvider;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.storage.api.IAdapterStorage;
 
@@ -57,12 +60,18 @@ public class AdapterHealthCheck implements Runnable {
    * In this method it is checked which adapters are currently running.
    * Then it calls all workers to validate if the adapter instance is
    * still running as expected. If the adapter is not running anymore a new worker instance is invoked.
+   * In addition, it publishes monitoring metrics for all running adapters (in line with
+   * {@link org.apache.streampipes.manager.health.PipelineHealthCheck}).
    */
   public void checkAndRestoreAdapters() {
-    // Get all adapters
+    // Get all running adapters
     Map<String, AdapterDescription> allRunningInstancesAdapterDescriptions =
         this.getAllRunningInstancesAdapterDescriptions();
 
+    if (!allRunningInstancesAdapterDescriptions.isEmpty()) {
+      updateMonitoringMetrics(allRunningInstancesAdapterDescriptions);
+    }
+
     // Get all worker containers that run adapters
     Map<String, List<AdapterDescription>> groupByWorker =
         this.getAllWorkersWithAdapters(allRunningInstancesAdapterDescriptions);
@@ -75,6 +84,37 @@ public class AdapterHealthCheck implements Runnable {
     this.recoverAdapters(allAdaptersToRecover);
   }
 
+  /**
+   * Updates the monitoring metrics based on the descriptions of running adapters.
+   *
+   * @param runningAdapterDescriptions A map containing the descriptions of running adapters, where the key is the
+   *                                   adapter's element ID and the value is the corresponding adapter description.
+   */
+  protected void updateMonitoringMetrics(Map<String, AdapterDescription> runningAdapterDescriptions) {
+
+    var adapterMetrics = AdapterMetricsManager.getInstance()
+                                              .getAdapterMetrics();
+    runningAdapterDescriptions.values()
+                              .forEach(
+                                  adapterDescription -> updateTotalEventsPublished(
+                                      adapterMetrics,
+                                      adapterDescription.getElementId(),
+                                      adapterDescription.getName()
+                                  ));
+    LOG.info("Monitoring {} adapter instances", adapterMetrics.size());
+  }
+
+  private void updateTotalEventsPublished(AdapterMetrics adapterMetrics, String adapterId, String adapterName) {
+    adapterMetrics.updateTotalEventsPublished(
+        adapterId,
+        adapterName,
+        ExtensionsLogProvider.INSTANCE.getMetricInfosForResource(adapterId)
+                                      .getMessagesOut()
+                                      .getCounter()
+    );
+  }
+
+
   public Map<String, AdapterDescription> getAllRunningInstancesAdapterDescriptions() {
     Map<String, AdapterDescription> result = new HashMap<>();
     List<AdapterDescription> allRunningInstancesAdapterDescription = this.adapterStorage.getAllAdapters();
diff --git a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
index c8d16e4d33..8df3721dec 100644
--- a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
+++ b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
@@ -21,6 +21,7 @@ package org.apache.streampipes.connect.management.management;
 import org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
 import org.apache.streampipes.commons.exceptions.SepaParseException;
 import org.apache.streampipes.commons.exceptions.connect.AdapterException;
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetrics;
 import org.apache.streampipes.connect.management.util.GroundingUtils;
 import org.apache.streampipes.connect.management.util.WorkerPaths;
 import org.apache.streampipes.manager.monitoring.pipeline.ExtensionsLogProvider;
@@ -30,9 +31,8 @@ import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.model.util.ElementIdGenerator;
 import org.apache.streampipes.resource.management.AdapterResourceManager;
 import org.apache.streampipes.resource.management.DataStreamResourceManager;
-import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.storage.api.IAdapterStorage;
-import org.apache.streampipes.storage.couchdb.impl.AdapterInstanceStorageImpl;
+import org.apache.streampipes.storage.management.StorageDispatcher;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -48,20 +48,18 @@ public class AdapterMasterManagement {
   private static final Logger LOG = LoggerFactory.getLogger(AdapterMasterManagement.class);
 
   private final IAdapterStorage adapterInstanceStorage;
+  private final AdapterMetrics adapterMetrics;
   private final AdapterResourceManager adapterResourceManager;
 
   private final DataStreamResourceManager dataStreamResourceManager;
 
-  public AdapterMasterManagement() {
-    this.adapterInstanceStorage = getAdapterInstanceStorage();
-    this.adapterResourceManager = new SpResourceManager().manageAdapters();
-    this.dataStreamResourceManager = new SpResourceManager().manageDataStreams();
-  }
-
   public AdapterMasterManagement(IAdapterStorage adapterStorage,
                                  AdapterResourceManager adapterResourceManager,
-                                 DataStreamResourceManager dataStreamResourceManager) {
+                                 DataStreamResourceManager dataStreamResourceManager,
+                                 AdapterMetrics adapterMetrics
+  ) {
     this.adapterInstanceStorage = adapterStorage;
+    this.adapterMetrics = adapterMetrics;
     this.adapterResourceManager = adapterResourceManager;
     this.dataStreamResourceManager = dataStreamResourceManager;
   }
@@ -149,6 +147,10 @@ public class AdapterMasterManagement {
 
     WorkerRestClient.stopStreamAdapter(ad.getSelectedEndpointUrl(), ad);
     ExtensionsLogProvider.INSTANCE.reset(elementId);
+
+    // remove the adapter from the metrics manager so that
+    // no metrics for this adapter are exposed anymore
+    adapterMetrics.remove(ad.getElementId(), ad.getName());
   }
 
   public void startStreamAdapter(String elementId) throws AdapterException {
@@ -166,6 +168,9 @@ public class AdapterMasterManagement {
       // Invoke adapter instance
       WorkerRestClient.invokeStreamAdapter(baseUrl, elementId);
 
+      // register the adapter at the metrics manager so that the AdapterHealthCheck can send metrics
+      adapterMetrics.register(ad.getElementId(), ad.getName());
+
       LOG.info("Started adapter " + elementId + " on: " + baseUrl);
     } catch (NoServiceEndpointsAvailableException | URISyntaxException e) {
       throw new AdapterException("Could not start adapter due to unavailable service endpoint", e);
@@ -184,6 +189,6 @@ public class AdapterMasterManagement {
   }
 
   private IAdapterStorage getAdapterInstanceStorage() {
-    return new AdapterInstanceStorageImpl();
+    return StorageDispatcher.INSTANCE.getNoSqlStore().getAdapterInstanceStorage();
   }
 }
diff --git a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/WorkerAdministrationManagement.java b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/WorkerAdministrationManagement.java
index 5378c9acc0..e058c40cbf 100644
--- a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/WorkerAdministrationManagement.java
+++ b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/WorkerAdministrationManagement.java
@@ -19,16 +19,18 @@
 package org.apache.streampipes.connect.management.management;
 
 import org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetrics;
 import org.apache.streampipes.connect.management.health.AdapterHealthCheck;
 import org.apache.streampipes.connect.management.health.AdapterOperationLock;
 import org.apache.streampipes.manager.assets.AssetManager;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.model.extensions.svcdiscovery.SpServiceTag;
+import org.apache.streampipes.resource.management.AdapterResourceManager;
+import org.apache.streampipes.resource.management.DataStreamResourceManager;
 import org.apache.streampipes.resource.management.PermissionResourceManager;
 import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.storage.api.IAdapterStorage;
 import org.apache.streampipes.storage.couchdb.CouchDbStorageManager;
-import org.apache.streampipes.storage.management.StorageDispatcher;
 import org.apache.streampipes.svcdiscovery.api.model.SpServiceUrlProvider;
 
 import org.slf4j.Logger;
@@ -47,11 +49,20 @@ public class WorkerAdministrationManagement {
 
   private final AdapterHealthCheck adapterHealthCheck;
 
-  public WorkerAdministrationManagement() {
+  public WorkerAdministrationManagement(
+      IAdapterStorage adapterStorage,
+      AdapterMetrics adapterMetrics,
+      AdapterResourceManager adapterResourceManager,
+      DataStreamResourceManager dataStreamResourceManager
+  ) {
     this.adapterHealthCheck = new AdapterHealthCheck(
-        StorageDispatcher.INSTANCE.getNoSqlStore()
-                                  .getAdapterInstanceStorage(),
-        new AdapterMasterManagement()
+        adapterStorage,
+        new AdapterMasterManagement(
+            adapterStorage,
+            adapterResourceManager,
+            dataStreamResourceManager,
+            adapterMetrics
+        )
     );
     this.adapterDescriptionStorage = CouchDbStorageManager.INSTANCE.getAdapterDescriptionStorage();
   }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java
index ff60661596..31f3fbe720 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java
@@ -19,6 +19,7 @@
 package org.apache.streampipes.rest;
 
 import org.apache.streampipes.commons.exceptions.connect.AdapterException;
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetricsManager;
 import org.apache.streampipes.connect.management.management.AdapterMasterManagement;
 import org.apache.streampipes.dataexplorer.DataExplorerQueryManagement;
 import org.apache.streampipes.dataexplorer.DataExplorerSchemaManagement;
@@ -30,6 +31,7 @@ import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 import org.apache.streampipes.model.file.FileMetadata;
 import org.apache.streampipes.model.pipeline.Pipeline;
+import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.resource.management.UserResourceManager;
 import org.apache.streampipes.storage.api.IDashboardStorage;
 import org.apache.streampipes.storage.api.IDashboardWidgetStorage;
@@ -72,7 +74,13 @@ public class ResetManagement {
     });
 
     // Stop and delete all adapters
-    AdapterMasterManagement adapterMasterManagement = new AdapterMasterManagement();
+    AdapterMasterManagement adapterMasterManagement = new AdapterMasterManagement(
+        StorageDispatcher.INSTANCE.getNoSqlStore()
+                                  .getAdapterInstanceStorage(),
+        new SpResourceManager().manageAdapters(),
+        new SpResourceManager().manageDataStreams(),
+        AdapterMetricsManager.INSTANCE.getAdapterMetrics()
+    );
 
     try {
       List<AdapterDescription> allAdapters = adapterMasterManagement.getAllAdapterInstances();
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java
index 4980bd993f..b34d50a73e 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java
@@ -19,6 +19,7 @@
 package org.apache.streampipes.rest.impl.connect;
 
 import org.apache.streampipes.commons.exceptions.connect.AdapterException;
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetricsManager;
 import org.apache.streampipes.connect.management.management.AdapterMasterManagement;
 import org.apache.streampipes.connect.management.management.AdapterUpdateManagement;
 import org.apache.streampipes.manager.pipeline.PipelineManager;
@@ -28,6 +29,7 @@ import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.model.message.Notifications;
 import org.apache.streampipes.model.monitoring.SpLogMessage;
 import org.apache.streampipes.resource.management.PermissionResourceManager;
+import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.rest.security.AuthConstants;
 import org.apache.streampipes.rest.shared.annotation.JacksonSerialized;
 import org.apache.streampipes.storage.api.IPipelineStorage;
@@ -61,7 +63,13 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage
   private static final Logger LOG = LoggerFactory.getLogger(AdapterResource.class);
 
   public AdapterResource() {
-    super(AdapterMasterManagement::new);
+    super(() -> new AdapterMasterManagement(
+        StorageDispatcher.INSTANCE.getNoSqlStore()
+                                  .getAdapterInstanceStorage(),
+        new SpResourceManager().manageAdapters(),
+        new SpResourceManager().manageDataStreams(),
+        AdapterMetricsManager.INSTANCE.getAdapterMetrics()
+    ));
   }
 
   @POST
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/RuntimeResolvableResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/RuntimeResolvableResource.java
index c8e5386b34..208ed85e28 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/RuntimeResolvableResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/RuntimeResolvableResource.java
@@ -21,13 +21,16 @@ package org.apache.streampipes.rest.impl.connect;
 import org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
 import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.commons.exceptions.connect.AdapterException;
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetricsManager;
 import org.apache.streampipes.connect.management.management.WorkerAdministrationManagement;
 import org.apache.streampipes.connect.management.management.WorkerRestClient;
 import org.apache.streampipes.connect.management.management.WorkerUrlProvider;
 import org.apache.streampipes.model.monitoring.SpLogMessage;
 import org.apache.streampipes.model.runtime.RuntimeOptionsRequest;
 import org.apache.streampipes.model.runtime.RuntimeOptionsResponse;
+import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.rest.shared.annotation.JacksonSerialized;
+import org.apache.streampipes.storage.management.StorageDispatcher;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,7 +52,13 @@ public class RuntimeResolvableResource extends AbstractAdapterResource<WorkerAdm
   private final WorkerUrlProvider workerUrlProvider;
 
   public RuntimeResolvableResource() {
-    super(WorkerAdministrationManagement::new);
+    super(() -> new WorkerAdministrationManagement(
+        StorageDispatcher.INSTANCE.getNoSqlStore()
+                                  .getAdapterInstanceStorage(),
+        AdapterMetricsManager.INSTANCE.getAdapterMetrics(),
+        new SpResourceManager().manageAdapters(),
+        new SpResourceManager().manageDataStreams()
+    ));
     this.workerUrlProvider = new WorkerUrlProvider();
   }
 
diff --git a/streampipes-service-base/src/main/java/org/apache/streampipes/service/base/security/UnauthorizedRequestEntryPoint.java b/streampipes-service-base/src/main/java/org/apache/streampipes/service/base/security/UnauthorizedRequestEntryPoint.java
index 144a990dc4..94304a6d5b 100644
--- a/streampipes-service-base/src/main/java/org/apache/streampipes/service/base/security/UnauthorizedRequestEntryPoint.java
+++ b/streampipes-service-base/src/main/java/org/apache/streampipes/service/base/security/UnauthorizedRequestEntryPoint.java
@@ -35,7 +35,7 @@ public class UnauthorizedRequestEntryPoint implements AuthenticationEntryPoint {
   @Override
   public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                        AuthenticationException e) throws IOException {
-    LOG.error("Unauthorized request to {}", httpServletRequest.getPathInfo());
+    LOG.error("Unauthorized request to {}", httpServletRequest.getServletPath());
 
     httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getLocalizedMessage());
   }
diff --git a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/PostStartupTask.java b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/PostStartupTask.java
index 2442816746..76ba815dcc 100644
--- a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/PostStartupTask.java
+++ b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/PostStartupTask.java
@@ -18,11 +18,13 @@
 
 package org.apache.streampipes.service.core;
 
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetricsManager;
 import org.apache.streampipes.connect.management.management.WorkerAdministrationManagement;
 import org.apache.streampipes.manager.operations.Operations;
 import org.apache.streampipes.model.extensions.svcdiscovery.SpServiceTagPrefix;
 import org.apache.streampipes.model.pipeline.Pipeline;
 import org.apache.streampipes.model.pipeline.PipelineOperationStatus;
+import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.storage.api.IPipelineStorage;
 import org.apache.streampipes.storage.couchdb.CouchDbStorageManager;
 import org.apache.streampipes.storage.management.StorageDispatcher;
@@ -52,7 +54,13 @@ public class PostStartupTask implements Runnable {
   public PostStartupTask(IPipelineStorage pipelineStorage) {
     this.pipelineStorage = pipelineStorage;
     this.executorService = Executors.newSingleThreadScheduledExecutor();
-    this.workerAdministrationManagement = new WorkerAdministrationManagement();
+    this.workerAdministrationManagement = new WorkerAdministrationManagement(
+        StorageDispatcher.INSTANCE.getNoSqlStore()
+                                  .getAdapterInstanceStorage(),
+        AdapterMetricsManager.INSTANCE.getAdapterMetrics(),
+        new SpResourceManager().manageAdapters(),
+        new SpResourceManager().manageDataStreams()
+    );
   }
 
   @Override
@@ -63,11 +71,13 @@ public class PostStartupTask implements Runnable {
   }
 
   private void performAdapterAssetUpdate() {
-    var installedAppIds = CouchDbStorageManager.INSTANCE.getExtensionsServiceStorage().getAll()
-        .stream()
-        .flatMap(config -> config.getTags().stream())
-        .filter(tag -> tag.getPrefix() == SpServiceTagPrefix.ADAPTER)
-        .toList();
+    var installedAppIds = CouchDbStorageManager.INSTANCE.getExtensionsServiceStorage()
+                                                        .getAll()
+                                                        .stream()
+                                                        .flatMap(config -> config.getTags()
+                                                                                 .stream())
+                                                        .filter(tag -> tag.getPrefix() == SpServiceTagPrefix.ADAPTER)
+                                                        .toList();
     workerAdministrationManagement.performAdapterMigrations(installedAppIds);
   }
 
@@ -118,16 +128,20 @@ public class PostStartupTask implements Runnable {
       storeFailedRestartAttempt(pipeline);
       int failedAttemptCount = failedPipelines.get(pipeline.getPipelineId());
       if (failedAttemptCount <= MAX_PIPELINE_START_RETRIES) {
-        LOG.error("Pipeline {} could not be restarted - I'll try again in {} seconds ({}/{} failed attempts)",
+        LOG.error(
+            "Pipeline {} could not be restarted - I'll try again in {} seconds ({}/{} failed attempts)",
             pipeline.getName(),
             WAIT_TIME_AFTER_FAILURE_IN_SECONDS,
             failedAttemptCount,
-            MAX_PIPELINE_START_RETRIES);
+            MAX_PIPELINE_START_RETRIES
+        );
 
         schedulePipelineStart(pipeline, restartOnReboot);
       } else {
-        LOG.error("Pipeline {} could not be restarted - are all pipeline element containers running?",
-            status.getPipelineName());
+        LOG.error(
+            "Pipeline {} could not be restarted - are all pipeline element containers running?",
+            status.getPipelineName()
+        );
       }
     }
   }
diff --git a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/StreamPipesCoreApplication.java b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/StreamPipesCoreApplication.java
index d8c9781bd7..f08e9939e7 100644
--- a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/StreamPipesCoreApplication.java
+++ b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/StreamPipesCoreApplication.java
@@ -18,6 +18,7 @@
 package org.apache.streampipes.service.core;
 
 import org.apache.streampipes.commons.environment.Environments;
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetricsManager;
 import org.apache.streampipes.connect.management.health.AdapterHealthCheck;
 import org.apache.streampipes.connect.management.management.AdapterMasterManagement;
 import org.apache.streampipes.manager.health.CoreInitialInstallationProgress;
@@ -37,6 +38,7 @@ import org.apache.streampipes.messaging.pulsar.SpPulsarProtocolFactory;
 import org.apache.streampipes.model.configuration.SpCoreConfigurationStatus;
 import org.apache.streampipes.model.pipeline.Pipeline;
 import org.apache.streampipes.model.pipeline.PipelineOperationStatus;
+import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.rest.security.SpPermissionEvaluator;
 import org.apache.streampipes.service.base.BaseNetworkingConfig;
 import org.apache.streampipes.service.base.StreamPipesServiceBase;
@@ -142,7 +144,13 @@ public class StreamPipesCoreApplication extends StreamPipesServiceBase {
             new PipelineHealthCheck(),
             new AdapterHealthCheck(
                 StorageDispatcher.INSTANCE.getNoSqlStore().getAdapterInstanceStorage(),
-                new AdapterMasterManagement()
+                new AdapterMasterManagement(
+                    StorageDispatcher.INSTANCE.getNoSqlStore()
+                                              .getAdapterInstanceStorage(),
+                    new SpResourceManager().manageAdapters(),
+                    new SpResourceManager().manageDataStreams(),
+                    AdapterMetricsManager.INSTANCE.getAdapterMetrics()
+                )
             ))
     );
 
diff --git a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
index e389f4afbf..ac4c833594 100644
--- a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
+++ b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
@@ -24,15 +24,18 @@ import org.apache.streampipes.model.client.user.ServiceAccount;
 import org.apache.streampipes.model.client.user.UserAccount;
 import org.apache.streampipes.storage.api.IUserStorage;
 import org.apache.streampipes.storage.management.StorageDispatcher;
+import org.apache.streampipes.user.management.encryption.SecretEncryptionManager;
 import org.apache.streampipes.user.management.jwt.JwtTokenProvider;
 import org.apache.streampipes.user.management.model.PrincipalUserDetails;
 import org.apache.streampipes.user.management.model.ServiceAccountDetails;
 import org.apache.streampipes.user.management.model.UserAccountDetails;
 import org.apache.streampipes.user.management.service.TokenService;
+import org.apache.streampipes.user.management.util.PasswordUtil;
 import org.apache.streampipes.user.management.util.TokenUtil;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
@@ -45,12 +48,20 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
 import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Base64;
+import java.util.List;
 
 public class TokenAuthenticationFilter extends OncePerRequestFilter {
 
   private final JwtTokenProvider tokenProvider;
   private final IUserStorage userStorage;
 
+  private final List<String> supportedBasicAuthPaths = List.of(
+      "/actuator/prometheus"
+  );
+
   private static final Logger logger = LoggerFactory.getLogger(TokenAuthenticationFilter.class);
 
   public TokenAuthenticationFilter() {
@@ -68,7 +79,7 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
       if (StringUtils.hasText(jwt) && tokenProvider.validateJwtToken(jwt)) {
         String username = tokenProvider.getUserIdFromToken(jwt);
         applySuccessfulAuth(request, username);
-      } else {
+      } else if (isApiKeyAuth(request)) {
         String apiKey = getApiKeyFromRequest(request);
         String apiUser = getApiUserFromRequest(request);
         if (StringUtils.hasText(apiKey) && StringUtils.hasText(apiUser)) {
@@ -78,6 +89,22 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
             applySuccessfulAuth(request, apiUser);
           }
         }
+      } else {
+        var authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
+        if (authorizationHeader != null && authorizationHeader.startsWith(HttpConstants.BASIC)) {
+          if (supportedBasicAuthPaths.contains(request.getServletPath())) {
+            String base64Credentials = authorizationHeader.substring(HttpConstants.BASIC.length()).trim();
+            String credentials = new String(Base64.getDecoder().decode(base64Credentials));
+
+            String[] splitCredentials = credentials.split(":");
+            String username = splitCredentials[0];
+            String passphrase = splitCredentials[1];
+            var principal = StorageDispatcher.INSTANCE.getNoSqlStore().getUserStorageAPI().getUser(username);
+            if (principal != null && checkCredentials(principal, passphrase)) {
+              applySuccessfulAuth(request, username);
+            }
+          }
+        }
       }
     } catch (Exception ex) {
       logger.error("Could not set user authentication in security context", ex);
@@ -86,6 +113,21 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
     filterChain.doFilter(request, response);
   }
 
+  private boolean checkCredentials(Principal principal, String passphrase)
+      throws NoSuchAlgorithmException, InvalidKeySpecException {
+    if (principal instanceof UserAccount) {
+      return PasswordUtil.validatePassword(passphrase, ((UserAccount) principal).getPassword());
+    } else if (principal instanceof ServiceAccount) {
+      return passphrase.equals(SecretEncryptionManager.decrypt(((ServiceAccount) principal).getClientSecret()));
+    } else {
+      throw new IllegalArgumentException("Unknown user instance");
+    }
+  }
+
+  private boolean isApiKeyAuth(HttpServletRequest request) {
+    return request.getHeader(HttpConstants.X_API_USER) != null && request.getHeader(HttpConstants.X_API_KEY) != null;
+  }
+
   private void applySuccessfulAuth(HttpServletRequest request,
                                    String username) {
     Principal user = userStorage.getUser(username);
diff --git a/ui/src/app/core-model/coco/Annotation.ts b/ui/src/app/core-model/coco/Annotation.ts
deleted file mode 100644
index 9eefe6d4c8..0000000000
--- a/ui/src/app/core-model/coco/Annotation.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- *
- */
-
-export class Annotation {
-    id: number;
-    image_id: number;
-    category_id: number;
-    segmentation: [any]; // RLE or [polygon] -> polygon [[x1,y1,x2,y2,... xn, yn]]
-    area: number;
-    bbox: [number, number, number, number]; // [x,y,width,height]
-    iscrowd: number; // (iscrowd=0 in which case polygons are used) or a collection of objects (iscrowd=1 in which case RLE is used)
-    brushSize: number[];
-
-    // For UI
-    isSelected = false;
-    isHovered = false;
-    category_name: string;
-    color: string;
-    label_name: string;
-
-    constructor() {
-        this.segmentation = undefined;
-        this.bbox = undefined;
-        this.brushSize = undefined;
-    }
-}
diff --git a/ui/src/app/core-model/coco/Category.ts b/ui/src/app/core-model/coco/Category.ts
deleted file mode 100644
index 1578ed5359..0000000000
--- a/ui/src/app/core-model/coco/Category.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- *
- */
-
-export class Category {
-    constructor(
-        id: number,
-        name: string,
-        supercategory: string,
-        label_name: string,
-    ) {
-        this.id = id;
-        this.name = name;
-        this.supercategory = supercategory;
-        this.label_name = label_name;
-    }
-
-    id: number;
-    name: string;
-    supercategory: string;
-    label_name: string;
-}
diff --git a/ui/src/app/core-model/coco/Coco.format.ts b/ui/src/app/core-model/coco/Coco.format.ts
deleted file mode 100644
index e5d720debb..0000000000
--- a/ui/src/app/core-model/coco/Coco.format.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- *
- */
-
-import { Annotation } from './Annotation';
-import { Category } from './Category';
-import { Image } from './Image';
-
-export class CocoFormat {
-    info: {
-        year: number;
-        version: string;
-        description: string;
-        contributor: string;
-        url: string;
-        date_created: Date;
-    };
-    licenses: [];
-    images: Image[] = [];
-    annotations: Annotation[] = [];
-    categories: Category[] = [];
-
-    constructor() {}
-}
diff --git a/ui/src/app/core-model/coco/Image.ts b/ui/src/app/core-model/coco/Image.ts
deleted file mode 100644
index 05bf61a248..0000000000
--- a/ui/src/app/core-model/coco/Image.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.
- *
- */
-
-export class Image {
-    id: number;
-    width: number;
-    height: number;
-    file_name: Date;
-    license: number;
-    flickr_url: Date;
-    coco_url: Date;
-    date_captured: Date;
-}
diff --git a/ui/src/app/core-ui/core-ui.module.ts b/ui/src/app/core-ui/core-ui.module.ts
index bc54dbff0e..7353d24752 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -31,19 +31,6 @@ import { CustomMaterialModule } from '../CustomMaterial/custom-material.module';
 import { MatChipsModule } from '@angular/material/chips';
 import { MatSliderModule } from '@angular/material/slider';
 import { PlotlyViaWindowModule } from 'angular-plotly.js';
-import { ImageAnnotationsComponent } from './image/components/image-annotations/image-annotations.component';
-import { ImageBarComponent } from './image/components/image-bar/image-bar.component';
-import { ImageContainerComponent } from './image/components/image-container/image-container.component';
-import { SelectLabelComponent } from './labels/components/select-label/select-label.component';
-import { ImageLabelingComponent } from './image/image-labeling/image-labeling.component';
-import { ImageViewerComponent } from './image/image-viewer/image-viewer.component';
-import { ImageComponent } from './image/image.component';
-import { BrushLabelingService } from './image/services/BrushLabeling.service';
-import { ColorService } from './image/services/color.service';
-import { PolygonLabelingService } from './image/services/PolygonLabeling.service';
-import { ReactLabelingService } from './image/services/ReactLabeling.service';
-import { CocoFormatService } from './image/services/CocoFormat.service';
-import { LabelingModeService } from './image/services/LabelingMode.service';
 import { PortalModule } from '@angular/cdk/portal';
 import { OverlayModule } from '@angular/cdk/overlay';
 import { StaticAnyInputComponent } from './static-properties/static-any-input/static-any-input.component';
@@ -79,7 +66,6 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
 import { StaticRuntimeResolvableTreeInputComponent } from './static-properties/static-runtime-resolvable-tree-input/static-tree-input.component';
 import { MatTreeModule } from '@angular/material/tree';
 import { PlatformServicesModule } from '@streampipes/platform-services';
-import { ImageBarPreviewComponent } from './image/components/image-bar/image-bar-preview/image-bar-preview.component';
 import { SharedUiModule } from '@streampipes/shared-ui';
 import { PipelineElementTemplateConfigComponent } from './pipeline-element-template-config/pipeline-element-template-config.component';
 import { PipelineElementTemplatePipe } from './pipeline-element-template-config/pipeline-element-template.pipe';
@@ -131,14 +117,6 @@ import { SpSimpleLogsComponent } from './monitoring/simple-logs/simple-logs.comp
         ConfigureLabelsComponent,
         DataDownloadDialogComponent,
         DisplayRecommendedPipe,
-        ImageBarPreviewComponent,
-        ImageComponent,
-        ImageContainerComponent,
-        ImageLabelingComponent,
-        SelectLabelComponent,
-        ImageBarComponent,
-        ImageAnnotationsComponent,
-        ImageViewerComponent,
         ObjectPermissionDialogComponent,
         PipelineElementTemplateConfigComponent,
         PipelineElementTemplatePipe,
@@ -174,22 +152,13 @@ import { SpSimpleLogsComponent } from './monitoring/simple-logs/simple-logs.comp
     ],
     providers: [
         MatDatepickerModule,
-        ColorService,
         DisplayRecommendedPipe,
-        ReactLabelingService,
-        PolygonLabelingService,
-        BrushLabelingService,
-        CocoFormatService,
-        LabelingModeService,
         RuntimeResolvableService,
     ],
     exports: [
         ConfigureLabelsComponent,
         DataDownloadDialogComponent,
-        ImageComponent,
-        ImageLabelingComponent,
         PipelineElementTemplateConfigComponent,
-        SelectLabelComponent,
         StaticAnyInputComponent,
         StaticPropertyComponent,
         StaticFreeInputComponent,
@@ -206,7 +175,6 @@ import { SpSimpleLogsComponent } from './monitoring/simple-logs/simple-logs.comp
         StaticRuntimeResolvableAnyInputComponent,
         StaticRuntimeResolvableOneOfInputComponent,
         StaticSlideToggleComponent,
-        ImageViewerComponent,
         ErrorHintComponent,
         PipelineStartedStatusComponent,
         SpSimpleLogsComponent,
diff --git a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html b/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html
deleted file mode 100644
index f29f566fec..0000000000
--- a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!--
-  ~ 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.
-  ~
-  -->
-
-<div fxLayout="column" fxLayoutAlign="space-between center">
-    <h2>Annotations</h2>
-    <mat-list>
-        <mat-list-item
-            *ngFor="let annotation of annotations"
-            (mouseover)="enterAnnotation(annotation)"
-            (mouseout)="leaveAnnotation(annotation)"
-            style="
-                height: 40px;
-                padding-top: 5px;
-                padding-buttom: 5px;
-                border-radius: 50px;
-                margin-bottom: 5px;
-            "
-            [style.background-color]="
-                annotation.isHovered || annotation.isSelected
-                    ? 'lightgrey'
-                    : 'white'
-            "
-        >
-            <mat-icon [style.color]="annotation.color">color_lens</mat-icon>
-            {{ annotation.label_name }}
-
-            <button
-                mat-icon-button
-                (click)="delete(annotation)"
-                style="margin-top: -10px; margin-left: -5px"
-            >
-                <mat-icon>delete_forever</mat-icon>
-            </button>
-        </mat-list-item>
-    </mat-list>
-</div>
diff --git a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.ts b/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.ts
deleted file mode 100644
index 1bd90b2a36..0000000000
--- a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- *
- */
-
-import { Component, EventEmitter, Input, Output } from '@angular/core';
-import { Annotation } from '../../../../core-model/coco/Annotation';
-import { Label } from '@streampipes/platform-services';
-
-@Component({
-    selector: 'sp-image-annotations',
-    templateUrl: './image-annotations.component.html',
-    styleUrls: ['./image-annotations.component.css'],
-})
-export class ImageAnnotationsComponent {
-    @Input() annotations: Annotation[];
-
-    @Input()
-    public labels: Label[];
-
-    @Output() changeAnnotationLabel: EventEmitter<[Annotation, Label]> =
-        new EventEmitter<[Annotation, Label]>();
-    @Output() deleteAnnotation: EventEmitter<Annotation> =
-        new EventEmitter<Annotation>();
-
-    constructor() {}
-
-    changeLabel(change: [Annotation, Label]) {
-        this.changeAnnotationLabel.emit([change[0], change[1]]);
-    }
-
-    delete(annotation) {
-        this.deleteAnnotation.emit(annotation);
-    }
-
-    enterAnnotation(annotation) {
-        annotation.isHovered = true;
-    }
-
-    leaveAnnotation(annotation) {
-        annotation.isHovered = false;
-    }
-}
diff --git a/ui/src/app/core-ui/image/components/image-container/image-container.component.css b/ui/src/app/core-ui/image/components/image-container/image-container.component.css
deleted file mode 100644
index 423e15ec76..0000000000
--- a/ui/src/app/core-ui/image/components/image-container/image-container.component.css
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- *
- */
-.canvas-container {
-    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAIUlEQVQoU2NkYGCQYiACMEIVPiOgVmpUIb4QggcPwSAHAJ3ICQ/mI97EAAAAAElFTkSuQmCC);
-    background-color: #fafafa;
-    border-color: lightgrey;
-    border-style: solid;
-}
diff --git a/ui/src/app/core-ui/image/components/image-container/image-container.component.html b/ui/src/app/core-ui/image/components/image-container/image-container.component.html
deleted file mode 100644
index 3c5d2bfa1a..0000000000
--- a/ui/src/app/core-ui/image/components/image-container/image-container.component.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-  ~ 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.
-  ~
-  -->
-<div class="canvas-container">
-    <mat-spinner
-        *ngIf="isDrawingVar"
-        [diameter]="40"
-        style="position: fixed"
-    ></mat-spinner>
-    <img
-        [src]="imagePath"
-        #mainImg
-        *ngIf="showImage"
-        [width]="canvasWidth"
-        (load)="onImageLoaded()"
-    />
-</div>
diff --git a/ui/src/app/core-ui/image/components/image-container/image-container.component.ts b/ui/src/app/core-ui/image/components/image-container/image-container.component.ts
deleted file mode 100644
index 548524576e..0000000000
--- a/ui/src/app/core-ui/image/components/image-container/image-container.component.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
-import { SafeUrl } from '@angular/platform-browser';
-import { Observable } from 'rxjs';
-
-@Component({
-    selector: 'sp-image-container',
-    templateUrl: './image-container.component.html',
-    styleUrls: ['./image-container.component.css'],
-})
-export class ImageContainerComponent implements OnInit {
-    imagePath: SafeUrl;
-    showImage = false;
-
-    @Input()
-    set imageSrc(src: Observable<Blob>) {
-        src.subscribe(url => {
-            this.imagePath = url;
-            this.showImage = true;
-        });
-    }
-
-    @ViewChild('mainImg') imageRef: ElementRef;
-
-    @Input()
-    public canvasHeight = 500;
-
-    @Input()
-    public canvasWidth = 800;
-
-    private scale: number;
-
-    public isDrawingVar: boolean;
-
-    constructor() {}
-
-    ngOnInit(): void {
-        this.scale = 1;
-    }
-
-    onImageLoaded() {
-        const naturalImgWidth = (
-            this.imageRef.nativeElement as HTMLImageElement
-        ).naturalWidth;
-        const naturalImageHeight = (
-            this.imageRef.nativeElement as HTMLImageElement
-        ).naturalHeight;
-        this.scale = Math.min(
-            1,
-            this.canvasWidth / naturalImgWidth,
-            this.canvasHeight / naturalImageHeight,
-        );
-    }
-}
diff --git a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.css b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.css
deleted file mode 100644
index 13cbc4aacb..0000000000
--- a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.css
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * 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.
- *
- */
diff --git a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html
deleted file mode 100644
index a4a52a8304..0000000000
--- a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html
+++ /dev/null
@@ -1,151 +0,0 @@
-<!--
-  ~ 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.
-  ~
-  -->
-
-<div
-    (mouseover)="isHoverComponent = true"
-    (mouseout)="isHoverComponent = false"
->
-    <div fxLayout="column" fxLayoutAlign="space-between ">
-        <div fxLayout="row" fxLayoutAlign="space-around start">
-            <sp-select-label
-                style="width: 270px"
-                [enableShortCuts]="isHoverComponent"
-                (labelChange)="handleLabelChange($event)"
-            >
-            </sp-select-label>
-
-            <div fxLayout="column" fxLayoutAlign="space-between ">
-                <div style="display: flex; position: relative">
-                    <button
-                        mat-button
-                        matTooltip="Edit Annotations"
-                        (click)="labelingMode.setNoneMode()"
-                        [style.background-color]="
-                            labelingMode.isNoneMode() ? 'lightgrey' : 'white'
-                        "
-                    >
-                        <mat-icon>create</mat-icon>
-                    </button>
-                    <button
-                        mat-button
-                        matTooltip="Create React Annotation"
-                        (click)="labelingMode.setReactMode()"
-                        [style.background-color]="
-                            labelingMode.isReactMode() ? 'lightgrey' : 'white'
-                        "
-                    >
-                        <mat-icon>crop_3_2</mat-icon>
-                    </button>
-                    <button
-                        mat-button
-                        matTooltip="Create Polygon Annotation"
-                        (click)="labelingMode.setPolygonMode()"
-                        [style.background-color]="
-                            labelingMode.isPolygonMode() ? 'lightgrey' : 'white'
-                        "
-                    >
-                        <mat-icon>details</mat-icon>
-                    </button>
-                    <button
-                        mat-button
-                        matTooltip="Create Brush Annotation"
-                        (click)="labelingMode.setBrushMode()"
-                        [style.background-color]="
-                            labelingMode.isBrushMode() ? 'lightgrey' : 'white'
-                        "
-                    >
-                        <mat-icon>blur_circular</mat-icon>
-                    </button>
-                    <mat-slider
-                        [min]="1"
-                        [max]="50"
-                        [step]="1"
-                        [(ngModel)]="brushSize"
-                    ></mat-slider>
-                    <button
-                        mat-button
-                        (click)="save(this.imagesIndex)"
-                        style="position: absolute; right: 0px"
-                    >
-                        <mat-icon>save</mat-icon>
-                    </button>
-                    <button
-                        mat-button
-                        matTooltip="Description"
-                        [matMenuTriggerFor]="aboveMenu"
-                    >
-                        <span class="material-icons"> info </span>
-                    </button>
-                    <mat-menu #aboveMenu="matMenu" yPosition="above">
-                        <p><em>The Description of the selected image</em></p>
-                    </mat-menu>
-                </div>
-
-                <sp-image-container
-                    [imageSrc]="_imagesRoutes[imagesIndex]"
-                    (childRedraw)="handleChildRedraw($event[0], $event[1])"
-                    (mouseDownLeft)="
-                        handleMouseDownLeft($event[0], $event[1], $event[2])
-                    "
-                    (mouseMove)="
-                        handleMouseMove($event[0], $event[1], $event[2])
-                    "
-                    (mouseMoveLeft)="
-                        handleMouseMoveLeft($event[0], $event[1], $event[2])
-                    "
-                    (mouseUpLeft)="
-                        handleMouseUpLeft(
-                            $event[0],
-                            $event[1],
-                            $event[2],
-                            $event[3]
-                        )
-                    "
-                    (shortCut)="handleImageViewShortCuts($event)"
-                    (dbclick)="
-                        handleImageViewDBClick($event[0], $event[1], $event[2])
-                    "
-                    (mouseDownRight)="
-                        handleMouseDownRight($event[0], $event[1], $event[2])
-                    "
-                    (isDrawing)="handleIsDrawing($event)"
-                >
-                </sp-image-container>
-            </div>
-
-            <sp-image-annotations
-                style="width: 270px"
-                [annotations]="this.cocoFile?.annotations"
-                [labels]="labels"
-                (changeAnnotationLabel)="handleChangeAnnotationLabel($event)"
-                (deleteAnnotation)="handleDeleteAnnotation($event)"
-            >
-            </sp-image-annotations>
-        </div>
-        <br />
-
-        <sp-image-bar
-            style="width: 100%"
-            [imagesRoutes]="_imagesRoutes"
-            [selectedIndex]="imagesIndex"
-            [enableShortCuts]="true"
-            (indexChange)="handleImageIndexChange($event)"
-        >
-        </sp-image-bar>
-    </div>
-</div>
diff --git a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts
deleted file mode 100644
index d119e84ff9..0000000000
--- a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * 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.
- *
- */
-
-import { Component, Input, OnInit, ViewChild } from '@angular/core';
-import { MatSnackBar } from '@angular/material/snack-bar';
-import Konva from 'konva';
-import { Annotation } from '../../../core-model/coco/Annotation';
-import { CocoFormat } from '../../../core-model/coco/Coco.format';
-import { ImageContainerComponent } from '../components/image-container/image-container.component';
-import { ICoordinates } from '../model/coordinates';
-import { LabelingMode } from '../model/labeling-mode';
-import { BrushLabelingService } from '../services/BrushLabeling.service';
-import { CocoFormatService } from '../services/CocoFormat.service';
-import { LabelingModeService } from '../services/LabelingMode.service';
-import { PolygonLabelingService } from '../services/PolygonLabeling.service';
-import { ReactLabelingService } from '../services/ReactLabeling.service';
-import { Label } from '@streampipes/platform-services';
-import { LabelService } from '../../labels/services/label.service';
-
-@Component({
-    selector: 'sp-image-labeling',
-    templateUrl: './image-labeling.component.html',
-    styleUrls: ['./image-labeling.component.css'],
-})
-export class ImageLabelingComponent implements OnInit {
-    // label
-    public labels;
-    public selectedLabel: Label;
-
-    public _imagesRoutes;
-    @Input()
-    set imagesRoutes(routes) {
-        this._imagesRoutes = routes;
-        this.getCocoFile(this._imagesRoutes, 0);
-    }
-
-    public imagesIndex = 0;
-
-    public cocoFile: CocoFormat;
-
-    public isHoverComponent;
-    public brushSize: number;
-
-    public isDrawing = false;
-
-    @ViewChild(ImageContainerComponent) imageView: ImageContainerComponent;
-
-    constructor(
-        private reactLabelingService: ReactLabelingService,
-        private polygonLabelingService: PolygonLabelingService,
-        private brushLabelingService: BrushLabelingService,
-        private snackBar: MatSnackBar,
-        private cocoFormatService: CocoFormatService,
-        private labelService: LabelService,
-        public labelingMode: LabelingModeService,
-    ) {}
-
-    ngOnInit(): void {
-        this.isHoverComponent = false;
-        this.brushSize = 5;
-        this.labelService.getAllLabels().subscribe(res => {
-            this.labels = res;
-        });
-    }
-
-    handleImageIndexChange(index) {
-        this.save(this.imagesIndex);
-        this.getCocoFile(this._imagesRoutes, index);
-    }
-
-    getCocoFile(routes, index) {}
-
-    /* sp-image-view handler */
-    handleMouseDownLeft(
-        layer: Konva.Layer,
-        shift: ICoordinates,
-        position: ICoordinates,
-    ) {
-        if (this.labelingEnabled()) {
-            switch (this.labelingMode.getMode()) {
-                case LabelingMode.ReactLabeling:
-                    this.reactLabelingService.startLabeling(position);
-                    break;
-                case LabelingMode.PolygonLabeling:
-                    this.polygonLabelingService.startLabeling(position);
-                    break;
-                case LabelingMode.BrushLabeling:
-                    this.brushLabelingService.startLabeling(
-                        position,
-                        this.brushSize,
-                    );
-            }
-        }
-    }
-
-    handleMouseMove(
-        layer: Konva.Layer,
-        shift: ICoordinates,
-        position: ICoordinates,
-    ) {
-        switch (this.labelingMode.getMode()) {
-            case LabelingMode.PolygonLabeling: {
-                this.polygonLabelingService.executeLabeling(position);
-                this.polygonLabelingService.tempDraw(
-                    layer,
-                    shift,
-                    this.selectedLabel,
-                );
-            }
-        }
-    }
-
-    handleMouseMoveLeft(
-        layer: Konva.Layer,
-        shift: ICoordinates,
-        position: ICoordinates,
-    ) {
-        if (this.labelingEnabled()) {
-            switch (this.labelingMode.getMode()) {
-                case LabelingMode.ReactLabeling:
-                    {
-                        this.reactLabelingService.executeLabeling(position);
-                        this.reactLabelingService.tempDraw(
-                            layer,
-                            shift,
-                            this.selectedLabel,
-                        );
-                    }
-                    break;
-                case LabelingMode.BrushLabeling: {
-                    this.brushLabelingService.executeLabeling(position);
-                    this.brushLabelingService.tempDraw(
-                        layer,
-                        shift,
-                        this.selectedLabel,
-                    );
-                }
-            }
-        }
-    }
-
-    handleMouseUpLeft(
-        annotationLayer: Konva.Layer,
-        drawLayer: Konva.Layer,
-        shift: ICoordinates,
-        position: ICoordinates,
-    ) {
-        if (this.labelingEnabled()) {
-            switch (this.labelingMode.getMode()) {
-                case LabelingMode.ReactLabeling:
-                    {
-                        const result =
-                            this.reactLabelingService.endLabeling(position);
-                        if (result !== undefined) {
-                            const coco = this.cocoFile;
-                            const annotation =
-                                this.cocoFormatService.addReactAnnotationToFirstImage(
-                                    coco,
-                                    result[0],
-                                    result[1],
-                                    this.selectedLabel.categoryId,
-                                    this.selectedLabel._id,
-                                    this.selectedLabel.color,
-                                    this.selectedLabel.name,
-                                );
-                            this.reactLabelingService.draw(
-                                annotationLayer,
-                                shift,
-                                annotation,
-                                this.imageView,
-                                this.selectedLabel.color,
-                            );
-                        }
-                    }
-                    break;
-                case LabelingMode.PolygonLabeling:
-                    {
-                        this.polygonLabelingService.tempDraw(
-                            drawLayer,
-                            shift,
-                            this.selectedLabel,
-                        );
-                    }
-                    break;
-                case LabelingMode.BrushLabeling: {
-                    const result =
-                        this.brushLabelingService.endLabeling(position);
-                    const coco = this.cocoFile;
-                    const annotation =
-                        this.cocoFormatService.addBrushAnnotationFirstImage(
-                            coco,
-                            result[0],
-                            result[1],
-                            this.selectedLabel.categoryId,
-                            this.selectedLabel.name,
-                            this.selectedLabel.name,
-                        );
-                    this.brushLabelingService.draw(
-                        annotationLayer,
-                        shift,
-                        annotation,
-                        this.imageView,
-                    );
-                }
-            }
-        }
-    }
-
-    handleMouseDownRight(
-        layer: Konva.Layer,
-        shift: ICoordinates,
-        position: ICoordinates,
-    ) {
-        this.labelingMode.toggleNoneMode();
-    }
-
-    handleImageViewDBClick(
-        layer: Konva.Layer,
-        shift: ICoordinates,
-        position: ICoordinates,
-    ) {
-        if (this.labelingEnabled()) {
-            switch (this.labelingMode.getMode()) {
-                case LabelingMode.PolygonLabeling:
-                    const points =
-                        this.polygonLabelingService.endLabeling(position);
-                    const coco = this.cocoFile;
-                    const annotation =
-                        this.cocoFormatService.addPolygonAnnotationFirstImage(
-                            coco,
-                            points,
-                            this.selectedLabel.categoryId,
-                            this.selectedLabel.name,
-                            this.selectedLabel.name,
-                        );
-                    this.polygonLabelingService.draw(
-                        layer,
-                        shift,
-                        annotation,
-                        this.imageView,
-                    );
-            }
-        }
-    }
-
-    handleChildRedraw(layer: Konva.Layer, shift: ICoordinates) {
-        const coco = this.cocoFile;
-        if (coco !== undefined) {
-            for (const annotation of coco.annotations) {
-                annotation.isHovered = false;
-                annotation.isSelected = false;
-                if (this.cocoFormatService.isBoxAnnonation(annotation)) {
-                    this.reactLabelingService.draw(
-                        layer,
-                        shift,
-                        annotation,
-                        this.imageView,
-                        annotation.color,
-                    );
-                } else if (
-                    this.cocoFormatService.isPolygonAnnonation(annotation) &&
-                    !this.cocoFormatService.isBrushAnnonation(annotation)
-                ) {
-                    this.polygonLabelingService.draw(
-                        layer,
-                        shift,
-                        annotation,
-                        this.imageView,
-                    );
-                } else if (
-                    this.cocoFormatService.isBrushAnnonation(annotation)
-                ) {
-                    this.brushLabelingService.draw(
-                        layer,
-                        shift,
-                        annotation,
-                        this.imageView,
-                    );
-                }
-            }
-        }
-    }
-
-    handleImageViewShortCuts(key) {
-        if (key === 'delete') {
-            const coco = this.cocoFile;
-            const toDelete = coco.annotations.filter(anno => anno.isSelected);
-            for (const anno of toDelete) {
-                this.handleDeleteAnnotation(anno);
-            }
-        }
-    }
-
-    handleIsDrawing(bool: boolean) {
-        this.isDrawing = bool;
-    }
-
-    /* sp-select-label handler */
-    handleLabelChange(label: Label) {
-        this.selectedLabel = label;
-    }
-
-    /* sp-image-annotations handlers */
-    handleChangeAnnotationLabel(change: [Annotation, Label]) {
-        if (!this.isDrawing) {
-            const coco = this.cocoFile;
-            const categoryId = this.cocoFormatService.getLabelId(
-                coco,
-                change[0].category_id,
-                change[1].name,
-                change[1].name,
-            );
-            change[0].category_id = categoryId;
-            change[0].category_name = change[1].categoryId;
-        }
-    }
-
-    handleDeleteAnnotation(annotation) {
-        if (!this.isDrawing) {
-            if (annotation !== undefined) {
-                const coco = this.cocoFile;
-                this.cocoFormatService.removeAnnotation(coco, annotation.id);
-            }
-        }
-    }
-
-    /* utils */
-
-    private labelingEnabled() {
-        const coco = this.cocoFile;
-        const annotation = coco.annotations.find(
-            anno => anno.isHovered && anno.isSelected,
-        );
-        if (annotation !== undefined) {
-            return false;
-        } else {
-            return true;
-        }
-    }
-
-    save(imageIndex) {
-        const coco = this.cocoFile;
-        if (coco !== undefined) {
-            const imageRoute = this._imagesRoutes[imageIndex];
-        }
-    }
-
-    private openSnackBar(message: string) {
-        this.snackBar.open(message, '', {
-            duration: 2000,
-            verticalPosition: 'top',
-            horizontalPosition: 'right',
-        });
-    }
-}
diff --git a/ui/src/app/core-ui/image/image.component.css b/ui/src/app/core-ui/image/image.component.css
deleted file mode 100644
index 13cbc4aacb..0000000000
--- a/ui/src/app/core-ui/image/image.component.css
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * 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.
- *
- */
diff --git a/ui/src/app/core-ui/image/image.component.html b/ui/src/app/core-ui/image/image.component.html
deleted file mode 100644
index ae11e9111f..0000000000
--- a/ui/src/app/core-ui/image/image.component.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
-  ~ 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.
-  ~
-  -->
-
-<mat-button-toggle-group
-    name="fontStyle"
-    aria-label="Font Style"
-    #group="matButtonToggleGroup"
-    value="view"
->
-    <mat-button-toggle value="view">Viewer</mat-button-toggle>
-    <mat-button-toggle value="label">Label</mat-button-toggle>
-    <mat-button-toggle value="categorize">Categorize</mat-button-toggle>
-</mat-button-toggle-group>
-
-<sp-image-viewer *ngIf="group.value === 'view'"></sp-image-viewer>
-<sp-image-labeling *ngIf="group.value === 'label'"></sp-image-labeling>
-<sp-image-categorize *ngIf="group.value === 'categorize'"></sp-image-categorize>
diff --git a/ui/src/app/core-ui/image/image.component.ts b/ui/src/app/core-ui/image/image.component.ts
deleted file mode 100644
index a82df0e3af..0000000000
--- a/ui/src/app/core-ui/image/image.component.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- *
- */
-
-import { Component, Input } from '@angular/core';
-import { SpQueryResult } from '@streampipes/platform-services';
-
-@Component({
-    selector: 'sp-image',
-    templateUrl: './image.component.html',
-    styleUrls: ['./image.component.css'],
-})
-export class ImageComponent {
-    @Input()
-    availableImageData: SpQueryResult;
-
-    constructor() {}
-}
diff --git a/ui/src/app/core-ui/image/model/coordinates.ts b/ui/src/app/core-ui/image/model/coordinates.ts
deleted file mode 100644
index 93a1afdbab..0000000000
--- a/ui/src/app/core-ui/image/model/coordinates.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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.
- */
-
-export interface ICoordinates {
-    x;
-    y;
-}
diff --git a/ui/src/app/core-ui/image/model/labeling-mode.ts b/ui/src/app/core-ui/image/model/labeling-mode.ts
deleted file mode 100644
index 5bcb2e6c38..0000000000
--- a/ui/src/app/core-ui/image/model/labeling-mode.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
-
-export enum LabelingMode {
-    ReactLabeling,
-    PolygonLabeling,
-    BrushLabeling,
-    NoneLabeling,
-}
diff --git a/ui/src/app/core-ui/image/services/BrushLabeling.service.ts b/ui/src/app/core-ui/image/services/BrushLabeling.service.ts
deleted file mode 100644
index afb1f2115a..0000000000
--- a/ui/src/app/core-ui/image/services/BrushLabeling.service.ts
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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.
- */
-
-import { Injectable } from '@angular/core';
-import Konva from 'konva';
-import { Annotation } from '../../../core-model/coco/Annotation';
-import { ICoordinates } from '../model/coordinates';
-import { ColorService } from './color.service';
-import { LabelingModeService } from './LabelingMode.service';
-import { Label } from '@streampipes/platform-services';
-
-@Injectable()
-export class BrushLabelingService {
-    private points: number[];
-    private brushsize: number;
-
-    private isLabeling: boolean;
-
-    constructor(
-        private colorService: ColorService,
-        private labelingMode: LabelingModeService,
-    ) {}
-
-    startLabeling(position: ICoordinates, brushSize: number) {
-        this.isLabeling = true;
-        this.points = [];
-        this.points.push(position.x);
-        this.points.push(position.y);
-        this.brushsize = brushSize;
-    }
-
-    executeLabeling(position: ICoordinates) {
-        this.points.push(position.x);
-        this.points.push(position.y);
-    }
-
-    endLabeling(position: ICoordinates) {
-        this.isLabeling = false;
-        return [this.points, this.brushsize];
-    }
-
-    tempDraw(layer: Konva.Layer, shift: ICoordinates, label: Label) {
-        if (this.isLabeling) {
-            const tmp = [];
-            for (let i = 0; i < this.points.length; i += 2) {
-                tmp.push(this.points[i] + shift.x);
-                tmp.push(this.points[i + 1] + shift.y);
-            }
-
-            const line = new Konva.Line({
-                points: tmp,
-                stroke: label.color,
-                opacity: 0.8,
-                strokeWidth: this.brushsize,
-                closed: false,
-            });
-            layer.add(line);
-        }
-    }
-
-    draw(
-        layer: Konva.Layer,
-        shift: ICoordinates,
-        annotation: Annotation,
-        imageView,
-    ) {
-        const tmp = [];
-        for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
-            tmp.push(annotation.segmentation[0][i] + shift.x);
-            tmp.push(annotation.segmentation[0][i + 1] + shift.y);
-        }
-
-        const line = new Konva.Line({
-            points: tmp,
-            stroke: this.colorService.getColor(annotation.category_name),
-            opacity: 0.5,
-            strokeWidth: this.brushsize,
-            closed: false,
-            draggable: false,
-        });
-        layer.add(line);
-
-        const transformer = new Konva.Transformer({
-            anchorFill: '#FFFFFF',
-            anchorSize: 10,
-            rotateEnabled: false,
-            enabledAnchors: [],
-            borderStroke: 'green',
-        });
-
-        if (annotation.isSelected) {
-            transformer.attachTo(line);
-        }
-
-        this.addMouseHandler(
-            line,
-            annotation,
-            layer,
-            transformer,
-            this.labelingMode,
-        );
-        this.addClickHandler(
-            line,
-            annotation,
-            layer,
-            transformer,
-            this.labelingMode,
-        );
-
-        layer.add(line);
-        layer.add(transformer);
-    }
-
-    private addClickHandler(
-        brush,
-        annotation,
-        layer,
-        transformer,
-        labelingMode,
-    ) {
-        brush.on('click', function () {
-            if (labelingMode.isNoneMode()) {
-                annotation.isSelected = !annotation.isSelected;
-
-                if (annotation.isSelected) {
-                    transformer.attachTo(this);
-                } else {
-                    transformer.detach();
-                }
-
-                layer.batchDraw();
-            }
-        });
-    }
-
-    private addMouseHandler(
-        brush,
-        annotation,
-        layer,
-        transformer,
-        labelingMode,
-    ) {
-        brush.on('mouseover', function () {
-            if (labelingMode.isNoneMode()) {
-                annotation.isHovered = true;
-                brush.opacity(0.8);
-                layer.batchDraw();
-            }
-        });
-
-        brush.on('mouseout', function () {
-            annotation.isHovered = false;
-            brush.opacity(0.5);
-            layer.batchDraw();
-        });
-
-        transformer.on('mouseover', function () {
-            if (labelingMode.isNoneMode()) {
-                annotation.isHovered = true;
-            }
-        });
-
-        transformer.on('mouseout', function () {
-            annotation.isHovered = false;
-        });
-    }
-}
diff --git a/ui/src/app/core-ui/image/services/CocoFormat.service.ts b/ui/src/app/core-ui/image/services/CocoFormat.service.ts
deleted file mode 100644
index f3ed735f1a..0000000000
--- a/ui/src/app/core-ui/image/services/CocoFormat.service.ts
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * 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.
- */
-
-import { Injectable } from '@angular/core';
-import { Annotation } from '../../../core-model/coco/Annotation';
-import { Category } from '../../../core-model/coco/Category';
-import { CocoFormat } from '../../../core-model/coco/Coco.format';
-import { Image } from '../../../core-model/coco/Image';
-
-@Injectable()
-export class CocoFormatService {
-    static getLabelId(
-        coco: CocoFormat,
-        supercategory,
-        name,
-        labelName,
-    ): number {
-        // TODO: Find  better solution instead of copy same code
-        let category = coco.categories.find(
-            elem => elem.name === name && elem.supercategory === supercategory,
-        );
-        if (category === undefined) {
-            category = new Category(
-                coco.categories.length + 1,
-                name,
-                supercategory,
-                labelName,
-            );
-            coco.categories.push(category);
-        }
-        return category.id;
-    }
-
-    addImage(coco: CocoFormat, fileName) {
-        const image = new Image();
-        image.file_name = fileName;
-        image.id = coco.images.length + 1;
-        coco.images.push(image);
-    }
-
-    getLabelById(coco: CocoFormat, id) {
-        return coco.categories.find(elem => elem.id === id).name;
-    }
-
-    getLabelId(coco: CocoFormat, supercategory, name, labelName): number {
-        let category = coco.categories.find(
-            elem => elem.name === name && elem.supercategory === supercategory,
-        );
-        if (category === undefined) {
-            category = new Category(
-                coco.categories.length + 1,
-                name,
-                supercategory,
-                labelName,
-            );
-            coco.categories.push(category);
-        }
-        return category.id;
-    }
-
-    addReactAnnotationToFirstImage(
-        coco: CocoFormat,
-        cords,
-        size,
-        supercategory,
-        category,
-        color,
-        labelName,
-    ): Annotation {
-        const annotation = new Annotation();
-        annotation.id = coco.annotations.length + 1;
-        annotation.iscrowd = 0;
-        annotation.image_id = 1;
-        annotation.bbox = [cords.x, cords.y, size.x, size.y];
-        annotation.area = size.x * size.y;
-        annotation.category_id = CocoFormatService.getLabelId(
-            coco,
-            supercategory,
-            category,
-            labelName,
-        );
-        annotation.category_name = category;
-        annotation.color = color;
-        annotation.label_name = labelName;
-        coco.annotations.push(annotation);
-        return annotation;
-    }
-
-    addPolygonAnnotationFirstImage(
-        coco: CocoFormat,
-        points,
-        supercategory,
-        category,
-        labelName,
-    ): Annotation {
-        const annotation = new Annotation();
-        annotation.id = coco.annotations.length + 1;
-        annotation.iscrowd = 0;
-        annotation.image_id = 1;
-        annotation.segmentation = [points];
-        annotation.category_id = CocoFormatService.getLabelId(
-            coco,
-            supercategory,
-            category,
-            labelName,
-        );
-        annotation.category_name = category;
-        coco.annotations.push(annotation);
-        return annotation;
-    }
-
-    addBrushAnnotationFirstImage(
-        coco: CocoFormat,
-        points,
-        brushSize,
-        supercategory,
-        category,
-        labelName,
-    ): Annotation {
-        const annotation = new Annotation();
-        annotation.id = coco.annotations.length + 1;
-        annotation.iscrowd = 0;
-        annotation.image_id = 1;
-        annotation.segmentation = [points];
-        annotation.brushSize = brushSize;
-        annotation.category_id = this.getLabelId(
-            coco,
-            supercategory,
-            category,
-            labelName,
-        );
-        annotation.category_name = category;
-        coco.annotations.push(annotation);
-        return annotation;
-    }
-
-    removeAnnotation(coco: CocoFormat, id) {
-        coco.annotations = coco.annotations.filter(anno => anno.id !== id);
-    }
-
-    isBoxAnnonation(annotation: Annotation) {
-        return annotation.bbox !== undefined;
-    }
-
-    isPolygonAnnonation(annotation: Annotation) {
-        return (
-            annotation.segmentation !== undefined &&
-            annotation.brushSize === undefined
-        );
-    }
-
-    isBrushAnnonation(annotation: Annotation) {
-        return annotation.brushSize !== undefined;
-    }
-}
diff --git a/ui/src/app/core-ui/image/services/LabelingMode.service.ts b/ui/src/app/core-ui/image/services/LabelingMode.service.ts
deleted file mode 100644
index 07c2c6445e..0000000000
--- a/ui/src/app/core-ui/image/services/LabelingMode.service.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.
- */
-
-import { Injectable } from '@angular/core';
-import { LabelingMode } from '../model/labeling-mode';
-
-@Injectable()
-export class LabelingModeService {
-    private labelingMode: LabelingMode;
-    private lastLabelingMode: LabelingMode;
-
-    constructor() {
-        this.labelingMode = LabelingMode.ReactLabeling;
-        this.lastLabelingMode = LabelingMode.ReactLabeling;
-    }
-
-    isReactMode() {
-        return this.labelingMode === LabelingMode.ReactLabeling;
-    }
-
-    setReactMode() {
-        this.labelingMode = LabelingMode.ReactLabeling;
-        this.lastLabelingMode = LabelingMode.ReactLabeling;
-    }
-
-    isPolygonMode() {
-        return this.labelingMode === LabelingMode.PolygonLabeling;
-    }
-
-    setPolygonMode() {
-        this.labelingMode = LabelingMode.PolygonLabeling;
-        this.lastLabelingMode = LabelingMode.PolygonLabeling;
-    }
-
-    isBrushMode() {
-        return this.labelingMode === LabelingMode.BrushLabeling;
-    }
-
-    setBrushMode() {
-        this.labelingMode = LabelingMode.BrushLabeling;
-        this.lastLabelingMode = LabelingMode.BrushLabeling;
-    }
-
-    isNoneMode() {
-        return this.labelingMode === LabelingMode.NoneLabeling;
-    }
-
-    setNoneMode() {
-        this.labelingMode = LabelingMode.NoneLabeling;
-    }
-
-    getMode() {
-        return this.labelingMode;
-    }
-
-    toggleNoneMode() {
-        if (this.labelingMode === LabelingMode.NoneLabeling) {
-            this.labelingMode = this.lastLabelingMode;
-        } else {
-            this.labelingMode = LabelingMode.NoneLabeling;
-        }
-    }
-}
diff --git a/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts b/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts
deleted file mode 100644
index 7e689967ee..0000000000
--- a/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * 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.
- */
-
-import { Injectable } from '@angular/core';
-import Konva from 'konva';
-import { Annotation } from '../../../core-model/coco/Annotation';
-import { ICoordinates } from '../model/coordinates';
-import { ColorService } from './color.service';
-import { LabelingModeService } from './LabelingMode.service';
-import { Label } from '@streampipes/platform-services';
-
-@Injectable()
-export class PolygonLabelingService {
-    private points: number[];
-    private tmpPoints: number[];
-
-    private isLabeling: boolean;
-
-    constructor(
-        private colorService: ColorService,
-        private labelingMode: LabelingModeService,
-    ) {
-        this.isLabeling = false;
-    }
-
-    static buildAnchors(layer, annotation, imageView, poly) {
-        this.removeAnchors(layer, annotation.id);
-        const anchorGroup = new Konva.Group({
-            name: 'anchor_group_' + annotation.id,
-        });
-
-        for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
-            PolygonLabelingService.buildAnchor(
-                layer,
-                annotation,
-                i,
-                imageView,
-                poly,
-                anchorGroup,
-            );
-        }
-        layer.add(anchorGroup);
-    }
-
-    static buildAnchor(layer, annotation, index, imageView, poly, anchorGroup) {
-        const shift: ICoordinates = imageView.getShift();
-
-        const xx = annotation.segmentation[0][index] + shift.x;
-        const yy = annotation.segmentation[0][index + 1] + shift.y;
-        const anchor = new Konva.Circle({
-            name: 'anchor_' + annotation.id,
-            x: xx,
-            y: yy,
-            radius: 10,
-            fill: 'white',
-            stroke: 'green',
-            strokeWidth: 4,
-            draggable: true,
-        });
-        anchorGroup.add(anchor);
-
-        anchor.on('mouseover', function () {
-            annotation.isHovered = true;
-        });
-
-        anchor.on('mouseout', function () {
-            annotation.isHovered = false;
-        });
-
-        anchor.on('dragmove', function () {
-            const position = imageView.getImagePointerPosition();
-            const _shift: ICoordinates = imageView.getShift();
-
-            annotation.segmentation[0][index] = position.x;
-            annotation.segmentation[0][index + 1] = position.y;
-
-            const tmp = [];
-            for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
-                tmp.push(annotation.segmentation[0][i] + _shift.x);
-                tmp.push(annotation.segmentation[0][i + 1] + _shift.y);
-            }
-            poly.points(tmp);
-            poly.x(0);
-            poly.y(0);
-            poly.draw();
-        });
-    }
-
-    static removeAnchors(layer, annotationId) {
-        const tmp = [];
-        for (const child of layer.children) {
-            if (child.name() === 'anchor_group_' + annotationId) {
-                child.destroy();
-            }
-        }
-        layer.batchDraw();
-    }
-
-    startLabeling(position: ICoordinates) {
-        if (this.isLabeling) {
-            this.points.push(position.x);
-            this.points.push(position.y);
-        } else {
-            this.isLabeling = true;
-            this.points = [];
-            this.tmpPoints = [];
-            this.points.push(position.x);
-            this.points.push(position.y);
-        }
-    }
-
-    executeLabeling(position: ICoordinates) {
-        if (this.isLabeling) {
-            this.tmpPoints = Object.assign([], this.points);
-            this.tmpPoints.push(position.x);
-            this.tmpPoints.push(position.y);
-        }
-    }
-
-    endLabeling(position: ICoordinates) {
-        this.isLabeling = false;
-        return this.points.slice(0, this.points.length - 2);
-    }
-
-    tempDraw(layer: Konva.Layer, shift: ICoordinates, label: Label) {
-        if (this.isLabeling) {
-            const tmp = [];
-            for (let i = 0; i < this.tmpPoints.length; i += 2) {
-                tmp.push(this.tmpPoints[i] + shift.x);
-                tmp.push(this.tmpPoints[i + 1] + shift.y);
-            }
-
-            const poly = new Konva.Line({
-                points: tmp,
-                fill: label.color,
-                stroke: 'black',
-                opacity: 0.8,
-                strokeWidth: 4,
-                closed: true,
-            });
-            layer.add(poly);
-        }
-    }
-
-    draw(
-        layer: Konva.Layer,
-        shift: ICoordinates,
-        annotation: Annotation,
-        imageView,
-    ) {
-        const tmp = [];
-        for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
-            tmp.push(annotation.segmentation[0][i] + shift.x);
-            tmp.push(annotation.segmentation[0][i + 1] + shift.y);
-        }
-
-        const poly = new Konva.Line({
-            points: tmp,
-            fill: annotation.color,
-            stroke: 'black',
-            opacity: 0.5,
-            strokeWidth: 4,
-            closed: true,
-            draggable: true,
-        });
-
-        const transformer = new Konva.Transformer({
-            anchorFill: '#FFFFFF',
-            anchorSize: 10,
-            rotateEnabled: false,
-            enabledAnchors: [],
-            borderStroke: 'green',
-        });
-
-        if (annotation.isSelected) {
-            transformer.attachTo(poly);
-            PolygonLabelingService.buildAnchors(
-                layer,
-                annotation,
-                imageView,
-                poly,
-            );
-        }
-
-        this.addDragHandler(
-            poly,
-            annotation,
-            layer,
-            imageView,
-            this.labelingMode,
-        );
-        this.addMouseHandler(
-            poly,
-            annotation,
-            layer,
-            transformer,
-            this.labelingMode,
-        );
-        this.addClickHandler(
-            poly,
-            annotation,
-            layer,
-            transformer,
-            imageView,
-            this.labelingMode,
-        );
-
-        layer.add(poly);
-        layer.add(transformer);
-    }
-
-    private addClickHandler(
-        poly,
-        annotation,
-        layer,
-        transformer,
-        imageView,
-        labelingMode,
-    ) {
-        poly.on('click', function () {
-            if (labelingMode.isNoneMode()) {
-                annotation.isSelected = !annotation.isSelected;
-
-                if (annotation.isSelected) {
-                    annotation.isSelected = true;
-                    transformer.attachTo(this);
-                    PolygonLabelingService.buildAnchors(
-                        layer,
-                        annotation,
-                        imageView,
-                        poly,
-                    );
-                } else {
-                    PolygonLabelingService.removeAnchors(layer, annotation.id);
-                    transformer.detach();
-                }
-                layer.batchDraw();
-            }
-        });
-    }
-
-    private addMouseHandler(
-        poly,
-        annotation,
-        layer,
-        transformer,
-        labelingMode,
-    ) {
-        poly.on('mouseover', function () {
-            if (labelingMode.isNoneMode()) {
-                annotation.isHovered = true;
-                poly.opacity(0.8);
-                layer.batchDraw();
-            }
-        });
-
-        poly.on('mouseout', function () {
-            annotation.isHovered = false;
-            this.opacity(0.5);
-            layer.batchDraw();
-        });
-    }
-
-    private addDragHandler(poly, annotation, layer, imageView, labelingMode) {
-        let offset: number[];
-
-        poly.on('dragstart', function () {
-            if (!labelingMode.isNoneMode()) {
-                poly.stopDrag();
-            } else {
-                const position = imageView.getImagePointerPosition();
-                offset = [];
-                for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
-                    offset.push(annotation.segmentation[0][i] - position.x);
-                    offset.push(annotation.segmentation[0][i + 1] - position.y);
-                }
-            }
-        });
-
-        poly.on('dragmove', function () {
-            if (!labelingMode.isNoneMode()) {
-                poly.stopDrag();
-            } else {
-                const position = imageView.getImagePointerPosition();
-                const tmp = [];
-                for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
-                    tmp.push(position.x + offset[i]);
-                    tmp.push(position.y + offset[i + 1]);
-                }
-                annotation.segmentation[0] = tmp;
-                if (annotation.isSelected) {
-                    PolygonLabelingService.buildAnchors(
-                        layer,
-                        annotation,
-                        imageView,
-                        poly,
-                    );
-                }
-            }
-        });
-    }
-}
diff --git a/ui/src/app/core-ui/image/services/ReactLabeling.service.ts b/ui/src/app/core-ui/image/services/ReactLabeling.service.ts
deleted file mode 100644
index 106d8dc421..0000000000
--- a/ui/src/app/core-ui/image/services/ReactLabeling.service.ts
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * 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.
- */
-
-import { Injectable } from '@angular/core';
-import Konva from 'konva';
-import { Annotation } from '../../../core-model/coco/Annotation';
-import { ICoordinates } from '../model/coordinates';
-import { ColorService } from './color.service';
-import { LabelingModeService } from './LabelingMode.service';
-import { Label } from '@streampipes/platform-services';
-
-@Injectable()
-export class ReactLabelingService {
-    private lastPosition: ICoordinates;
-    private reactSize: ICoordinates;
-
-    private isLabeling: boolean;
-
-    constructor(
-        private colorService: ColorService,
-        private labelingMode: LabelingModeService,
-    ) {}
-
-    startLabeling(position: ICoordinates) {
-        this.isLabeling = true;
-        this.lastPosition = position;
-        this.reactSize = { x: 0, y: 0 };
-    }
-
-    executeLabeling(position: ICoordinates) {
-        this.reactSize.x = position.x - this.lastPosition.x;
-        this.reactSize.y = position.y - this.lastPosition.y;
-    }
-
-    endLabeling(position: ICoordinates) {
-        this.isLabeling = false;
-        if (this.reactSize.x > 0 || this.reactSize.y > 0) {
-            return [this.lastPosition, this.reactSize];
-        }
-    }
-
-    tempDraw(layer: Konva.Layer, shift: ICoordinates, label: Label) {
-        if (this.isLabeling && (this.reactSize.x > 0 || this.reactSize.y > 0)) {
-            const box = new Konva.Rect({
-                x: this.lastPosition.x + shift.x,
-                y: this.lastPosition.y + shift.y,
-                width: this.reactSize.x,
-                height: this.reactSize.y,
-                fill: label.color,
-                opacity: 0.5,
-                stroke: 'black',
-                strokeWidth: 4,
-                draggable: false,
-            });
-            layer.add(box);
-        }
-    }
-
-    draw(
-        layer: Konva.Layer,
-        shift: ICoordinates,
-        annotation: Annotation,
-        imageView,
-        color: string,
-    ) {
-        const rect = new Konva.Rect({
-            x: annotation.bbox[0] + shift.x,
-            y: annotation.bbox[1] + shift.y,
-            width: annotation.bbox[2],
-            height: annotation.bbox[3],
-            fill: color,
-            opacity: 0.5,
-            stroke: 'black',
-            strokeWidth: 4,
-            draggable: true,
-        });
-
-        const transformer = new Konva.Transformer({
-            anchorFill: '#FFFFFF',
-            anchorSize: 10,
-            rotateEnabled: false,
-            keepRatio: false,
-            borderStroke: 'green',
-        });
-
-        if (annotation.isSelected) {
-            transformer.attachTo(rect);
-        }
-
-        this.addDragHandler(rect, annotation, imageView, this.labelingMode);
-        this.addTransformHandler(rect, annotation, imageView);
-        this.addMouseHandler(
-            rect,
-            annotation,
-            layer,
-            transformer,
-            this.labelingMode,
-        );
-        this.addClickHandler(
-            rect,
-            annotation,
-            layer,
-            transformer,
-            this.labelingMode,
-        );
-
-        layer.add(rect);
-        layer.add(transformer);
-    }
-
-    private addClickHandler(
-        rect,
-        annotation,
-        layer,
-        transformer,
-        labelingMode,
-    ) {
-        rect.on('click', function () {
-            if (labelingMode.isNoneMode()) {
-                annotation.isSelected = !annotation.isSelected;
-
-                if (annotation.isSelected) {
-                    transformer.attachTo(this);
-                } else {
-                    transformer.detach();
-                }
-
-                layer.batchDraw();
-            }
-        });
-    }
-
-    private addMouseHandler(
-        rect,
-        annotation,
-        layer,
-        transformer,
-        labelingMode,
-    ) {
-        rect.on('mouseover', function () {
-            if (labelingMode.isNoneMode()) {
-                annotation.isHovered = true;
-                rect.opacity(0.8);
-                layer.batchDraw();
-            }
-        });
-
-        rect.on('mouseout', function () {
-            annotation.isHovered = false;
-            rect.opacity(0.5);
-            layer.batchDraw();
-        });
-
-        transformer.on('mouseover', function () {
-            if (labelingMode.isNoneMode()) {
-                annotation.isHovered = true;
-            }
-        });
-
-        transformer.on('mouseout', function () {
-            annotation.isHovered = false;
-        });
-    }
-
-    private addTransformHandler(rect, annotation, imageView) {
-        let resizer: string;
-
-        rect.on('transformstart', function (e) {
-            resizer = e.evt.currentTarget.parent.movingResizer;
-        });
-
-        rect.on('transform', function (e) {
-            const position = imageView.getImagePointerPosition();
-            if (resizer === 'top-left') {
-                annotation.bbox[2] += annotation.bbox[0] - position.x;
-                annotation.bbox[3] += annotation.bbox[1] - position.y;
-                annotation.bbox[0] = position.x;
-                annotation.bbox[1] = position.y;
-            } else if (resizer === 'top-right') {
-                annotation.bbox[2] = Math.abs(annotation.bbox[0] - position.x);
-                annotation.bbox[3] += annotation.bbox[1] - position.y;
-                annotation.bbox[1] = position.y;
-            } else if (resizer === 'bottom-left') {
-                annotation.bbox[2] += annotation.bbox[0] - position.x;
-                annotation.bbox[3] = Math.abs(annotation.bbox[1] - position.y);
-                annotation.bbox[0] = position.x;
-            } else if (resizer === 'bottom-right') {
-                annotation.bbox[2] = Math.abs(annotation.bbox[0] - position.x);
-                annotation.bbox[3] = Math.abs(annotation.bbox[1] - position.y);
-            } else if (resizer === 'top-center') {
-                annotation.bbox[3] += annotation.bbox[1] - position.y;
-                annotation.bbox[1] = position.y;
-            } else if (resizer === 'middle-right') {
-                annotation.bbox[2] = Math.abs(annotation.bbox[0] - position.x);
-            } else if (resizer === 'middle-left') {
-                annotation.bbox[2] += annotation.bbox[0] - position.x;
-                annotation.bbox[0] = position.x;
-            } else if (resizer === 'bottom-center') {
-                annotation.bbox[3] = Math.abs(annotation.bbox[1] - position.y);
-            }
-        });
-    }
-
-    private addDragHandler(rect, annotation, imageView, labelingMode) {
-        let offset: ICoordinates;
-
-        rect.on('dragstart', function () {
-            if (!labelingMode.isNoneMode()) {
-                rect.stopDrag();
-            } else {
-                const position = imageView.getImagePointerPosition();
-                offset = {
-                    x: annotation.bbox[0] - position.x,
-                    y: annotation.bbox[1] - position.y,
-                };
-            }
-        });
-
-        rect.on('dragmove', function () {
-            if (!labelingMode.isNoneMode()) {
-                rect.stopDrag();
-            } else {
-                const position = imageView.getImagePointerPosition();
-                annotation.bbox[0] =
-                    imageView.getImagePointerPosition().x + offset.x;
-                annotation.bbox[1] =
-                    imageView.getImagePointerPosition().y + offset.y;
-            }
-        });
-    }
-}
diff --git a/ui/src/app/core-ui/image/services/color.service.ts b/ui/src/app/core-ui/image/services/color.service.ts
deleted file mode 100644
index 419dca4c82..0000000000
--- a/ui/src/app/core-ui/image/services/color.service.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- */
-
-import { Injectable } from '@angular/core';
-
-@Injectable()
-export class ColorService {
-    getColor(str) {
-        let hash = 0;
-        for (let i = 0; i < str.length; i++) {
-            // eslint-disable-next-line no-bitwise
-            hash = str.charCodeAt(i) + ((hash << 5) - hash);
-        }
-        let colour = '#';
-        for (let i = 0; i < 3; i++) {
-            // eslint-disable-next-line no-bitwise
-            const value = (hash >> (i * 8)) & 0xff;
-            colour += ('00' + value.toString(16)).substr(-2);
-        }
-        return colour;
-    }
-}
diff --git a/ui/src/app/core-ui/image/util/color.util.ts b/ui/src/app/core-ui/image/util/color.util.ts
deleted file mode 100644
index f1c9855f5b..0000000000
--- a/ui/src/app/core-ui/image/util/color.util.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-export class ColorUtil {
-    static getColor(label) {
-        let hash = 0;
-        for (let i = 0; i < label.length; i++) {
-            // eslint-disable-next-line no-bitwise
-            hash = label.charCodeAt(i) + ((hash << 5) - hash);
-        }
-        let colour = '#';
-        for (let i = 0; i < 3; i++) {
-            // eslint-disable-next-line no-bitwise
-            const value = (hash >> (i * 8)) & 0xff;
-            colour += ('00' + value.toString(16)).substr(-2);
-        }
-        return colour;
-    }
-}
diff --git a/ui/src/app/core-ui/image/util/imageTranslation.util.ts b/ui/src/app/core-ui/image/util/imageTranslation.util.ts
deleted file mode 100644
index e5c174cb1f..0000000000
--- a/ui/src/app/core-ui/image/util/imageTranslation.util.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-export class ImageTranslationUtil {
-    private static lastImageTranslationX = 0;
-    private static lastImageTranslationY = 0;
-    private static lastMouseX = 0;
-    private static lastMouseY = 0;
-
-    static mouseDown(mousePos, imageTranslationX, imageTranslationY) {
-        this.lastMouseX = mousePos[0];
-        this.lastMouseY = mousePos[1];
-        this.lastImageTranslationX = imageTranslationX;
-        this.lastImageTranslationY = imageTranslationY;
-    }
-
-    static mouseMove(mousePos): [number, number] {
-        const mouseX = mousePos[0];
-        const mouseY = mousePos[1];
-        return [
-            this.lastImageTranslationX + (mouseX - this.lastMouseX),
-            this.lastImageTranslationY + (mouseY - this.lastMouseY),
-        ];
-    }
-}
diff --git a/ui/src/app/core-ui/imageLabeler/classification/imageClassification.ts b/ui/src/app/core-ui/imageLabeler/classification/imageClassification.ts
deleted file mode 100644
index 807a911fdb..0000000000
--- a/ui/src/app/core-ui/imageLabeler/classification/imageClassification.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-import { Injectable } from '@angular/core';
-
-@Injectable()
-export class ImageClassification {
-    private classes: string[];
-    public saved = true;
-    private src;
-
-    newImage(src) {
-        this.saved = true;
-        this.src = src;
-        this.classes = [];
-    }
-
-    addClass(clazz) {
-        this.saved = false;
-        this.classes.push(clazz);
-    }
-
-    getClasses() {
-        return this.classes;
-    }
-
-    removeClass(clazz) {
-        this.saved = false;
-        this.classes = this.classes.filter(c => c !== clazz);
-    }
-
-    save(): boolean {
-        this.saved = true;
-        return true;
-    }
-}
diff --git a/ui/src/app/core-ui/labels/components/configure-labels/configure-labels.component.ts b/ui/src/app/core-ui/labels/components/configure-labels/configure-labels.component.ts
index eb605305b6..bca5b59a22 100644
--- a/ui/src/app/core-ui/labels/components/configure-labels/configure-labels.component.ts
+++ b/ui/src/app/core-ui/labels/components/configure-labels/configure-labels.component.ts
@@ -17,7 +17,6 @@
  */
 
 import { Component, OnInit } from '@angular/core';
-import { ColorService } from '../../../image/services/color.service';
 import { LabelService } from '../../services/label.service';
 import { Category, Label } from '@streampipes/platform-services';
 
@@ -27,10 +26,7 @@ import { Category, Label } from '@streampipes/platform-services';
     styleUrls: ['./configure-labels.component.css'],
 })
 export class ConfigureLabelsComponent implements OnInit {
-    constructor(
-        public colorService: ColorService,
-        public labelService: LabelService,
-    ) {}
+    constructor(public labelService: LabelService) {}
 
     public categories: Category[];
     public selectedCategory: Category;
diff --git a/ui/src/app/core-ui/labels/components/select-label/select-label.component.ts b/ui/src/app/core-ui/labels/components/select-label/select-label.component.ts
index 44c820918b..fe456b4f8b 100644
--- a/ui/src/app/core-ui/labels/components/select-label/select-label.component.ts
+++ b/ui/src/app/core-ui/labels/components/select-label/select-label.component.ts
@@ -24,7 +24,6 @@ import {
     OnInit,
     Output,
 } from '@angular/core';
-import { ColorService } from '../../../image/services/color.service';
 import { Category, Label } from '@streampipes/platform-services';
 import { LabelService } from '../../services/label.service';
 
@@ -45,10 +44,7 @@ export class SelectLabelComponent implements OnInit {
 
     public noCategories = true;
 
-    constructor(
-        public labelService: LabelService,
-        public colorService: ColorService,
-    ) {}
+    constructor(public labelService: LabelService) {}
 
     ngOnInit(): void {
         this.labelService.getCategories().subscribe(res => {
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.html b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.html
similarity index 100%
rename from ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.html
rename to ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.html
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.scss b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.scss
similarity index 100%
rename from ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.scss
rename to ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.scss
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.ts b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.ts
similarity index 100%
rename from ui/src/app/core-ui/image/components/image-bar/image-bar-preview/image-bar-preview.component.ts
rename to ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component.ts
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.css
similarity index 100%
rename from ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
rename to ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.css
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.html b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.html
similarity index 100%
rename from ui/src/app/core-ui/image/components/image-bar/image-bar.component.html
rename to ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.html
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.ts b/ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.ts
similarity index 100%
rename from ui/src/app/core-ui/image/components/image-bar/image-bar.component.ts
rename to ui/src/app/data-explorer/components/widgets/image/image-bar/image-bar.component.ts
diff --git a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.css b/ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.css
similarity index 100%
rename from ui/src/app/core-ui/image/image-viewer/image-viewer.component.css
rename to ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.css
diff --git a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.html b/ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.html
similarity index 100%
rename from ui/src/app/core-ui/image/image-viewer/image-viewer.component.html
rename to ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.html
diff --git a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.ts b/ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.ts
similarity index 100%
rename from ui/src/app/core-ui/image/image-viewer/image-viewer.component.ts
rename to ui/src/app/data-explorer/components/widgets/image/image-viewer/image-viewer.component.ts
diff --git a/ui/src/app/data-explorer/data-explorer.module.ts b/ui/src/app/data-explorer/data-explorer.module.ts
index 1b19a70f60..767478890c 100644
--- a/ui/src/app/data-explorer/data-explorer.module.ts
+++ b/ui/src/app/data-explorer/data-explorer.module.ts
@@ -101,6 +101,9 @@ import { DataExplorerDashboardSlideViewComponent } from './components/widget-vie
 import { SharedUiModule } from '@streampipes/shared-ui';
 import { DataExplorerPanelCanDeactivateGuard } from './data-explorer-panel.can-deactivate.guard';
 import { NgxEchartsModule } from 'ngx-echarts';
+import { ImageViewerComponent } from './components/widgets/image/image-viewer/image-viewer.component';
+import { ImageBarComponent } from './components/widgets/image/image-bar/image-bar.component';
+import { ImageBarPreviewComponent } from './components/widgets/image/image-bar/image-bar-preview/image-bar-preview.component';
 
 export const MY_NATIVE_FORMATS = {
     fullPickerInput: {
@@ -197,6 +200,8 @@ export const MY_NATIVE_FORMATS = {
         FilterSelectionPanelComponent,
         GroupConfigurationComponent,
         ImageWidgetComponent,
+        ImageBarComponent,
+        ImageBarPreviewComponent,
         ImageWidgetConfigComponent,
         IndicatorChartWidgetComponent,
         IndicatorWidgetConfigComponent,
@@ -215,6 +220,7 @@ export const MY_NATIVE_FORMATS = {
         MapWidgetComponent,
         HeatmapWidgetConfigComponent,
         HeatmapWidgetComponent,
+        ImageViewerComponent,
         TimeRangeSelectorComponent,
         DataExplorerVisualisationSettingsComponent,
         GroupSelectionPanelComponent,


Test patch:
diff --git a/streampipes-commons/src/test/java/org/apache/streampipes/commons/prometheus/adapter/AdapterMetricsTest.java b/streampipes-commons/src/test/java/org/apache/streampipes/commons/prometheus/adapter/AdapterMetricsTest.java
new file mode 100644
index 0000000000..24370f10c5
--- /dev/null
+++ b/streampipes-commons/src/test/java/org/apache/streampipes/commons/prometheus/adapter/AdapterMetricsTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.commons.prometheus.adapter;
+
+import org.junit.Test;
+
+import java.util.NoSuchElementException;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertThrows;
+
+public class AdapterMetricsTest {
+
+  private static final String ADAPTER_ID = "adapterId";
+  private static final String ADAPTER_NAME = "adapterName";
+  private static final String OTHER_ADAPTER_ID = "otherAdapterId";
+
+  @Test
+  public void register() {
+
+    var metrics = AdapterMetricsManager.INSTANCE.getAdapterMetrics();
+
+    assertEquals(0, metrics.size());
+
+    metrics.register(ADAPTER_ID, ADAPTER_NAME);
+
+    assertEquals(1, metrics.size());
+    assertTrue(metrics.contains(ADAPTER_ID));
+  }
+
+  @Test
+  public void removeNoSuchElement() {
+    var metrics = AdapterMetricsManager.INSTANCE.getAdapterMetrics();
+
+    assertThrows(NoSuchElementException.class, () -> metrics.remove(OTHER_ADAPTER_ID, ADAPTER_NAME));
+  }
+
+  @Test
+  public void updateTotalEventsPublishedNoSuchElement() {
+    var metrics = AdapterMetricsManager.INSTANCE.getAdapterMetrics();
+
+    assertThrows(
+        NoSuchElementException.class,
+        () -> metrics.updateTotalEventsPublished(OTHER_ADAPTER_ID, ADAPTER_NAME, 0)
+    );
+
+  }
+}
diff --git a/streampipes-commons/src/test/java/TestResources.java b/streampipes-commons/src/test/java/org/apache/streampipes/commons/resources/TestResources.java
similarity index 90%
rename from streampipes-commons/src/test/java/TestResources.java
rename to streampipes-commons/src/test/java/org/apache/streampipes/commons/resources/TestResources.java
index ec7174e336..7db525a3a7 100644
--- a/streampipes-commons/src/test/java/TestResources.java
+++ b/streampipes-commons/src/test/java/org/apache/streampipes/commons/resources/TestResources.java
@@ -16,7 +16,7 @@
  *
  */
 
-import org.apache.streampipes.commons.resources.Resources;
+package org.apache.streampipes.commons.resources;
 
 import org.junit.Test;
 
@@ -31,6 +31,6 @@ public class TestResources {
   public void testResourceLoader() throws IOException {
     String filename = "test.txt";
     String content = Resources.asString(filename, StandardCharsets.UTF_8);
-    assertEquals("HelloWorld", content.replaceAll("\\s+",""));
+    assertEquals("HelloWorld", content.replaceAll("\\s+", ""));
   }
 }
diff --git a/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/health/AdapterHealthCheckTest.java b/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/health/AdapterHealthCheckTest.java
index 12f34c4c5c..8eedbd725f 100644
--- a/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/health/AdapterHealthCheckTest.java
+++ b/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/health/AdapterHealthCheckTest.java
@@ -18,9 +18,12 @@
 
 package org.apache.streampipes.connect.management.health;
 
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetricsManager;
 import org.apache.streampipes.connect.management.management.AdapterMasterManagement;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
+import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.storage.api.IAdapterStorage;
+import org.apache.streampipes.storage.management.StorageDispatcher;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -45,7 +48,16 @@ public class AdapterHealthCheckTest {
   public void getAllRunningInstancesAdapterDescriptionsEmpty() {
     when(adapterInstanceStorageMock.getAllAdapters()).thenReturn(List.of());
 
-    var healthCheck = new AdapterHealthCheck(adapterInstanceStorageMock, new AdapterMasterManagement());
+    var healthCheck = new AdapterHealthCheck(
+        adapterInstanceStorageMock,
+        new AdapterMasterManagement(
+            StorageDispatcher.INSTANCE.getNoSqlStore()
+                                      .getAdapterInstanceStorage(),
+            new SpResourceManager().manageAdapters(),
+            new SpResourceManager().manageDataStreams(),
+            AdapterMetricsManager.INSTANCE.getAdapterMetrics()
+        )
+    );
     var result = healthCheck.getAllRunningInstancesAdapterDescriptions();
 
     assertTrue(result.isEmpty());
@@ -67,7 +79,16 @@ public class AdapterHealthCheckTest {
 
     when(adapterInstanceStorageMock.getAllAdapters()).thenReturn(List.of(stoppedAdapter, runningAdapter));
 
-    var healthCheck = new AdapterHealthCheck(adapterInstanceStorageMock, new AdapterMasterManagement());
+    var healthCheck = new AdapterHealthCheck(
+        adapterInstanceStorageMock,
+        new AdapterMasterManagement(
+            StorageDispatcher.INSTANCE.getNoSqlStore()
+                                      .getAdapterInstanceStorage(),
+            new SpResourceManager().manageAdapters(),
+            new SpResourceManager().manageDataStreams(),
+            AdapterMetricsManager.INSTANCE.getAdapterMetrics()
+        )
+    );
     var result = healthCheck.getAllRunningInstancesAdapterDescriptions();
 
     assertEquals(1, result.size());
diff --git a/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java b/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
index bafde99a37..30b4c07ac1 100644
--- a/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
+++ b/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
@@ -19,6 +19,7 @@
 package org.apache.streampipes.connect.management.management;
 
 import org.apache.streampipes.commons.exceptions.connect.AdapterException;
+import org.apache.streampipes.commons.prometheus.adapter.AdapterMetricsManager;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.resource.management.AdapterResourceManager;
 import org.apache.streampipes.storage.couchdb.impl.AdapterInstanceStorageImpl;
@@ -40,7 +41,12 @@ public class AdapterMasterManagementTest {
     when(adapterStorage.getAllAdapters()).thenReturn(null);
 
     AdapterMasterManagement adapterMasterManagement =
-        new AdapterMasterManagement(adapterStorage, resourceManager, null);
+        new AdapterMasterManagement(
+            adapterStorage,
+            resourceManager,
+            null,
+            AdapterMetricsManager.INSTANCE.getAdapterMetrics()
+        );
 
     adapterMasterManagement.getAdapter("id2");
   }
@@ -53,7 +59,12 @@ public class AdapterMasterManagementTest {
     when(adapterStorage.getAllAdapters()).thenReturn(adapterDescriptions);
 
     AdapterMasterManagement adapterMasterManagement =
-        new AdapterMasterManagement(adapterStorage, resourceManager, null);
+        new AdapterMasterManagement(
+            adapterStorage,
+            resourceManager,
+            null,
+            AdapterMetricsManager.INSTANCE.getAdapterMetrics()
+        );
 
     adapterMasterManagement.getAdapter("id2");
   }
@@ -66,7 +77,12 @@ public class AdapterMasterManagementTest {
     when(adapterStorage.getAllAdapters()).thenReturn(adapterDescriptions);
 
     AdapterMasterManagement adapterMasterManagement =
-        new AdapterMasterManagement(adapterStorage, resourceManager, null);
+        new AdapterMasterManagement(
+            adapterStorage,
+            resourceManager,
+            null,
+            AdapterMetricsManager.INSTANCE.getAdapterMetrics()
+        );
 
     List<AdapterDescription> result = adapterMasterManagement.getAllAdapterInstances();
 
@@ -80,7 +96,12 @@ public class AdapterMasterManagementTest {
     when(adapterStorage.getAllAdapters()).thenReturn(null);
 
     AdapterMasterManagement adapterMasterManagement =
-        new AdapterMasterManagement(adapterStorage, resourceManager, null);
+        new AdapterMasterManagement(
+            adapterStorage,
+            resourceManager,
+            null,
+            AdapterMetricsManager.INSTANCE.getAdapterMetrics()
+        );
 
     adapterMasterManagement.getAllAdapterInstances();