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

[incubator-streampipes] branch edge-extensions updated: add support for reconfigurable CondeInputStaticProperty

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

wiener pushed a commit to branch edge-extensions
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git


The following commit(s) were added to refs/heads/edge-extensions by this push:
     new 8f040a3  add support for reconfigurable CondeInputStaticProperty
8f040a3 is described below

commit 8f040a321f862eae73cc008c2fe08020d102ee34
Author: Patrick Wiener <wi...@fzi.de>
AuthorDate: Tue Aug 17 10:58:17 2021 +0200

    add support for reconfigurable CondeInputStaticProperty
---
 .../streampipes/container/util/ConsulUtil.java     |  2 +-
 .../staticproperty/CodeInputStaticProperty.java    | 12 +++++
 .../staticproperty/StaticPropertyAlternative.java  |  4 +-
 .../controller/api/InvocableEntityResource.java    | 15 +++++-
 .../node/controller/config/EnvConfigParam.java     |  2 +
 .../node/controller/config/NodeConfiguration.java  | 13 +++++
 .../PipelineElementReconfigurationHandler.java     |  7 ++-
 .../management/resource/ResourceManager.java       |  4 +-
 streampipes-performance-tests/pom.xml              |  1 -
 .../PipelineElementReconfigurationHandler.java     | 35 +++++++++++--
 ...AbstractConfigurablePipelineElementBuilder.java | 34 +++++++++++++
 ui/src/app/core-model/gen/streampipes-model.ts     |  4 +-
 .../dashboard/components/widgets/map/map-config.ts | 11 ++++-
 .../widgets/map/map-widget.component.html          |  1 +
 .../components/widgets/map/map-widget.component.ts | 57 +++++++++++++++++++---
 .../dashboard/registry/widget-config-builder.ts    | 39 ++++++++++++++-
 .../sdk/extractor/static-property-extractor.ts     |  6 +++
 17 files changed, 224 insertions(+), 23 deletions(-)

diff --git a/streampipes-container/src/main/java/org/apache/streampipes/container/util/ConsulUtil.java b/streampipes-container/src/main/java/org/apache/streampipes/container/util/ConsulUtil.java
index a1b2407..5f21928 100644
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/util/ConsulUtil.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/util/ConsulUtil.java
@@ -85,7 +85,7 @@ public class ConsulUtil {
   }
 
   /**
-   * Method called by {@link org.apache.streampipes.node.controller.container.NodeControllerInit} to
+   * Method called by {@link org.apache.streampipes.node.controller.NodeControllerInit} to
    * register new node controller service endpoint.
    *
    * @param svcId unique service id
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/staticproperty/CodeInputStaticProperty.java b/streampipes-model/src/main/java/org/apache/streampipes/model/staticproperty/CodeInputStaticProperty.java
index 1d2ae74..69db96c 100644
--- a/streampipes-model/src/main/java/org/apache/streampipes/model/staticproperty/CodeInputStaticProperty.java
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/staticproperty/CodeInputStaticProperty.java
@@ -37,6 +37,9 @@ public class CodeInputStaticProperty extends StaticProperty {
   @RdfProperty(StreamPipes.HAS_CODE_INPUT)
   private String value;
 
+  @RdfProperty(StreamPipes.IS_RECONFIGURABLE)
+  private boolean reconfigurable;
+
   public CodeInputStaticProperty() {
     super(StaticPropertyType.CodeInputStaticProperty);
   }
@@ -46,6 +49,7 @@ public class CodeInputStaticProperty extends StaticProperty {
     this.language = other.getLanguage();
     this.value = other.getValue();
     this.codeTemplate = other.getCodeTemplate();
+    this.reconfigurable = other.isReconfigurable();
   }
 
   public CodeInputStaticProperty(String internalName, String label, String description) {
@@ -76,6 +80,14 @@ public class CodeInputStaticProperty extends StaticProperty {
     this.codeTemplate = codeTemplate;
   }
 
+  public boolean isReconfigurable() {
+    return reconfigurable;
+  }
+
+  public void setReconfigurable(boolean reconfigurable) {
+    this.reconfigurable = reconfigurable;
+  }
+
   @Override
   public void accept(StaticPropertyVisitor visitor) {
     visitor.visit(this);
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/staticproperty/StaticPropertyAlternative.java b/streampipes-model/src/main/java/org/apache/streampipes/model/staticproperty/StaticPropertyAlternative.java
index 8da3907..2139e3f 100644
--- a/streampipes-model/src/main/java/org/apache/streampipes/model/staticproperty/StaticPropertyAlternative.java
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/staticproperty/StaticPropertyAlternative.java
@@ -34,7 +34,7 @@ public class StaticPropertyAlternative extends StaticProperty {
   @OneToOne(fetch = FetchType.EAGER,
           cascade = {CascadeType.ALL})
   @RdfProperty(StreamPipes.IS_SELECTED)
-  private Boolean selected;
+  private boolean selected;
 
   @OneToOne(fetch = FetchType.EAGER,
           cascade = {CascadeType.ALL})
@@ -58,7 +58,7 @@ public class StaticPropertyAlternative extends StaticProperty {
     }
   }
 
-  public Boolean getSelected() {
+  public boolean getSelected() {
     return selected;
   }
 
diff --git a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/api/InvocableEntityResource.java b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/api/InvocableEntityResource.java
index 727cc16..324efb5 100644
--- a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/api/InvocableEntityResource.java
+++ b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/api/InvocableEntityResource.java
@@ -22,6 +22,7 @@ import org.apache.streampipes.logging.evaluation.EvaluationLogger;
 import org.apache.streampipes.model.Response;
 import org.apache.streampipes.model.base.InvocableStreamPipesEntity;
 import org.apache.streampipes.model.pipeline.PipelineElementReconfigurationEntity;
+import org.apache.streampipes.model.staticproperty.CodeInputStaticProperty;
 import org.apache.streampipes.model.staticproperty.FreeTextStaticProperty;
 import org.apache.streampipes.node.controller.management.pe.PipelineElementManager;
 import org.apache.streampipes.node.controller.management.pe.storage.RunningInvocableInstances;
@@ -101,7 +102,19 @@ public class InvocableEntityResource extends AbstractResource {
                                             PipelineElementReconfigurationEntity reconfigurationEntity) {
         //TODO: Remove Logger after debugging
         EvaluationLogger logger = EvaluationLogger.getInstance();
-        Object[] line = {System.currentTimeMillis() ,"reconfiguration request received", nrRuns++,((FreeTextStaticProperty) reconfigurationEntity.getReconfiguredStaticProperties().get(0)).getValue()};
+        String value = "";
+        if (reconfigurationEntity.getReconfiguredStaticProperties().get(0) instanceof FreeTextStaticProperty) {
+            value = ((FreeTextStaticProperty) reconfigurationEntity
+                    .getReconfiguredStaticProperties()
+                    .get(0))
+                    .getValue();
+        } else if (reconfigurationEntity.getReconfiguredStaticProperties().get(0) instanceof CodeInputStaticProperty) {
+            value = ((CodeInputStaticProperty) reconfigurationEntity
+                    .getReconfiguredStaticProperties()
+                    .get(0))
+                    .getValue();
+        }
+        Object[] line = {System.currentTimeMillis() ,"reconfiguration request received", nrRuns++, value};
         logger.logMQTT("Reconfiguration", line);
         InvocableStreamPipesEntity graph = RunningInvocableInstances.INSTANCE.get(runningInstanceId);
         return ok(PipelineElementManager.getInstance().reconfigure(graph, reconfigurationEntity));
diff --git a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/config/EnvConfigParam.java b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/config/EnvConfigParam.java
index bae3ced..f19e69a 100644
--- a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/config/EnvConfigParam.java
+++ b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/config/EnvConfigParam.java
@@ -49,10 +49,12 @@ public enum EnvConfigParam {
     NODE_ACCESSIBLE_FIELD_DEVICE("SP_NODE_ACCESSIBLE_FIELD_DEVICE", ""),
     CONSUL_LOCATION("CONSUL_LOCATION", "consul"),
     SUPPORTED_PIPELINE_ELEMENTS("SP_SUPPORTED_PIPELINE_ELEMENTS", ""),
+    AUTO_OFFLOADING("SP_AUTO_OFFLOADING_ACTIVATED", "false"),
     AUTO_OFFLOADING_STRATEGY("SP_AUTO_OFFLOADING_STRATEGY", "default"),
     NODE_STORAGE_PATH("SP_NODE_STORAGE_PATH", "/var/lib/streampipes"),
     LOGGING_MQTT_URL("SP_LOGGING_MQTT_URL", "tcp://localhost:1883");
 
+
     private final String environmentKey;
     private final String defaultValue;
 
diff --git a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/config/NodeConfiguration.java b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/config/NodeConfiguration.java
index 364fd18..2b3a79a 100644
--- a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/config/NodeConfiguration.java
+++ b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/config/NodeConfiguration.java
@@ -58,6 +58,7 @@ public final class NodeConfiguration {
     private static int resourceMonitorFreqSecs;
     private static int relayEventBufferSize;
     private static String consulHost;
+    private static boolean autoOffloadingActivated;
     private static OffloadingStrategyType autoOffloadingStrategy;
     private static String nodeStoragePath;
     private static String loggingMqttUrl;
@@ -276,6 +277,14 @@ public final class NodeConfiguration {
         NodeConfiguration.supportedPipelineElements.addAll(supportedPipelineElements);
     }
 
+    public static boolean isAutoOffloadingActivated() {
+        return autoOffloadingActivated;
+    }
+
+    public static void setAutoOffloadingActivated(boolean autoOffloadingActivated) {
+        NodeConfiguration.autoOffloadingActivated = autoOffloadingActivated;
+    }
+
     public static OffloadingStrategyType getAutoOffloadingStrategy() {
         return autoOffloadingStrategy;
     }
@@ -506,6 +515,10 @@ public final class NodeConfiguration {
                     List<String> supportedPipelineElements = Arrays.asList(value.split(";").clone());
                     addSupportedPipelineElements(supportedPipelineElements);
                     break;
+                case AUTO_OFFLOADING:
+                    configMap.put(envKey, value);
+                    setAutoOffloadingActivated(Boolean.parseBoolean(value));
+                    break;
                 case AUTO_OFFLOADING_STRATEGY:
                     configMap.put(envKey, value);
                     setAutoOffloadingStrategy(OffloadingStrategyType.fromString(value));
diff --git a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/management/pe/handler/PipelineElementReconfigurationHandler.java b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/management/pe/handler/PipelineElementReconfigurationHandler.java
index fe1486c..979cdec 100644
--- a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/management/pe/handler/PipelineElementReconfigurationHandler.java
+++ b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/management/pe/handler/PipelineElementReconfigurationHandler.java
@@ -26,6 +26,7 @@ import org.apache.streampipes.model.Response;
 import org.apache.streampipes.model.base.InvocableStreamPipesEntity;
 import org.apache.streampipes.model.grounding.*;
 import org.apache.streampipes.model.pipeline.PipelineElementReconfigurationEntity;
+import org.apache.streampipes.model.staticproperty.CodeInputStaticProperty;
 import org.apache.streampipes.model.staticproperty.FreeTextStaticProperty;
 import org.apache.streampipes.model.staticproperty.StaticProperty;
 import org.apache.streampipes.node.controller.management.IHandler;
@@ -87,14 +88,16 @@ public class PipelineElementReconfigurationHandler implements IHandler<Response>
         return pub;
     }
 
-    // TODO: currently only FreeTextProperty is supported - need to updated in case other StaticProperty types will
-    //  be supported in the future
+    // TODO: Update in case other StaticProperty types will be supported in the future
     private byte[] reconfigurationToByteArray() {
         Map<String, String> reconfigurationEventMap = new HashMap<>();
         reconfigurationEntity.getReconfiguredStaticProperties().forEach(staticProperty -> {
             if (staticProperty instanceof FreeTextStaticProperty) {
                 reconfigurationEventMap.put(staticProperty.getInternalName(),
                         ((FreeTextStaticProperty) staticProperty).getValue());
+            } else if (staticProperty instanceof CodeInputStaticProperty) {
+                reconfigurationEventMap.put(staticProperty.getInternalName(),
+                        ((CodeInputStaticProperty) staticProperty).getValue());
             }
         });
         return HttpUtils.serialize(reconfigurationEventMap).getBytes(StandardCharsets.UTF_8);
diff --git a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/management/resource/ResourceManager.java b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/management/resource/ResourceManager.java
index 5710a5c..ee393a7 100644
--- a/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/management/resource/ResourceManager.java
+++ b/streampipes-node-controller/src/main/java/org/apache/streampipes/node/controller/management/resource/ResourceManager.java
@@ -71,7 +71,9 @@ public class ResourceManager {
             try {
                 // get current node resource metrics
                 retrieveResources();
-                checkOffloadingPolicy();
+                if (NodeConfiguration.isAutoOffloadingActivated()) {
+                    checkOffloadingPolicy();
+                }
                 Thread.sleep( NodeConfiguration.getResourceMonitorFreqSecs() * 1000);
             } catch (InterruptedException e) {
                 LOG.error("Thread interrupted. {}", e.toString());
diff --git a/streampipes-performance-tests/pom.xml b/streampipes-performance-tests/pom.xml
index a86a437..d15f80a 100644
--- a/streampipes-performance-tests/pom.xml
+++ b/streampipes-performance-tests/pom.xml
@@ -78,5 +78,4 @@
         </plugins>
         <finalName>generic-test</finalName>
     </build>
-
 </project>
\ No newline at end of file
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/reconfiguration/PipelineElementReconfigurationHandler.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/reconfiguration/PipelineElementReconfigurationHandler.java
index 31ad843..9a4ff83 100644
--- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/reconfiguration/PipelineElementReconfigurationHandler.java
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/reconfiguration/PipelineElementReconfigurationHandler.java
@@ -24,6 +24,7 @@ import org.apache.streampipes.manager.execution.pipeline.PipelineElementReconfig
 import org.apache.streampipes.manager.operations.Operations;
 import org.apache.streampipes.model.graph.DataProcessorInvocation;
 import org.apache.streampipes.model.pipeline.*;
+import org.apache.streampipes.model.staticproperty.CodeInputStaticProperty;
 import org.apache.streampipes.model.staticproperty.FreeTextStaticProperty;
 import org.apache.streampipes.model.staticproperty.StaticProperty;
 import org.apache.streampipes.storage.api.IPipelineStorage;
@@ -78,10 +79,10 @@ public class PipelineElementReconfigurationHandler {
         reconfiguredPipeline.getSepas().forEach(reconfiguredProcessor -> storedPipeline.getSepas().forEach(storedProcessor -> {
             if (matchingElementIds(reconfiguredProcessor, storedProcessor)) {
                 List<StaticProperty> list = new ArrayList<>();
-                getReconfigurableFsp(reconfiguredProcessor).forEach(reconfiguredFsp ->
-                        getReconfigurableFsp(storedProcessor).forEach(storedFsp -> {
-                            if (compareForChanges(reconfiguredFsp, storedFsp)) {
-                                list.add(reconfiguredFsp);
+                getReconfigurableStaticProperties(reconfiguredProcessor).forEach(reconfiguredStaticProperty ->
+                        getReconfigurableStaticProperties(storedProcessor).forEach(storedStaticProperty -> {
+                            if (matchAndCompare(reconfiguredStaticProperty, storedStaticProperty)) {
+                                list.add(reconfiguredStaticProperty);
                             }
                         }));
                 PipelineElementReconfigurationEntity entity = reconfigurationEntity(reconfiguredProcessor, list);
@@ -116,6 +117,19 @@ public class PipelineElementReconfigurationHandler {
         return one.getInternalName().equals(two.getInternalName()) && !one.getValue().equals(two.getValue());
     }
 
+    private boolean matchAndCompare(StaticProperty one, StaticProperty two) {
+
+        if (one instanceof FreeTextStaticProperty && two instanceof FreeTextStaticProperty) {
+            return one.getInternalName().equals(two.getInternalName()) &&
+                    !((FreeTextStaticProperty) one).getValue().equals(((FreeTextStaticProperty) two).getValue());
+        } else if (one instanceof CodeInputStaticProperty && two instanceof CodeInputStaticProperty) {
+            return one.getInternalName().equals(two.getInternalName()) &&
+                    !((CodeInputStaticProperty) one).getValue().equals(((CodeInputStaticProperty) two).getValue());
+        } else {
+            return false;
+        }
+    }
+
     private List<FreeTextStaticProperty> getReconfigurableFsp(DataProcessorInvocation graph) {
         return graph.getStaticProperties().stream()
                 .filter(FreeTextStaticProperty.class::isInstance)
@@ -124,6 +138,19 @@ public class PipelineElementReconfigurationHandler {
                 .collect(Collectors.toList());
     }
 
+    private List<StaticProperty> getReconfigurableStaticProperties(DataProcessorInvocation graph) {
+        return graph.getStaticProperties().stream()
+                .filter(sp -> {
+                    if (sp instanceof FreeTextStaticProperty) {
+                        return ((FreeTextStaticProperty) sp).isReconfigurable();
+                    } else if (sp instanceof CodeInputStaticProperty) {
+                        return ((CodeInputStaticProperty) sp).isReconfigurable();
+                    }
+                    return false;
+                })
+                .collect(Collectors.toList());
+    }
+
     private boolean matchingElementIds(DataProcessorInvocation one, DataProcessorInvocation two) {
         return one.getElementId().equals(two.getElementId());
     }
diff --git a/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/builder/AbstractConfigurablePipelineElementBuilder.java b/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/builder/AbstractConfigurablePipelineElementBuilder.java
index 3be4c1f..defae20 100644
--- a/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/builder/AbstractConfigurablePipelineElementBuilder.java
+++ b/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/builder/AbstractConfigurablePipelineElementBuilder.java
@@ -172,6 +172,26 @@ public abstract class AbstractConfigurablePipelineElementBuilder<BU extends
   }
 
   /**
+   * Assigns a new code block parameter which is required
+   * by the processing element.
+   * @param label The {@link org.apache.streampipes.sdk.helpers.Label} that describes why this parameter is needed in a
+   *              user-friendly manner.
+   * @param codeLanguage The {@link org.apache.streampipes.sdk.helpers.CodeLanguage} code language the code block is built for.
+   * @param defaultSkeleton The code skeleton that is used as a default value.
+   * @return this
+   */
+  public BU requiredReconfigurableCodeblock(Label label, CodeLanguage codeLanguage, String defaultSkeleton) {
+    CodeInputStaticProperty codeInputStaticProperty = new CodeInputStaticProperty(label.getInternalId(),
+            label.getLabel(), label.getDescription());
+    codeInputStaticProperty.setLanguage(codeLanguage.name());
+    codeInputStaticProperty.setCodeTemplate(defaultSkeleton);
+    codeInputStaticProperty.setReconfigurable(true);
+    this.staticProperties.add(codeInputStaticProperty);
+
+    return me();
+  }
+
+  /**
    * Assigns a new text-based configuration parameter (a string) which is required by the pipeline
    * element.
    * @param label The {@link org.apache.streampipes.sdk.helpers.Label} that describes why this parameter is needed in a
@@ -185,6 +205,20 @@ public abstract class AbstractConfigurablePipelineElementBuilder<BU extends
   }
 
   /**
+   * Assigns new text-based configuration parameter (a string) which is required by the pipeline
+   * element. This parameter can be reconfigured by the user at pipeline run-time.
+   * @param label The {@link org.apache.streampipes.sdk.helpers.Label} that describes why this parameter is needed in a
+   *              user-friendly manner.
+   * @return
+   */
+  public BU requiredReconfigurableTextParameter(Label label) {
+    FreeTextStaticProperty fsp = prepareFreeTextStaticProperty(label, XSD._string.toString());
+    fsp.setReconfigurable(true);
+    this.staticProperties.add(fsp);
+    return me();
+  }
+
+  /**
    * Assigns a new color picker parameter which is required by the pipeline
    * element.
    * @param label The {@link org.apache.streampipes.sdk.helpers.Label} that describes why this parameter is needed in a
diff --git a/ui/src/app/core-model/gen/streampipes-model.ts b/ui/src/app/core-model/gen/streampipes-model.ts
index 2ff1e90..f0e9657 100644
--- a/ui/src/app/core-model/gen/streampipes-model.ts
+++ b/ui/src/app/core-model/gen/streampipes-model.ts
@@ -19,7 +19,7 @@
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 2.27.744 on 2021-05-28 12:46:35.
+// Generated using typescript-generator version 2.27.744 on 2021-07-13 18:02:18.
 
 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 [...]
@@ -650,6 +650,7 @@ export class CodeInputStaticProperty extends StaticProperty {
     "@class": "org.apache.streampipes.model.staticproperty.CodeInputStaticProperty";
     codeTemplate: string;
     language: string;
+    reconfigurable: boolean;
     value: string;
 
     static fromData(data: CodeInputStaticProperty, target?: CodeInputStaticProperty): CodeInputStaticProperty {
@@ -661,6 +662,7 @@ export class CodeInputStaticProperty extends StaticProperty {
         instance.language = data.language;
         instance.codeTemplate = data.codeTemplate;
         instance.value = data.value;
+        instance.reconfigurable = data.reconfigurable;
         return instance;
     }
 }
diff --git a/ui/src/app/dashboard/components/widgets/map/map-config.ts b/ui/src/app/dashboard/components/widgets/map/map-config.ts
index 1f15c41..3bfefa1 100644
--- a/ui/src/app/dashboard/components/widgets/map/map-config.ts
+++ b/ui/src/app/dashboard/components/widgets/map/map-config.ts
@@ -19,7 +19,11 @@ import { WidgetConfigBuilder } from '../../../registry/widget-config-builder';
 import { SchemaRequirementsBuilder } from '../../../sdk/schema-requirements-builder';
 import { EpRequirements } from '../../../sdk/ep-requirements';
 import { WidgetConfig } from '../base/base-config';
-import { DashboardWidgetSettings } from '../../../../core-model/gen/streampipes-model';
+import {
+    CodeInputStaticProperty,
+    DashboardWidgetSettings,
+    StaticPropertyAlternative
+} from '../../../../core-model/gen/streampipes-model';
 
 export class MapConfig extends WidgetConfig {
 
@@ -29,6 +33,8 @@ export class MapConfig extends WidgetConfig {
     static readonly ID_MAPPING_KEY: string = 'ids-mapping';
     static readonly MARKER_TYPE_KEY: string = 'marker-type-mapping';
     static readonly CENTER_MAP_KEY: string = 'center-map-mapping';
+    static readonly ZOOM_LEVEL_KEY: string = 'zoom-level-mapping';
+    static readonly GEOFENCE_KEY: string = 'geofence-mapping';
 
     constructor() {
         super();
@@ -45,6 +51,9 @@ export class MapConfig extends WidgetConfig {
             .requiredPropertyWithNaryMapping(MapConfig.ID_MAPPING_KEY, 'Group Markers', 'Each id gets its own marker', EpRequirements.anyProperty())
             .requiredPropertyWithNaryMapping(MapConfig.ITEMS_MAPPING_KEY, 'Fields to display', '', EpRequirements.anyProperty())
             .build())
+          .requiredCodeBlock(MapConfig.GEOFENCE_KEY, 'Geofence', 'Specify Geofence (GeoJSON)',
+              '// add GeoJSON polygon', true)
+          .requiredIntegerParameter(MapConfig.ZOOM_LEVEL_KEY, 'Zoom level', 'Set map zoom level (min:3, max:19)')
           .requiredSingleValueSelection(
             MapConfig.CENTER_MAP_KEY, 'Center map', 'Center the map around new markers', [this.makeOption('Center'), this.makeOption('No Center')])
           .requiredSingleValueSelection(
diff --git a/ui/src/app/dashboard/components/widgets/map/map-widget.component.html b/ui/src/app/dashboard/components/widgets/map/map-widget.component.html
index 4fd2c93..3e58ed2 100644
--- a/ui/src/app/dashboard/components/widgets/map/map-widget.component.html
+++ b/ui/src/app/dashboard/components/widgets/map/map-widget.component.html
@@ -23,6 +23,7 @@
          leaflet
          [leafletOptions]="options"
          (leafletMapReady)="onMapReady($event)">
+        <div *ngIf="addLayers" [leafletLayers]="layers"></div>
 <!--        <div *ngIf="showMarkers" [leafletLayer]="markerLayer"></div>-->
         <div *ngFor="let markerLayer of markerLayers" [leafletLayer]="markerLayer"></div>
     </div>
diff --git a/ui/src/app/dashboard/components/widgets/map/map-widget.component.ts b/ui/src/app/dashboard/components/widgets/map/map-widget.component.ts
index 4cc420f..f12cb29 100644
--- a/ui/src/app/dashboard/components/widgets/map/map-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/map/map-widget.component.ts
@@ -20,9 +20,22 @@ import {RxStompService} from "@stomp/ng2-stompjs";
 import {BaseStreamPipesWidget} from "../base/base-widget";
 import {StaticPropertyExtractor} from "../../../sdk/extractor/static-property-extractor";
 import {MapConfig} from "./map-config";
-import {latLng, marker, Marker, tileLayer, Map, LatLngExpression, LatLng, icon, Content} from "leaflet";
+import {
+    latLng,
+    marker,
+    Marker,
+    tileLayer,
+    Map,
+    LatLngExpression,
+    LatLng,
+    icon,
+    Content,
+    MapOptions,
+    geoJSON, Control, Layer
+} from "leaflet";
 import {ResizeService} from "../../../services/resize.service";
 import {DashboardService} from "../../../services/dashboard.service";
+import Layers = Control.Layers;
 
 @Component({
     selector: 'map-widget',
@@ -39,6 +52,7 @@ export class MapWidgetComponent extends BaseStreamPipesWidget implements OnInit,
     idsToDisplay: string[];
     additionalItemsToDisplay: string[];
     centerMap: boolean;
+    zoomLevel: number = 18;
 
     map: Map;
     showMarkers = false;
@@ -47,14 +61,12 @@ export class MapWidgetComponent extends BaseStreamPipesWidget implements OnInit,
 
     mapWidth: number;
     mapHeight: number;
+    options: MapOptions;
+
+    layers: Layer[];
+    geofence: string;
+    addLayers = false;
 
-    options = {
-        layers: [
-            tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: "© <a href=\'https://www.openstreetmap.org/copyright\'>OpenStreetMap</a> Contributors" })
-        ],
-        zoom: 5,
-        center: latLng(46.879966, -121.726909)
-    };
 
     constructor(rxStompService: RxStompService, dashboardService: DashboardService, resizeService: ResizeService) {
         super(rxStompService, dashboardService, resizeService, false);
@@ -68,6 +80,33 @@ export class MapWidgetComponent extends BaseStreamPipesWidget implements OnInit,
         this.showMarkers = true;
         this.mapWidth = this.computeCurrentWidth(this.gridsterItemComponent);
         this.mapHeight = this.computeCurrentHeight(this.gridsterItemComponent);
+
+        if (!this.geofence.startsWith("// add GeoJSON")) {
+            this.addLayers = true;
+            let geofence = {
+                id: 'geoJSON',
+                name: 'Geofence',
+                enabled: true,
+                layer: geoJSON((JSON.parse(this.geofence)) as any,
+                    { style: () => ({ color: '#39b54a' })})
+            };
+
+            this.layers = [
+                geofence.layer
+            ];
+        }
+
+        this.options = {
+            layers: [
+                tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
+                    minZoom: 3,
+                    maxZoom: 19,
+                    attribution: "© <a href=\'https://www.openstreetmap.org/copyright\'>OpenStreetMap</a> Contributors"
+                })
+            ],
+            zoom: this.zoomLevel,
+            center: latLng(46.879966, -121.726909)
+        };
     }
 
     ngOnDestroy(): void {
@@ -82,6 +121,8 @@ export class MapWidgetComponent extends BaseStreamPipesWidget implements OnInit,
         this.idsToDisplay = extractor.mappingPropertyValues(MapConfig.ID_MAPPING_KEY);
         const b = extractor.singleValueParameter(MapConfig.CENTER_MAP_KEY);
         this.centerMap = extractor.selectedSingleValue(MapConfig.CENTER_MAP_KEY) === 'Center';
+        this.zoomLevel = extractor.integerParameter(MapConfig.ZOOM_LEVEL_KEY);
+        this.geofence = extractor.codeBlockValue(MapConfig.GEOFENCE_KEY)
     }
 
     markerImage(selectedMarker: string): string {
diff --git a/ui/src/app/dashboard/registry/widget-config-builder.ts b/ui/src/app/dashboard/registry/widget-config-builder.ts
index da0332e..2dd763f 100644
--- a/ui/src/app/dashboard/registry/widget-config-builder.ts
+++ b/ui/src/app/dashboard/registry/widget-config-builder.ts
@@ -22,7 +22,12 @@ import {Tuple2} from "../../core-model/base/Tuple2";
 import {
     ColorPickerStaticProperty,
     DashboardWidgetSettings,
-    FreeTextStaticProperty, OneOfStaticProperty, StaticProperty, Option
+    FreeTextStaticProperty,
+    OneOfStaticProperty,
+    StaticProperty,
+    Option,
+    CodeInputStaticProperty,
+    StaticPropertyAlternatives, StaticPropertyAlternative
 } from "../../core-model/gen/streampipes-model";
 
 export class WidgetConfigBuilder {
@@ -95,6 +100,38 @@ export class WidgetConfigBuilder {
         return this;
     }
 
+    requiredCodeBlock(id: string, label: string, description: string, defaultCode: string, reconfigurable: boolean): WidgetConfigBuilder {
+        let csp: CodeInputStaticProperty = new CodeInputStaticProperty();
+        csp["@class"] = "org.apache.streampipes.model.staticproperty.CodeInputStaticProperty";
+        csp.internalName = id;
+        csp.label = label;
+        csp.description = description;
+        csp.codeTemplate = defaultCode;
+        csp.reconfigurable = reconfigurable;
+        this.widget.config.push(csp);
+        return this;
+    }
+
+    requiredAlternatives(id: string, label: string, description: string, ...alternatives: StaticPropertyAlternative[]): WidgetConfigBuilder {
+        let spa: StaticPropertyAlternatives = new StaticPropertyAlternatives();
+        spa["@class"] = "org.apache.streampipes.model.staticproperty.StaticPropertyAlternatives";
+        spa.internalName = id;
+        spa.label = label;
+        spa.description = description;
+        spa.alternatives = []
+        alternatives.forEach((a,i) => {
+            alternatives[i].index = i;
+            let alternative = new StaticPropertyAlternative();
+            alternative["@class"] = "org.apache.streampipes.model.staticproperty.StaticPropertyAlternative";
+            alternative.internalName = a.internalName;
+            alternative.label = a.label;
+            alternative.description = a.description;
+            alternative.selected = a.selected;
+            spa.alternatives.push(alternative);
+        })
+        this.widget.config.push(spa);
+        return this;
+    }
 
     requiredIntegerParameter(id: string, label: string, description: string): WidgetConfigBuilder {
         let fst: FreeTextStaticProperty = this.prepareFreeTextStaticProperty(id, label, description, Datatypes.Integer.toUri())
diff --git a/ui/src/app/dashboard/sdk/extractor/static-property-extractor.ts b/ui/src/app/dashboard/sdk/extractor/static-property-extractor.ts
index 00fcb14..341b9d2 100644
--- a/ui/src/app/dashboard/sdk/extractor/static-property-extractor.ts
+++ b/ui/src/app/dashboard/sdk/extractor/static-property-extractor.ts
@@ -18,6 +18,7 @@
 
 
 import {
+    CodeInputStaticProperty,
     ColorPickerStaticProperty, EventPropertyUnion,
     EventSchema, FreeTextStaticProperty, MappingPropertyNary,
     MappingPropertyUnary, OneOfStaticProperty,
@@ -79,6 +80,11 @@ export class StaticPropertyExtractor {
         return this.singleValueParameter(internalId) as number;
     }
 
+    codeBlockValue(internalId: string): string {
+        let sp: CodeInputStaticProperty = this.getStaticPropertyByName(internalId) as CodeInputStaticProperty;
+        return sp.value;
+    }
+
     getStaticPropertyByName(internalId: string): StaticPropertyUnion {
         return this.staticProperties.find(sp => (sp.internalName == internalId));
     }