diff --git a/README.md b/README.md index 1618416d..7f6eaf3e 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ developer machine - like any other regular Java project - **recommended**). + [JRuleItems and JRuleItemNames](#jruleitems-and-jruleitemnames) + [JRuleThings](#jrulethings) + [JRuleThingActions](#jrulethingactions) + + [JRuleModuleActions](#jrulemoduleactions) * [Other built-in actions](#other-built-in-actions) - [GUI support](#gui-support) - [Examples](#examples) @@ -274,6 +275,12 @@ Logging from rule can be done in 3 different ways See example #34 +### JRuleModuleActions + +* `org.openhab.automation.jrule.generated.moduleactions` package contains a class with methods for each rule module action available. +Note that these are not specific to any item or thing, but are supplied by core and addon modules such as myopenhab. + + ## Other built-in actions These are method inherited from the JRule superclass. diff --git a/src/main/java/org/openhab/automation/jrule/actions/JRuleModuleActionClassGenerator.java b/src/main/java/org/openhab/automation/jrule/actions/JRuleModuleActionClassGenerator.java new file mode 100644 index 00000000..b697c5ee --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/actions/JRuleModuleActionClassGenerator.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.actions; + +import java.io.File; +import java.io.FileWriter; +import java.util.*; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.openhab.automation.jrule.internal.JRuleConfig; +import org.openhab.automation.jrule.internal.JRuleConstants; +import org.openhab.automation.jrule.internal.JRuleLog; +import org.openhab.automation.jrule.internal.generator.JRuleAbstractClassGenerator; +import org.openhab.core.automation.type.ActionType; +import org.openhab.core.automation.type.ModuleTypeRegistry; +import org.openhab.core.config.core.ConfigDescriptionParameter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import freemarker.template.Template; + +/** + * The {@link JRuleModuleActionClassGenerator} Class Generator for Module actions + * + * @author Arne Seime - Initial contribution + */ +public class JRuleModuleActionClassGenerator extends JRuleAbstractClassGenerator { + + private static final String TEMPLATE_SUFFIX = ".ftlh"; + + protected static final String LOG_NAME_CLASS_GENERATOR = "JRuleModuleActionClassGen"; + + private final Logger logger = LoggerFactory.getLogger(JRuleModuleActionClassGenerator.class); + + private ModuleTypeRegistry moduleTypeRegistry; + + public JRuleModuleActionClassGenerator(JRuleConfig jRuleConfig, ModuleTypeRegistry moduleTypeRegistry) { + super(jRuleConfig); + this.moduleTypeRegistry = moduleTypeRegistry; + } + + public boolean generateActionSources() { + Collection actions = moduleTypeRegistry.getActions(); + return generateActionSource(actions); + } + + public boolean generateActionSource(Collection action) { + try { + + Map freemarkerModel = new HashMap<>(); + freemarkerModel.put("packageName", jRuleConfig.getGeneratedModuleActionPackage()); + + List methods = new ArrayList<>(); + freemarkerModel.put("methods", methods); + + action.forEach(act -> { + Map actionModel = createActionModel(act); + methods.add(actionModel); + }); + + File targetSourceFile = new File( + new StringBuilder().append(jRuleConfig.getModuleActionsDirectory()).append(File.separator) + .append("JRuleModuleActions").append(JRuleConstants.JAVA_FILE_TYPE).toString()); + + try (FileWriter fileWriter = new FileWriter(targetSourceFile)) { + Template template = freemarkerConfiguration.getTemplate("module/ModuleActions" + TEMPLATE_SUFFIX); + template.process(freemarkerModel, fileWriter); + } + + JRuleLog.debug(logger, LOG_NAME_CLASS_GENERATOR, "Wrote Generated class: {}", + targetSourceFile.getAbsolutePath()); + return true; + + } catch (Exception e) { + JRuleLog.error(logger, LOG_NAME_CLASS_GENERATOR, + "Internal error when generating java source for module actions: {}", + ExceptionUtils.getStackTrace(e)); + + } + + return false; + } + + private Map createActionModel(ActionType actionType) { + + Map actionModel = new HashMap<>(); + actionModel.put("name", getActionFriendlyName(actionType.getUID())); + actionModel.put("uid", actionType.getUID()); + actionModel.put("description", actionType.getDescription()); + actionModel.put("label", actionType.getLabel()); + + List> args = new ArrayList<>(); + actionModel.put("args", args); + + actionType.getConfigurationDescriptions().forEach(config -> { + Map configMap = new HashMap<>(); + configMap.put("name", config.getName()); + configMap.put("type", toJavaType(config.getType())); + configMap.put("default", config.getDefault()); + args.add(configMap); + }); + + return actionModel; + } + + private Object toJavaType(ConfigDescriptionParameter.Type type) { + switch (type) { + case BOOLEAN: + return "Boolean"; + case DECIMAL: + return "Double"; + case INTEGER: + return "Integer"; + case TEXT: + return "String"; + + default: + return "Object"; + } + } + + public static String getActionFriendlyName(String moduleUID) { + String[] split = moduleUID.split("[\\.:\\-]"); + return StringUtils.uncapitalize(split[0]) + StringUtils.capitalize(split[1]); + } +} diff --git a/src/main/java/org/openhab/automation/jrule/actions/JRuleActionClassGenerator.java b/src/main/java/org/openhab/automation/jrule/actions/JRuleThingActionClassGenerator.java similarity index 92% rename from src/main/java/org/openhab/automation/jrule/actions/JRuleActionClassGenerator.java rename to src/main/java/org/openhab/automation/jrule/actions/JRuleThingActionClassGenerator.java index 6cc07e97..522a8817 100644 --- a/src/main/java/org/openhab/automation/jrule/actions/JRuleActionClassGenerator.java +++ b/src/main/java/org/openhab/automation/jrule/actions/JRuleThingActionClassGenerator.java @@ -14,15 +14,7 @@ import java.io.File; import java.io.FileWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import java.util.stream.Collectors; import org.apache.commons.lang3.ClassUtils; @@ -43,19 +35,19 @@ import freemarker.template.Template; /** - * The {@link JRuleActionClassGenerator} Class Generator for actions + * The {@link JRuleThingActionClassGenerator} Class Generator for Thing actions * * @author Robert Delbrück - Initial contribution */ -public class JRuleActionClassGenerator extends JRuleAbstractClassGenerator { +public class JRuleThingActionClassGenerator extends JRuleAbstractClassGenerator { private static final String TEMPLATE_SUFFIX = ".ftlh"; protected static final String LOG_NAME_CLASS_GENERATOR = "JRuleActionClassGen"; - private final Logger logger = LoggerFactory.getLogger(JRuleActionClassGenerator.class); + private final Logger logger = LoggerFactory.getLogger(JRuleThingActionClassGenerator.class); - public JRuleActionClassGenerator(JRuleConfig jRuleConfig) { + public JRuleThingActionClassGenerator(JRuleConfig jRuleConfig) { super(jRuleConfig); } @@ -73,7 +65,7 @@ public boolean generateActionSource(Thing thing) { try (FileWriter fileWriter = new FileWriter(targetSourceFile)) { Template template = freemarkerConfiguration - .getTemplate("actions/" + actionModel.get("templateName") + TEMPLATE_SUFFIX); + .getTemplate("things/actions/" + actionModel.get("templateName") + TEMPLATE_SUFFIX); template.process(processingModel, fileWriter); } @@ -104,7 +96,7 @@ public boolean generateActionsSource(Collection things) { .append(File.separator).append("JRuleActions.java").toString()); try (FileWriter fileWriter = new FileWriter(targetSourceFile)) { - Template template = freemarkerConfiguration.getTemplate("actions/Actions" + TEMPLATE_SUFFIX); + Template template = freemarkerConfiguration.getTemplate("things/actions/Actions" + TEMPLATE_SUFFIX); template.process(processingModel, fileWriter); } diff --git a/src/main/java/org/openhab/automation/jrule/internal/JRuleConfig.java b/src/main/java/org/openhab/automation/jrule/internal/JRuleConfig.java index c63e208d..20dac671 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/JRuleConfig.java +++ b/src/main/java/org/openhab/automation/jrule/internal/JRuleConfig.java @@ -45,6 +45,7 @@ public class JRuleConfig { private static final String GENERATED_ITEM_PREFIX_PROPERTY = "org.openhab.automation.jrule.itemprefix"; private static final String GENERATED_ITEM_PACKAGE_PROPERTY = "org.openhab.automation.jrule.itempackage"; private static final String GENERATED_THING_PACKAGE_PROPERTY = "org.openhab.automation.jrule.thingpackage"; + private static final String GENERATED_MODULE_ACTION_PACKAGE_PROPERTY = "org.openhab.automation.jrule.moduleactionpackage"; private static final String EXECUTORS_MIN_PROPERTY = "org.openhab.automation.jrule.engine.executors.min"; private static final String EXECUTORS_MAX_PROPERTY = "org.openhab.automation.jrule.engine.executors.max"; private static final String EXECUTORS_ENABLE_PROPERTY = "org.openhab.automation.jrule.engine.executors.enable"; @@ -65,6 +66,7 @@ public class JRuleConfig { private static final String DEFAULT_GENERATED_ITEM_PACKAGE = "org.openhab.automation.jrule.generated.items"; private static final String DEFAULT_GENERATED_THING_PACKAGE = "org.openhab.automation.jrule.generated.things"; private static final String DEFAULT_GENERATED_ACTION_PACKAGE = "org.openhab.automation.jrule.generated.actions"; + private static final String DEFAULT_GENERATED_MODULE_ACTION_PACKAGE = "org.openhab.automation.jrule.generated.moduleactions"; private static final String CLASS_DIR = "class"; @@ -154,6 +156,14 @@ public String getActionsDirectory() { return sb.toString(); } + public String getModuleActionsDirectory() { + final StringBuilder sb = new StringBuilder(getWorkingDirectory()); + sb.append(File.separator).append(GEN).append(File.separator); + final String p = JRuleUtil.packageNameToPath(getGeneratedModuleActionPackage()); + sb.append(p); + return sb.toString(); + } + public String getJarDirectory() { return new StringBuilder().append(getWorkingDirectory()).append(File.separator).append(JAR_DIR).toString(); } @@ -253,6 +263,11 @@ public String getGeneratedActionPackage() { return getConfigPropertyOrDefaultValue(GENERATED_THING_PACKAGE_PROPERTY, DEFAULT_GENERATED_ACTION_PACKAGE); } + public String getGeneratedModuleActionPackage() { + return getConfigPropertyOrDefaultValue(GENERATED_MODULE_ACTION_PACKAGE_PROPERTY, + DEFAULT_GENERATED_MODULE_ACTION_PACKAGE); + } + public int getRulesInitDelaySeconds() { return getIntConfigProperty(INIT_RULES_DELAY_PROPERTY, DEFAULT_RULES_INIT_DELAY); } diff --git a/src/main/java/org/openhab/automation/jrule/internal/JRuleFactory.java b/src/main/java/org/openhab/automation/jrule/internal/JRuleFactory.java index deccf8d2..69224817 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/JRuleFactory.java +++ b/src/main/java/org/openhab/automation/jrule/internal/JRuleFactory.java @@ -13,6 +13,8 @@ package org.openhab.automation.jrule.internal; import java.util.Map; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -23,6 +25,8 @@ import org.openhab.automation.jrule.internal.module.JRuleRuleProvider; import org.openhab.automation.jrule.items.JRuleItemRegistry; import org.openhab.core.audio.AudioHTTPServer; +import org.openhab.core.automation.handler.ModuleHandlerFactory; +import org.openhab.core.automation.type.ModuleTypeRegistry; import org.openhab.core.events.EventPublisher; import org.openhab.core.items.ItemRegistry; import org.openhab.core.items.MetadataRegistry; @@ -35,10 +39,7 @@ import org.openhab.core.voice.VoiceManager; import org.osgi.framework.FrameworkUtil; import org.osgi.service.component.ComponentContext; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,6 +62,8 @@ public class JRuleFactory { private final JRuleDelayedDebouncingExecutor delayedInit = new JRuleDelayedDebouncingExecutor(5, TimeUnit.SECONDS); + private Set allModuleHandlerFactories = new CopyOnWriteArraySet<>(); + @Activate public JRuleFactory(Map properties, final @Reference JRuleEventSubscriber eventSubscriber, final @Reference ItemRegistry itemRegistry, @@ -71,7 +74,8 @@ public JRuleFactory(Map properties, final @Reference JRuleEventS final @Reference NetworkAddressService networkAddressService, final ComponentContext componentContext, final @Reference CronScheduler cronScheduler, final @Reference MetadataRegistry metadataRegistry, final @Reference JRuleRuleProvider ruleProvider, - @Reference final PersistenceServiceRegistry persistenceServiceRegistry) { + @Reference final PersistenceServiceRegistry persistenceServiceRegistry, + @Reference ModuleTypeRegistry moduleTypeRegistry) { JRuleConfig config = new JRuleConfig(properties); config.initConfig(); jRuleEngine = JRuleEngine.get(); @@ -85,10 +89,20 @@ public JRuleFactory(Map properties, final @Reference JRuleEventS JRuleItemRegistry.setMetadataRegistry(metadataRegistry); jRuleHandler = new JRuleHandler(config, itemRegistry, itemChannelLinkRegistry, thingRegistry, thingManager, eventPublisher, eventSubscriber, voiceManager, audioHTTPServer, networkAddressService, cronScheduler, - componentContext.getBundleContext(), metadataRegistry); + componentContext.getBundleContext(), metadataRegistry, moduleTypeRegistry, allModuleHandlerFactories); delayedInit.call(this::init); } + @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) + protected void addModuleHandlerFactory(ModuleHandlerFactory moduleHandlerFactory) { + logger.debug("ModuleHandlerFactory added {}", moduleHandlerFactory.getClass().getSimpleName()); + allModuleHandlerFactories.add(moduleHandlerFactory); + } + + protected void removeModuleHandlerFactory(ModuleHandlerFactory moduleHandlerFactory) { + allModuleHandlerFactories.remove(moduleHandlerFactory); + } + @Nullable private Boolean init() { JRuleLog.info(logger, LOG_NAME_FACTORY, "Initializing Java Rules Engine v{}", getBundleVersion()); diff --git a/src/main/java/org/openhab/automation/jrule/internal/compiler/JRuleCompiler.java b/src/main/java/org/openhab/automation/jrule/internal/compiler/JRuleCompiler.java index c00e8cae..272326cc 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/compiler/JRuleCompiler.java +++ b/src/main/java/org/openhab/automation/jrule/internal/compiler/JRuleCompiler.java @@ -229,7 +229,8 @@ private boolean compileGeneratedSource(File sourceFolder) { return compile(List.of(new File(jRuleConfig.getItemsDirectory(), "JRuleItems.java"), new File(jRuleConfig.getItemsDirectory(), "JRuleItemNames.java"), new File(jRuleConfig.getThingsDirectory(), "JRuleThings.java"), - new File(jRuleConfig.getActionsDirectory(), "JRuleActions.java")), genClassPath); + new File(jRuleConfig.getActionsDirectory(), "JRuleActions.java"), + new File(jRuleConfig.getModuleActionsDirectory(), "JRuleModuleActions.java")), genClassPath); } public boolean compile(List javaSourceFiles, String classPath) { diff --git a/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleHandler.java b/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleHandler.java index e09ff78b..371c7718 100644 --- a/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleHandler.java +++ b/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleHandler.java @@ -14,32 +14,21 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.automation.jrule.actions.JRuleActionClassGenerator; -import org.openhab.automation.jrule.internal.JRuleConfig; -import org.openhab.automation.jrule.internal.JRuleConstants; -import org.openhab.automation.jrule.internal.JRuleDelayedDebouncingExecutor; -import org.openhab.automation.jrule.internal.JRuleLog; -import org.openhab.automation.jrule.internal.JRuleUtil; +import org.openhab.automation.jrule.actions.JRuleModuleActionClassGenerator; +import org.openhab.automation.jrule.actions.JRuleThingActionClassGenerator; +import org.openhab.automation.jrule.internal.*; import org.openhab.automation.jrule.internal.compiler.JRuleCompiler; import org.openhab.automation.jrule.internal.compiler.JRuleJarExtractor; import org.openhab.automation.jrule.internal.engine.JRuleEngine; @@ -50,7 +39,10 @@ import org.openhab.automation.jrule.items.JRuleItemRegistry; import org.openhab.automation.jrule.things.JRuleThingClassGenerator; import org.openhab.automation.jrule.things.JRuleThingRegistry; +import org.openhab.core.addon.AddonEvent; import org.openhab.core.audio.AudioHTTPServer; +import org.openhab.core.automation.handler.ModuleHandlerFactory; +import org.openhab.core.automation.type.ModuleTypeRegistry; import org.openhab.core.events.Event; import org.openhab.core.events.EventPublisher; import org.openhab.core.items.Item; @@ -103,6 +95,7 @@ public class JRuleHandler implements PropertyChangeListener { @NonNullByDefault({}) private final MetadataRegistry metadataRegistry; + private final JRuleModuleActionClassGenerator moduleActionGenerator; @Nullable private JRuleRulesWatcher directoryWatcher; @@ -111,7 +104,7 @@ public class JRuleHandler implements PropertyChangeListener { private final JRuleItemNameClassGenerator itemNameGenerator; private JRuleThingClassGenerator thingGenerator; - private final JRuleActionClassGenerator actionGenerator; + private final JRuleThingActionClassGenerator thingActionGenerator; private final JRuleCompiler compiler; private final JRuleConfig config; @@ -127,7 +120,8 @@ public JRuleHandler(JRuleConfig config, ItemRegistry itemRegistry, ItemChannelLi ThingRegistry thingRegistry, ThingManager thingManager, EventPublisher eventPublisher, JRuleEventSubscriber eventSubscriber, VoiceManager voiceManager, AudioHTTPServer audioHTTPServer, NetworkAddressService networkAddressService, CronScheduler cronScheduler, BundleContext bundleContext, - MetadataRegistry metadataRegistry) { + MetadataRegistry metadataRegistry, ModuleTypeRegistry moduleTypeRegistry, + Set moduleHandlerFactories) { this.itemRegistry = itemRegistry; this.itemChannelLinkRegistry = itemChannelLinkRegistry; this.thingRegistry = thingRegistry; @@ -141,8 +135,13 @@ public JRuleHandler(JRuleConfig config, ItemRegistry itemRegistry, ItemChannelLi itemGenerator = new JRuleItemClassGenerator(config); itemNameGenerator = new JRuleItemNameClassGenerator(config); thingGenerator = new JRuleThingClassGenerator(config); - actionGenerator = new JRuleActionClassGenerator(config); + thingActionGenerator = new JRuleThingActionClassGenerator(config); compiler = new JRuleCompiler(config); + this.moduleActionGenerator = new JRuleModuleActionClassGenerator(config, moduleTypeRegistry); + + JRuleModuleActionHandler jRuleModuleActionHandler = JRuleModuleActionHandler.get(); + jRuleModuleActionHandler.setModuleHandlerFactories(moduleHandlerFactories); + jRuleModuleActionHandler.setModuleTypeRegistry(moduleTypeRegistry); final JRuleEventHandler jRuleEventHandler = JRuleEventHandler.get(); jRuleEventHandler.setEventPublisher(eventPublisher); @@ -194,6 +193,9 @@ public synchronized final void initialize() { if (!initializeFolder(config.getRulesDirectory())) { return; } + if (!initializeFolder(config.getModuleActionsDirectory())) { + return; + } logInfo("Initializing JRule writing external Jars: {}", config.getJarDirectory()); @@ -207,7 +209,9 @@ public synchronized final void initialize() { things.forEach(thingGenerator::generateThingSource); things.stream().filter(thing -> thing.getHandler() != null).filter( thing -> thing.getHandler().getServices().stream().anyMatch(ThingActions.class::isAssignableFrom)) - .forEach(actionGenerator::generateActionSource); + .forEach(thingActionGenerator::generateActionSource); + + moduleActionGenerator.generateActionSources(); // Compilation of items compileGeneratedSourcesInternal(); @@ -342,8 +346,10 @@ private synchronized Boolean compileGeneratedSourcesInternal() { }).collect(Collectors.toSet()); logDebug("generating actions for: {}", filteredThings.stream().map(thing -> thing.getUID().toString()).collect(Collectors.joining(", "))); - filteredThings.forEach(actionGenerator::generateActionSource); - actionGenerator.generateActionsSource(filteredThings); + filteredThings.forEach(thingActionGenerator::generateActionSource); + thingActionGenerator.generateActionsSource(filteredThings); + moduleActionGenerator.generateActionSources(); + Boolean result = false; if (compiler.compileGeneratedSource()) { @@ -430,7 +436,7 @@ public void propertyChange(@Nullable PropertyChangeEvent evt) { thingGenerator.generateThingSource(thing); if (thing.getHandler() != null && thing.getHandler().getServices().stream() .anyMatch(ThingActions.class::isAssignableFrom)) { - actionGenerator.generateActionSource(thing); + thingActionGenerator.generateActionSource(thing); } delayedItemsCompiler.call(this::compileAndReloadGeneratedSources); } @@ -444,7 +450,7 @@ public void propertyChange(@Nullable PropertyChangeEvent evt) { thingGenerator.generateThingSource(thing); if (thing.getHandler() != null && thing.getHandler().getServices().stream() .anyMatch(ThingActions.class::isAssignableFrom)) { - actionGenerator.generateActionSource(thing); + thingActionGenerator.generateActionSource(thing); } delayedItemsCompiler.call(this::compileAndReloadGeneratedSources); } @@ -452,6 +458,9 @@ public void propertyChange(@Nullable PropertyChangeEvent evt) { logDebug("Thing updated, but no real change"); } + } else if (event.getType().equals(AddonEvent.TYPE)) { + logDebug("AddonEvent: {}", evt); + delayedItemsCompiler.call(this::compileAndReloadGeneratedSources); } else { logDebug("Failed to do something with item event"); } @@ -500,8 +509,8 @@ private synchronized void deleteSourceFileForThing(String thingUID) { private synchronized void deleteSourceFileForAction(String thingUID) { deleteFile(new File(new StringBuilder().append(config.getActionsDirectory()).append(File.separator) .append(config.getGeneratedItemPrefix()) - .append(JRuleActionClassGenerator.getActionFriendlyName(thingUID)).append(JRuleConstants.JAVA_FILE_TYPE) - .toString())); + .append(JRuleThingActionClassGenerator.getActionFriendlyName(thingUID)) + .append(JRuleConstants.JAVA_FILE_TYPE).toString())); } private void logDebug(String message, Object... parameters) { diff --git a/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleModuleActionHandler.java b/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleModuleActionHandler.java new file mode 100644 index 00000000..fbced725 --- /dev/null +++ b/src/main/java/org/openhab/automation/jrule/internal/handler/JRuleModuleActionHandler.java @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.internal.handler; + +import java.util.*; + +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.automation.Action; +import org.openhab.core.automation.handler.ActionHandler; +import org.openhab.core.automation.handler.ModuleHandlerFactory; +import org.openhab.core.automation.type.ActionType; +import org.openhab.core.automation.type.ModuleTypeRegistry; +import org.openhab.core.config.core.Configuration; + +/** + * The {@link JRuleEventHandler} is responsible for handling commands and status + * updates for JRule + * + * @author Arne Seime - Initial contribution + */ +public class JRuleModuleActionHandler { + + private static volatile JRuleModuleActionHandler instance = null; + + private ModuleTypeRegistry moduleTypeRegistry; + + private Set moduleHandlerFactories; + + public void setModuleTypeRegistry(ModuleTypeRegistry moduleTypeRegistry) { + this.moduleTypeRegistry = moduleTypeRegistry; + } + + public void setModuleHandlerFactories(Set moduleHandlerFactories) { + this.moduleHandlerFactories = moduleHandlerFactories; + } + + private JRuleModuleActionHandler() { + } + + public Map execute(String uid, Map inputs) { + + // Find module + ActionType actionType = moduleTypeRegistry.getActions().stream().filter(type -> type.getUID().equals(uid)) + .findFirst().orElseThrow(() -> new IllegalArgumentException("Action not found")); + + Action module = new Action() { + + @Override + public String getId() { + return actionType.getUID(); + } + + @Override + public String getTypeUID() { + return actionType.getUID(); + } + + @Override + public @Nullable String getLabel() { + return actionType.getLabel(); + } + + @Override + public @Nullable String getDescription() { + return actionType.getDescription(); + } + + @Override + public Configuration getConfiguration() { + Configuration configuration = new Configuration(); + inputs.forEach(configuration::put); + return configuration; + } + + @Override + public Map getInputs() { + return Map.of(); + } + }; + + // Find handler for this action + ModuleHandlerFactory handlerFactory = moduleHandlerFactories.stream() + .filter(factory -> factory.getTypes().contains(uid)).findFirst() + .orElseThrow(() -> new IllegalArgumentException("Handler not found")); + + ActionHandler moduleHandler = null; + + try { + moduleHandler = (ActionHandler) handlerFactory.getHandler(module, "jrule"); + // Execute on handler + Map execute = moduleHandler.execute(inputs); + + return execute; + } finally { + // Clean up + if (moduleHandler != null) { + handlerFactory.ungetHandler(module, "jrule", moduleHandler); + } + } + } + + public static JRuleModuleActionHandler get() { + if (instance == null) { + synchronized (JRuleModuleActionHandler.class) { + if (instance == null) { + instance = new JRuleModuleActionHandler(); + } + } + } + return instance; + } +} diff --git a/src/main/resources/templates/actions/Actions.ftlh b/src/main/resources/templates/actions/Actions.ftlh deleted file mode 100644 index 9064cef2..00000000 --- a/src/main/resources/templates/actions/Actions.ftlh +++ /dev/null @@ -1,30 +0,0 @@ -<#include "../CommonLicense.ftlh"> - -package ${packageName}; - -<#list actions as action> -import org.openhab.automation.jrule.generated.actions.${action.class}; - - -/** -* Automatically Generated Class for Actions - DO NOT EDIT! -*
-* Includes all available actions for all things -* -* @author Robert Delbrück - Initial contribution -*/ -public class JRuleActions { - <#list actions as action> - /** - * thingUid: ${action.id} - */ - public static final ${action.class} ${action.name}; - - - static { - <#list actions as action> - ${action.name} = new ${action.class}("${action.scope}", "${action.id}"); - - } -} - diff --git a/src/main/resources/templates/module/ModuleActions.ftlh b/src/main/resources/templates/module/ModuleActions.ftlh new file mode 100644 index 00000000..331561c6 --- /dev/null +++ b/src/main/resources/templates/module/ModuleActions.ftlh @@ -0,0 +1,38 @@ +<#include "../CommonLicense.ftlh"> + +package ${packageName}; + +import org.openhab.automation.jrule.internal.handler.JRuleModuleActionHandler; +import java.util.Objects; +import java.util.Map; +import java.util.HashMap; + + +/** +* Automatically Generated Class for Rule Module Actions - DO NOT EDIT! +*
+* Includes all available actions for all rule modules +* +* @author Arne Seime- Initial contribution +*/ +public class JRuleModuleActions { +<#list methods as action> + + /** + * ${action.label} + * ${action.description} + * @return Map - The result of the action + */ + + public static Map ${action.name} (<#list action.args as arg>${arg.type} ${arg.name}<#sep>, ) { + Map context = new HashMap<>(); + <#list action.args as arg> + context.put("${arg.name}", ${arg.name}); + + + return JRuleModuleActionHandler.get().execute("${action.uid}", context); + } + + +} + diff --git a/src/main/resources/templates/actions/Action.ftlh b/src/main/resources/templates/things/actions/Action.ftlh similarity index 91% rename from src/main/resources/templates/actions/Action.ftlh rename to src/main/resources/templates/things/actions/Action.ftlh index defe24f0..0054891b 100644 --- a/src/main/resources/templates/actions/Action.ftlh +++ b/src/main/resources/templates/things/actions/Action.ftlh @@ -1,4 +1,4 @@ -<#include "../CommonLicense.ftlh"> +<#include "../../CommonLicense.ftlh"> package ${action.package}; diff --git a/src/main/resources/templates/actions/ActionConstructor.ftlh b/src/main/resources/templates/things/actions/ActionConstructor.ftlh similarity index 100% rename from src/main/resources/templates/actions/ActionConstructor.ftlh rename to src/main/resources/templates/things/actions/ActionConstructor.ftlh diff --git a/src/main/resources/templates/actions/ActionJavadoc.ftlh b/src/main/resources/templates/things/actions/ActionJavadoc.ftlh similarity index 100% rename from src/main/resources/templates/actions/ActionJavadoc.ftlh rename to src/main/resources/templates/things/actions/ActionJavadoc.ftlh diff --git a/src/main/resources/templates/actions/ActionMethod.ftlh b/src/main/resources/templates/things/actions/ActionMethod.ftlh similarity index 100% rename from src/main/resources/templates/actions/ActionMethod.ftlh rename to src/main/resources/templates/things/actions/ActionMethod.ftlh diff --git a/src/main/resources/templates/things/actions/Actions.ftlh b/src/main/resources/templates/things/actions/Actions.ftlh new file mode 100644 index 00000000..9a778980 --- /dev/null +++ b/src/main/resources/templates/things/actions/Actions.ftlh @@ -0,0 +1,30 @@ +<#include "../../CommonLicense.ftlh"> + +package ${packageName}; + +<#list actions as action> + import org.openhab.automation.jrule.generated.actions.${action.class}; + + +/** +* Automatically Generated Class for Actions - DO NOT EDIT! +*
+* Includes all available actions for all things +* +* @author Robert Delbrück - Initial contribution +*/ +public class JRuleActions { +<#list actions as action> + /** + * thingUid: ${action.id} + */ + public static final ${action.class} ${action.name}; + + +static { +<#list actions as action> + ${action.name} = new ${action.class}("${action.scope}", "${action.id}"); + +} +} + diff --git a/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleItemClassGeneratorTest.java b/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleItemClassGeneratorTest.java index 9232e36d..a0b209f8 100644 --- a/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleItemClassGeneratorTest.java +++ b/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleItemClassGeneratorTest.java @@ -29,11 +29,7 @@ import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.*; import org.mockito.Mockito; import org.openhab.automation.jrule.internal.JRuleConfig; import org.openhab.automation.jrule.internal.compiler.JRuleCompiler; @@ -41,12 +37,7 @@ import org.openhab.automation.jrule.items.JRuleItemClassGenerator; import org.openhab.automation.jrule.items.JRuleItemRegistry; import org.openhab.automation.jrule.test_utils.JRuleItemTestUtils; -import org.openhab.core.items.Item; -import org.openhab.core.items.ItemNotFoundException; -import org.openhab.core.items.ItemRegistry; -import org.openhab.core.items.Metadata; -import org.openhab.core.items.MetadataKey; -import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.items.*; /** * The {@link JRuleItemClassGeneratorTest} @@ -113,7 +104,7 @@ public void testGenerateItemsFile() assertTrue(compiledClass.exists()); URLClassLoader classLoader = new URLClassLoader(new URL[] { new File("target/gen").toURI().toURL() }, - JRuleActionClassGeneratorTest.class.getClassLoader()); + JRuleThingActionClassGeneratorTest.class.getClassLoader()); final String className = "org.openhab.automation.jrule.generated.items.JRuleItems"; Class aClass = classLoader.loadClass(className); Object jRuleItems = aClass.getConstructor().newInstance(); diff --git a/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleItemNameClassGeneratorTest.java b/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleItemNameClassGeneratorTest.java index 880262e1..00e934fa 100644 --- a/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleItemNameClassGeneratorTest.java +++ b/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleItemNameClassGeneratorTest.java @@ -20,53 +20,19 @@ import java.lang.reflect.Method; import java.net.MalformedURLException; import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Stream; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.*; import org.mockito.Mockito; import org.openhab.automation.jrule.internal.JRuleConfig; import org.openhab.automation.jrule.internal.compiler.JRuleCompiler; import org.openhab.automation.jrule.items.JRuleItemNameClassGenerator; import org.openhab.automation.jrule.items.JRuleItemRegistry; import org.openhab.automation.jrule.test_utils.JRuleItemTestUtils; -import org.openhab.core.items.GenericItem; -import org.openhab.core.items.GroupItem; -import org.openhab.core.items.Item; -import org.openhab.core.items.ItemNotFoundException; -import org.openhab.core.items.Metadata; -import org.openhab.core.items.MetadataKey; -import org.openhab.core.items.MetadataRegistry; -import org.openhab.core.library.items.CallItem; -import org.openhab.core.library.items.ColorItem; -import org.openhab.core.library.items.ContactItem; -import org.openhab.core.library.items.DateTimeItem; -import org.openhab.core.library.items.DimmerItem; -import org.openhab.core.library.items.ImageItem; -import org.openhab.core.library.items.LocationItem; -import org.openhab.core.library.items.NumberItem; -import org.openhab.core.library.items.PlayerItem; -import org.openhab.core.library.items.RollershutterItem; -import org.openhab.core.library.items.StringItem; -import org.openhab.core.library.items.SwitchItem; -import org.openhab.core.library.types.DateTimeType; -import org.openhab.core.library.types.DecimalType; -import org.openhab.core.library.types.HSBType; -import org.openhab.core.library.types.OnOffType; -import org.openhab.core.library.types.OpenClosedType; -import org.openhab.core.library.types.PercentType; -import org.openhab.core.library.types.PlayPauseType; -import org.openhab.core.library.types.PointType; -import org.openhab.core.library.types.RawType; -import org.openhab.core.library.types.StringType; +import org.openhab.core.items.*; +import org.openhab.core.library.items.*; +import org.openhab.core.library.types.*; import org.openhab.core.types.State; /** @@ -177,7 +143,7 @@ public void testGenerateItemsFile() // assertTrue(compiledClass.exists()); // // URLClassLoader classLoader = new URLClassLoader(new URL[] { new File("target/gen").toURI().toURL() }, - // JRuleActionClassGeneratorTest.class.getClassLoader()); + // JRuleThingActionClassGeneratorTest.class.getClassLoader()); // final String className = "org.openhab.automation.jrule.generated.items.JRuleItems"; // Class aClass = classLoader.loadClass(className); // Object jRuleItems = aClass.getConstructor().newInstance(); diff --git a/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleModuleActionClassGeneratorTest.java b/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleModuleActionClassGeneratorTest.java new file mode 100644 index 00000000..d0fb88e6 --- /dev/null +++ b/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleModuleActionClassGeneratorTest.java @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.internal.codegenerator; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.util.*; + +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.openhab.automation.jrule.actions.JRuleModuleActionClassGenerator; +import org.openhab.automation.jrule.internal.JRuleConfig; +import org.openhab.automation.jrule.internal.compiler.JRuleCompiler; +import org.openhab.core.automation.Visibility; +import org.openhab.core.automation.type.ActionType; +import org.openhab.core.automation.type.Input; +import org.openhab.core.automation.type.Output; +import org.openhab.core.config.core.ConfigDescriptionParameter; +import org.openhab.core.config.core.ConfigDescriptionParameterBuilder; + +/** + * The {@link JRuleModuleActionClassGeneratorTest} + * + * + * @author Robert Delbrück - Initial contribution + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class JRuleModuleActionClassGeneratorTest { + + private JRuleModuleActionClassGenerator sourceFileGenerator; + private File targetFolder; + private JRuleCompiler compiler; + + @BeforeAll + public void setup() { + targetFolder = new File("target/gen/org/openhab/automation/jrule/generated/moduleactions/"); + targetFolder.mkdirs(); + + Map map = new HashMap<>(); + map.put("org.openhab.automation.jrule.directory", "target"); + JRuleConfig config = new JRuleConfig(map); + sourceFileGenerator = new JRuleModuleActionClassGenerator(config, null); + compiler = new JRuleCompiler(config); + } + + @BeforeEach + public void wipeFiles() { + // Wipe any existing files + Arrays.stream(targetFolder.listFiles()).forEach(File::delete); + } + + @Test + public void testGenerateAndCompileActionFile() { + + @Nullable + List configDescriptions = new ArrayList<>(); + configDescriptions.add(ConfigDescriptionParameterBuilder.create("param1", ConfigDescriptionParameter.Type.TEXT) + .withDescription("Description").withLabel("Label").build()); + + @Nullable + Set tags = Set.of("TAG1", "TAG2"); + @Nullable + List inputs = new ArrayList<>(); + @Nullable + List outputs = new ArrayList<>(); + ActionType actionType = new ActionType("script.ScriptAction", configDescriptions, "Label", "Description", tags, + Visibility.VISIBLE, inputs, outputs); + + generateAndCompile(actionType); + } + + /* + * @Test + * public void testGenerateActionsFile() throws ClassNotFoundException, MalformedURLException, NoSuchFieldException, + * NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + * Thing thing1 = new ThingImpl(new ThingTypeUID("mybinding", "thingtype"), + * new ThingUID("mybinding", "thingtype", "id1")); + * thing1.setHandler(new MagicActionModuleThingHandler(thing1) { + * + * @Override + * public Collection> getServices() { + * return List.of(MyThingActions.class); + * } + * }); + * generateAndCompile(thing1); + * + * Thing thing2 = new ThingImpl(new ThingTypeUID("mybinding", "thingtype"), + * new ThingUID("mybinding", "thingtype", "id2")); + * thing2.setHandler(new MagicActionModuleThingHandler(thing2) { + * + * @Override + * public Collection> getServices() { + * return List.of(MyThingActions.class); + * } + * }); + * generateAndCompile(thing2); + * + * List things = List.of(thing1, thing2); + * + * boolean success = sourceFileGenerator.generateActionsSource(things); + * assertTrue(success, "Failed to generate source file for things"); + * + * compiler.compile(List.of(new File(targetFolder, "JRuleActions.java")), + * "target/classes" + File.pathSeparator + "target/gen"); + * + * File compiledClass = new File(targetFolder, "JRuleActions.class"); + * assertTrue(compiledClass.exists()); + * + * MockedStatic thingActionServiceMock = Mockito.mockStatic(ThingActionService.class); + * Mockito.when(ThingActionService.getActions(Mockito.any(), Mockito.any())).thenReturn(new MyThingActions()); + * Thing thingMock = Mockito.mock(Thing.class); + * Mockito.when(thingMock.getHandler()).thenReturn(new MagicActionModuleThingHandler(thingMock)); + * Mockito.when(Mockito.mock(ThingRegistry.class).get(Mockito.any())).thenReturn(thingMock); + * + * URLClassLoader classLoader = new URLClassLoader(new URL[] { new File("target/gen").toURI().toURL() }, + * JRuleModuleActionClassGeneratorTest.class.getClassLoader()); + * final String className = "org.openhab.automation.jrule.generated.actions.JRuleActions"; + * // compiler.loadClass(classLoader, className, true); + * Class aClass = classLoader.loadClass(className); + * Object jRuleActions = aClass.getConstructor().newInstance(); + * Field mybindingThingtypeId1 = aClass.getDeclaredField("mybindingThingtypeId1"); + * Object action = mybindingThingtypeId1.get(jRuleActions); + * + * // Verify methods exists + * Method doSomethingAbstract = action.getClass().getDeclaredMethod("doSomethingAbstract", Command.class); + * Object res = doSomethingAbstract.invoke(action, new StringType("blub")); + * assertEquals(10, res); + * + * // Verify method with unsupported types are mapped to Object + * Method returnObjectOnUnsupportedClass = action.getClass().getDeclaredMethod("returnObjectOnUnsupportedClass", + * Object.class); + * assertNotNull(returnObjectOnUnsupportedClass); + * assertEquals(Object.class, returnObjectOnUnsupportedClass.getReturnType()); + * JRule returnObject = (JRule) returnObjectOnUnsupportedClass.invoke(action, new JRule()); + * assertNotNull(returnObject); + * } + */ + private void generateAndCompile(ActionType action) { + boolean success = sourceFileGenerator.generateActionSource(List.of(action)); + assertTrue(success, "Failed to generate source file for " + action); + + compiler.compile(List.of(new File(targetFolder, "JRuleModuleActions.java")), "target/classes"); + + File compiledClass = new File(targetFolder, "JRuleModuleActions.class"); + assertTrue(compiledClass.exists()); + } +} diff --git a/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleActionClassGeneratorTest.java b/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleThingActionClassGeneratorTest.java similarity index 91% rename from src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleActionClassGeneratorTest.java rename to src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleThingActionClassGeneratorTest.java index 94195cb0..0609c379 100644 --- a/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleActionClassGeneratorTest.java +++ b/src/test/java/org/openhab/automation/jrule/internal/codegenerator/JRuleThingActionClassGeneratorTest.java @@ -12,9 +12,7 @@ */ package org.openhab.automation.jrule.internal.codegenerator; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import java.io.File; import java.lang.reflect.Field; @@ -23,11 +21,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -37,7 +31,7 @@ import org.junit.jupiter.api.TestInstance; import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.openhab.automation.jrule.actions.JRuleActionClassGenerator; +import org.openhab.automation.jrule.actions.JRuleThingActionClassGenerator; import org.openhab.automation.jrule.internal.JRuleConfig; import org.openhab.automation.jrule.internal.compiler.JRuleCompiler; import org.openhab.automation.jrule.internal.thingaction.MyThingActions; @@ -54,15 +48,15 @@ import org.openhab.core.types.Command; /** - * The {@link JRuleActionClassGeneratorTest} + * The {@link JRuleThingActionClassGeneratorTest} * * * @author Robert Delbrück - Initial contribution */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class JRuleActionClassGeneratorTest { +public class JRuleThingActionClassGeneratorTest { - private JRuleActionClassGenerator sourceFileGenerator; + private JRuleThingActionClassGenerator sourceFileGenerator; private File targetFolder; private JRuleCompiler compiler; @@ -74,7 +68,7 @@ public void setup() { Map map = new HashMap<>(); map.put("org.openhab.automation.jrule.directory", "target"); JRuleConfig config = new JRuleConfig(map); - sourceFileGenerator = new JRuleActionClassGenerator(config); + sourceFileGenerator = new JRuleThingActionClassGenerator(config); compiler = new JRuleCompiler(config); } @@ -139,7 +133,7 @@ public Collection> getServices() { Mockito.when(Mockito.mock(ThingRegistry.class).get(Mockito.any())).thenReturn(thingMock); URLClassLoader classLoader = new URLClassLoader(new URL[] { new File("target/gen").toURI().toURL() }, - JRuleActionClassGeneratorTest.class.getClassLoader()); + JRuleThingActionClassGeneratorTest.class.getClassLoader()); final String className = "org.openhab.automation.jrule.generated.actions.JRuleActions"; // compiler.loadClass(classLoader, className, true); Class aClass = classLoader.loadClass(className); diff --git a/src/test/java/org/openhab/automation/jrule/rules/integration_test/ITModuleAction.java b/src/test/java/org/openhab/automation/jrule/rules/integration_test/ITModuleAction.java new file mode 100644 index 00000000..f6cc7f3e --- /dev/null +++ b/src/test/java/org/openhab/automation/jrule/rules/integration_test/ITModuleAction.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules.integration_test; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +/** + * Test for module action testcase + * + * @author Arne Seime - Initial contribution + */ +public class ITModuleAction extends JRuleITBase { + + @Test + public void testModuleAction() throws IOException { + sendCommand("ModuleActionCommandItem", "Trigger rule"); + verifyRuleWasExecuted("Call module action"); + verifyCommandEventFor("ModuleActionRecipientItem"); + verifyNoError(); + } +} diff --git a/src/test/java/org/openhab/automation/jrule/rules/user/ModuleActionTestRule.java b/src/test/java/org/openhab/automation/jrule/rules/user/ModuleActionTestRule.java new file mode 100644 index 00000000..13410912 --- /dev/null +++ b/src/test/java/org/openhab/automation/jrule/rules/user/ModuleActionTestRule.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2010-2023 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.automation.jrule.rules.user; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.openhab.automation.jrule.rules.*; +import org.openhab.automation.jrule.rules.event.JRuleEvent; + +/** + * Module actions test + * + * @author Arne Seime - Initial contribution + */ +public class ModuleActionTestRule extends JRule { + + @JRuleName("Call module action") + @JRuleWhenItemReceivedCommand(item = "ModuleActionCommandItem") + public void callModuleAction(JRuleEvent event) + throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + // Generated file so must use reflection to get hold of the generated class + Class moduleActionClass = Class + .forName("org.openhab.automation.jrule.generated.moduleactions.JRuleModuleActions"); + Method method = moduleActionClass.getMethod("coreItemCommandAction", String.class, String.class); + method.invoke(null, "ModuleActionRecipientItem", "CommandToSend"); + } +} diff --git a/src/test/resources/docker/conf/items/default.items b/src/test/resources/docker/conf/items/default.items index 3be1dc89..3260cd31 100644 --- a/src/test/resources/docker/conf/items/default.items +++ b/src/test/resources/docker/conf/items/default.items @@ -87,3 +87,6 @@ Number Number_To_Persist_Future (InfluxDbPersist) Dimmer Dimmer_With_Tags_And_Metadata ["Control", "Light"] { Speech="SetLightState" [ location="Livingroom" ] } + +String ModuleActionCommandItem "Module Action Command" +String ModuleActionRecipientItem "Module Action Recipient"