This is an automated email from the ASF dual-hosted git repository.
shuber pushed a commit to branch draft-custom-items
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/draft-custom-items by this
push:
new 1919bc8 Support for custom items: - Made it possible to declare new
custom item types that will inherit from the Item class and be loaded as
CustomItem instances, but that can have any structure in the properties map.
Custom item types have a "subtype" that defines its unique type and that is
used to store the custom item objects in seperate indices in the persistence
service. It is also possible to query custom item types using new methods in
the PersistenceService SPI. Moved t [...]
1919bc8 is described below
commit 1919bc80961f3411d91bd09efc5bc7a34e66d30e
Author: Serge Huber <[email protected]>
AuthorDate: Wed Sep 29 09:21:14 2021 +0200
Support for custom items:
- Made it possible to declare new custom item types that will inherit from
the Item class and be loaded as CustomItem instances, but that can have any
structure in the properties map. Custom item types have a "subtype" that
defines its unique type and that is used to store the custom item objects in
seperate indices in the persistence service. It is also possible to query
custom item types using new methods in the PersistenceService SPI.
Moved the ParserHelper into the API
- The ParserHelper is very useful to load conditions and apply visitors on
them, so it was moved to the API in order to be easier to re-use in multiple
subsystems or plugins.
Moved the ValueExtractors into the ParserHelper
- The ValueExtractors used in the ActionExecutorDispatcherImpl have been
moved into the ParserHelper so that they can be re-used more easily, for
example to use them in other places such as conditions or other objects. The
script value extractor, that has security issues is not part of the default set
of value extractors and is currently only added in the
ActionExecutorDispatcherImpl.
Make is possible to add custom ItemDeserializers from plugins
- It is now possible for plugins to add their own Item deserializers in the
CustomObjectMapper using new methods to register mappings. This can be used by
plugins to add new Item types and is used by the new custom item type subsystem.
---
api/pom.xml | 8 ++
.../main/java/org/apache/unomi/api/CustomItem.java | 9 ++
.../java/org/apache/unomi/api/MetadataItem.java | 4 +-
.../org/apache/unomi/api/utils}/ParserHelper.java | 112 +++++++++++++++-
.../test/java/org/apache/unomi/itests/BasicIT.java | 5 +
.../ElasticSearchPersistenceServiceImpl.java | 142 +++++++++++++++++----
.../unomi/persistence/spi/CustomObjectMapper.java | 60 ++++++---
.../unomi/persistence/spi/ItemDeserializer.java | 7 +
.../unomi/persistence/spi/PersistenceService.java | 51 ++++++++
.../spi/PropertyTypedObjectDeserializer.java | 11 ++
.../actions/impl/ActionExecutorDispatcherImpl.java | 135 +-------------------
.../impl/definitions/DefinitionsServiceImpl.java | 2 +-
.../services/impl/events/EventServiceImpl.java | 2 +-
.../services/impl/goals/GoalsServiceImpl.java | 2 +-
.../services/impl/profiles/ProfileServiceImpl.java | 2 +-
.../services/impl/queries/QueryServiceImpl.java | 2 +-
.../services/impl/rules/RulesServiceImpl.java | 2 +-
.../services/impl/segments/SegmentServiceImpl.java | 3 +-
18 files changed, 372 insertions(+), 187 deletions(-)
diff --git a/api/pom.xml b/api/pom.xml
index 7bc17fa..75da098 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -49,6 +49,14 @@
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
+ <dependency>
+ <groupId>commons-beanutils</groupId>
+ <artifactId>commons-beanutils</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ </dependency>
</dependencies>
<reporting>
diff --git a/api/src/main/java/org/apache/unomi/api/CustomItem.java
b/api/src/main/java/org/apache/unomi/api/CustomItem.java
index df012dd..a5dceab 100644
--- a/api/src/main/java/org/apache/unomi/api/CustomItem.java
+++ b/api/src/main/java/org/apache/unomi/api/CustomItem.java
@@ -30,6 +30,7 @@ public class CustomItem extends Item {
*/
public static final String ITEM_TYPE = "custom";
private static final long serialVersionUID = -7178914125308851922L;
+ private String customItemType;
private Map<String,Object> properties = new HashMap<String,Object>();
/**
@@ -66,4 +67,12 @@ public class CustomItem extends Item {
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
+
+ public String getCustomItemType() {
+ return customItemType;
+ }
+
+ public void setCustomItemType(String customItemType) {
+ this.customItemType = customItemType;
+ }
}
diff --git a/api/src/main/java/org/apache/unomi/api/MetadataItem.java
b/api/src/main/java/org/apache/unomi/api/MetadataItem.java
index fb8b521..78a4198 100644
--- a/api/src/main/java/org/apache/unomi/api/MetadataItem.java
+++ b/api/src/main/java/org/apache/unomi/api/MetadataItem.java
@@ -46,7 +46,9 @@ public abstract class MetadataItem extends Item {
}
public void setMetadata(Metadata metadata) {
- this.itemId = metadata.getId();
+ if (metadata != null) {
+ this.itemId = metadata.getId();
+ }
this.metadata = metadata;
}
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/ParserHelper.java
b/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java
similarity index 61%
rename from
services/src/main/java/org/apache/unomi/services/impl/ParserHelper.java
rename to api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java
index 0b90ec5..6396e19 100644
--- a/services/src/main/java/org/apache/unomi/services/impl/ParserHelper.java
+++ b/api/src/main/java/org/apache/unomi/api/utils/ParserHelper.java
@@ -15,8 +15,11 @@
* limitations under the License.
*/
-package org.apache.unomi.services.impl;
+package org.apache.unomi.api.utils;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.unomi.api.Event;
import org.apache.unomi.api.PropertyType;
import org.apache.unomi.api.ValueType;
import org.apache.unomi.api.actions.Action;
@@ -28,6 +31,7 @@ import org.apache.unomi.api.services.DefinitionsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.lang.reflect.InvocationTargetException;
import java.util.*;
/**
@@ -40,6 +44,29 @@ public class ParserHelper {
private static final Set<String> unresolvedActionTypes = new HashSet<>();
private static final Set<String> unresolvedConditionTypes = new
HashSet<>();
+ private static final String VALUE_NAME_SEPARATOR = "::";
+ private static final String PLACEHOLDER_PREFIX = "${";
+ private static final String PLACEHOLDER_SUFFIX = "}";
+
+ public interface ConditionVisitor {
+ void visit(Condition condition);
+ void postVisit(Condition condition);
+ }
+
+ public interface ValueExtractor {
+ Object extract(String valueAsString, Event event) throws
IllegalAccessException, NoSuchMethodException, InvocationTargetException;
+ }
+
+ public static final Map<String,ValueExtractor> DEFAULT_VALUE_EXTRACTORS =
new HashMap<>();
+ static {
+ DEFAULT_VALUE_EXTRACTORS.put("profileProperty", (valueAsString, event)
-> PropertyUtils.getProperty(event.getProfile(), "properties." +
valueAsString));
+ DEFAULT_VALUE_EXTRACTORS.put("simpleProfileProperty", (valueAsString,
event) -> event.getProfile().getProperty(valueAsString));
+ DEFAULT_VALUE_EXTRACTORS.put("sessionProperty", (valueAsString, event)
-> PropertyUtils.getProperty(event.getSession(), "properties." +
valueAsString));
+ DEFAULT_VALUE_EXTRACTORS.put("simpleSessionProperty", (valueAsString,
event) -> event.getSession().getProperty(valueAsString));
+ DEFAULT_VALUE_EXTRACTORS.put("eventProperty", (valueAsString, event)
-> PropertyUtils.getProperty(event, valueAsString));
+ DEFAULT_VALUE_EXTRACTORS.put("simpleEventProperty", (valueAsString,
event) -> event.getProperty(valueAsString));
+ }
+
public static boolean resolveConditionType(final DefinitionsService
definitionsService, Condition rootCondition, String contextObjectName) {
if (rootCondition == null) {
logger.warn("Couldn't resolve null condition for {}",
contextObjectName);
@@ -86,7 +113,7 @@ public class ParserHelper {
return result;
}
- private static void visitConditions(Condition rootCondition,
ConditionVisitor visitor) {
+ public static void visitConditions(Condition rootCondition,
ConditionVisitor visitor) {
visitor.visit(rootCondition);
// recursive call for sub-conditions as parameters
for (Object parameterValue :
rootCondition.getParameterValues().values()) {
@@ -152,10 +179,6 @@ public class ParserHelper {
}
}
- interface ConditionVisitor {
- void visit(Condition condition);
- void postVisit(Condition condition);
- }
public static Set<String> resolveConditionEventTypes(Condition
rootCondition) {
if (rootCondition == null) {
@@ -166,7 +189,7 @@ public class ParserHelper {
return eventTypeConditionVisitor.getEventTypeIds();
}
- static class EventTypeConditionVisitor implements ConditionVisitor {
+ public static class EventTypeConditionVisitor implements ConditionVisitor {
private Set<String> eventTypeIds = new HashSet<>();
private Stack<String> conditionTypeStack = new Stack<>();
@@ -199,4 +222,79 @@ public class ParserHelper {
return eventTypeIds;
}
}
+
+ @SuppressWarnings("unchecked")
+ public static Map<String, Object> parseMap(Event event, Map<String,
Object> map, Map<String, ValueExtractor> valueExtractors) {
+ Map<String, Object> values = new HashMap<>();
+ for (Map.Entry<String, Object> entry : map.entrySet()) {
+ Object value = entry.getValue();
+ if (value instanceof String) {
+ String s = (String) value;
+ try {
+ if (s.contains(PLACEHOLDER_PREFIX)) {
+ while (s.contains(PLACEHOLDER_PREFIX)) {
+ String substring =
s.substring(s.indexOf(PLACEHOLDER_PREFIX) + 2, s.indexOf(PLACEHOLDER_SUFFIX));
+ Object v = extractValue(substring, event,
valueExtractors);
+ if (v != null) {
+ s = s.replace(PLACEHOLDER_PREFIX + substring +
PLACEHOLDER_SUFFIX, v.toString());
+ } else {
+ break;
+ }
+ }
+ value = s;
+ } else {
+ // check if we have special values
+ if (s.contains(VALUE_NAME_SEPARATOR)) {
+ value = extractValue(s, event, valueExtractors);
+ }
+ }
+ } catch (UnsupportedOperationException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new UnsupportedOperationException(e);
+ }
+ } else if (value instanceof Map) {
+ value = parseMap(event, (Map<String, Object>) value,
valueExtractors);
+ }
+ values.put(entry.getKey(), value);
+ }
+ return values;
+ }
+
+ public static Object extractValue(String s, Event event, Map<String,
ValueExtractor> valueExtractors) throws IllegalAccessException,
NoSuchMethodException, InvocationTargetException {
+ Object value = null;
+
+ String valueType = StringUtils.substringBefore(s,
VALUE_NAME_SEPARATOR);
+ String valueAsString = StringUtils.substringAfter(s,
VALUE_NAME_SEPARATOR);
+ ValueExtractor extractor = valueExtractors.get(valueType);
+ if (extractor != null) {
+ value = extractor.extract(valueAsString, event);
+ }
+
+ return value;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static boolean hasContextualParameter(Map<String, Object> values,
Map<String, ValueExtractor> valueExtractors) {
+ for (Map.Entry<String, Object> entry : values.entrySet()) {
+ Object value = entry.getValue();
+ if (value instanceof String) {
+ String s = (String) value;
+ String str = s.contains(PLACEHOLDER_PREFIX) ?
+ s.substring(s.indexOf(PLACEHOLDER_PREFIX) + 2,
s.indexOf(PLACEHOLDER_SUFFIX)) :
+ s;
+
+ if (str.contains(VALUE_NAME_SEPARATOR) && valueExtractors
+ .containsKey(StringUtils.substringBefore(str,
VALUE_NAME_SEPARATOR))) {
+ return true;
+ }
+ } else if (value instanceof Map) {
+ if (hasContextualParameter((Map<String, Object>) value,
valueExtractors)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
}
diff --git a/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
b/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
index 8d86dcb..d7a2387 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
@@ -152,12 +152,15 @@ public class BasicIT extends BaseIT {
ConditionType conditionType =
CustomObjectMapper.getObjectMapper().readValue(
new
File("data/tmp/testLoginEventCondition.json").toURI().toURL(),
ConditionType.class);
definitionsService.setConditionType(conditionType);
+
+ refreshPersistence();
Thread.sleep(2000);
// Add login rule
Rule rule = CustomObjectMapper.getObjectMapper().readValue(new
File("data/tmp/testLogin.json").toURI().toURL(),
Rule.class);
rulesService.setRule(rule);
Thread.sleep(2000);
+ refreshPersistence();
CustomItem sourceSite = new CustomItem(ITEM_ID_SITE, ITEM_TYPE_SITE);
sourceSite.setScope(TEST_SCOPE);
@@ -260,6 +263,8 @@ public class BasicIT extends BaseIT {
checkVisitor2ResponseProperties(requestResponsePageView4.getContextResponse().getProfileProperties());
Thread.sleep(1000);
+ refreshPersistence();
+
// Check both visitor profile at the end by loading them directly
Profile profileVisitor1 = profileService.load(profileIdVisitor1);
checkVisitor1ResponseProperties(profileVisitor1.getProperties());
diff --git
a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
index aadb3b6..6cd8b84 100644
---
a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
+++
b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
@@ -28,10 +28,7 @@ import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.lucene.search.TotalHits;
-import org.apache.unomi.api.Item;
-import org.apache.unomi.api.PartialList;
-import org.apache.unomi.api.PropertyType;
-import org.apache.unomi.api.TimestampedItem;
+import org.apache.unomi.api.*;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.query.DateRange;
import org.apache.unomi.api.query.IpRange;
@@ -774,22 +771,48 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
@Override
public <T extends Item> T load(final String itemId, final Date dateHint,
final Class<T> clazz) {
+ return load(itemId, dateHint, clazz, null);
+ }
+
+ @Override
+ public CustomItem loadCustomItem(final String itemId, final Date dateHint,
String customItemType) {
+ return load(itemId, dateHint, CustomItem.class, customItemType);
+ }
+
+ private <T extends Item> T load(final String itemId, final Date dateHint,
final Class<T> clazz, final String customItemType) {
return new InClassLoaderExecute<T>(metricsService,
this.getClass().getName() + ".loadItem", this.bundleContext,
this.fatalIllegalStateErrors, throwExceptions) {
protected T execute(Object... args) throws Exception {
try {
String itemType = Item.getItemType(clazz);
- T itemFromCache = getFromCache(itemId, clazz);
- if (itemFromCache != null) {
- return itemFromCache;
+ String className = clazz.getName();
+ if (customItemType == null) {
+ T itemFromCache = getFromCache(itemId,
clazz.getName());
+ if (itemFromCache != null) {
+ return itemFromCache;
+ }
+ } else {
+ T itemFromCache = getFromCache(itemId,
CustomItem.class.getName() + "." + customItemType);
+ if (itemFromCache != null) {
+ return itemFromCache;
+ }
+ className = CustomItem.class.getName() + "." +
customItemType;
+ itemType = customItemType;
}
if (itemsMonthlyIndexed.contains(itemType) && dateHint ==
null) {
return new MetricAdapter<T>(metricsService,
".loadItemWithQuery") {
@Override
public T execute(Object... args) throws Exception {
- PartialList<T> r =
query(QueryBuilders.idsQuery().addIds(itemId), null, clazz, 0, 1, null, null);
- if (r.size() > 0) {
- return r.get(0);
+ if (customItemType == null) {
+ PartialList<T> r =
query(QueryBuilders.idsQuery().addIds(itemId), null, clazz, 0, 1, null, null);
+ if (r.size() > 0) {
+ return r.get(0);
+ }
+ } else {
+ PartialList<CustomItem> r =
query(QueryBuilders.idsQuery().addIds(itemId), null, customItemType, 0, 1,
null, null);
+ if (r.size() > 0) {
+ return (T) r.get(0);
+ }
}
return null;
}
@@ -801,7 +824,7 @@ public class ElasticSearchPersistenceServiceImpl implements
PersistenceService,
String sourceAsString =
response.getSourceAsString();
final T value =
ESCustomObjectMapper.getObjectMapper().readValue(sourceAsString, clazz);
setMetadata(value, response.getId(),
response.getVersion(), response.getSeqNo(), response.getPrimaryTerm());
- putInCache(itemId, value);
+ putInCache(itemId, value, className);
return value;
} else {
return null;
@@ -812,12 +835,12 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
// this can happen if we are just testing the
existence of the item, it is not always an error.
return null;
}
- throw new Exception("Error loading itemType=" +
clazz.getName() + " itemId=" + itemId, ese);
+ throw new Exception("Error loading itemType=" +
clazz.getName() + " customItemType=" + customItemType + " itemId=" + itemId,
ese);
} catch (IndexNotFoundException e) {
// this can happen if we are just testing the existence of
the item, it is not always an error.
return null;
} catch (Exception ex) {
- throw new Exception("Error loading itemType=" +
clazz.getName() + " itemId=" + itemId, ex);
+ throw new Exception("Error loading itemType=" +
clazz.getName() + " customItemType=" + customItemType+ " itemId=" + itemId, ex);
}
}
}.catchingExecuteInClassLoader(true);
@@ -856,8 +879,13 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
try {
String source =
ESCustomObjectMapper.getObjectMapper().writeValueAsString(item);
String itemType = item.getItemType();
+ String className = item.getClass().getName();
+ if (item instanceof CustomItem) {
+ itemType = ((CustomItem) item).getCustomItemType();
+ className = CustomItem.class.getName() + "." +
itemType;
+ }
String itemId = item.getItemId();
- putInCache(itemId, item);
+ putInCache(itemId, item, className);
String index = getIndex(itemType,
itemsMonthlyIndexed.contains(itemType) ? ((TimestampedItem)
item).getTimeStamp() : null);
IndexRequest indexRequest = new IndexRequest(index);
indexRequest.id(itemId);
@@ -882,7 +910,7 @@ public class ElasticSearchPersistenceServiceImpl implements
PersistenceService,
try {
if (bulkProcessor == null || !useBatching) {
-
indexRequest.setRefreshPolicy(getRefreshPolicy(item.getItemType()));
+
indexRequest.setRefreshPolicy(getRefreshPolicy(itemType));
IndexResponse response =
client.index(indexRequest, RequestOptions.DEFAULT);
setMetadata(item, response.getId(),
response.getVersion(), response.getSeqNo(), response.getPrimaryTerm());
} else {
@@ -1093,10 +1121,22 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
@Override
public <T extends Item> boolean remove(final String itemId, final Class<T>
clazz) {
+ return remove(itemId, clazz, null);
+ }
+
+ @Override
+ public boolean removeCustomItem(final String itemId, final String
customItemType) {
+ return remove(itemId, CustomItem.class, customItemType);
+ }
+
+ private <T extends Item> boolean remove(final String itemId, final
Class<T> clazz, String customItemType) {
Boolean result = new InClassLoaderExecute<Boolean>(metricsService,
this.getClass().getName() + ".removeItem", this.bundleContext,
this.fatalIllegalStateErrors, throwExceptions) {
protected Boolean execute(Object... args) throws Exception {
try {
String itemType = Item.getItemType(clazz);
+ if (customItemType != null) {
+ itemType = customItemType;
+ }
DeleteRequest deleteRequest = new
DeleteRequest(getIndexNameForQuery(itemType), itemId);
client.delete(deleteRequest, RequestOptions.DEFAULT);
@@ -1137,7 +1177,7 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
for (SearchHit hit : response.getHits().getHits()) {
// add hit to bulk delete
- deleteFromCache(hit.getId(), clazz);
+ deleteFromCache(hit.getId(), clazz.getName());
deleteByScopeBulkRequest.add(Requests.deleteRequest(hit.getIndex()).type(hit.getType()).id(hit.getId()));
}
@@ -1313,7 +1353,9 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
" }\n" +
"}\n", XContentType.JSON);
- createIndexRequest.mapping(mappingSource, XContentType.JSON);
+ if (mappingSource != null) {
+ createIndexRequest.mapping(mappingSource, XContentType.JSON);
+ }
CreateIndexResponse createIndexResponse =
client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
logger.info("Index created: [{}], acknowledge: [{}], shards
acknowledge: [{}]", createIndexResponse.index(),
createIndexResponse.isAcknowledged(),
createIndexResponse.isShardsAcknowledged());
@@ -1640,6 +1682,11 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
}
@Override
+ public PartialList<CustomItem> queryCustomItem(final Condition query,
String sortBy, final String customItemType, final int offset, final int size,
final String scrollTimeValidity) {
+ return query(conditionESQueryBuilderDispatcher.getQueryBuilder(query),
sortBy, customItemType, offset, size, null, scrollTimeValidity);
+ }
+
+ @Override
public <T extends Item> PartialList<T> queryFullText(final String
fulltext, final Condition query, String sortBy, final Class<T> clazz, final int
offset, final int size) {
return
query(QueryBuilders.boolQuery().must(QueryBuilders.queryStringQuery(fulltext)).must(conditionESQueryBuilderDispatcher.getQueryBuilder(query)),
sortBy, clazz, offset, size, null, null);
}
@@ -1711,6 +1758,14 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
}
private <T extends Item> PartialList<T> query(final QueryBuilder query,
final String sortBy, final Class<T> clazz, final int offset, final int size,
final String[] routing, final String scrollTimeValidity) {
+ return query(query, sortBy, clazz, null, offset, size, routing,
scrollTimeValidity);
+ }
+
+ private PartialList<CustomItem> query(final QueryBuilder query, final
String sortBy, final String customItemType, final int offset, final int size,
final String[] routing, final String scrollTimeValidity) {
+ return query(query, sortBy, CustomItem.class, customItemType, offset,
size, routing, scrollTimeValidity);
+ }
+
+ private <T extends Item> PartialList<T> query(final QueryBuilder query,
final String sortBy, final Class<T> clazz, final String customItemType, final
int offset, final int size, final String[] routing, final String
scrollTimeValidity) {
return new InClassLoaderExecute<PartialList<T>>(metricsService,
this.getClass().getName() + ".query", this.bundleContext,
this.fatalIllegalStateErrors, throwExceptions) {
@Override
@@ -1721,6 +1776,9 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
PartialList.Relation totalHitsRelation =
PartialList.Relation.EQUAL;
try {
String itemType = Item.getItemType(clazz);
+ if (customItemType != null) {
+ itemType = customItemType;
+ }
TimeValue keepAlive = TimeValue.timeValueHours(1);
SearchRequest searchRequest = new
SearchRequest(getIndexNameForQuery(itemType));
SearchSourceBuilder searchSourceBuilder = new
SearchSourceBuilder()
@@ -1876,6 +1934,47 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
}.catchingExecuteInClassLoader(true);
}
+ @Override
+ public PartialList<CustomItem> continueCustomItemScrollQuery(final String
customItemType, final String scrollIdentifier, final String scrollTimeValidity)
{
+ return new
InClassLoaderExecute<PartialList<CustomItem>>(metricsService,
this.getClass().getName() + ".continueScrollQuery", this.bundleContext,
this.fatalIllegalStateErrors, throwExceptions) {
+
+ @Override
+ protected PartialList<CustomItem> execute(Object... args) throws
Exception {
+ List<CustomItem> results = new ArrayList<CustomItem>();
+ long totalHits = 0;
+ try {
+ TimeValue keepAlive =
TimeValue.parseTimeValue(scrollTimeValidity, TimeValue.timeValueMinutes(10),
"scrollTimeValidity");
+
+ SearchScrollRequest searchScrollRequest = new
SearchScrollRequest(scrollIdentifier);
+ searchScrollRequest.scroll(keepAlive);
+ SearchResponse response =
client.scroll(searchScrollRequest, RequestOptions.DEFAULT);
+
+ if (response.getHits().getHits().length == 0) {
+ ClearScrollRequest clearScrollRequest = new
ClearScrollRequest();
+ clearScrollRequest.addScrollId(response.getScrollId());
+ client.clearScroll(clearScrollRequest,
RequestOptions.DEFAULT);
+ } else {
+ for (SearchHit searchHit :
response.getHits().getHits()) {
+ // add hit to results
+ String sourceAsString =
searchHit.getSourceAsString();
+ final CustomItem value =
ESCustomObjectMapper.getObjectMapper().readValue(sourceAsString,
CustomItem.class);
+ setMetadata(value, searchHit.getId(),
searchHit.getVersion(), searchHit.getSeqNo(), searchHit.getPrimaryTerm());
+ results.add(value);
+ }
+ }
+ PartialList<CustomItem> result = new
PartialList<CustomItem>(results, 0, response.getHits().getHits().length,
response.getHits().getTotalHits().value,
getTotalHitsRelation(response.getHits().getTotalHits()));
+ if (scrollIdentifier != null) {
+ result.setScrollIdentifier(scrollIdentifier);
+ result.setScrollTimeValidity(scrollTimeValidity);
+ }
+ return result;
+ } catch (Exception t) {
+ throw new Exception("Error continuing scrolling query for
itemType=" + customItemType + " scrollIdentifier=" + scrollIdentifier + "
scrollTimeValidity=" + scrollTimeValidity, t);
+ }
+ }
+ }.catchingExecuteInClassLoader(true);
+ }
+
/**
* @deprecated As of version 1.3.0-incubating, use {@link
#aggregateWithOptimizedQuery(Condition, BaseAggregate, String)} instead
*/
@@ -2348,8 +2447,7 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
return false;
}
- private <T extends Item> T getFromCache(String itemId, Class<T> clazz) {
- String className = clazz.getName();
+ private <T extends Item> T getFromCache(String itemId, String className) {
if (!isCacheActiveForClass(className)) {
return null;
}
@@ -2357,8 +2455,7 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
return itemCache.get(itemId);
}
- private <T extends Item> T putInCache(String itemId, T item) {
- String className = item.getClass().getName();
+ private <T extends Item> T putInCache(String itemId, T item, String
className) {
if (!isCacheActiveForClass(className)) {
return null;
}
@@ -2366,8 +2463,7 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
return itemCache.put(itemId, item);
}
- private <T extends Item> T deleteFromCache(String itemId, Class clazz) {
- String className = clazz.getName();
+ private <T extends Item> T deleteFromCache(String itemId, String
className) {
if (!isCacheActiveForClass(className)) {
return null;
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/CustomObjectMapper.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/CustomObjectMapper.java
index 4f967c8..f5f2844 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/CustomObjectMapper.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/CustomObjectMapper.java
@@ -44,6 +44,10 @@ import java.util.TimeZone;
public class CustomObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 4578277612897061535L;
+
+ private Map<String,Class<? extends Item>> builtinItemTypeClasses = new
HashMap<>();
+ private PropertyTypedObjectDeserializer propertyTypedObjectDeserializer;
+ private ItemDeserializer itemDeserializer;
public CustomObjectMapper() {
super();
@@ -56,45 +60,61 @@ public class CustomObjectMapper extends ObjectMapper {
new SimpleModule("PropertyTypedObjectDeserializerModule",
new Version(1, 0, 0, null, "org.apache.unomi.rest",
"deserializer"));
- PropertyTypedObjectDeserializer propertyTypedObjectDeserializer = new
PropertyTypedObjectDeserializer(null, null);
+ propertyTypedObjectDeserializer = new
PropertyTypedObjectDeserializer(null, null);
propertyTypedObjectDeserializer.registerMapping("type=.*Condition",
Condition.class);
deserializerModule.addDeserializer(Object.class,
propertyTypedObjectDeserializer);
- ItemDeserializer itemDeserializer = new ItemDeserializer();
+ itemDeserializer = new ItemDeserializer();
deserializerModule.addDeserializer(Item.class, itemDeserializer);
- Map<String, Class<? extends Item>> classes = new HashMap<>();
- classes.put(Campaign.ITEM_TYPE, Campaign.class);
- classes.put(CampaignEvent.ITEM_TYPE, CampaignEvent.class);
- classes.put(Event.ITEM_TYPE, Event.class);
- classes.put(Goal.ITEM_TYPE, Goal.class);
- classes.put(Persona.ITEM_TYPE, Persona.class);
- classes.put(Profile.ITEM_TYPE, Profile.class);
- classes.put(Rule.ITEM_TYPE, Rule.class);
- classes.put(Scoring.ITEM_TYPE, Scoring.class);
- classes.put(Segment.ITEM_TYPE, Segment.class);
- classes.put(Session.ITEM_TYPE, Session.class);
- classes.put(ConditionType.ITEM_TYPE, ConditionType.class);
- classes.put(ActionType.ITEM_TYPE, ActionType.class);
- classes.put(Topic.ITEM_TYPE, Topic.class);
- classes.put(SourceItem.ITEM_TYPE, SourceItem.class);
- classes.put(ProfileAlias.ITEM_TYPE, ProfileAlias.class);
- for (Map.Entry<String, Class<? extends Item>> entry :
classes.entrySet()) {
+ builtinItemTypeClasses = new HashMap<>();
+ builtinItemTypeClasses.put(Campaign.ITEM_TYPE, Campaign.class);
+ builtinItemTypeClasses.put(CampaignEvent.ITEM_TYPE,
CampaignEvent.class);
+ builtinItemTypeClasses.put(Event.ITEM_TYPE, Event.class);
+ builtinItemTypeClasses.put(Goal.ITEM_TYPE, Goal.class);
+ builtinItemTypeClasses.put(Persona.ITEM_TYPE, Persona.class);
+ builtinItemTypeClasses.put(Profile.ITEM_TYPE, Profile.class);
+ builtinItemTypeClasses.put(Rule.ITEM_TYPE, Rule.class);
+ builtinItemTypeClasses.put(Scoring.ITEM_TYPE, Scoring.class);
+ builtinItemTypeClasses.put(Segment.ITEM_TYPE, Segment.class);
+ builtinItemTypeClasses.put(Session.ITEM_TYPE, Session.class);
+ builtinItemTypeClasses.put(ConditionType.ITEM_TYPE,
ConditionType.class);
+ builtinItemTypeClasses.put(ActionType.ITEM_TYPE, ActionType.class);
+ builtinItemTypeClasses.put(Topic.ITEM_TYPE, Topic.class);
+ builtinItemTypeClasses.put(SourceItem.ITEM_TYPE, SourceItem.class);
+ builtinItemTypeClasses.put(ProfileAlias.ITEM_TYPE, ProfileAlias.class);
+ for (Map.Entry<String, Class<? extends Item>> entry :
builtinItemTypeClasses.entrySet()) {
propertyTypedObjectDeserializer.registerMapping("itemType=" +
entry.getKey(), entry.getValue());
itemDeserializer.registerMapping(entry.getKey(), entry.getValue());
}
propertyTypedObjectDeserializer.registerMapping("itemType=.*",
CustomItem.class);
-
super.registerModule(deserializerModule);
}
+ public void registerBuiltInItemTypeClass(String itemType, Class clazz) {
+ propertyTypedObjectDeserializer.registerMapping("itemType=" +
itemType, clazz);
+ itemDeserializer.registerMapping(itemType, clazz);
+ }
+
+ public void unregisterBuiltInItemTypeClass(String itemType) {
+ propertyTypedObjectDeserializer.unregisterMapping("itemType=" +
itemType);
+ itemDeserializer.unregisterMapping(itemType);
+ }
+
public static ObjectMapper getObjectMapper() {
return Holder.INSTANCE;
}
+ public static CustomObjectMapper getCustomInstance() { return
Holder.INSTANCE; }
+
+ public Class<? extends Item> getBuiltinItemTypeClass(String itemType) {
+ return builtinItemTypeClasses.get(itemType);
+ }
+
private static class Holder {
static final CustomObjectMapper INSTANCE = new CustomObjectMapper();
}
+
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/ItemDeserializer.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/ItemDeserializer.java
index 9080907..32cc135 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/ItemDeserializer.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/ItemDeserializer.java
@@ -42,6 +42,10 @@ public class ItemDeserializer extends StdDeserializer<Item> {
classes.put(type, clazz);
}
+ public void unregisterMapping(String type) {
+ classes.remove(type);
+ }
+
@Override
public Item deserialize(JsonParser jp, DeserializationContext ctxt) throws
IOException {
ObjectCodec codec = jp.getCodec();
@@ -55,6 +59,9 @@ public class ItemDeserializer extends StdDeserializer<Item> {
}
Item item = codec.treeToValue(treeNode, objectClass);
item.setItemId(treeNode.get("itemId").asText());
+ if (item instanceof CustomItem) {
+ ((CustomItem) item).setCustomItemType(type);
+ }
return item;
}
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java
index fff2373..b9b459d 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PersistenceService.java
@@ -17,6 +17,7 @@
package org.apache.unomi.persistence.spi;
+import org.apache.unomi.api.CustomItem;
import org.apache.unomi.api.Item;
import org.apache.unomi.api.PartialList;
import org.apache.unomi.api.PropertyType;
@@ -215,6 +216,15 @@ public interface PersistenceService {
<T extends Item> T load(String itemId, Date dateHint, Class<T> clazz);
/**
+ * Load a custom item type identified by an identifier, an optional date
hint and the identifier of the custom item type
+ * @param itemId the identifier of the custom type we want to retrieve
+ * @param dateHint an optional Date object if the custom item types are
stored by date
+ * @param customItemType an identifier of the custom item type to load
+ * @return the CustomItem instance with the specified identifier and the
custom item type if it exists, {@code null} otherwise
+ */
+ CustomItem loadCustomItem(String itemId, Date dateHint, String
customItemType);
+
+ /**
* Deletes the item identified with the specified identifier and with the
specified Item subclass if it exists.
*
* @param <T> the type of the Item subclass we want to delete
@@ -225,6 +235,14 @@ public interface PersistenceService {
<T extends Item> boolean remove(String itemId, Class<T> clazz);
/**
+ * Remove a custom item identified by the custom item identifier and the
custom item type identifier
+ * @param itemId the identifier of the custom item to be removed
+ * @param customItemType the name of the custom item type
+ * @return {@code true} if the deletion was successful, {@code false}
otherwise
+ */
+ boolean removeCustomItem(String itemId, String customItemType);
+
+ /**
* Deletes items with the specified Item subclass matching the specified
{@link Condition}.
*
* @param <T> the type of the Item subclass we want to delete
@@ -455,6 +473,39 @@ public interface PersistenceService {
<T extends Item> PartialList<T> continueScrollQuery(Class<T> clazz, String
scrollIdentifier, String scrollTimeValidity);
/**
+ * Retrieves a list of items satisfying the specified {@link Condition},
ordered according to the specified
+ * {@code sortBy} String and paged: only {@code size} of them are
retrieved, starting with the
+ * {@code offset}-th one. If a scroll identifier and time validity are
specified, they will be used to perform a
+ * scrolling query, meaning that only partial results will be returned,
but the scrolling can be continued.
+ *
+ * @param query the {@link Condition} the items must satisfy to be
retrieved
+ * @param sortBy an optional ({@code null} if no sorting is required)
String of comma ({@code ,}) separated property names on which ordering should
be performed, ordering
+ * elements according to the property order in the
+ * String, considering each in turn and moving on to the
next one in case of equality of all preceding ones. Each property name is
optionally followed by
+ * a column ({@code :}) and an order specifier: {@code asc}
or {@code desc}.
+ * @param customItemType the identifier of the custom item type we want to
query
+ * @param offset zero or a positive integer specifying the position of the
first item in the total ordered collection of matching items
+ * @param size a positive integer specifying how many matching items
should be retrieved or {@code -1} if all of them should be retrieved. In the
case of a scroll query
+ * this will be used as the scrolling window size.
+ * @param scrollTimeValidity the time the scrolling query should stay
valid. This must contain a time unit value such as the ones supported by
ElasticSearch, such as
+ * the ones declared here :
https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units
+ * @return a {@link PartialList} of items matching the specified criteria,
with an scroll identifier and the scroll validity used if a scroll query was
requested.
+ */
+ PartialList<CustomItem> queryCustomItem(Condition query, String sortBy,
String customItemType, int offset, int size, String scrollTimeValidity);
+
+ /**
+ * Continues the execution of a scroll query, to retrieve the next
results. If there are no more results the scroll query is also cleared.
+ *
+ * @param customItemType the identifier of the custom item type we want to
continue querying
+ * @param scrollIdentifier a scroll identifier obtained by the execution
of a first query and returned in the {@link PartialList} object
+ * @param scrollTimeValidity a scroll time validity value for the scroll
query to stay valid. This must contain a time unit value such as the ones
supported by ElasticSearch, such as
+ * the ones declared here :
https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units
+ * @return a {@link PartialList} of items matching the specified criteria,
with an scroll identifier and the scroll validity used if a scroll query was
requested. Note that if
+ * there are no more results the list will be empty but not null.
+ */
+ PartialList<CustomItem> continueCustomItemScrollQuery(String
customItemType, String scrollIdentifier, String scrollTimeValidity);
+
+ /**
* Retrieves the same items as {@code query(query, sortBy, clazz, 0, -1)}
with the added constraints that the matching elements must also have at least a
field matching the
* specified full text query.
*
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PropertyTypedObjectDeserializer.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PropertyTypedObjectDeserializer.java
index 23a556e..83f676b 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PropertyTypedObjectDeserializer.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/PropertyTypedObjectDeserializer.java
@@ -75,6 +75,17 @@ public class PropertyTypedObjectDeserializer extends
UntypedObjectDeserializer {
fieldValuesToMatch.put(fieldParts[0], valuesToMatch);
}
+ public void unregisterMapping(String matchExpression) {
+ registry.remove(matchExpression);
+ String[] fieldParts = matchExpression.split("=");
+ Set<String> valuesToMatch = fieldValuesToMatch.get(fieldParts[0]);
+ if (valuesToMatch == null) {
+ valuesToMatch = new LinkedHashSet<String>();
+ }
+ valuesToMatch.remove(fieldParts[1]);
+ fieldValuesToMatch.remove(fieldParts[0], valuesToMatch);
+ }
+
@Override
public Object deserialize(
JsonParser jp, DeserializationContext ctxt)
diff --git
a/services/src/main/java/org/apache/unomi/services/actions/impl/ActionExecutorDispatcherImpl.java
b/services/src/main/java/org/apache/unomi/services/actions/impl/ActionExecutorDispatcherImpl.java
index 4270abc..b54fcdf 100644
---
a/services/src/main/java/org/apache/unomi/services/actions/impl/ActionExecutorDispatcherImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/actions/impl/ActionExecutorDispatcherImpl.java
@@ -24,6 +24,7 @@ import org.apache.unomi.api.actions.Action;
import org.apache.unomi.api.actions.ActionDispatcher;
import org.apache.unomi.api.actions.ActionExecutor;
import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.api.utils.ParserHelper;
import org.apache.unomi.metrics.MetricAdapter;
import org.apache.unomi.metrics.MetricsService;
import org.apache.unomi.scripting.ScriptExecutor;
@@ -40,10 +41,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class ActionExecutorDispatcherImpl implements ActionExecutorDispatcher {
private static final Logger logger =
LoggerFactory.getLogger(ActionExecutorDispatcherImpl.class.getName());
- private static final String VALUE_NAME_SEPARATOR = "::";
- private static final String PLACEHOLDER_PREFIX = "${";
- private static final String PLACEHOLDER_SUFFIX = "}";
- private final Map<String, ValueExtractor> valueExtractors = new
HashMap<>(11);
+ private final Map<String, ParserHelper.ValueExtractor> valueExtractors =
new HashMap<>(11);
private Map<String, ActionExecutor> executors = new ConcurrentHashMap<>();
private MetricsService metricsService;
private Map<String, ActionDispatcher> actionDispatchers = new
ConcurrentHashMap<>();
@@ -63,142 +61,27 @@ public class ActionExecutorDispatcherImpl implements
ActionExecutorDispatcher {
}
public ActionExecutorDispatcherImpl() {
- valueExtractors.put("profileProperty", new ValueExtractor() {
- @Override
- public Object extract(String valueAsString, Event event)
- throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- return PropertyUtils.getProperty(event.getProfile(),
"properties." + valueAsString);
- }
- });
- valueExtractors.put("simpleProfileProperty", new ValueExtractor() {
- @Override
- public Object extract(String valueAsString, Event event)
- throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- return event.getProfile().getProperty(valueAsString);
- }
- });
- valueExtractors.put("sessionProperty", new ValueExtractor() {
- @Override
- public Object extract(String valueAsString, Event event)
- throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- return PropertyUtils.getProperty(event.getSession(),
"properties." + valueAsString);
- }
- });
- valueExtractors.put("simpleSessionProperty", new ValueExtractor() {
- @Override
- public Object extract(String valueAsString, Event event)
- throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- return event.getSession().getProperty(valueAsString);
- }
- });
- valueExtractors.put("eventProperty", new ValueExtractor() {
- @Override
- public Object extract(String valueAsString, Event event)
- throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- return PropertyUtils.getProperty(event, valueAsString);
- }
- });
- valueExtractors.put("simpleEventProperty", new ValueExtractor() {
- @Override
- public Object extract(String valueAsString, Event event)
- throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- return event.getProperty(valueAsString);
- }
- });
- valueExtractors.put("script", new ValueExtractor() {
+ valueExtractors.putAll(ParserHelper.DEFAULT_VALUE_EXTRACTORS);
+ valueExtractors.put("script", new ParserHelper.ValueExtractor() {
@Override
public Object extract(String valueAsString, Event event)
throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
return executeScript(valueAsString, event);
}
-
});
}
public Action getContextualAction(Action action, Event event) {
- if (!hasContextualParameter(action.getParameterValues())) {
+ if (!ParserHelper.hasContextualParameter(action.getParameterValues(),
valueExtractors)) {
return action;
}
- Map<String, Object> values = parseMap(event,
action.getParameterValues());
+ Map<String, Object> values = ParserHelper.parseMap(event,
action.getParameterValues(), valueExtractors);
Action n = new Action(action.getActionType());
n.setParameterValues(values);
return n;
}
- @SuppressWarnings("unchecked")
- private Map<String, Object> parseMap(Event event, Map<String, Object> map)
{
- Map<String, Object> values = new HashMap<>();
- for (Map.Entry<String, Object> entry : map.entrySet()) {
- Object value = entry.getValue();
- if (value instanceof String) {
- String s = (String) value;
- try {
- if (s.contains(PLACEHOLDER_PREFIX)) {
- while (s.contains(PLACEHOLDER_PREFIX)) {
- String substring =
s.substring(s.indexOf(PLACEHOLDER_PREFIX) + 2, s.indexOf(PLACEHOLDER_SUFFIX));
- Object v = extractValue(substring, event);
- if (v != null) {
- s = s.replace(PLACEHOLDER_PREFIX + substring +
PLACEHOLDER_SUFFIX, v.toString());
- } else {
- break;
- }
- }
- value = s;
- } else {
- // check if we have special values
- if (s.contains(VALUE_NAME_SEPARATOR)) {
- value = extractValue(s, event);
- }
- }
- } catch (UnsupportedOperationException e) {
- throw e;
- } catch (Exception e) {
- throw new UnsupportedOperationException(e);
- }
- } else if (value instanceof Map) {
- value = parseMap(event, (Map<String, Object>) value);
- }
- values.put(entry.getKey(), value);
- }
- return values;
- }
-
- private Object extractValue(String s, Event event) throws
IllegalAccessException, NoSuchMethodException, InvocationTargetException {
- Object value = null;
-
- String valueType = StringUtils.substringBefore(s,
VALUE_NAME_SEPARATOR);
- String valueAsString = StringUtils.substringAfter(s,
VALUE_NAME_SEPARATOR);
- ValueExtractor extractor = valueExtractors.get(valueType);
- if (extractor != null) {
- value = extractor.extract(valueAsString, event);
- }
-
- return value;
- }
-
- @SuppressWarnings("unchecked")
- private boolean hasContextualParameter(Map<String, Object> values) {
- for (Map.Entry<String, Object> entry : values.entrySet()) {
- Object value = entry.getValue();
- if (value instanceof String) {
- String s = (String) value;
- String str = s.contains(PLACEHOLDER_PREFIX) ?
- s.substring(s.indexOf(PLACEHOLDER_PREFIX) + 2,
s.indexOf(PLACEHOLDER_SUFFIX)) :
- s;
-
- if (str.contains(VALUE_NAME_SEPARATOR) && valueExtractors
- .containsKey(StringUtils.substringBefore(str,
VALUE_NAME_SEPARATOR))) {
- return true;
- }
- } else if (value instanceof Map) {
- if (hasContextualParameter((Map<String, Object>) value)) {
- return true;
- }
- }
- }
- return false;
- }
public int execute(Action action, Event event) {
String actionKey = action.getActionType().getActionExecutor();
@@ -214,7 +97,7 @@ public class ActionExecutorDispatcherImpl implements
ActionExecutorDispatcher {
if (actionDispatcher == null) {
logger.warn("Couldn't find any action dispatcher for prefix
'{}', action {} won't execute !", actionPrefix, actionKey);
}
- actionDispatcher.execute(action, event, actionName);
+ return actionDispatcher.execute(action, event, actionName);
} else if (executors.containsKey(actionKey)) {
ActionExecutor actionExecutor = executors.get(actionKey);
try {
@@ -231,10 +114,6 @@ public class ActionExecutorDispatcherImpl implements
ActionExecutorDispatcher {
return EventService.NO_CHANGE;
}
- private interface ValueExtractor {
- Object extract(String valueAsString, Event event) throws
IllegalAccessException, NoSuchMethodException, InvocationTargetException;
- }
-
public void bindExecutor(ServiceReference<ActionExecutor>
actionExecutorServiceReference) {
ActionExecutor actionExecutor =
bundleContext.getService(actionExecutorServiceReference);
executors.put(actionExecutorServiceReference.getProperty("actionExecutorId").toString(),
actionExecutor);
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 8cb4e8f..52f41e5 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
@@ -27,7 +27,7 @@ import org.apache.unomi.api.services.DefinitionsService;
import org.apache.unomi.api.services.SchedulerService;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.persistence.spi.PersistenceService;
-import org.apache.unomi.services.impl.ParserHelper;
+import org.apache.unomi.api.utils.ParserHelper;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
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 ced1351..00695f8 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
@@ -38,7 +38,7 @@ import org.apache.unomi.api.services.EventTypeRegistry;
import org.apache.unomi.api.services.SourceService;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.persistence.spi.aggregate.TermsAggregate;
-import org.apache.unomi.services.impl.ParserHelper;
+import org.apache.unomi.api.utils.ParserHelper;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/goals/GoalsServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/goals/GoalsServiceImpl.java
index fcf3afc..ad8e512 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/goals/GoalsServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/goals/GoalsServiceImpl.java
@@ -37,7 +37,7 @@ import org.apache.unomi.api.services.RulesService;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.persistence.spi.aggregate.*;
-import org.apache.unomi.services.impl.ParserHelper;
+import org.apache.unomi.api.utils.ParserHelper;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
index b241c44..148a98d 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
@@ -43,7 +43,7 @@ import org.apache.unomi.api.services.SegmentService;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.persistence.spi.PropertyHelper;
-import org.apache.unomi.services.impl.ParserHelper;
+import org.apache.unomi.api.utils.ParserHelper;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/queries/QueryServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/queries/QueryServiceImpl.java
index c9317ee..fa1799c 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/queries/QueryServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/queries/QueryServiceImpl.java
@@ -23,7 +23,7 @@ import org.apache.unomi.api.services.DefinitionsService;
import org.apache.unomi.api.services.QueryService;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.persistence.spi.aggregate.*;
-import org.apache.unomi.services.impl.ParserHelper;
+import org.apache.unomi.api.utils.ParserHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/rules/RulesServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/rules/RulesServiceImpl.java
index 01ae985..dc2f725 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/rules/RulesServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/rules/RulesServiceImpl.java
@@ -30,7 +30,7 @@ import org.apache.unomi.api.services.*;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.services.actions.ActionExecutorDispatcher;
-import org.apache.unomi.services.impl.ParserHelper;
+import org.apache.unomi.api.utils.ParserHelper;
import org.osgi.framework.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/segments/SegmentServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/segments/SegmentServiceImpl.java
index 0122345..f4844ce 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/segments/SegmentServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/segments/SegmentServiceImpl.java
@@ -39,8 +39,7 @@ import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.persistence.spi.aggregate.TermsAggregate;
import org.apache.unomi.services.impl.AbstractServiceImpl;
import org.apache.unomi.services.impl.scheduler.SchedulerServiceImpl;
-import org.apache.unomi.services.impl.scheduler.SchedulerServiceImpl;
-import org.apache.unomi.services.impl.ParserHelper;
+import org.apache.unomi.api.utils.ParserHelper;
import org.apache.unomi.api.exceptions.BadSegmentConditionException;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;