You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2020/03/10 09:50:29 UTC

[unomi] 01/01: UNOMI-286 Event type definitions & set value type for property types This commit adds the following: - Event type definitions : possibility to define event types and retrieve them through the REST API. - Set value type for property types. Make it possible to define complex property types that may include other property types and therefore define complex object structures to be accepted as property types. Event types may also use this possibility to define their complete structure.

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

shuber pushed a commit to branch UNOMI-286-setvaluetype-eventtypes
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit 20d4524a500ff1bf7f2ba95b8877cf1c96ed3241
Author: Serge Huber <sh...@apache.org>
AuthorDate: Tue Mar 10 10:50:22 2020 +0100

    UNOMI-286 Event type definitions & set value type for property types
    This commit adds the following:
    - Event type definitions : possibility to define event types and retrieve them through the REST API.
    - Set value type for property types. Make it possible to define complex property types that may include other property types and therefore define complex object structures to be accepted as property types. Event types may also use this possibility to define their complete structure.
---
 .../main/java/org/apache/unomi/api/EventType.java  |  66 ++++++++++
 .../java/org/apache/unomi/api/PropertyType.java    |   9 ++
 .../apache/unomi/api/services/EventService.java    |  15 ++-
 .../apache/unomi/rest/EventServiceEndpoint.java    |  28 +++-
 .../impl/definitions/DefinitionsServiceImpl.java   |  14 +-
 .../services/impl/events/EventServiceImpl.java     | 141 ++++++++++++++++++++-
 .../main/resources/META-INF/cxs/events/common.json |  24 ++++
 7 files changed, 281 insertions(+), 16 deletions(-)

diff --git a/api/src/main/java/org/apache/unomi/api/EventType.java b/api/src/main/java/org/apache/unomi/api/EventType.java
new file mode 100644
index 0000000..45ce0b3
--- /dev/null
+++ b/api/src/main/java/org/apache/unomi/api/EventType.java
@@ -0,0 +1,66 @@
+/*
+ * 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.unomi.api;
+
+import java.util.List;
+
+/**
+ * An event type definition, used to define the structure of accepted events
+ */
+public class EventType implements PluginType {
+
+    private String type;
+
+    private List<PropertyType> propertyTypes;
+
+    private long pluginId;
+
+    public EventType() {
+    }
+
+    public EventType(String type, List<PropertyType> propertyTypes, long pluginId) {
+        this.type = type;
+        this.propertyTypes = propertyTypes;
+        this.pluginId = pluginId;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public List<PropertyType> getPropertyTypes() {
+        return propertyTypes;
+    }
+
+    public void setPropertyTypes(List<PropertyType> propertyTypes) {
+        this.propertyTypes = propertyTypes;
+    }
+
+    @Override
+    public long getPluginId() {
+        return pluginId;
+    }
+
+    @Override
+    public void setPluginId(long pluginId) {
+        this.pluginId = pluginId;
+    }
+}
diff --git a/api/src/main/java/org/apache/unomi/api/PropertyType.java b/api/src/main/java/org/apache/unomi/api/PropertyType.java
index 45a81cb..6a192dd 100644
--- a/api/src/main/java/org/apache/unomi/api/PropertyType.java
+++ b/api/src/main/java/org/apache/unomi/api/PropertyType.java
@@ -49,6 +49,8 @@ public class PropertyType extends MetadataItem {
     private Boolean multivalued;
     private Boolean protekted;
 
+    private Set<PropertyType> childPropertyTypes = new LinkedHashSet<>();
+
     /**
      * Instantiates a new Property type.
      */
@@ -293,4 +295,11 @@ public class PropertyType extends MetadataItem {
         this.protekted = protekted;
     }
 
+    public Set<PropertyType> getChildPropertyTypes() {
+        return childPropertyTypes;
+    }
+
+    public void setChildPropertyTypes(Set<PropertyType> childPropertyTypes) {
+        this.childPropertyTypes = childPropertyTypes;
+    }
 }
diff --git a/api/src/main/java/org/apache/unomi/api/services/EventService.java b/api/src/main/java/org/apache/unomi/api/services/EventService.java
index c3a09d3..a87a1b4 100644
--- a/api/src/main/java/org/apache/unomi/api/services/EventService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/EventService.java
@@ -17,10 +17,7 @@
 
 package org.apache.unomi.api.services;
 
-import org.apache.unomi.api.Event;
-import org.apache.unomi.api.EventProperty;
-import org.apache.unomi.api.PartialList;
-import org.apache.unomi.api.Session;
+import org.apache.unomi.api.*;
 import org.apache.unomi.api.actions.ActionPostExecutor;
 import org.apache.unomi.api.conditions.Condition;
 
@@ -75,10 +72,20 @@ public interface EventService {
      * Retrieves the list of available event properties.
      *
      * @return a list of available event properties
+     * Retrieves an event type
+     * @return the EventType object corresponding to the name, or null if not found.
+     * @deprecated use event types instead
      */
     List<EventProperty> getEventProperties();
 
     /**
+     * Retrieves an event type
+     * @param typeName the name identifier for the event type
+     * @return the EventType object corresponding to the name, or null if not found.
+     */
+    EventType getEventType(String typeName);
+
+    /**
      * Retrieves the set of known event type identifiers.
      *
      * @return the set of known event type identifiers.
diff --git a/rest/src/main/java/org/apache/unomi/rest/EventServiceEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/EventServiceEndpoint.java
index d9e028b..4a02ca4 100644
--- a/rest/src/main/java/org/apache/unomi/rest/EventServiceEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/EventServiceEndpoint.java
@@ -18,16 +18,18 @@ package org.apache.unomi.rest;
 
 import org.apache.cxf.rs.security.cors.CrossOriginResourceSharing;
 import org.apache.unomi.api.Event;
+import org.apache.unomi.api.EventType;
 import org.apache.unomi.api.PartialList;
+import org.apache.unomi.api.PropertyType;
 import org.apache.unomi.api.query.Query;
 import org.apache.unomi.api.services.EventService;
 
 import javax.jws.WebMethod;
 import javax.jws.WebService;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
+import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
+import java.util.List;
+import java.util.Set;
 
 /**
  * A JAX-RS endpoint to access information about the context server's events.
@@ -59,4 +61,24 @@ public class EventServiceEndpoint {
         return eventService.searchEvents(query.getCondition(), query.getOffset(), query.getLimit());
     }
 
+    /**
+     * Retrieves the list of event types identifiers that the server has processed.
+     * @return a Set of strings that contain event type identifiers.
+     */
+    @GET
+    @Path("types")
+    public Set<String> getEventTypeNames() {
+        return eventService.getEventTypeIds();
+    }
+
+    /**
+     * Returns the list of event properties
+     * @return a List of EventProperty objects that make up the properties that the server has seen.
+     */
+    @GET
+    @Path("types/{typeName}")
+    public EventType getEventType(@PathParam("typeName") String typeName) {
+        return eventService.getEventType(typeName);
+    }
+
 }
diff --git a/services/src/main/java/org/apache/unomi/services/impl/definitions/DefinitionsServiceImpl.java b/services/src/main/java/org/apache/unomi/services/impl/definitions/DefinitionsServiceImpl.java
index 10cb598..6efb502 100644
--- a/services/src/main/java/org/apache/unomi/services/impl/definitions/DefinitionsServiceImpl.java
+++ b/services/src/main/java/org/apache/unomi/services/impl/definitions/DefinitionsServiceImpl.java
@@ -59,7 +59,17 @@ public class DefinitionsServiceImpl implements DefinitionsService, SynchronousBu
 
     private BundleContext bundleContext;
     public DefinitionsServiceImpl() {
-
+        // let's add the built-in value types
+        ValueType setValueType = new ValueType();
+        setValueType.setId("set");
+        setValueType.setNameKey("set");
+        setValueType.setTags(new HashSet<>());
+        valueTypeById.put(setValueType.getId(), setValueType);
+        ValueType unknownValueType = new ValueType();
+        setValueType.setId("unknown");
+        setValueType.setNameKey("unknown");
+        setValueType.setTags(new HashSet<>());
+        valueTypeById.put(setValueType.getId(), setValueType);
     }
 
     public void setBundleContext(BundleContext bundleContext) {
@@ -160,7 +170,7 @@ public class DefinitionsServiceImpl implements DefinitionsService, SynchronousBu
         if (bundleContext == null) {
             return;
         }
-        List<PluginType> types = pluginTypes.get(bundleContext.getBundle().getBundleId());
+        List<PluginType> types = pluginTypes.remove(bundleContext.getBundle().getBundleId());
         if (types != null) {
             for (PluginType type : types) {
                 if (type instanceof ValueType) {
diff --git a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
index ab1cd77..186fd34 100644
--- a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
+++ b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
@@ -20,26 +20,24 @@ package org.apache.unomi.services.impl.events;
 import inet.ipaddr.IPAddress;
 import inet.ipaddr.IPAddressString;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.unomi.api.Event;
-import org.apache.unomi.api.EventProperty;
-import org.apache.unomi.api.PartialList;
-import org.apache.unomi.api.Session;
+import org.apache.unomi.api.*;
 import org.apache.unomi.api.actions.ActionPostExecutor;
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.services.DefinitionsService;
 import org.apache.unomi.api.services.EventListenerService;
 import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.persistence.spi.CustomObjectMapper;
 import org.apache.unomi.persistence.spi.PersistenceService;
 import org.apache.unomi.persistence.spi.aggregate.TermsAggregate;
 import org.apache.unomi.services.impl.ParserHelper;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
+import org.osgi.framework.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.net.URL;
 import java.util.*;
 
-public class EventServiceImpl implements EventService {
+public class EventServiceImpl implements EventService, SynchronousBundleListener {
     private static final Logger logger = LoggerFactory.getLogger(EventServiceImpl.class.getName());
     private static final int MAX_RECURSION_DEPTH = 10;
 
@@ -57,11 +55,26 @@ public class EventServiceImpl implements EventService {
 
     private Map<String, ThirdPartyServer> thirdPartyServers = new HashMap<>();
 
+    private Map<Long, List<PluginType>> pluginTypes = new HashMap<>();
+
+    private Map<String, EventType> eventTypes = new LinkedHashMap<>();
+
     public void init() {
+        processBundleStartup(bundleContext);
+
+        // process already started bundles
+        for (Bundle bundle : bundleContext.getBundles()) {
+            if (bundle.getBundleContext() != null && bundle.getBundleId() != bundleContext.getBundle().getBundleId()) {
+                processBundleStartup(bundle.getBundleContext());
+            }
+        }
+
+        bundleContext.addBundleListener(this);
         logger.info("Event service initialized.");
     }
 
     public void destroy() {
+        bundleContext.removeBundleListener(this);
         logger.info("Event service shutdown.");
     }
 
@@ -185,6 +198,11 @@ public class EventServiceImpl implements EventService {
     }
 
     @Override
+    public EventType getEventType(String typeName) {
+        return eventTypes.get(typeName);
+    }
+
+    @Override
     public List<EventProperty> getEventProperties() {
         Map<String, Map<String, Object>> mappings = persistenceService.getPropertiesMapping(Event.ITEM_TYPE);
         List<EventProperty> props = new ArrayList<>(mappings.size());
@@ -203,10 +221,61 @@ public class EventServiceImpl implements EventService {
         }
     }
 
+    private List<PropertyType> getEventPropertyTypes() {
+        Map<String, Map<String, Object>> mappings = persistenceService.getPropertiesMapping(Event.ITEM_TYPE);
+        return new ArrayList<>(getEventPropertyTypes(mappings));
+    }
+
+    @SuppressWarnings("unchecked")
+    private Set<PropertyType> getEventPropertyTypes(Map<String, Map<String, Object>> mappings) {
+        Set<PropertyType> properties = new LinkedHashSet<>();
+        for (Map.Entry<String, Map<String, Object>> e : mappings.entrySet()) {
+            Set<PropertyType> childProperties = null;
+            Metadata propertyMetadata = new Metadata(null, e.getKey(), e.getKey(), null);
+            Set<String> systemTags = new HashSet<>();
+            propertyMetadata.setSystemTags(systemTags);
+            PropertyType propertyType = new PropertyType(propertyMetadata);
+            propertyType.setTarget("event");
+            ValueType valueType = null;
+            if (e.getValue().get("properties") != null) {
+                childProperties = getEventPropertyTypes((Map<String, Map<String, Object>>) e.getValue().get("properties"));
+                valueType = definitionsService.getValueType("set");
+                if (childProperties != null && childProperties.size() > 0) {
+                    propertyType.setChildPropertyTypes(childProperties);
+                }
+            } else {
+                valueType = mappingTypeToValueType( (String) e.getValue().get("type"));
+            }
+            propertyType.setValueTypeId(valueType.getId());
+            propertyType.setValueType(valueType);
+            properties.add(propertyType);
+        }
+        return properties;
+    }
+
+    private ValueType mappingTypeToValueType(String mappingType) {
+        if ("text".equals(mappingType)) {
+            return definitionsService.getValueType("string");
+        } else if ("date".equals(mappingType)) {
+            return definitionsService.getValueType("date");
+        } else if ("long".equals(mappingType)) {
+            return definitionsService.getValueType("integer");
+        } else if ("boolean".equals(mappingType)) {
+            return definitionsService.getValueType("boolean");
+        } else if ("set".equals(mappingType)) {
+            return definitionsService.getValueType("set");
+        } else if ("object".equals(mappingType)) {
+            return definitionsService.getValueType("set");
+        } else {
+            return definitionsService.getValueType("unknown");
+        }
+    }
+
     public Set<String> getEventTypeIds() {
         Map<String, Long> dynamicEventTypeIds = persistenceService.aggregateWithOptimizedQuery(null, new TermsAggregate("eventType"), Event.ITEM_TYPE);
         Set<String> eventTypeIds = new LinkedHashSet<String>(predefinedEventTypeIds);
         eventTypeIds.addAll(dynamicEventTypeIds.keySet());
+        eventTypeIds.remove("_filtered");
         return eventTypeIds;
     }
 
@@ -301,4 +370,62 @@ public class EventServiceImpl implements EventService {
             eventListeners.remove(eventListenerService);
         }
     }
+
+    private void processBundleStartup(BundleContext bundleContext) {
+        if (bundleContext == null) {
+            return;
+        }
+        pluginTypes.put(bundleContext.getBundle().getBundleId(), new ArrayList<PluginType>());
+        loadPredefinedEventTypes(bundleContext);
+    }
+
+    private void processBundleStop(BundleContext bundleContext) {
+        if (bundleContext == null) {
+            return;
+        }
+        List<PluginType> types = pluginTypes.remove(bundleContext.getBundle().getBundleId());
+        if (types != null) {
+            for (PluginType type : types) {
+                if (type instanceof EventType) {
+                    EventType eventType = (EventType) type;
+                    eventTypes.remove(eventType.getType());
+                }
+            }
+        }
+    }
+
+    public void bundleChanged(BundleEvent event) {
+        switch (event.getType()) {
+            case BundleEvent.STARTED:
+                processBundleStartup(event.getBundle().getBundleContext());
+                break;
+            case BundleEvent.STOPPING:
+                processBundleStop(event.getBundle().getBundleContext());
+                break;
+        }
+    }
+
+    private void loadPredefinedEventTypes(BundleContext bundleContext) {
+        Enumeration<URL> predefinedPropertiesEntries = bundleContext.getBundle().findEntries("META-INF/cxs/events", "*.json", true);
+        if (predefinedPropertiesEntries == null) {
+            return;
+        }
+        ArrayList<PluginType> pluginTypeArrayList = (ArrayList<PluginType>) pluginTypes.get(bundleContext.getBundle().getBundleId());
+        while (predefinedPropertiesEntries.hasMoreElements()) {
+            URL predefinedPropertyURL = predefinedPropertiesEntries.nextElement();
+            logger.debug("Found predefined event type at " + predefinedPropertyURL + ", loading... ");
+
+            try {
+                EventType eventType = CustomObjectMapper.getObjectMapper().readValue(predefinedPropertyURL, EventType.class);
+                eventType.setPluginId(bundleContext.getBundle().getBundleId());
+                eventTypes.put(eventType.getType(), eventType);
+                pluginTypeArrayList.add(eventType);
+            } catch (Exception e) {
+                logger.error("Error while loading property type definition " + predefinedPropertyURL, e);
+            }
+        }
+
+    }
+
+
 }
diff --git a/services/src/main/resources/META-INF/cxs/events/common.json b/services/src/main/resources/META-INF/cxs/events/common.json
new file mode 100644
index 0000000..5b5c869
--- /dev/null
+++ b/services/src/main/resources/META-INF/cxs/events/common.json
@@ -0,0 +1,24 @@
+{
+  "propertyTypes" : [
+    {
+      "id": "timestamp",
+      "type" : "date"
+    },
+    {
+      "id": "sessionId",
+      "type" : "string"
+    },
+    {
+      "id": "profileId",
+      "type" : "string"
+    },
+    {
+      "id" : "eventType",
+      "type" : "string"
+    },
+    {
+      "id" : "scope",
+      "type" : "string"
+    }
+  ]
+}
\ No newline at end of file