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

shuber pushed a commit to branch unomi-1.6.x
in repository https://gitbox.apache.org/repos/asf/unomi.git


The following commit(s) were added to refs/heads/unomi-1.6.x by this push:
     new b39e1e2  UNOMI-508 : refacto to allow to use simple scripts (#342)
b39e1e2 is described below

commit b39e1e25aeeea336d8ed30eaa96ac8f67a46d1e0
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
    
    (cherry picked from commit 7c4322ad8baecce398df0b24db4a9dbdd15e325b)
---
 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     |   8 +-
 16 files changed, 325 insertions(+), 365 deletions(-)

diff --git a/extensions/groovy-actions/karaf-kar/pom.xml 
b/extensions/groovy-actions/karaf-kar/pom.xml
index a4dd4c7..a04d936 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 a257b27..3603215 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 fad99b9..95b93eb 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
@@ -152,8 +152,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 1a888d9..b55490c 100644
--- a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -141,12 +141,18 @@
     </service>
 
     <bean id="actionExecutorDispatcherImpl"
-          class="org.apache.unomi.services.actions.ActionExecutorDispatcher">
+        
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"/>

Reply via email to