This is an automated email from the ASF dual-hosted git repository.
shuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/master by this push:
new 7c4322a UNOMI-508 : refacto to allow to use simple scripts (#342)
7c4322a is described below
commit 7c4322ad8baecce398df0b24db4a9dbdd15e325b
Author: jsinovassin <[email protected]>
AuthorDate: Fri Sep 24 10:04:11 2021 +0200
UNOMI-508 : refacto to allow to use simple scripts (#342)
* UNOMI-508 : refacto to allow to use simple scripts
* update javadoc and integrations tests
* handle feedback
---
extensions/groovy-actions/karaf-kar/pom.xml | 6 +
extensions/groovy-actions/services/pom.xml | 6 +
.../groovy/actions/GroovyActionDispatcher.java | 21 +-
.../unomi/groovy/actions/annotations/Action.java | 3 +
.../actions/listener/GroovyActionListener.java | 14 +-
.../actions/services/GroovyActionsService.java | 15 +-
.../services/impl/GroovyActionsServiceImpl.java | 149 +++++++++---
.../main/resources/META-INF/base/BaseScript.groovy | 23 +-
.../resources/OSGI-INF/blueprint/blueprint.xml | 5 +-
.../test/java/org/apache/unomi/itests/BaseIT.java | 6 +-
.../unomi/itests/GroovyActionsServiceIT.java | 104 +++++++--
...{MyAction.groovy => UpdateAddressAction.groovy} | 18 +-
.../src/test/resources/testRuleGroovyAction.json | 19 ++
.../services/actions/ActionExecutorDispatcher.java | 254 ++-------------------
.../ActionExecutorDispatcherImpl.java} | 39 ++--
.../resources/OSGI-INF/blueprint/blueprint.xml | 10 +-
16 files changed, 326 insertions(+), 366 deletions(-)
diff --git a/extensions/groovy-actions/karaf-kar/pom.xml
b/extensions/groovy-actions/karaf-kar/pom.xml
index b34afa2..f5be462 100644
--- a/extensions/groovy-actions/karaf-kar/pom.xml
+++ b/extensions/groovy-actions/karaf-kar/pom.xml
@@ -40,6 +40,12 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-services</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>${groovy.version}</version>
diff --git a/extensions/groovy-actions/services/pom.xml
b/extensions/groovy-actions/services/pom.xml
index a5d1c8d..ed6bbaa 100644
--- a/extensions/groovy-actions/services/pom.xml
+++ b/extensions/groovy-actions/services/pom.xml
@@ -64,6 +64,12 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.apache.unomi</groupId>
+ <artifactId>unomi-services</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>provided</scope>
diff --git
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/GroovyActionDispatcher.java
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/GroovyActionDispatcher.java
index fadce74..8376813 100644
---
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/GroovyActionDispatcher.java
+++
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/GroovyActionDispatcher.java
@@ -16,14 +16,15 @@
*/
package org.apache.unomi.groovy.actions;
-import groovy.lang.GroovyObject;
+import groovy.lang.GroovyCodeSource;
+import groovy.lang.GroovyShell;
+import groovy.lang.Script;
import org.apache.unomi.api.Event;
import org.apache.unomi.api.actions.Action;
import org.apache.unomi.api.actions.ActionDispatcher;
import org.apache.unomi.groovy.actions.services.GroovyActionsService;
import org.apache.unomi.metrics.MetricAdapter;
import org.apache.unomi.metrics.MetricsService;
-import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,8 +42,6 @@ public class GroovyActionDispatcher implements
ActionDispatcher {
private GroovyActionsService groovyActionsService;
- private BundleContext bundleContext;
-
public void setMetricsService(MetricsService metricsService) {
this.metricsService = metricsService;
}
@@ -51,24 +50,24 @@ public class GroovyActionDispatcher implements
ActionDispatcher {
this.groovyActionsService = groovyActionsService;
}
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
public String getPrefix() {
return GROOVY_PREFIX;
}
public Integer execute(Action action, Event event, String actionName) {
- GroovyObject groovyObject =
groovyActionsService.getGroovyObject(actionName);
- if (groovyObject == null) {
+ GroovyCodeSource groovyCodeSource =
groovyActionsService.getGroovyCodeSource(actionName);
+ if (groovyCodeSource == null) {
logger.warn("Couldn't find a Groovy action with name {}, action
will not execute !", actionName);
} else {
+ GroovyShell groovyShell = groovyActionsService.getGroovyShell();
+ groovyShell.setVariable("action", action);
+ groovyShell.setVariable("event", event);
+ Script script = groovyShell.parse(groovyCodeSource);
try {
return new MetricAdapter<Integer>(metricsService,
this.getClass().getName() + ".action.groovy." + actionName) {
@Override
public Integer execute(Object... args) throws Exception {
- return Integer.valueOf((String)
groovyObject.invokeMethod("execute", new Object[] { action, event }));
+ return (Integer) script.invokeMethod("execute", null);
}
}.runWithTimer();
} catch (Exception e) {
diff --git
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/annotations/Action.java
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/annotations/Action.java
index 58f1ae6..5f19e60 100644
---
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/annotations/Action.java
+++
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/annotations/Action.java
@@ -16,14 +16,17 @@
*/
package org.apache.unomi.groovy.actions.annotations;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
* This annotation is designed to describe the groovy actions which are
created from groovy file, the informations added with this
* annotation will be processed to create an action type entry in elastic
search.
*/
@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
public @interface Action {
/**
diff --git
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/listener/GroovyActionListener.java
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/listener/GroovyActionListener.java
index 0101c1c..2ac3e37 100644
---
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/listener/GroovyActionListener.java
+++
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/listener/GroovyActionListener.java
@@ -117,21 +117,17 @@ public class GroovyActionListener implements
SynchronousBundleListener {
private void addGroovyAction(URL groovyActionURL) {
try {
-
groovyActionsService.save(FilenameUtils.getName(groovyActionURL.getPath()),
IOUtils.toString(groovyActionURL.openStream()));
+
groovyActionsService.save(FilenameUtils.getName(groovyActionURL.getPath()).replace(".groovy",
""),
+ IOUtils.toString(groovyActionURL.openStream()));
} catch (IOException e) {
logger.error("Failed to load the groovy action {}",
groovyActionURL.getPath(), e);
}
}
private void removeGroovyAction(URL groovyActionURL) {
- GroovyScriptEngine engine =
groovyActionsService.getGroovyScriptEngine();
- try {
- Class classScript =
engine.getGroovyClassLoader().parseClass(IOUtils.toString(groovyActionURL.openStream()));
- groovyActionsService.remove(classScript.getName());
- logger.info("The script {} has been removed.", classScript);
- } catch (IOException e) {
- logger.error("Failed to parse groovy action file", e);
- }
+ String actionName =
FilenameUtils.getName(groovyActionURL.getPath()).replace(".groovy", "");
+ groovyActionsService.remove(actionName);
+ logger.info("The script {} has been removed.", actionName);
}
private void loadGroovyActions(BundleContext bundleContext) {
diff --git
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/services/GroovyActionsService.java
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/services/GroovyActionsService.java
index 3a5fcd5..4b6d545 100644
---
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/services/GroovyActionsService.java
+++
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/services/GroovyActionsService.java
@@ -16,7 +16,8 @@
*/
package org.apache.unomi.groovy.actions.services;
-import groovy.lang.GroovyObject;
+import groovy.lang.GroovyCodeSource;
+import groovy.lang.GroovyShell;
import groovy.util.GroovyScriptEngine;
import org.apache.unomi.groovy.actions.GroovyAction;
@@ -41,17 +42,17 @@ public interface GroovyActionsService {
void remove(String id);
/**
- * Get a groovy object by an id
+ * Get a groovy code source object by an id
*
* @param id of the action to get
- * @return Groovy object
+ * @return Groovy code source
*/
- GroovyObject getGroovyObject(String id);
+ GroovyCodeSource getGroovyCodeSource(String id);
/**
- * Get the groovy script engine to allow to execute groovy script
+ * Get an instantiated groovy shell object
*
- * @return GroovyScriptEngine
+ * @return GroovyShell
*/
- GroovyScriptEngine getGroovyScriptEngine();
+ GroovyShell getGroovyShell();
}
diff --git
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/services/impl/GroovyActionsServiceImpl.java
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/services/impl/GroovyActionsServiceImpl.java
index 546fbff..bf4842f 100644
---
a/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/services/impl/GroovyActionsServiceImpl.java
+++
b/extensions/groovy-actions/services/src/main/java/org/apache/unomi/groovy/actions/services/impl/GroovyActionsServiceImpl.java
@@ -16,9 +16,11 @@
*/
package org.apache.unomi.groovy.actions.services.impl;
+import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
-import groovy.lang.GroovyObject;
+import groovy.lang.GroovyShell;
import groovy.util.GroovyScriptEngine;
+import org.apache.commons.io.IOUtils;
import org.apache.unomi.api.Metadata;
import org.apache.unomi.api.actions.ActionType;
import org.apache.unomi.api.services.DefinitionsService;
@@ -28,13 +30,17 @@ import
org.apache.unomi.groovy.actions.GroovyBundleResourceConnector;
import org.apache.unomi.groovy.actions.annotations.Action;
import org.apache.unomi.groovy.actions.services.GroovyActionsService;
import org.apache.unomi.persistence.spi.PersistenceService;
+import org.apache.unomi.services.actions.ActionExecutorDispatcher;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.Arrays;
+import java.io.IOException;
+import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -43,6 +49,8 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static java.util.Arrays.asList;
+
/**
* Implementation of the GroovyActionService. Allows to create a groovy action
from a groovy file
*/
@@ -52,8 +60,14 @@ public class GroovyActionsServiceImpl implements
GroovyActionsService {
private GroovyScriptEngine groovyScriptEngine;
+ private GroovyShell groovyShell;
+
+ private Map<String, GroovyCodeSource> groovyCodeSourceMap;
+
private static final Logger logger =
LoggerFactory.getLogger(GroovyActionsServiceImpl.class.getName());
+ private static final String BASE_SCRIPT_NAME = "BaseScript";
+
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
@@ -67,7 +81,8 @@ public class GroovyActionsServiceImpl implements
GroovyActionsService {
@Reference
private SchedulerService schedulerService;
- private Map<String, GroovyObject> groovyObjects;
+ @Reference
+ private ActionExecutorDispatcher actionExecutorDispatcher;
private Integer groovyActionsRefreshInterval = 1000;
@@ -87,40 +102,104 @@ public class GroovyActionsServiceImpl implements
GroovyActionsService {
this.schedulerService = schedulerService;
}
- public GroovyScriptEngine getGroovyScriptEngine() {
- return groovyScriptEngine;
+ public void setActionExecutorDispatcher(ActionExecutorDispatcher
actionExecutorDispatcher) {
+ this.actionExecutorDispatcher = actionExecutorDispatcher;
+ }
+
+ public GroovyShell getGroovyShell() {
+ return groovyShell;
}
public void postConstruct() {
- groovyObjects = new HashMap<>();
logger.debug("postConstruct {}", bundleContext.getBundle());
+ groovyCodeSourceMap = new HashMap<>();
GroovyBundleResourceConnector bundleResourceConnector = new
GroovyBundleResourceConnector(bundleContext);
- groovyScriptEngine = new GroovyScriptEngine(bundleResourceConnector,
-
bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader());
+ GroovyClassLoader groovyLoader = new
GroovyClassLoader(bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader());
+ groovyScriptEngine = new GroovyScriptEngine(bundleResourceConnector,
groovyLoader);
+ initializeGroovyShell();
+ try {
+ loadBaseScript();
+ } catch (IOException e) {
+ logger.error("Failed to load base script", e);
+ }
initializeTimers();
logger.info("Groovy action service initialized.");
}
+ /**
+ * Load the Base script.
+ * It's a script which provides utility functions that we can use in other
groovy script
+ * The functions added by the base script could be called by the groovy
actions executed in
+ * {@link org.apache.unomi.groovy.actions.GroovyActionDispatcher#execute}
+ * The base script would be added in the configuration of the {@link
GroovyActionsServiceImpl#groovyShell GroovyShell} , so when a
+ * script will be parsed with the GroovyShell (groovyShell.parse(...)),
the action will extends the base script, so the functions
+ * could be called
+ *
+ * @throws IOException
+ */
+ private void loadBaseScript() throws IOException {
+ URL groovyBaseScriptURL =
bundleContext.getBundle().getEntry("META-INF/base/BaseScript.groovy");
+ if (groovyBaseScriptURL == null) {
+ return;
+ }
+ logger.debug("Found Groovy base script at {}, loading... ",
groovyBaseScriptURL.getPath());
+ GroovyCodeSource groovyCodeSource = new
GroovyCodeSource(IOUtils.toString(groovyBaseScriptURL.openStream()),
BASE_SCRIPT_NAME,
+ "/groovy/script");
+ groovyCodeSourceMap.put(BASE_SCRIPT_NAME, groovyCodeSource);
+ groovyScriptEngine.getGroovyClassLoader().parseClass(groovyCodeSource,
true);
+ }
+
+ /**
+ * Initialize the groovyShell object and define the configuration which
contains the name of the base script
+ */
+ private void initializeGroovyShell() {
+ CompilerConfiguration compilerConfiguration = new
CompilerConfiguration();
+
compilerConfiguration.addCompilationCustomizers(createImportCustomizer());
+
+ compilerConfiguration.setScriptBaseClass(BASE_SCRIPT_NAME);
+ groovyScriptEngine.setConfig(compilerConfiguration);
+ groovyShell = new
GroovyShell(groovyScriptEngine.getGroovyClassLoader(), compilerConfiguration);
+ groovyShell.setVariable("actionExecutorDispatcher",
actionExecutorDispatcher);
+ groovyShell.setVariable("definitionsService", definitionsService);
+ groovyShell.setVariable("logger",
LoggerFactory.getLogger("GroovyAction"));
+ }
+
+ private ImportCustomizer createImportCustomizer() {
+ ImportCustomizer importCustomizer = new ImportCustomizer();
+
importCustomizer.addImports("org.apache.unomi.api.services.EventService",
"org.apache.unomi.groovy.actions.annotations.Action",
+ "org.apache.unomi.groovy.actions.annotations.Parameter");
+ return importCustomizer;
+ }
+
@Override
public void save(String actionName, String groovyScript) {
handleFile(actionName, groovyScript);
}
private void handleFile(String actionName, String groovyScript) {
- Class classScript = buildClassScript(groovyScript, actionName);
- saveActionType((Action) classScript.getAnnotation(Action.class));
-
- saveScript(actionName, groovyScript);
- logger.info("The script {} has been loaded.", actionName);
+ GroovyCodeSource groovyCodeSource = buildClassScript(groovyScript,
actionName);
+ try {
+
saveActionType(groovyShell.parse(groovyCodeSource).getClass().getMethod("execute").getAnnotation(Action.class));
+ saveScript(actionName, groovyScript);
+ groovyCodeSourceMap.put(actionName, groovyCodeSource);
+ logger.info("The script {} has been loaded.", actionName);
+ } catch (NoSuchMethodException e) {
+ logger.error("Failed to save the script {}", actionName, e);
+ }
}
+ /**
+ * Build an action type from the annotation {@link Action}
+ *
+ * @param action Annotation containing the values to save
+ */
private void saveActionType(Action action) {
Metadata metadata = new Metadata(null, action.id(),
action.name().equals("") ? action.id() : action.name(), action.description());
metadata.setHidden(action.hidden());
metadata.setReadOnly(true);
- metadata.setSystemTags(new
HashSet<>(Arrays.asList(action.systemTags())));
+ metadata.setSystemTags(new HashSet<>(asList(action.systemTags())));
ActionType actionType = new ActionType(metadata);
actionType.setActionExecutor(action.actionExecutor());
@@ -132,23 +211,30 @@ public class GroovyActionsServiceImpl implements
GroovyActionsService {
@Override
public void remove(String id) {
- removeActionType(id);
+ try {
+ definitionsService.removeActionType(
+
groovyShell.parse(groovyCodeSourceMap.get(id)).getClass().getMethod("execute").getAnnotation(Action.class).id());
+ } catch (NoSuchMethodException e) {
+ logger.error("Failed to delete the action type for the id {}", id,
e);
+ }
persistenceService.remove(id, GroovyAction.class);
+ groovyCodeSourceMap.remove(id);
}
@Override
- public GroovyObject getGroovyObject(String id) {
- return groovyObjects.get(id);
- }
-
- private void removeActionType(String actionId) {
- GroovyObject groovyObject = getGroovyObject(actionId);
-
definitionsService.removeActionType(groovyObject.getClass().getAnnotation(Action.class).id());
+ public GroovyCodeSource getGroovyCodeSource(String id) {
+ return groovyCodeSourceMap.get(id);
}
- private Class buildClassScript(String groovyScript, String actionName) {
- GroovyCodeSource groovyCodeSource = new GroovyCodeSource(groovyScript,
actionName, "/groovy/script");
- return
groovyScriptEngine.getGroovyClassLoader().parseClass(groovyCodeSource);
+ /**
+ * Build a GroovyCodeSource object and add it to the class loader of the
groovyScriptEngine
+ *
+ * @param groovyScript groovy script as a string
+ * @param actionName Name of the action
+ * @return Built GroovyCodeSource
+ */
+ private GroovyCodeSource buildClassScript(String groovyScript, String
actionName) {
+ return new GroovyCodeSource(groovyScript, actionName,
"/groovy/script");
}
private void saveScript(String name, String script) {
@@ -157,14 +243,11 @@ public class GroovyActionsServiceImpl implements
GroovyActionsService {
}
private void refreshGroovyActions() {
-
persistenceService.getAllItems(GroovyAction.class).forEach(groovyAction -> {
- try {
- GroovyObject groovyObject = (GroovyObject)
buildClassScript(groovyAction.getScript(),
groovyAction.getName()).newInstance();
- groovyObjects.put(groovyAction.getName(), groovyObject);
- } catch (InstantiationException | IllegalAccessException e) {
- logger.error("Failed to instantiate groovy action {}",
groovyAction.getName(), e);
- }
- });
+ GroovyCodeSource baseScript =
groovyCodeSourceMap.get(BASE_SCRIPT_NAME);
+ groovyCodeSourceMap = new HashMap<>();
+ groovyCodeSourceMap.put(BASE_SCRIPT_NAME, baseScript);
+
persistenceService.getAllItems(GroovyAction.class).forEach(groovyAction ->
groovyCodeSourceMap
+ .put(groovyAction.getName(),
buildClassScript(groovyAction.getScript(), groovyAction.getName())));
}
private void initializeTimers() {
diff --git a/itests/src/test/resources/groovy/MyAction.groovy
b/extensions/groovy-actions/services/src/main/resources/META-INF/base/BaseScript.groovy
similarity index 51%
copy from itests/src/test/resources/groovy/MyAction.groovy
copy to
extensions/groovy-actions/services/src/main/resources/META-INF/base/BaseScript.groovy
index 15d8f45..09f2925 100644
--- a/itests/src/test/resources/groovy/MyAction.groovy
+++
b/extensions/groovy-actions/services/src/main/resources/META-INF/base/BaseScript.groovy
@@ -16,24 +16,13 @@
*/
-import org.apache.unomi.api.services.EventService
-import org.apache.unomi.groovy.actions.annotations.Action
-import org.apache.unomi.groovy.actions.annotations.Parameter
+import org.apache.unomi.api.Event
+import org.apache.unomi.api.actions.Action
-import java.util.logging.Logger
+abstract class BaseScript extends Script {
-@Action(id = "scriptGroovyAction",
- description = "A test Groovy Action",
- actionExecutor = "groovy:MyAction",
- hidden = false,
- systemTags = ["tag1", "tag2"],
- parameters = [@Parameter(id = "param1", type = "string", multivalued =
false), @Parameter(id = "param2", type = "string", multivalued =
- false)])
-class MyAction {
- Logger logger = Logger.getLogger("")
-
- String execute(action, event) {
- logger.info("Groovy action for event type: " + event.getEventType())
- EventService.NO_CHANGE
+ Integer executeAction(String actionName, Action action, Event event) {
+ action.setActionType(definitionsService.getActionType(actionName))
+ actionExecutorDispatcher.execute(action, event)
}
}
diff --git
a/extensions/groovy-actions/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
b/extensions/groovy-actions/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 48df30c..1f9bd40 100644
---
a/extensions/groovy-actions/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++
b/extensions/groovy-actions/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -26,11 +26,12 @@
<cm:property name="services.groovy.actions.refresh.interval"
value="1000"/>
</cm:default-properties>
</cm:property-placeholder>
-
+
<reference id="metricsService"
interface="org.apache.unomi.metrics.MetricsService"/>
<reference id="definitionsService"
interface="org.apache.unomi.api.services.DefinitionsService"/>
<reference id="persistenceService"
interface="org.apache.unomi.persistence.spi.PersistenceService"/>
<reference id="schedulerService"
interface="org.apache.unomi.api.services.SchedulerService"/>
+ <reference id="actionExecutorDispatcher"
interface="org.apache.unomi.services.actions.ActionExecutorDispatcher"/>
<bean id="groovyActionsServiceImpl"
class="org.apache.unomi.groovy.actions.services.impl.GroovyActionsServiceImpl"
init-method="postConstruct">
@@ -38,6 +39,7 @@
<property name="definitionsService" ref="definitionsService"/>
<property name="persistenceService" ref="persistenceService"/>
<property name="schedulerService" ref="schedulerService"/>
+ <property name="actionExecutorDispatcher"
ref="actionExecutorDispatcher"/>
<property name="groovyActionsRefreshInterval"
value="${services.groovy.actions.refresh.interval}"/>
</bean>
<service id="groovyActionsService" ref="groovyActionsServiceImpl"
@@ -46,7 +48,6 @@
<bean id="groovyActionDispatcherImpl"
class="org.apache.unomi.groovy.actions.GroovyActionDispatcher">
<property name="metricsService" ref="metricsService"/>
<property name="groovyActionsService" ref="groovyActionsServiceImpl"/>
- <property name="bundleContext" ref="blueprintBundleContext"/>
</bean>
<service id="groovyActionDispatcher" ref="groovyActionDispatcherImpl">
<interfaces>
diff --git a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
index 88bfba0..9f8a2d9 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
@@ -158,8 +158,10 @@ public abstract class BaseIT {
"src/test/resources/testCopyPropertiesWithoutSystemTags.json")),
replaceConfigurationFile("data/tmp/testLoginEventCondition.json", new File(
"src/test/resources/testLoginEventCondition.json")),
- replaceConfigurationFile("data/tmp/groovy/MyAction.groovy",
new File(
- "src/test/resources/groovy/MyAction.groovy")),
+ replaceConfigurationFile("data/tmp/testRuleGroovyAction.json",
new File(
+ "src/test/resources/testRuleGroovyAction.json")),
+
replaceConfigurationFile("data/tmp/groovy/UpdateAddressAction.groovy", new File(
+
"src/test/resources/groovy/UpdateAddressAction.groovy")),
keepRuntimeFolder(),
// configureConsole().ignoreLocalConsole(),
logLevel(LogLevel.INFO),
diff --git
a/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
b/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
index 3a798b3..6fb794c 100644
--- a/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/GroovyActionsServiceIT.java
@@ -17,11 +17,18 @@
package org.apache.unomi.itests;
-import groovy.lang.GroovyObject;
+import groovy.lang.GroovyCodeSource;
import org.apache.commons.io.IOUtils;
+import org.apache.unomi.api.Event;
+import org.apache.unomi.api.Profile;
import org.apache.unomi.api.actions.ActionType;
+import org.apache.unomi.api.rules.Rule;
import org.apache.unomi.api.services.DefinitionsService;
+import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.api.services.ProfileService;
+import org.apache.unomi.api.services.RulesService;
import org.apache.unomi.groovy.actions.services.GroovyActionsService;
+import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -36,11 +43,19 @@ import javax.inject.Inject;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Objects;
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerSuite.class)
public class GroovyActionsServiceIT extends BaseIT {
+ public static final String UPDATE_ADDRESS_GROOVY_ACTION =
"updateAddressGroovyAction";
+ public static final String PROFILE_ID = "profile1";
+ public static final String UPDATE_ADDRESS_ACTION_GROOVY_FILE =
"data/tmp/groovy/UpdateAddressAction.groovy";
+ public static final String UPDATE_ADDRESS_ACTION = "UpdateAddressAction";
+
@Inject
@Filter(timeout = 600000)
protected GroovyActionsService groovyActionsService;
@@ -49,13 +64,33 @@ public class GroovyActionsServiceIT extends BaseIT {
@Filter(timeout = 600000)
protected DefinitionsService definitionsService;
+ @Inject
+ @Filter(timeout = 600000)
+ protected RulesService rulesService;
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected ProfileService profileService;
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected EventService eventService;
+
@Before
public void setUp() throws InterruptedException {
+ Profile profile = new Profile();
+ profile.setItemId(PROFILE_ID);
+ profile.setProperties(new HashMap<>());
+ profile.setProperty("lastName", "Jose");
+ profile.setProperty("firstname", "Alexandre");
+ profile.setProperty("address", "Address");
+ profileService.save(profile);
refreshPersistence();
}
@After
public void cleanUp() throws InterruptedException {
+ profileService.delete(PROFILE_ID, false);
refreshPersistence();
}
@@ -63,47 +98,78 @@ public class GroovyActionsServiceIT extends BaseIT {
return IOUtils.toString(new FileInputStream(new File(pathname)));
}
+ private void createRule(String filename) throws IOException,
InterruptedException {
+ Rule rule = CustomObjectMapper.getObjectMapper().readValue(new
File(filename).toURI().toURL(), Rule.class);
+ rulesService.setRule(rule);
+ Thread.sleep(2000);
+ }
+
+ private Event sendGroovyActionEvent() {
+ Profile profile = profileService.load(PROFILE_ID);
+
+ Event event = new Event("updateAddress", null, profile, null, null,
profile, new Date());
+
+ event.setProperty("address", "New address");
+
+ eventService.send(event);
+ return event;
+ }
+
@Test
- public void testGroovyActionsService_saveActionAndTestSavedValues() throws
IOException, InterruptedException {
- groovyActionsService.save("MyAction",
loadGroovyAction("data/tmp/groovy/MyAction.groovy"));
+ public void testGroovyActionsService_triggerGroovyAction() throws
IOException, InterruptedException {
+ createRule("data/tmp/testRuleGroovyAction.json");
+ groovyActionsService.save(UPDATE_ADDRESS_ACTION,
loadGroovyAction(UPDATE_ADDRESS_ACTION_GROOVY_FILE));
- Thread.sleep(2000);
+ keepTrying("Failed waiting for the creation of the GroovyAction for
the trigger action test",
+ () ->
groovyActionsService.getGroovyCodeSource(UPDATE_ADDRESS_ACTION),
Objects::nonNull, 1000, 100);
+
+ ActionType actionType = keepTrying("Failed waiting for the creation of
the GroovyAction for trigger action test",
+ () ->
definitionsService.getActionType(UPDATE_ADDRESS_GROOVY_ACTION),
Objects::nonNull, 1000, 100);
+
+ Assert.assertNotNull(actionType);
+
+ Event event = sendGroovyActionEvent();
- GroovyObject groovyObject =
groovyActionsService.getGroovyObject("MyAction");
+ Assert.assertEquals("New address",
event.getProfile().getProperty("address"));
+ }
+
+ @Test
+ public void testGroovyActionsService_saveActionAndTestSavedValues() throws
IOException, InterruptedException, ClassNotFoundException {
+ groovyActionsService.save(UPDATE_ADDRESS_ACTION,
loadGroovyAction(UPDATE_ADDRESS_ACTION_GROOVY_FILE));
- ActionType actionType =
definitionsService.getActionType("scriptGroovyAction");
+ ActionType actionType = keepTrying("Failed waiting for the creation of
the GroovyAction for the save test",
+ () ->
definitionsService.getActionType(UPDATE_ADDRESS_GROOVY_ACTION),
Objects::nonNull, 1000, 100);
- Assert.assertEquals("MyAction", groovyObject.getClass().getName());
+ Assert.assertEquals(UPDATE_ADDRESS_ACTION,
groovyActionsService.getGroovyCodeSource(UPDATE_ADDRESS_ACTION).getName());
-
Assert.assertTrue(actionType.getMetadata().getId().contains("scriptGroovyAction"));
+
Assert.assertTrue(actionType.getMetadata().getId().contains(UPDATE_ADDRESS_GROOVY_ACTION));
Assert.assertEquals(2,
actionType.getMetadata().getSystemTags().size());
Assert.assertTrue(actionType.getMetadata().getSystemTags().contains("tag1"));
Assert.assertEquals(2, actionType.getParameters().size());
Assert.assertEquals("param1",
actionType.getParameters().get(0).getId());
- Assert.assertEquals("groovy:MyAction", actionType.getActionExecutor());
+ Assert.assertEquals("groovy:UpdateAddressAction",
actionType.getActionExecutor());
Assert.assertFalse(actionType.getMetadata().isHidden());
}
@Test
public void testGroovyActionsService_removeGroovyAction() throws
IOException, InterruptedException {
- groovyActionsService.save("MyAction",
loadGroovyAction("data/tmp/groovy/MyAction.groovy"));
+ groovyActionsService.save(UPDATE_ADDRESS_ACTION,
loadGroovyAction(UPDATE_ADDRESS_ACTION_GROOVY_FILE));
- Thread.sleep(2000);
+ GroovyCodeSource groovyCodeSource = keepTrying("Failed waiting for the
creation of the GroovyAction for the remove test",
+ () ->
groovyActionsService.getGroovyCodeSource(UPDATE_ADDRESS_ACTION),
Objects::nonNull, 1000, 100);
- GroovyObject groovyObject =
groovyActionsService.getGroovyObject("MyAction");
+ Assert.assertNotNull(groovyCodeSource);
- Assert.assertNotNull(groovyObject);
-
- groovyActionsService.remove("MyAction");
+ groovyActionsService.remove(UPDATE_ADDRESS_ACTION);
+ refreshPersistence();
Thread.sleep(2000);
+ groovyCodeSource =
groovyActionsService.getGroovyCodeSource(UPDATE_ADDRESS_ACTION);
- groovyObject = groovyActionsService.getGroovyObject("MyAction");
-
- Assert.assertNull(groovyObject);
+ Assert.assertNull(groovyCodeSource);
- ActionType actionType =
definitionsService.getActionType("scriptGroovyAction");
+ ActionType actionType =
definitionsService.getActionType(UPDATE_ADDRESS_GROOVY_ACTION);
Assert.assertNull(actionType);
diff --git a/itests/src/test/resources/groovy/MyAction.groovy
b/itests/src/test/resources/groovy/UpdateAddressAction.groovy
similarity index 76%
rename from itests/src/test/resources/groovy/MyAction.groovy
rename to itests/src/test/resources/groovy/UpdateAddressAction.groovy
index 15d8f45..9ccb2d7 100644
--- a/itests/src/test/resources/groovy/MyAction.groovy
+++ b/itests/src/test/resources/groovy/UpdateAddressAction.groovy
@@ -17,23 +17,19 @@
import org.apache.unomi.api.services.EventService
-import org.apache.unomi.groovy.actions.annotations.Action
-import org.apache.unomi.groovy.actions.annotations.Parameter
import java.util.logging.Logger
-@Action(id = "scriptGroovyAction",
+@Action(id = "updateAddressGroovyAction",
description = "A test Groovy Action",
- actionExecutor = "groovy:MyAction",
+ actionExecutor = "groovy:UpdateAddressAction",
hidden = false,
systemTags = ["tag1", "tag2"],
parameters = [@Parameter(id = "param1", type = "string", multivalued =
false), @Parameter(id = "param2", type = "string", multivalued =
false)])
-class MyAction {
+def execute() {
Logger logger = Logger.getLogger("")
-
- String execute(action, event) {
- logger.info("Groovy action for event type: " + event.getEventType())
- EventService.NO_CHANGE
- }
-}
+ logger.info("Groovy action to update adress for event type: " +
event.getEventType())
+ event.profile.properties.address = event.getProperty("address")
+ EventService.PROFILE_UPDATED
+}
\ No newline at end of file
diff --git a/itests/src/test/resources/testRuleGroovyAction.json
b/itests/src/test/resources/testRuleGroovyAction.json
new file mode 100644
index 0000000..e7c1b51
--- /dev/null
+++ b/itests/src/test/resources/testRuleGroovyAction.json
@@ -0,0 +1,19 @@
+{
+ "metadata": {
+ "id": "scriptGroovyActionRule",
+ "name": "Call groovy action",
+ "description": "Rule to call a groovy action",
+ "readOnly": true
+ },
+ "condition": {
+ "type": "eventTypeCondition",
+ "parameterValues": {
+ "eventTypeId": "updateAddress"
+ }
+ },
+ "actions": [
+ {
+ "type": "updateAddressGroovyAction"
+ }
+ ]
+}
diff --git
a/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
b/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
index 99aefa7..be6a925 100644
---
a/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
+++
b/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
@@ -17,246 +17,22 @@
package org.apache.unomi.services.actions;
-import org.apache.commons.beanutils.PropertyUtils;
-import org.apache.commons.lang3.StringUtils;
import org.apache.unomi.api.Event;
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.scripting.ScriptExecutor;
-import org.apache.unomi.metrics.MetricAdapter;
-import org.apache.unomi.metrics.MetricsService;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class ActionExecutorDispatcher {
- private static final Logger logger =
LoggerFactory.getLogger(ActionExecutorDispatcher.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 Map<String, ActionExecutor> executors = new ConcurrentHashMap<>();
- private MetricsService metricsService;
- private Map<String, ActionDispatcher> actionDispatchers = new
ConcurrentHashMap<>();
- private BundleContext bundleContext;
- private ScriptExecutor scriptExecutor;
-
- public void setMetricsService(MetricsService metricsService) {
- this.metricsService = metricsService;
- }
-
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- public void setScriptExecutor(ScriptExecutor scriptExecutor) {
- this.scriptExecutor = scriptExecutor;
- }
-
- public ActionExecutorDispatcher() {
- 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() {
- @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())) {
- return action;
- }
-
- Map<String, Object> values = parseMap(event,
action.getParameterValues());
- 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();
- if (actionKey == null) {
- throw new UnsupportedOperationException("No service defined for :
" + action.getActionType());
- }
-
- int colonPos = actionKey.indexOf(":");
- if (colonPos > 0) {
- String actionPrefix = actionKey.substring(0, colonPos);
- String actionName = actionKey.substring(colonPos + 1);
- ActionDispatcher actionDispatcher =
actionDispatchers.get(actionPrefix);
- if (actionDispatcher == null) {
- logger.warn("Couldn't find any action dispatcher for prefix
'{}', action {} won't execute !", actionPrefix, actionKey);
- }
- actionDispatcher.execute(action, event, actionName);
- } else if (executors.containsKey(actionKey)) {
- ActionExecutor actionExecutor = executors.get(actionKey);
- try {
- return new MetricAdapter<Integer>(metricsService,
this.getClass().getName() + ".action." + actionKey) {
- @Override
- public Integer execute(Object... args) throws Exception {
- return
actionExecutor.execute(getContextualAction(action, event), event);
- }
- }.runWithTimer();
- } catch (Exception e) {
- logger.error("Error executing action with key=" + actionKey,
e);
- }
- }
- 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);
- }
-
- public void unbindExecutor(ServiceReference<ActionExecutor>
actionExecutorServiceReference) {
- if (actionExecutorServiceReference == null) {
- return;
- }
-
executors.remove(actionExecutorServiceReference.getProperty("actionExecutorId").toString());
- }
-
- public void bindDispatcher(ServiceReference<ActionDispatcher>
actionDispatcherServiceReference) {
- ActionDispatcher actionDispatcher =
bundleContext.getService(actionDispatcherServiceReference);
- actionDispatchers.put(actionDispatcher.getPrefix(), actionDispatcher);
- }
-
- public void unbindDispatcher(ServiceReference<ActionDispatcher>
actionDispatcherServiceReference) {
- if (actionDispatcherServiceReference == null) {
- return;
- }
- ActionDispatcher actionDispatcher =
bundleContext.getService(actionDispatcherServiceReference);
- if (actionDispatcher != null) {
- actionDispatchers.remove(actionDispatcher.getPrefix());
- }
- }
-
- protected Object executeScript(String script, Event event) {
- Map<String, Object> context = new HashMap<>();
- context.put("event", event);
- context.put("session", event.getSession());
- context.put("profile", event.getProfile());
- return scriptExecutor.execute(script, context);
- }
+/**
+ * This class is the base interface to define the action dispatcher to execute
according to the action type of the action
+ * The action executor dispatcher get the list of the action dispatchers
present in unomi
+ * When the execute method is called, the dispatch will be done according to
the prefix of the action executor of the action type
+ */
+public interface ActionExecutorDispatcher {
+
+ /**
+ * Execute an action dispatcher according to the action type of the action
+ *
+ * @param action action to execute
+ * @param event received event
+ * @return result code of the execution
+ */
+ int execute(Action action, Event event);
}
diff --git
a/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
b/services/src/main/java/org/apache/unomi/services/actions/impl/ActionExecutorDispatcherImpl.java
similarity index 89%
copy from
services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
copy to
services/src/main/java/org/apache/unomi/services/actions/impl/ActionExecutorDispatcherImpl.java
index 99aefa7..4270abc 100644
---
a/services/src/main/java/org/apache/unomi/services/actions/ActionExecutorDispatcher.java
+++
b/services/src/main/java/org/apache/unomi/services/actions/impl/ActionExecutorDispatcherImpl.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.unomi.services.actions;
+package org.apache.unomi.services.actions.impl;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
@@ -24,9 +24,10 @@ 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.scripting.ScriptExecutor;
import org.apache.unomi.metrics.MetricAdapter;
import org.apache.unomi.metrics.MetricsService;
+import org.apache.unomi.scripting.ScriptExecutor;
+import org.apache.unomi.services.actions.ActionExecutorDispatcher;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
@@ -37,8 +38,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-public class ActionExecutorDispatcher {
- private static final Logger logger =
LoggerFactory.getLogger(ActionExecutorDispatcher.class.getName());
+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 = "}";
@@ -61,46 +62,53 @@ public class ActionExecutorDispatcher {
this.scriptExecutor = scriptExecutor;
}
- public ActionExecutorDispatcher() {
+ public ActionExecutorDispatcherImpl() {
valueExtractors.put("profileProperty", new ValueExtractor() {
@Override
- public Object extract(String valueAsString, Event event) throws
IllegalAccessException, NoSuchMethodException, InvocationTargetException {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ public Object extract(String valueAsString, Event event)
+ throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
return event.getProperty(valueAsString);
}
});
valueExtractors.put("script", new ValueExtractor() {
@Override
- public Object extract(String valueAsString, Event event) throws
IllegalAccessException, NoSuchMethodException, InvocationTargetException {
+ public Object extract(String valueAsString, Event event)
+ throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
return executeScript(valueAsString, event);
}
@@ -175,9 +183,12 @@ public class ActionExecutorDispatcher {
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;
+ 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))) {
+ if (str.contains(VALUE_NAME_SEPARATOR) && valueExtractors
+ .containsKey(StringUtils.substringBefore(str,
VALUE_NAME_SEPARATOR))) {
return true;
}
} else if (value instanceof Map) {
diff --git a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 55c41cb..771c740 100644
--- a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -150,12 +150,18 @@
</service>
<bean id="actionExecutorDispatcherImpl"
- class="org.apache.unomi.services.actions.ActionExecutorDispatcher">
- <property name="metricsService" ref="metricsService"/>
+
class="org.apache.unomi.services.actions.impl.ActionExecutorDispatcherImpl">
+ <property name="metricsService" ref="metricsService" />
<property name="scriptExecutor" ref="scriptExecutor"/>
<property name="bundleContext" ref="blueprintBundleContext"/>
</bean>
+ <service id="actionExecutorDispatcher" ref="actionExecutorDispatcherImpl">
+ <interfaces>
+
<value>org.apache.unomi.services.actions.ActionExecutorDispatcher</value>
+ </interfaces>
+ </service>
+
<bean id="rulesServiceImpl"
class="org.apache.unomi.services.impl.rules.RulesServiceImpl"
init-method="postConstruct" destroy-method="preDestroy">
<property name="persistenceService" ref="persistenceService"/>