You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2022/08/18 20:33:08 UTC

[incubator-streampipes] 02/15: [STREAMPIPES-577] Improve error handling in StreamPipes Connect

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

riemer pushed a commit to branch rel/0.70.0
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit 7a1f96bfb31ceb81367c68f0ecae7176afab89eb
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Mon Aug 15 14:48:11 2022 +0200

    [STREAMPIPES-577] Improve error handling in StreamPipes Connect
---
 .../exceptions/SpConfigurationException.java       |  32 +++
 .../master/management/GuessManagement.java         |   6 +-
 .../master/management/WorkerRestClient.java        |  23 +-
 .../worker/rest/RuntimeResolvableResource.java     |  26 +-
 .../api/InvocablePipelineElementResource.java      |  27 +-
 .../api/ResolvesContainerProvidedOptions.java      |   3 +-
 .../ResolvesContainerProvidedOutputStrategy.java   |   4 +-
 .../api/RuntimeResolvableRequestHandler.java       |   5 +-
 .../container/api/SupportsRuntimeConfig.java       |   3 +-
 .../opcua/MiloOpcUaConfigurationProvider.java      |   8 +-
 .../connect/iiot/adapters/opcua/OpcUaAdapter.java  |   4 +-
 .../connect/iiot/adapters/opcua/SpOpcUaClient.java |   8 +-
 .../opcua/utils/ExceptionMessageExtractor.java     |  34 ++-
 .../iiot/adapters/opcua/utils/OpcUaUtil.java       | 287 +++++++++++----------
 .../org/apache/streampipes/model/MessageLd.java    | 117 ---------
 .../apache/streampipes/model/NotificationLd.java   |  95 -------
 .../streampipes/model/StreamPipesErrorMessage.java |  98 +++++++
 .../rest/impl/connect/GuessResource.java           |  14 +-
 .../impl/connect/RuntimeResolvableResource.java    |  19 +-
 ui/package.json                                    |   4 +-
 .../src/lib/model/gen/streampipes-model.ts         |  35 ++-
 ui/projects/streampipes/shared-ui/package.json     |   1 +
 .../exception-details-dialog.component.html        |  43 +++
 .../exception-details-dialog.component.scss}       |  39 +--
 .../exception-details-dialog.component.ts}         |  25 +-
 .../sp-exception-message.component.html            |  38 +++
 .../sp-exception-message.component.scss            |  17 +-
 .../sp-exception-message.component.ts              |  56 ++++
 .../shared-ui/src/lib/shared-ui.module.ts          |  10 +-
 .../streampipes/shared-ui/src/public-api.ts        |   2 +
 .../new-adapter/new-adapter.component.ts           |   1 -
 .../error-message/error-message.component.html     |  18 +-
 .../error-message/error-message.component.ts       |   3 +-
 .../event-schema/event-schema.component.html       |   2 +-
 .../event-schema/event-schema.component.ts         |   5 +-
 ui/src/app/connect/connect.module.ts               |   3 +-
 ...tatic-runtime-resolvable-any-input.component.ts |   3 +
 .../base-runtime-resolvable-input.ts               |  13 +
 ...tic-runtime-resolvable-oneof-input.component.ts |   3 +
 .../static-tree-input.component.html               |   3 +
 .../static-tree-input.component.ts                 |  31 +++
 41 files changed, 687 insertions(+), 481 deletions(-)

diff --git a/streampipes-commons/src/main/java/org/apache/streampipes/commons/exceptions/SpConfigurationException.java b/streampipes-commons/src/main/java/org/apache/streampipes/commons/exceptions/SpConfigurationException.java
new file mode 100644
index 000000000..a1436f4f1
--- /dev/null
+++ b/streampipes-commons/src/main/java/org/apache/streampipes/commons/exceptions/SpConfigurationException.java
@@ -0,0 +1,32 @@
+package org.apache.streampipes.commons.exceptions;
+
+public class SpConfigurationException extends Exception {
+
+  /**
+   * Creates a new Exception with the given message and null as the cause.
+   *
+   * @param message The exception message
+   */
+  public SpConfigurationException(String message) {
+    super(message);
+  }
+
+  /**
+   * Creates a new exception with a null message and the given cause.
+   *
+   * @param cause The exception that caused this exception
+   */
+  public SpConfigurationException(Throwable cause) {
+    super(cause);
+  }
+
+  /**
+   * Creates a new exception with the given message and cause
+   *
+   * @param message The exception message
+   * @param cause The exception that caused this exception
+   */
+  public SpConfigurationException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
diff --git a/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/GuessManagement.java b/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/GuessManagement.java
index f5385e317..e54ade7a4 100644
--- a/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/GuessManagement.java
+++ b/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/GuessManagement.java
@@ -67,10 +67,10 @@ public class GuessManagement {
             if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                 return mapper.readValue(responseString, GuessSchema.class);
             }  else {
-                    ErrorMessage errorMessage = mapper.readValue(responseString, ErrorMessage.class);
+                ErrorMessage errorMessage = mapper.readValue(responseString, ErrorMessage.class);
 
-                    LOG.error(errorMessage.getElementName());
-                    throw new WorkerAdapterException(errorMessage);
+                LOG.error(errorMessage.getElementName());
+                throw new WorkerAdapterException(errorMessage);
             }
     }
 
diff --git a/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/WorkerRestClient.java b/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/WorkerRestClient.java
index 8c9f1074c..dab1e4bd7 100644
--- a/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/WorkerRestClient.java
+++ b/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/WorkerRestClient.java
@@ -18,8 +18,11 @@
 
 package org.apache.streampipes.connect.container.master.management;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.io.IOUtils;
 import org.apache.http.client.fluent.Request;
 import org.apache.http.entity.ContentType;
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.connect.api.exception.AdapterException;
 import org.apache.streampipes.connect.container.master.util.WorkerPaths;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
@@ -37,6 +40,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 
 /**
@@ -146,19 +150,26 @@ public class WorkerRestClient {
 
     public static RuntimeOptionsResponse getConfiguration(String workerEndpoint,
                                                           String appId,
-                                                          RuntimeOptionsRequest runtimeOptionsRequest) throws AdapterException {
+                                                          RuntimeOptionsRequest runtimeOptionsRequest) throws AdapterException, SpConfigurationException {
         String url = workerEndpoint + WorkerPaths.getRuntimeResolvablePath(appId);
 
         try {
             String payload = JacksonSerializer.getObjectMapper().writeValueAsString(runtimeOptionsRequest);
-            String responseString = Request.Post(url)
+            var response = Request.Post(url)
                        .bodyString(payload, ContentType.APPLICATION_JSON)
                        .connectTimeout(1000)
                        .socketTimeout(100000)
-                       .execute().returnContent().asString();
+                       .execute()
+                        .returnResponse();
 
-            return JacksonSerializer.getObjectMapper().readValue(responseString, RuntimeOptionsResponse.class);
+            String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
 
+            if (response.getStatusLine().getStatusCode() == 200) {
+                return getSerializer().readValue(responseString, RuntimeOptionsResponse.class);
+            } else {
+                var exception = getSerializer().readValue(responseString, SpConfigurationException.class);
+                throw new SpConfigurationException(exception.getMessage(), exception.getCause());
+            }
         } catch (IOException e) {
             e.printStackTrace();
             throw new AdapterException("Could not resolve runtime configurations from " + url);
@@ -245,5 +256,9 @@ public class WorkerRestClient {
     private static IAdapterStorage getAdapterStorage() {
         return StorageDispatcher.INSTANCE.getNoSqlStore().getAdapterInstanceStorage();
     }
+
+    private static ObjectMapper getSerializer() {
+        return JacksonSerializer.getObjectMapper();
+    }
 }
 
diff --git a/streampipes-connect-container-worker/src/main/java/org/apache/streampipes/connect/container/worker/rest/RuntimeResolvableResource.java b/streampipes-connect-container-worker/src/main/java/org/apache/streampipes/connect/container/worker/rest/RuntimeResolvableResource.java
index 92a81fdec..72a209462 100644
--- a/streampipes-connect-container-worker/src/main/java/org/apache/streampipes/connect/container/worker/rest/RuntimeResolvableResource.java
+++ b/streampipes-connect-container-worker/src/main/java/org/apache/streampipes/connect/container/worker/rest/RuntimeResolvableResource.java
@@ -18,6 +18,8 @@
 
 package org.apache.streampipes.connect.container.worker.rest;
 
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
+import org.apache.streampipes.commons.exceptions.SpRuntimeException;
 import org.apache.streampipes.connect.api.Connector;
 import org.apache.streampipes.connect.container.worker.management.RuntimeResovable;
 import org.apache.streampipes.container.api.ResolvesContainerProvidedOptions;
@@ -47,15 +49,21 @@ public class RuntimeResolvableResource extends AbstractSharedRestInterface {
         RuntimeOptionsResponse response;
         RuntimeResolvableRequestHandler handler = new RuntimeResolvableRequestHandler();
 
-        if (connector instanceof ResolvesContainerProvidedOptions) {
-            response = handler.handleRuntimeResponse((ResolvesContainerProvidedOptions) connector, runtimeOptionsRequest);
-        } else if (connector instanceof SupportsRuntimeConfig) {
-            response = handler.handleRuntimeResponse((SupportsRuntimeConfig) connector, runtimeOptionsRequest);
-        } else {
-            throw new WebApplicationException(javax.ws.rs.core.Response.Status.BAD_REQUEST);
+        try {
+            if (connector instanceof ResolvesContainerProvidedOptions) {
+                response = handler.handleRuntimeResponse((ResolvesContainerProvidedOptions) connector, runtimeOptionsRequest);
+                return ok(response);
+            } else if (connector instanceof SupportsRuntimeConfig) {
+                response = handler.handleRuntimeResponse((SupportsRuntimeConfig) connector, runtimeOptionsRequest);
+                return ok(response);
+            } else {
+                throw new SpRuntimeException("This element does not support dynamic options - is the pipeline element description up to date?");
+            }
+        } catch (SpConfigurationException e) {
+            return javax.ws.rs.core.Response
+              .status(400)
+              .entity(e)
+              .build();
         }
-
-        return ok(response);
     }
-
 }
diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java b/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java
index 9936bd2bf..780b70434 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java
@@ -19,6 +19,7 @@
 package org.apache.streampipes.container.api;
 
 import org.apache.streampipes.commons.constants.Envs;
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.commons.exceptions.SpRuntimeException;
 import org.apache.streampipes.container.declarer.Declarer;
 import org.apache.streampipes.container.declarer.InvocableDeclarer;
@@ -106,14 +107,21 @@ public abstract class InvocablePipelineElementResource<I extends InvocableStream
     D declarer = getDeclarerById(elementId);
     RuntimeOptionsResponse responseOptions;
 
-    if (declarer instanceof ResolvesContainerProvidedOptions) {
-      responseOptions = new RuntimeResolvableRequestHandler().handleRuntimeResponse((ResolvesContainerProvidedOptions) declarer, req);
-      return ok(responseOptions);
-    } else if (declarer instanceof SupportsRuntimeConfig) {
-      responseOptions = new RuntimeResolvableRequestHandler().handleRuntimeResponse((SupportsRuntimeConfig) declarer, req);
-      return ok(responseOptions);
-    } else {
-      throw new WebApplicationException(javax.ws.rs.core.Response.Status.BAD_REQUEST);
+    try {
+      if (declarer instanceof ResolvesContainerProvidedOptions) {
+        responseOptions = new RuntimeResolvableRequestHandler().handleRuntimeResponse((ResolvesContainerProvidedOptions) declarer, req);
+        return ok(responseOptions);
+      } else if (declarer instanceof SupportsRuntimeConfig) {
+          responseOptions = new RuntimeResolvableRequestHandler().handleRuntimeResponse((SupportsRuntimeConfig) declarer, req);
+          return ok(responseOptions);
+      } else {
+        return javax.ws.rs.core.Response.status(500).build();
+      }
+    } catch (SpConfigurationException e) {
+      return javax.ws.rs.core.Response
+        .status(400)
+        .entity(e)
+        .build();
     }
   }
 
@@ -131,8 +139,7 @@ public abstract class InvocablePipelineElementResource<I extends InvocableStream
                               (elementId);
       return ok(resolvesOutput.resolveOutputStrategy
               (runtimeOptionsRequest, getExtractor(runtimeOptionsRequest)));
-    } catch (SpRuntimeException e) {
-      e.printStackTrace();
+    } catch (SpRuntimeException | SpConfigurationException e) {
       return ok(new Response(elementId, false));
     }
   }
diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/api/ResolvesContainerProvidedOptions.java b/streampipes-container/src/main/java/org/apache/streampipes/container/api/ResolvesContainerProvidedOptions.java
index a32a1decb..110d95eaa 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/api/ResolvesContainerProvidedOptions.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/api/ResolvesContainerProvidedOptions.java
@@ -17,6 +17,7 @@
  */
 package org.apache.streampipes.container.api;
 
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.model.staticproperty.Option;
 import org.apache.streampipes.sdk.extractor.StaticPropertyExtractor;
 
@@ -29,5 +30,5 @@ import java.util.List;
 public interface ResolvesContainerProvidedOptions {
 
   List<Option> resolveOptions(String staticPropertyInternalName,
-                              StaticPropertyExtractor parameterExtractor);
+                              StaticPropertyExtractor parameterExtractor) throws SpConfigurationException;
 }
diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/api/ResolvesContainerProvidedOutputStrategy.java b/streampipes-container/src/main/java/org/apache/streampipes/container/api/ResolvesContainerProvidedOutputStrategy.java
index 83ed622b0..ab44539ae 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/api/ResolvesContainerProvidedOutputStrategy.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/api/ResolvesContainerProvidedOutputStrategy.java
@@ -17,7 +17,7 @@
  */
 package org.apache.streampipes.container.api;
 
-import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.model.base.InvocableStreamPipesEntity;
 import org.apache.streampipes.model.schema.EventSchema;
 import org.apache.streampipes.sdk.extractor.AbstractParameterExtractor;
@@ -25,5 +25,5 @@ import org.apache.streampipes.sdk.extractor.AbstractParameterExtractor;
 public interface ResolvesContainerProvidedOutputStrategy<T extends InvocableStreamPipesEntity, P
         extends AbstractParameterExtractor<T>> {
 
-  EventSchema resolveOutputStrategy(T processingElement, P parameterExtractor) throws SpRuntimeException;
+  EventSchema resolveOutputStrategy(T processingElement, P parameterExtractor) throws SpConfigurationException;
 }
diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/api/RuntimeResolvableRequestHandler.java b/streampipes-container/src/main/java/org/apache/streampipes/container/api/RuntimeResolvableRequestHandler.java
index aba13222a..91e18f97f 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/api/RuntimeResolvableRequestHandler.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/api/RuntimeResolvableRequestHandler.java
@@ -18,6 +18,7 @@
 
 package org.apache.streampipes.container.api;
 
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.model.runtime.RuntimeOptionsRequest;
 import org.apache.streampipes.model.runtime.RuntimeOptionsResponse;
 import org.apache.streampipes.model.staticproperty.Option;
@@ -31,7 +32,7 @@ public class RuntimeResolvableRequestHandler {
 
   // for backwards compatibility
   public RuntimeOptionsResponse handleRuntimeResponse(ResolvesContainerProvidedOptions resolvesOptions,
-                                                      RuntimeOptionsRequest req) {
+                                                      RuntimeOptionsRequest req) throws SpConfigurationException {
     List<Option> availableOptions =
             resolvesOptions.resolveOptions(req.getRequestId(),
                     makeExtractor(req));
@@ -43,7 +44,7 @@ public class RuntimeResolvableRequestHandler {
   }
 
   public RuntimeOptionsResponse handleRuntimeResponse(SupportsRuntimeConfig declarer,
-                                                      RuntimeOptionsRequest req) {
+                                                      RuntimeOptionsRequest req) throws SpConfigurationException {
     StaticProperty result = declarer.resolveConfiguration(
             req.getRequestId(),
             makeExtractor(req));
diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/api/SupportsRuntimeConfig.java b/streampipes-container/src/main/java/org/apache/streampipes/container/api/SupportsRuntimeConfig.java
index 10cacd125..4ebd76854 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/api/SupportsRuntimeConfig.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/api/SupportsRuntimeConfig.java
@@ -18,12 +18,13 @@
 
 package org.apache.streampipes.container.api;
 
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.model.staticproperty.StaticProperty;
 import org.apache.streampipes.sdk.extractor.StaticPropertyExtractor;
 
 public interface SupportsRuntimeConfig {
 
   StaticProperty resolveConfiguration(String staticPropertyInternalName,
-                                      StaticPropertyExtractor extractor);
+                                      StaticPropertyExtractor extractor) throws SpConfigurationException;
 
 }
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/MiloOpcUaConfigurationProvider.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/MiloOpcUaConfigurationProvider.java
index e4336d842..7145858ea 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/MiloOpcUaConfigurationProvider.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/MiloOpcUaConfigurationProvider.java
@@ -18,6 +18,7 @@
 
 package org.apache.streampipes.connect.iiot.adapters.opcua;
 
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.connect.iiot.adapters.opcua.configuration.SpOpcUaConfig;
 import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
 import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfigBuilder;
@@ -31,10 +32,11 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
 
 public class MiloOpcUaConfigurationProvider {
 
-  public OpcUaClientConfig makeClientConfig(SpOpcUaConfig spOpcConfig) throws Exception {
+  public OpcUaClientConfig makeClientConfig(SpOpcUaConfig spOpcConfig) throws ExecutionException, InterruptedException, SpConfigurationException, URISyntaxException {
     String opcServerUrl = spOpcConfig.getOpcServerURL();
     List<EndpointDescription> endpoints = DiscoveryClient.getEndpoints(opcServerUrl).get();
     String host = opcServerUrl.split("://")[1].split(":")[0];
@@ -43,7 +45,7 @@ public class MiloOpcUaConfigurationProvider {
             .stream()
             .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
             .findFirst()
-            .orElseThrow(() -> new Exception("No endpoint with security policy none"));
+            .orElseThrow(() -> new SpConfigurationException("No endpoint with security policy none"));
 
     tmpEndpoint = updateEndpointUrl(tmpEndpoint, host);
     endpoints = Collections.singletonList(tmpEndpoint);
@@ -51,7 +53,7 @@ public class MiloOpcUaConfigurationProvider {
     EndpointDescription endpoint = endpoints
             .stream()
             .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
-            .findFirst().orElseThrow(() -> new Exception("no desired endpoints returned"));
+            .findFirst().orElseThrow(() -> new SpConfigurationException("no desired endpoints returned"));
 
     return buildConfig(endpoint, spOpcConfig);
   }
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/OpcUaAdapter.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/OpcUaAdapter.java
index 57039d1ee..76b3df7e2 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/OpcUaAdapter.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/OpcUaAdapter.java
@@ -18,6 +18,7 @@
 
 package org.apache.streampipes.connect.iiot.adapters.opcua;
 
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.connect.adapter.Adapter;
 import org.apache.streampipes.connect.adapter.util.PollingSettings;
 import org.apache.streampipes.connect.api.exception.AdapterException;
@@ -270,7 +271,8 @@ public class OpcUaAdapter extends PullAdapter implements SupportsRuntimeConfig {
     }
 
     @Override
-    public StaticProperty resolveConfiguration(String staticPropertyInternalName, StaticPropertyExtractor extractor) {
+    public StaticProperty resolveConfiguration(String staticPropertyInternalName,
+                                               StaticPropertyExtractor extractor) throws SpConfigurationException {
         return OpcUaUtil.resolveConfiguration(staticPropertyInternalName, extractor);
     }
 }
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/SpOpcUaClient.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/SpOpcUaClient.java
index da00cbf1b..6e7b9af8f 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/SpOpcUaClient.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/SpOpcUaClient.java
@@ -19,12 +19,14 @@
 package org.apache.streampipes.connect.iiot.adapters.opcua;
 
 
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.connect.iiot.adapters.opcua.configuration.SpOpcUaConfig;
 import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
 import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
 import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
 import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
 import org.eclipse.milo.opcua.stack.core.AttributeId;
+import org.eclipse.milo.opcua.stack.core.UaException;
 import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
 import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
 import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;
@@ -37,9 +39,11 @@ import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.AtomicLong;
 
 import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
@@ -71,9 +75,9 @@ public class SpOpcUaClient {
     /***
      * Establishes appropriate connection to OPC UA endpoint depending on the {@link SpOpcUaClient} instance
      *
-     * @throws Exception An exception occurring during OPC connection
+     * @throws UaException An exception occurring during OPC connection
      */
-    public void connect() throws Exception {
+    public void connect() throws UaException, ExecutionException, InterruptedException, SpConfigurationException, URISyntaxException {
         OpcUaClientConfig clientConfig = new MiloOpcUaConfigurationProvider().makeClientConfig(spOpcConfig);
         this.client = OpcUaClient.create(clientConfig);
         client.connect().get();
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/ErrorMessageLd.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/utils/ExceptionMessageExtractor.java
similarity index 61%
rename from streampipes-model/src/main/java/org/apache/streampipes/model/ErrorMessageLd.java
rename to streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/utils/ExceptionMessageExtractor.java
index 091f35b1d..7c1b45396 100644
--- a/streampipes-model/src/main/java/org/apache/streampipes/model/ErrorMessageLd.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/utils/ExceptionMessageExtractor.java
@@ -16,25 +16,23 @@
  *
  */
 
-package org.apache.streampipes.model;
+package org.apache.streampipes.connect.iiot.adapters.opcua.utils;
 
-import java.util.List;
+import org.eclipse.milo.opcua.stack.core.UaException;
 
-public class ErrorMessageLd extends MessageLd {
+public class ExceptionMessageExtractor {
 
-	public ErrorMessageLd() {
-		super(false);
-	}
-
-	public ErrorMessageLd(NotificationLd...notifications) {
-		super(false, notifications);
-	}
-
-	public ErrorMessageLd(List<NotificationLd> notifications) {
-		super(false, notifications.toArray(new NotificationLd[0]));
-	}
-
-	public ErrorMessageLd(String elementName, List<NotificationLd> notifications) {
-		super(false, notifications, elementName);
-	}
+  public static String getDescription(UaException e) {
+    String[] parts = e.getMessage().split(", ");
+    if (parts.length > 1) {
+      String[] kv = parts[1].split("=");
+      if (kv.length > 1) {
+        return kv[1];
+      } else {
+        return parts[1];
+      }
+    } else {
+      return e.getMessage();
+    }
+  }
 }
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/utils/OpcUaUtil.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/utils/OpcUaUtil.java
index 3bf5430c8..d9f95872f 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/utils/OpcUaUtil.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/adapters/opcua/utils/OpcUaUtil.java
@@ -18,6 +18,7 @@
 
 package org.apache.streampipes.connect.iiot.adapters.opcua.utils;
 
+import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.connect.api.exception.AdapterException;
 import org.apache.streampipes.connect.api.exception.ParseException;
 import org.apache.streampipes.connect.iiot.adapters.opcua.OpcNode;
@@ -37,170 +38,174 @@ import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
 import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
 
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
 
 /***
  * Collection of several utility functions in context of OPC UA
  */
 public class OpcUaUtil {
 
-    /***
-     * Ensures server address starts with {@code opc.tcp://}
-     * @param serverAddress server address as given by user
-     * @return correctly formated server address
-     */
-    public static String formatServerAddress(String serverAddress) {
+  /***
+   * Ensures server address starts with {@code opc.tcp://}
+   * @param serverAddress server address as given by user
+   * @return correctly formated server address
+   */
+  public static String formatServerAddress(String serverAddress) {
 
-        if (!serverAddress.startsWith("opc.tcp://")) {
-            serverAddress = "opc.tcp://" + serverAddress;
-        }
-
-        return serverAddress;
+    if (!serverAddress.startsWith("opc.tcp://")) {
+      serverAddress = "opc.tcp://" + serverAddress;
     }
 
-    /***
-     * OPC UA specific implementation of {@link org.apache.streampipes.connect.adapter.Adapter}
-     * @param adapterStreamDescription
-     * @return guess schema
-     * @throws AdapterException
-     * @throws ParseException
-     */
-    public static GuessSchema getSchema(SpecificAdapterStreamDescription adapterStreamDescription)
-            throws AdapterException, ParseException {
-        GuessSchema guessSchema = new GuessSchema();
-        EventSchema eventSchema = new EventSchema();
-        List<EventProperty> allProperties = new ArrayList<>();
-
-        SpOpcUaClient spOpcUaClient = new SpOpcUaClient(SpOpcUaConfigBuilder.from(adapterStreamDescription));
-
-        try {
-            spOpcUaClient.connect();
-            OpcUaNodeBrowser nodeBrowser =
-                    new OpcUaNodeBrowser(spOpcUaClient.getClient(), spOpcUaClient.getSpOpcConfig());
-            List<OpcNode> selectedNodes = nodeBrowser.findNodes();
-
-            if (!selectedNodes.isEmpty()) {
-                for (OpcNode opcNode : selectedNodes) {
-                    if (opcNode.hasUnitId()) {
-                        allProperties.add(PrimitivePropertyBuilder
-                                .create(opcNode.getType(), opcNode.getLabel())
-                                .label(opcNode.getLabel())
-                                .measurementUnit(new URI(opcNode.getQudtURI()))
-                                .build());
-                    } else {
-                        allProperties.add(PrimitivePropertyBuilder
-                                .create(opcNode.getType(), opcNode.getLabel())
-                                .label(opcNode.getLabel())
-                                .build());
-                    }
-
-                }
-            }
-
-            spOpcUaClient.disconnect();
-
-        } catch (Exception e) {
-            throw new AdapterException("Could not guess schema for opc node:  " + e.getMessage(), e.getCause());
+    return serverAddress;
+  }
+
+  /***
+   * OPC UA specific implementation of {@link org.apache.streampipes.connect.adapter.Adapter}
+   * @param adapterStreamDescription
+   * @return guess schema
+   * @throws AdapterException
+   * @throws ParseException
+   */
+  public static GuessSchema getSchema(SpecificAdapterStreamDescription adapterStreamDescription)
+    throws AdapterException, ParseException {
+    GuessSchema guessSchema = new GuessSchema();
+    EventSchema eventSchema = new EventSchema();
+    List<EventProperty> allProperties = new ArrayList<>();
+
+    SpOpcUaClient spOpcUaClient = new SpOpcUaClient(SpOpcUaConfigBuilder.from(adapterStreamDescription));
+
+    try {
+      spOpcUaClient.connect();
+      OpcUaNodeBrowser nodeBrowser =
+        new OpcUaNodeBrowser(spOpcUaClient.getClient(), spOpcUaClient.getSpOpcConfig());
+      List<OpcNode> selectedNodes = nodeBrowser.findNodes();
+
+      if (!selectedNodes.isEmpty()) {
+        for (OpcNode opcNode : selectedNodes) {
+          if (opcNode.hasUnitId()) {
+            allProperties.add(PrimitivePropertyBuilder
+              .create(opcNode.getType(), opcNode.getLabel())
+              .label(opcNode.getLabel())
+              .measurementUnit(new URI(opcNode.getQudtURI()))
+              .build());
+          } else {
+            allProperties.add(PrimitivePropertyBuilder
+              .create(opcNode.getType(), opcNode.getLabel())
+              .label(opcNode.getLabel())
+              .build());
+          }
+
         }
+      }
 
-        eventSchema.setEventProperties(allProperties);
-        guessSchema.setEventSchema(eventSchema);
+      spOpcUaClient.disconnect();
 
-        return guessSchema;
+    } catch (Exception e) {
+      throw new AdapterException("Could not guess schema for opc node:  " + e.getMessage(), e.getCause());
     }
 
-
-    /***
-     * OPC UA specific implementation of {@link
-     * org.apache.streampipes.container.api.ResolvesContainerProvidedOptions#
-     * resolveOptions(String, StaticPropertyExtractor)}.
-     * @param internalName The internal name of the Static Property
-     * @param parameterExtractor
-     * @return {@code List<Option>} with available node names for the given OPC UA configuration
-     */
-    public static RuntimeResolvableTreeInputStaticProperty
-    resolveConfiguration(String internalName,
-                            StaticPropertyExtractor parameterExtractor) {
-
-        RuntimeResolvableTreeInputStaticProperty config = parameterExtractor
-                .getStaticPropertyByName(internalName, RuntimeResolvableTreeInputStaticProperty.class);
-        // access mode and host/url have to be selected
-        try {
-            parameterExtractor.selectedAlternativeInternalId(OpcUaLabels.OPC_HOST_OR_URL.name());
-            parameterExtractor.selectedAlternativeInternalId(OpcUaLabels.ACCESS_MODE.name());
-        } catch (NullPointerException nullPointerException) {
-            return config;
-        }
-
-        SpOpcUaClient spOpcUaClient = new SpOpcUaClient(SpOpcUaConfigBuilder.from(parameterExtractor));
-        try {
-            spOpcUaClient.connect();
-            OpcUaNodeBrowser nodeBrowser =
-                    new OpcUaNodeBrowser(spOpcUaClient.getClient(), spOpcUaClient.getSpOpcConfig());
-            config.setNodes(nodeBrowser.buildNodeTreeFromOrigin());
-            spOpcUaClient.disconnect();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        return config;
+    eventSchema.setEventProperties(allProperties);
+    guessSchema.setEventSchema(eventSchema);
+
+    return guessSchema;
+  }
+
+
+  /***
+   * OPC UA specific implementation of {@link
+   * org.apache.streampipes.container.api.ResolvesContainerProvidedOptions#
+   * resolveOptions(String, StaticPropertyExtractor)}.
+   * @param internalName The internal name of the Static Property
+   * @param parameterExtractor to extract parameters from the OPC UA config
+   * @return {@code List<Option>} with available node names for the given OPC UA configuration
+   */
+  public static RuntimeResolvableTreeInputStaticProperty resolveConfiguration(String internalName,
+                                                                              StaticPropertyExtractor parameterExtractor) throws SpConfigurationException {
+
+    RuntimeResolvableTreeInputStaticProperty config = parameterExtractor
+      .getStaticPropertyByName(internalName, RuntimeResolvableTreeInputStaticProperty.class);
+    // access mode and host/url have to be selected
+    try {
+      parameterExtractor.selectedAlternativeInternalId(OpcUaLabels.OPC_HOST_OR_URL.name());
+      parameterExtractor.selectedAlternativeInternalId(OpcUaLabels.ACCESS_MODE.name());
+    } catch (NullPointerException nullPointerException) {
+      return config;
     }
 
-    public static String getRuntimeNameOfNode(NodeId nodeId) {
-        String[] keys = nodeId.getIdentifier().toString().split("\\.");
-        String key;
+    SpOpcUaClient spOpcUaClient = new SpOpcUaClient(SpOpcUaConfigBuilder.from(parameterExtractor));
 
-        if (keys.length > 0) {
-            key = keys[keys.length - 1];
-        } else {
-            key = nodeId.getIdentifier().toString();
-        }
+    try {
+      spOpcUaClient.connect();
+      OpcUaNodeBrowser nodeBrowser =
+        new OpcUaNodeBrowser(spOpcUaClient.getClient(), spOpcUaClient.getSpOpcConfig());
+      config.setNodes(nodeBrowser.buildNodeTreeFromOrigin());
+      spOpcUaClient.disconnect();
 
-        return key;
+      return config;
+    } catch (UaException e) {
+      throw new SpConfigurationException(ExceptionMessageExtractor.getDescription(e), e);
+    } catch (ExecutionException | InterruptedException | URISyntaxException e) {
+      throw new SpConfigurationException("Could not connect to the OPC UA server with the provided settings", e);
     }
+  }
 
-    /**
-     * connects to each node individually and updates the data type in accordance to the data from the server.
-     *
-     * @param opcNodes List of opcNodes where the data type is not determined appropriately
-     */
-    public static void retrieveDataTypesFromServer(OpcUaClient client, List<OpcNode> opcNodes) throws AdapterException {
-
-        for (OpcNode opcNode : opcNodes) {
-            try {
-                UInteger dataTypeId =
-                        (UInteger) client.getAddressSpace().getVariableNode(opcNode.getNodeId()).getDataType()
-                                .getIdentifier();
-                OpcUaTypes.getType(dataTypeId);
-                opcNode.setType(OpcUaTypes.getType(dataTypeId));
-            } catch (UaException e) {
-                throw new AdapterException("Could not guess schema for opc node! " + e.getMessage());
-            }
-        }
+  public static String getRuntimeNameOfNode(NodeId nodeId) {
+    String[] keys = nodeId.getIdentifier().toString().split("\\.");
+    String key;
+
+    if (keys.length > 0) {
+      key = keys[keys.length - 1];
+    } else {
+      key = nodeId.getIdentifier().toString();
     }
 
-    /***
-     * Enum for all possible labels in the context of OPC UA adapters
-     */
-    public enum OpcUaLabels {
-        OPC_HOST_OR_URL,
-        OPC_URL,
-        OPC_HOST,
-        OPC_SERVER_URL,
-        OPC_SERVER_HOST,
-        OPC_SERVER_PORT,
-        NAMESPACE_INDEX,
-        NODE_ID,
-        ACCESS_MODE,
-        USERNAME_GROUP,
-        USERNAME,
-        PASSWORD,
-        UNAUTHENTICATED,
-        AVAILABLE_NODES,
-        PULLING_INTERVAL,
-        ADAPTER_TYPE,
-        PULL_MODE,
-        SUBSCRIPTION_MODE;
+    return key;
+  }
+
+  /**
+   * connects to each node individually and updates the data type in accordance to the data from the server.
+   *
+   * @param opcNodes List of opcNodes where the data type is not determined appropriately
+   */
+  public static void retrieveDataTypesFromServer(OpcUaClient client, List<OpcNode> opcNodes) throws AdapterException {
+
+    for (OpcNode opcNode : opcNodes) {
+      try {
+        UInteger dataTypeId =
+          (UInteger) client.getAddressSpace().getVariableNode(opcNode.getNodeId()).getDataType()
+            .getIdentifier();
+        OpcUaTypes.getType(dataTypeId);
+        opcNode.setType(OpcUaTypes.getType(dataTypeId));
+      } catch (UaException e) {
+        throw new AdapterException("Could not guess schema for opc node! " + e.getMessage());
+      }
     }
+  }
+
+  /***
+   * Enum for all possible labels in the context of OPC UA adapters
+   */
+  public enum OpcUaLabels {
+    OPC_HOST_OR_URL,
+    OPC_URL,
+    OPC_HOST,
+    OPC_SERVER_URL,
+    OPC_SERVER_HOST,
+    OPC_SERVER_PORT,
+    NAMESPACE_INDEX,
+    NODE_ID,
+    ACCESS_MODE,
+    USERNAME_GROUP,
+    USERNAME,
+    PASSWORD,
+    UNAUTHENTICATED,
+    AVAILABLE_NODES,
+    PULLING_INTERVAL,
+    ADAPTER_TYPE,
+    PULL_MODE,
+    SUBSCRIPTION_MODE;
+  }
 }
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/MessageLd.java b/streampipes-model/src/main/java/org/apache/streampipes/model/MessageLd.java
deleted file mode 100644
index 61b3cd87c..000000000
--- a/streampipes-model/src/main/java/org/apache/streampipes/model/MessageLd.java
+++ /dev/null
@@ -1,117 +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.
- *
- */
-
-package org.apache.streampipes.model;
-
-import org.apache.commons.lang3.RandomStringUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class MessageLd {
-
-	private static final String prefix = "urn:streampipes.org:spi:";
-
-	private String elementId;
-
-	private boolean success;
-
-	private String elementName;
-
-	private List<NotificationLd> notifications;
-
-	public MessageLd() {
-		this.elementId = prefix
-				+ this.getClass().getSimpleName().toLowerCase()
-				+ ":"
-				+ RandomStringUtils.randomAlphabetic(6);
-		this.elementName = "";
-	}
-
-	public MessageLd(MessageLd other) {
-		this();
-		this.success = other.isSuccess();
-		this.elementName = other.getElementName();
-		this.notifications = other.getNotifications();
-	}
-
-	public MessageLd(boolean success){
-		this();
-		this.success = success;
-		this.notifications = null;
-	}
-
-	public MessageLd(boolean success, List<NotificationLd> notifications) {
-		this();
-		this.success = success;
-		this.notifications = notifications;
-	}
-
-	public MessageLd(boolean success, List<NotificationLd> notifications, String elementName) {
-		this(success, notifications);
-		this.elementName = elementName;
-	}
-
-
-	public MessageLd(boolean success, NotificationLd...notifications) {
-		this();
-		this.success = success;
-		this.notifications = new ArrayList<>();
-		this.notifications.addAll(Arrays.asList(notifications));
-	}
-
-	public boolean isSuccess() {
-		return success;
-	}
-
-	public void setSuccess(boolean success) {
-		this.success = success;
-	}
-
-	public List<NotificationLd> getNotifications() {
-		return notifications;
-	}
-
-	public void setNotifications(List<NotificationLd> notifications) {
-		this.notifications = notifications;
-	}
-	
-	public boolean addNotification(NotificationLd notification)
-	{
-		return notifications.add(notification);
-	}
-
-	public String getElementName() {
-		return elementName;
-	}
-
-	public void setElementName(String elementName) {
-		this.elementName = elementName;
-	}
-
-	public String getElementId() {
-		return elementId;
-	}
-
-	public void setElementId(String elementId) {
-		this.elementId = elementId;
-	}
-	
-	
-}
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/NotificationLd.java b/streampipes-model/src/main/java/org/apache/streampipes/model/NotificationLd.java
deleted file mode 100644
index 7b6b7b89c..000000000
--- a/streampipes-model/src/main/java/org/apache/streampipes/model/NotificationLd.java
+++ /dev/null
@@ -1,95 +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.
- *
- */
-
-package org.apache.streampipes.model;
-
-import org.apache.commons.lang3.RandomStringUtils;
-
-public class NotificationLd {
-
-    private static final String prefix = "urn:streampipes.org:spi:";
-
-    private String elementId;
-
-    private String title;
-
-    private String description;
-
-    private String additionalInformation;
-
-    public NotificationLd() {
-        this.elementId = prefix
-                + this.getClass().getSimpleName().toLowerCase()
-                + ":"
-                + RandomStringUtils.randomAlphabetic(6);
-        this.additionalInformation = "";
-    }
-
-    public NotificationLd(NotificationLd other) {
-        this();
-        this.title = other.getTitle();
-        this.description = other.getDescription();
-        this.additionalInformation = other.getAdditionalInformation();
-    }
-
-    public NotificationLd(String title, String description) {
-        this();
-        this.title = title;
-        this.description = description;
-    }
-
-    public NotificationLd(String title, String description,
-                        String additionalInformation) {
-        this();
-        this.title = title;
-        this.description = description;
-        this.additionalInformation = additionalInformation;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getAdditionalInformation() {
-        return additionalInformation;
-    }
-
-    public void setAdditionalInformation(String additionalInformation) {
-        this.additionalInformation = additionalInformation;
-    }
-
-    public String getElementId() {
-        return elementId;
-    }
-
-    public void setElementId(String elementId) {
-        this.elementId = elementId;
-    }
-}
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/StreamPipesErrorMessage.java b/streampipes-model/src/main/java/org/apache/streampipes/model/StreamPipesErrorMessage.java
new file mode 100644
index 000000000..d2d964090
--- /dev/null
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/StreamPipesErrorMessage.java
@@ -0,0 +1,98 @@
+/*
+ * 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.model;
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.streampipes.model.shared.annotation.TsModel;
+
+@TsModel
+public class StreamPipesErrorMessage {
+
+  private String level;
+  private String title;
+  private String detail;
+
+  private String cause;
+  private String fullStackTrace;
+
+  public StreamPipesErrorMessage() {
+  }
+
+  public static StreamPipesErrorMessage from(Exception exception) {
+    String cause = exception.getCause() != null ? exception.getCause().getMessage() : exception.getMessage();
+    return new StreamPipesErrorMessage(
+      "error",
+      exception.getMessage(),
+      "",
+      ExceptionUtils.getStackTrace(exception),
+      cause);
+  }
+
+  public StreamPipesErrorMessage(String level,
+                                 String title,
+                                 String detail,
+                                 String fullStackTrace,
+                                 String cause) {
+    this.level = level;
+    this.title = title;
+    this.detail = detail;
+    this.fullStackTrace = fullStackTrace;
+    this.cause = cause;
+  }
+
+  public String getLevel() {
+    return level;
+  }
+
+  public void setLevel(String level) {
+    this.level = level;
+  }
+
+  public String getTitle() {
+    return title;
+  }
+
+  public void setTitle(String title) {
+    this.title = title;
+  }
+
+  public String getDetail() {
+    return detail;
+  }
+
+  public void setDetail(String detail) {
+    this.detail = detail;
+  }
+
+  public String getFullStackTrace() {
+    return fullStackTrace;
+  }
+
+  public void setFullStackTrace(String fullStackTrace) {
+    this.fullStackTrace = fullStackTrace;
+  }
+
+  public String getCause() {
+    return cause;
+  }
+
+  public void setCause(String cause) {
+    this.cause = cause;
+  }
+}
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java
index 6593be344..7cdd81281 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java
@@ -18,12 +18,13 @@
 
 package org.apache.streampipes.rest.impl.connect;
 
+import org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
 import org.apache.streampipes.connect.api.exception.ParseException;
 import org.apache.streampipes.connect.api.exception.WorkerAdapterException;
 import org.apache.streampipes.connect.container.master.management.GuessManagement;
+import org.apache.streampipes.model.StreamPipesErrorMessage;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.model.connect.guess.GuessSchema;
-import org.apache.streampipes.model.message.Notifications;
 import org.apache.streampipes.rest.shared.annotation.JacksonSerialized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,6 +34,7 @@ import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import java.io.IOException;
 
 
 @Path("/v2/connect/master/guess")
@@ -56,12 +58,12 @@ public class GuessResource extends AbstractAdapterResource<GuessManagement> {
           return ok(result);
       } catch (ParseException e) {
           LOG.error("Error while parsing events: ", e);
-          return serverError(Notifications.error(e.getMessage()));
+          return badRequest(StreamPipesErrorMessage.from(e));
       } catch (WorkerAdapterException e) {
-          return serverError(e.getContent());
-      } catch (Exception e) {
-          LOG.error("Error while guessing the schema for AdapterDescription: {}", e.getMessage());
-          return serverError(Notifications.error(e.getMessage()));
+          return serverError(StreamPipesErrorMessage.from(e));
+      } catch (NoServiceEndpointsAvailableException | IOException e) {
+        LOG.error(e.getMessage());
+        return serverError(StreamPipesErrorMessage.from(e));
       }
   }
 }
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 1db6196b6..5f3fe1f67 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
@@ -19,13 +19,17 @@
 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.connect.api.exception.AdapterException;
 import org.apache.streampipes.connect.container.master.management.WorkerAdministrationManagement;
 import org.apache.streampipes.connect.container.master.management.WorkerRestClient;
 import org.apache.streampipes.connect.container.master.management.WorkerUrlProvider;
+import org.apache.streampipes.model.StreamPipesErrorMessage;
 import org.apache.streampipes.model.runtime.RuntimeOptionsRequest;
 import org.apache.streampipes.model.runtime.RuntimeOptionsResponse;
 import org.apache.streampipes.rest.shared.annotation.JacksonSerialized;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
@@ -34,6 +38,8 @@ import javax.ws.rs.core.Response;
 @Path("/v2/connect/master/resolvable")
 public class RuntimeResolvableResource extends AbstractAdapterResource<WorkerAdministrationManagement> {
 
+    private static final Logger LOG = LoggerFactory.getLogger(RuntimeResolvableResource.class);
+
     private final WorkerUrlProvider workerUrlProvider;
 
     public RuntimeResolvableResource() {
@@ -56,11 +62,16 @@ public class RuntimeResolvableResource extends AbstractAdapterResource<WorkerAdm
             RuntimeOptionsResponse result = WorkerRestClient.getConfiguration(workerEndpoint, appId, runtimeOptionsRequest);
 
             return ok(result);
-        } catch (AdapterException | NoServiceEndpointsAvailableException e) {
-            e.printStackTrace();
-            return fail();
+        } catch (AdapterException e) {
+            LOG.error("Adapter exception occurred", e);
+            return serverError(StreamPipesErrorMessage.from(e));
+        } catch (NoServiceEndpointsAvailableException e) {
+            LOG.error("Could not find service endpoint for {} while fetching configuration", appId);
+            return serverError(StreamPipesErrorMessage.from(e));
+        } catch (SpConfigurationException e) {
+            LOG.error("Tried to fetch a runtime configuration with insufficient settings");
+            return badRequest(StreamPipesErrorMessage.from(e));
         }
-
     }
 
 }
diff --git a/ui/package.json b/ui/package.json
index 30e2878f0..5be6f1bb5 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -8,8 +8,8 @@
     "url": "https://github.com/apache/incubator-streampipes"
   },
   "scripts": {
-    "build-libs": "ng build @streampipes/shared-ui && ng build @streampipes/platform-services",
-    "install-libs": "npm install @streampipes/shared-ui@file:./dist/streampipes/shared-ui @streampipes/platform-services@file:./dist/streampipes/platform-services --no-save",
+    "build-libs": "ng build @streampipes/platform-services && ng build @streampipes/shared-ui",
+    "install-libs": "npm install @streampipes/platform-services@file:./dist/streampipes/platform-services @streampipes/shared-ui@file:./dist/streampipes/shared-ui --no-save",
     "build-libraries": "npm run build-libs && npm run install-libs",
     "start": "node ./deployment/prebuild.js && npm run build-libraries && ng serve",
     "test": "node ./deployment/prebuild.js && npm run build-libraries && ng test",
diff --git a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
index c4f9a582e..24ebb69e4 100644
--- a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
@@ -18,7 +18,7 @@
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 2.27.744 on 2022-08-01 10:55:40.
+// Generated using typescript-generator version 2.27.744 on 2022-08-14 22:39:25.
 
 export class AbstractStreamPipesEntity {
     "@class": "org.apache.streampipes.model.base.AbstractStreamPipesEntity" | "org.apache.streampipes.model.base.NamedStreamPipesEntity" | "org.apache.streampipes.model.connect.adapter.AdapterDescription" | "org.apache.streampipes.model.connect.adapter.AdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.GenericAdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.SpecificAdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.AdapterStre [...]
@@ -192,9 +192,9 @@ export class AdapterDescription extends NamedStreamPipesEntity {
         instance.selectedEndpointUrl = data.selectedEndpointUrl;
         instance.correspondingServiceGroup = data.correspondingServiceGroup;
         instance.correspondingDataStreamElementId = data.correspondingDataStreamElementId;
+        instance.streamRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.streamRules);
         instance.schemaRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.schemaRules);
         instance.valueRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.valueRules);
-        instance.streamRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.streamRules);
         return instance;
     }
 
@@ -987,11 +987,14 @@ export class DataExplorerWidgetModel extends DashboardEntity {
 
 export class DataLakeMeasure extends UnnamedStreamPipesEntity {
     "@class": "org.apache.streampipes.model.datalake.DataLakeMeasure";
+    _rev: string;
     eventSchema: EventSchema;
     measureName: string;
     pipelineId: string;
     pipelineIsRunning: boolean;
     pipelineName: string;
+    schemaVersion: string;
+    timestampField: string;
 
     static fromData(data: DataLakeMeasure, target?: DataLakeMeasure): DataLakeMeasure {
         if (!data) {
@@ -1000,10 +1003,13 @@ export class DataLakeMeasure extends UnnamedStreamPipesEntity {
         const instance = target || new DataLakeMeasure();
         super.fromData(data, instance);
         instance.measureName = data.measureName;
+        instance.timestampField = data.timestampField;
         instance.eventSchema = EventSchema.fromData(data.eventSchema);
         instance.pipelineId = data.pipelineId;
         instance.pipelineName = data.pipelineName;
         instance.pipelineIsRunning = data.pipelineIsRunning;
+        instance.schemaVersion = data.schemaVersion;
+        instance._rev = data._rev;
         return instance;
     }
 }
@@ -2561,8 +2567,8 @@ export class PipelineTemplateDescription extends NamedStreamPipesEntity {
         const instance = target || new PipelineTemplateDescription();
         super.fromData(data, instance);
         instance.boundTo = __getCopyArrayFn(BoundPipelineElement.fromData)(data.boundTo);
-        instance.pipelineTemplateName = data.pipelineTemplateName;
         instance.pipelineTemplateId = data.pipelineTemplateId;
+        instance.pipelineTemplateName = data.pipelineTemplateName;
         instance.pipelineTemplateDescription = data.pipelineTemplateDescription;
         return instance;
     }
@@ -2964,8 +2970,8 @@ export class SpDataSet extends SpDataStream {
         instance.datasetInvocationId = data.datasetInvocationId;
         instance.correspondingPipeline = data.correspondingPipeline;
         instance.selectedEndpointUrl = data.selectedEndpointUrl;
-        instance.brokerHostname = data.brokerHostname;
         instance.actualTopicName = data.actualTopicName;
+        instance.brokerHostname = data.brokerHostname;
         return instance;
     }
 }
@@ -3105,6 +3111,27 @@ export class StreamPipesApplicationPackage {
     }
 }
 
+export class StreamPipesErrorMessage {
+    cause: string;
+    detail: string;
+    fullStackTrace: string;
+    level: string;
+    title: string;
+
+    static fromData(data: StreamPipesErrorMessage, target?: StreamPipesErrorMessage): StreamPipesErrorMessage {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new StreamPipesErrorMessage();
+        instance.level = data.level;
+        instance.title = data.title;
+        instance.detail = data.detail;
+        instance.cause = data.cause;
+        instance.fullStackTrace = data.fullStackTrace;
+        return instance;
+    }
+}
+
 export class SuccessMessage extends Message {
 
     static fromData(data: SuccessMessage, target?: SuccessMessage): SuccessMessage {
diff --git a/ui/projects/streampipes/shared-ui/package.json b/ui/projects/streampipes/shared-ui/package.json
index a602da9ba..8a55c7fd6 100644
--- a/ui/projects/streampipes/shared-ui/package.json
+++ b/ui/projects/streampipes/shared-ui/package.json
@@ -9,6 +9,7 @@
     "@angular/flex-layout": "^13.0.0-beta.38",
     "@angular/material": "^13.3.0",
     "@angular/router": "^13.3.0",
+    "@streampipes/platform-services": "0.0.1",
     "rxjs": "^6.6.2"
   },
   "dependencies": {
diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component.html
new file mode 100644
index 000000000..3ed79a1df
--- /dev/null
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component.html
@@ -0,0 +1,43 @@
+<!--
+  ~ 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="sp-dialog-container">
+    <div class="sp-dialog-content p-20">
+        <div fxFlex="100" fxLayout="column" class="mt-10">
+            <div class="error-details-title">Probable cause</div>
+            <div class="log-message" [innerText]="message.cause">
+            </div>
+            <div class="mt-10">
+            <button mat-button color="accent" (click)="showDetails = !showDetails">Full details</button>
+            </div>
+            <div fxFlex="100" fxLayout="column" *ngIf="showDetails" class="mt-10">
+                <div class="error-details-title">Full stack trace</div>
+                <div class="log-message">
+                    <div [innerText]="message.fullStackTrace"></div>
+                </div>
+            </div>
+        </div>
+
+    </div>
+    <mat-divider></mat-divider>
+    <div class="sp-dialog-actions actions-align-right">
+        <button mat-button mat-raised-button class="mat-basic" (click)="close()">
+            Close
+        </button>
+    </div>
+</div>
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component.scss
similarity index 63%
copy from ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts
copy to ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component.scss
index 4e64c258c..c3e663fc4 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component.scss
@@ -16,23 +16,30 @@
  *
  */
 
-import { Component, Input, OnInit } from '@angular/core';
-import { Notification } from '@streampipes/platform-services';
-
-@Component({
-  selector: 'sp-error-message',
-  templateUrl: './error-message.component.html',
-  styleUrls: ['./error-message.component.scss']
-})
-export class ErrorMessageComponent implements OnInit {
-
-  @Input() errorMessages: Notification[];
-
-  showErrorMessage = false;
+.log-message {
+  background-color: black;
+  font: 9pt Inconsolata, monospace;
+  text-shadow: 0 0 5px #C8C8C8;
+  color: white;
+  padding: 10px;
+  max-width: 100%;
+  max-height: 300px;
+  overflow-y: scroll;
+  white-space: pre-wrap;
+}
 
-  constructor() { }
+.error-details-title {
+  font-size: 13pt;
+  font-weight: var(--color-default-text);
+  border-left: 3px solid var(--color-accent);
+  padding-left: 10px;
+  margin-bottom: 15px;
+}
 
-  ngOnInit(): void {
-  }
+.mt-10 {
+  margin-top: 10px;
+}
 
+.p-20 {
+  padding: 20px;
 }
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component.ts
similarity index 56%
copy from ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts
copy to ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component.ts
index 4e64c258c..28eef5829 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component.ts
@@ -16,23 +16,28 @@
  *
  */
 
-import { Component, Input, OnInit } from '@angular/core';
-import { Notification } from '@streampipes/platform-services';
+import { Component, Input } from '@angular/core';
+import { StreamPipesErrorMessage } from '@streampipes/platform-services';
+import { DialogRef } from '../../../dialog/base-dialog/dialog-ref';
 
 @Component({
-  selector: 'sp-error-message',
-  templateUrl: './error-message.component.html',
-  styleUrls: ['./error-message.component.scss']
+  selector: 'sp-exception-details-dialog',
+  templateUrl: './exception-details-dialog.component.html',
+  styleUrls: ['./exception-details-dialog.component.scss', '../../../../../../../../src/scss/sp/sp-dialog.scss']
 })
-export class ErrorMessageComponent implements OnInit {
+export class SpExceptionDetailsDialogComponent {
 
-  @Input() errorMessages: Notification[];
+  @Input()
+  message: StreamPipesErrorMessage;
 
-  showErrorMessage = false;
+  showDetails = false;
 
-  constructor() { }
+  constructor(private dialogRef: DialogRef<SpExceptionDetailsDialogComponent>) {
 
-  ngOnInit(): void {
+  }
+
+  close() {
+    this.dialogRef.close();
   }
 
 }
diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.html
new file mode 100644
index 000000000..46238bfc4
--- /dev/null
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.html
@@ -0,0 +1,38 @@
+<!--
+  ~ 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" fxFlex="100" class="error-panel error">
+    <div fxLayout="row">
+        <div fxFlex="100" fxLayoutAlign="start center">
+            <div fxLayout="row" fxLayoutAlign="start center" class="p-5" fxFlex="100">
+                <div fxLayoutAlign="start center">
+                <i class="material-icons color-warn" style="margin-right: 15px;">warning</i>
+                <h5 fxFlex class="color-warn">{{message.title}}</h5>
+                </div>
+                <span fxFlex></span>
+                <div fxLayoutAlign="end center" *ngIf="showDetails">
+                    <button mat-button (click)="openDetailsDialog()">
+                        <i class="material-icons">visibility</i>&nbsp;Details
+                    </button>
+                </div>
+            </div>
+
+        </div>
+    </div>
+
+</div>
diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/api/SupportsRuntimeConfig.java b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.scss
similarity index 68%
copy from streampipes-container/src/main/java/org/apache/streampipes/container/api/SupportsRuntimeConfig.java
copy to ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.scss
index 10cacd125..15a00db00 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/api/SupportsRuntimeConfig.java
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.scss
@@ -16,14 +16,15 @@
  *
  */
 
-package org.apache.streampipes.container.api;
-
-import org.apache.streampipes.model.staticproperty.StaticProperty;
-import org.apache.streampipes.sdk.extractor.StaticPropertyExtractor;
-
-public interface SupportsRuntimeConfig {
+.error-panel {
+  background: var(--color-bg-1);
+  border-radius: 5px;
+}
 
-  StaticProperty resolveConfiguration(String staticPropertyInternalName,
-                                      StaticPropertyExtractor extractor);
+.error {
+  border: 1px solid var(--color-warn);
+}
 
+.color-warn {
+  color: var(--color-warn);
 }
diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.ts
new file mode 100644
index 000000000..2d78e1b02
--- /dev/null
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.ts
@@ -0,0 +1,56 @@
+/*
+ * 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 { StreamPipesErrorMessage } from '@streampipes/platform-services';
+import { DialogService } from '../../dialog/base-dialog/base-dialog.service';
+import { PanelType } from '../../dialog/base-dialog/base-dialog.model';
+import { SpExceptionDetailsDialogComponent } from './exception-details-dialog/exception-details-dialog.component';
+
+@Component({
+  selector: 'sp-exception-message',
+  templateUrl: './sp-exception-message.component.html',
+  styleUrls: ['./sp-exception-message.component.scss']
+})
+export class SpExceptionMessageComponent {
+
+  @Input()
+  level = 'error';
+
+  @Input()
+  showDetails = true;
+
+  @Input()
+  message: StreamPipesErrorMessage;
+
+  constructor(private dialogService: DialogService) {
+
+  }
+
+  openDetailsDialog() {
+    this.dialogService.open(SpExceptionDetailsDialogComponent, {
+      panelType: PanelType.STANDARD_PANEL,
+      width: '80vw',
+      title: 'Error Details',
+      data: {
+        'message': this.message
+      }
+    });
+  }
+
+}
diff --git a/ui/projects/streampipes/shared-ui/src/lib/shared-ui.module.ts b/ui/projects/streampipes/shared-ui/src/lib/shared-ui.module.ts
index d69664dd2..8e40ee51f 100644
--- a/ui/projects/streampipes/shared-ui/src/lib/shared-ui.module.ts
+++ b/ui/projects/streampipes/shared-ui/src/lib/shared-ui.module.ts
@@ -32,6 +32,9 @@ import { SpBasicNavTabsComponent } from './components/basic-nav-tabs/basic-nav-t
 import { MatTabsModule } from '@angular/material/tabs';
 import { SpBasicInnerPanelComponent } from './components/basic-inner-panel/basic-inner-panel.component';
 import { SpBasicHeaderTitleComponent } from './components/basic-header-title/header-title.component';
+import { SpExceptionMessageComponent } from './components/sp-exception-message/sp-exception-message.component';
+import { SpExceptionDetailsDialogComponent } from './components/sp-exception-message/exception-details-dialog/exception-details-dialog.component';
+import { MatDividerModule } from '@angular/material/divider';
 
 @NgModule({
   declarations: [
@@ -41,12 +44,15 @@ import { SpBasicHeaderTitleComponent } from './components/basic-header-title/hea
     SpBasicInnerPanelComponent,
     SpBasicHeaderTitleComponent,
     SpBasicViewComponent,
-    SpBasicNavTabsComponent
+    SpBasicNavTabsComponent,
+    SpExceptionMessageComponent,
+    SpExceptionDetailsDialogComponent
   ],
   imports: [
     CommonModule,
     FlexLayoutModule,
     MatButtonModule,
+    MatDividerModule,
     MatIconModule,
     MatTabsModule,
     MatTooltipModule,
@@ -61,6 +67,8 @@ import { SpBasicHeaderTitleComponent } from './components/basic-header-title/hea
     SpBasicHeaderTitleComponent,
     SpBasicViewComponent,
     SpBasicNavTabsComponent,
+    SpExceptionMessageComponent,
+    SpExceptionDetailsDialogComponent
   ]
 })
 export class SharedUiModule {
diff --git a/ui/projects/streampipes/shared-ui/src/public-api.ts b/ui/projects/streampipes/shared-ui/src/public-api.ts
index 41e289808..a19dfd5af 100644
--- a/ui/projects/streampipes/shared-ui/src/public-api.ts
+++ b/ui/projects/streampipes/shared-ui/src/public-api.ts
@@ -30,6 +30,8 @@ export * from './lib/components/basic-header-title/header-title.component';
 export * from './lib/components/basic-inner-panel/basic-inner-panel.component';
 export * from './lib/components/basic-view/basic-view.component';
 export * from './lib/components/basic-nav-tabs/basic-nav-tabs.component';
+export * from './lib/components/sp-exception-message/sp-exception-message.component';
+export * from './lib/components/sp-exception-message/exception-details-dialog/exception-details-dialog.component';
 
 export * from './lib/models/sp-navigation.model';
 
diff --git a/ui/src/app/connect/components/new-adapter/new-adapter.component.ts b/ui/src/app/connect/components/new-adapter/new-adapter.component.ts
index 1cdee14b4..2a0566e89 100644
--- a/ui/src/app/connect/components/new-adapter/new-adapter.component.ts
+++ b/ui/src/app/connect/components/new-adapter/new-adapter.component.ts
@@ -176,7 +176,6 @@ export class NewAdapterComponent implements OnInit, AfterViewInit {
 
   clickSpecificSettingsNextButton() {
     this.shepherdService.trigger('specific-settings-next-button');
-    console.log(this.adapter);
     this.guessEventSchema();
     this.goForward();
   }
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.html b/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.html
index 8172ea552..ecfdf152a 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.html
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.html
@@ -17,22 +17,10 @@
   -->
 
 <div fxLayout="column" fxFlex="100">
-    <div fxLayout="row" fxLayoutAlign="center center" class="error-color" fxFlex="100">
-        <mat-icon fxLayoutAlign="start center">warning</mat-icon>
-        <div fxLayoutAlign="start center" class="error-text">&nbsp;Sorry, there was an error while guessing the schema of your configured data source...</div>
+    <div fxLayout="row" fxLayoutAlign="center center" fxFlex="100">
+        <div fxLayoutAlign="start center" class="error-text">&nbsp;There was an error while guessing the schema of your configured data source:</div>
     </div>
     <div fxLayout="row" fxLayoutAlign="center center" class="mt-10">
-        <button mat-button color="accent">
-            <div *ngIf="!showErrorMessage" (click)="showErrorMessage = true">Show Details</div>
-            <div *ngIf="showErrorMessage" (click)="showErrorMessage = false">Hide Details</div>
-        </button>
-    </div>
-    <div fxLayoutAlign="center center" *ngIf="showErrorMessage">
-        <div class="error-message">
-            <div *ngFor="let error of errorMessages" style="margin-bottom: 5px; margin-top: 5px">
-                <div>{{error.title}}</div>
-                <div>{{error.description}}</div>
-            </div>
-        </div>
+        <sp-exception-message [message]="errorMessage"></sp-exception-message>
     </div>
 </div>
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts b/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts
index 4e64c258c..b6dc01d70 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/error-message/error-message.component.ts
@@ -18,6 +18,7 @@
 
 import { Component, Input, OnInit } from '@angular/core';
 import { Notification } from '@streampipes/platform-services';
+import { StreamPipesErrorMessage } from '../../../../../../../projects/streampipes/platform-services/src/lib/model/gen/streampipes-model';
 
 @Component({
   selector: 'sp-error-message',
@@ -26,7 +27,7 @@ import { Notification } from '@streampipes/platform-services';
 })
 export class ErrorMessageComponent implements OnInit {
 
-  @Input() errorMessages: Notification[];
+  @Input() errorMessage: StreamPipesErrorMessage;
 
   showErrorMessage = false;
 
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.html b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.html
index 163fbb730..6e85d424f 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.html
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.html
@@ -72,7 +72,7 @@
                 <sp-loading-message *ngIf="isLoading"></sp-loading-message>
 
                 <sp-error-message
-                        [errorMessages]="errorMessages"
+                        [errorMessage]="errorMessage"
                         *ngIf="isError">
                 </sp-error-message>
 
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.ts b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.ts
index 6ad0b7299..5ca2b4e58 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.ts
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.ts
@@ -32,6 +32,7 @@ import {
 } from '@streampipes/platform-services';
 import { MatStepper } from '@angular/material/stepper';
 import { UserErrorMessage } from '../../../../../core-model/base/UserErrorMessage';
+import { StreamPipesErrorMessage } from '../../../../../../../projects/streampipes/platform-services/src/lib/model/gen/streampipes-model';
 
 @Component({
   selector: 'sp-event-schema',
@@ -73,7 +74,7 @@ export class EventSchemaComponent implements OnChanges {
   isLoading = false;
   isError = false;
   isPreviewEnabled = false;
-  errorMessages: Notification[];
+  errorMessage: StreamPipesErrorMessage;
   nodes: EventProperty[] = new Array<EventProperty>();
   validEventSchema = false;
   schemaErrorHints: UserErrorMessage[] = [];
@@ -117,7 +118,7 @@ export class EventSchemaComponent implements OnChanges {
         this.isLoading = false;
       },
       errorMessage => {
-        this.errorMessages = errorMessage.error.notifications;
+        this.errorMessage = errorMessage.error;
         this.isError = true;
         this.isLoading = false;
         this.eventSchema = new EventSchema();
diff --git a/ui/src/app/connect/connect.module.ts b/ui/src/app/connect/connect.module.ts
index e7e324795..1b23c969c 100644
--- a/ui/src/app/connect/connect.module.ts
+++ b/ui/src/app/connect/connect.module.ts
@@ -123,7 +123,8 @@ import { SpAdapterTemplateDialogComponent } from './dialog/adapter-template/adap
     SharedUiModule
   ],
   exports: [
-    PipelineElementRuntimeInfoComponent
+    PipelineElementRuntimeInfoComponent,
+    ErrorMessageComponent
   ],
   declarations: [
     AdapterDescriptionComponent,
diff --git a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-any-input/static-runtime-resolvable-any-input.component.ts b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-any-input/static-runtime-resolvable-any-input.component.ts
index b24bc7afa..c1589be9e 100644
--- a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-any-input/static-runtime-resolvable-any-input.component.ts
+++ b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-any-input/static-runtime-resolvable-any-input.component.ts
@@ -53,4 +53,7 @@ export class StaticRuntimeResolvableAnyInputComponent
         return staticProperty as RuntimeResolvableAnyStaticProperty;
     }
 
+    afterErrorReceived() {
+    }
+
 }
diff --git a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-input/base-runtime-resolvable-input.ts b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-input/base-runtime-resolvable-input.ts
index 229beba9b..b2f33e393 100644
--- a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-input/base-runtime-resolvable-input.ts
+++ b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-input/base-runtime-resolvable-input.ts
@@ -30,6 +30,7 @@ import { RuntimeResolvableService } from './runtime-resolvable.service';
 import { Observable } from 'rxjs';
 import { Directive, Input, OnChanges, SimpleChanges } from '@angular/core';
 import { ConfigurationInfo } from '../../../connect/model/ConfigurationInfo';
+import { StreamPipesErrorMessage } from '../../../../../projects/streampipes/platform-services/src/lib/model/gen/streampipes-model';
 
 @Directive()
 // tslint:disable-next-line:directive-class-suffix
@@ -43,6 +44,8 @@ export abstract class BaseRuntimeResolvableInput<T
 
   showOptions = false;
   loading = false;
+  error = false;
+  errorMessage: StreamPipesErrorMessage;
   dependentStaticProperties: any = new Map();
 
   constructor(private runtimeResolvableService: RuntimeResolvableService) {
@@ -70,6 +73,8 @@ export abstract class BaseRuntimeResolvableInput<T
 
     this.showOptions = false;
     this.loading = true;
+    this.error = false;
+    this.errorMessage = undefined;
     const observable: Observable<RuntimeOptionsResponse> = this.adapterId ?
       this.runtimeResolvableService.fetchRemoteOptionsForAdapter(resolvableOptionsParameterRequest, this.adapterId) :
       this.runtimeResolvableService.fetchRemoteOptionsForPipelineElement(resolvableOptionsParameterRequest);
@@ -80,6 +85,12 @@ export abstract class BaseRuntimeResolvableInput<T
       }
       this.loading = false;
       this.showOptions = true;
+    }, errorMessage => {
+      this.loading = false;
+      this.showOptions = true;
+      this.error = true;
+      this.errorMessage = errorMessage.error as StreamPipesErrorMessage;
+      this.afterErrorReceived();
     });
   }
 
@@ -107,4 +118,6 @@ export abstract class BaseRuntimeResolvableInput<T
 
   abstract afterOptionsLoaded(staticProperty: T);
 
+  abstract afterErrorReceived();
+
 }
diff --git a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-oneof-input/static-runtime-resolvable-oneof-input.component.ts b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-oneof-input/static-runtime-resolvable-oneof-input.component.ts
index a6c12b1d6..cae01d7c1 100644
--- a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-oneof-input/static-runtime-resolvable-oneof-input.component.ts
+++ b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-oneof-input/static-runtime-resolvable-oneof-input.component.ts
@@ -54,4 +54,7 @@ export class StaticRuntimeResolvableOneOfInputComponent
     parse(staticProperty: StaticPropertyUnion): RuntimeResolvableOneOfStaticProperty {
         return staticProperty as RuntimeResolvableOneOfStaticProperty;
     }
+
+    afterErrorReceived() {
+    }
 }
diff --git a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.html b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.html
index c9216e29c..49e036de3 100644
--- a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.html
+++ b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.html
@@ -29,6 +29,9 @@
                      [mode]="'indeterminate'"
                      [diameter]="20"></mat-spinner>
     </div>
+    <div fxLayout="column" *ngIf="error" class="mt-10">
+        <sp-exception-message [message]="errorMessage"></sp-exception-message>
+    </div>
     <mat-tree [dataSource]="dataSource" [treeControl]="treeControl" class="sp-tree">
         <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
             <mat-checkbox color="accent"
diff --git a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.ts b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.ts
index 3be93789f..cb2fa1883 100644
--- a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.ts
+++ b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input.component.ts
@@ -26,6 +26,7 @@ import {
 import { RuntimeResolvableService } from '../static-runtime-resolvable-input/runtime-resolvable.service';
 import { NestedTreeControl } from '@angular/cdk/tree';
 import { MatTreeNestedDataSource } from '@angular/material/tree';
+import { AbstractControl, FormControl, ValidationErrors, ValidatorFn } from '@angular/forms';
 
 @Component({
   selector: 'sp-runtime-resolvable-tree-input',
@@ -55,6 +56,7 @@ export class StaticRuntimeResolvableTreeInputComponent
       this.showOptions = true;
     }
     super.onInit();
+    this.parentForm.addControl(this.staticProperty.internalName, new FormControl(this.staticProperty.nodes, []));
   }
 
   parse(staticProperty: StaticPropertyUnion): RuntimeResolvableTreeInputStaticProperty {
@@ -64,10 +66,12 @@ export class StaticRuntimeResolvableTreeInputComponent
   afterOptionsLoaded(staticProperty: RuntimeResolvableTreeInputStaticProperty) {
     this.staticProperty.nodes = staticProperty.nodes;
     this.dataSource.data = this.staticProperty.nodes;
+    this.performValidation();
   }
 
   toggleNodeSelection(node: TreeInputNode) {
     node.selected = !node.selected;
+    this.performValidation();
   }
 
   toggleAllNodeSelection(node: any) {
@@ -75,6 +79,7 @@ export class StaticRuntimeResolvableTreeInputComponent
     const newState = !node.selected;
     node.selected = newState;
     descendants.forEach(d => d.selected = newState);
+    this.performValidation();
   }
 
   descendantsAllSelected(node: TreeInputNode) {
@@ -93,4 +98,30 @@ export class StaticRuntimeResolvableTreeInputComponent
     return result && !this.descendantsAllSelected(node);
   }
 
+  performValidation() {
+    let error = {error: true};
+    if (this.anyNodeSelected()) {
+      error = undefined;
+    }
+    this.parentForm.controls[this.staticProperty.internalName].setErrors(error);
+  }
+
+  anyNodeSelected(): boolean {
+    return this.dataSource.data.find(d => this.anySelected(d)) !== undefined;
+  }
+
+  anySelected(node: TreeInputNode): boolean {
+    if (node.selected) {
+      return true;
+    } else {
+      return node.children.find(n => this.anySelected(n)) !== undefined;
+    }
+  }
+
+  afterErrorReceived() {
+    this.staticProperty.nodes = [];
+    this.dataSource.data = [];
+    this.performValidation();
+  }
+
 }