Skip to content

Commit

Permalink
Merge pull request #177 from seaside1/dynamic-rules
Browse files Browse the repository at this point in the history
Dynamic rules
  • Loading branch information
seaside1 committed Nov 28, 2023
2 parents 4d04520 + ed67bea commit a07c951
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import org.openhab.automation.jrule.internal.handler.JRuleHandler;
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.events.EventPublisher;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.items.MetadataRegistry;
import org.openhab.core.net.NetworkAddressService;
import org.openhab.core.persistence.PersistenceServiceRegistry;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.ThingManager;
Expand Down Expand Up @@ -62,7 +64,8 @@ public class JRuleFactory {
public JRuleFactory(Map<String, Object> properties, final @Reference JRuleEventSubscriber eventSubscriber,
final @Reference ItemRegistry itemRegistry, final @Reference ThingRegistry thingRegistry,
final @Reference ThingManager thingManager, final @Reference EventPublisher eventPublisher,
final @Reference VoiceManager voiceManager, final ComponentContext componentContext,
final @Reference VoiceManager voiceManager, final @Reference AudioHTTPServer audioHTTPServer,
final @Reference NetworkAddressService networkAddressService, final ComponentContext componentContext,
final @Reference CronScheduler cronScheduler, final @Reference MetadataRegistry metadataRegistry,
final @Reference JRuleRuleProvider ruleProvider,
@Reference final PersistenceServiceRegistry persistenceServiceRegistry) {
Expand All @@ -74,10 +77,12 @@ public JRuleFactory(Map<String, Object> properties, final @Reference JRuleEventS
jRuleEngine.setItemRegistry(itemRegistry);
jRuleEngine.setCronScheduler(cronScheduler);
jRuleEngine.setRuleProvider(ruleProvider);

jRuleEngine.initialize();
JRuleItemRegistry.setMetadataRegistry(metadataRegistry);
jRuleHandler = new JRuleHandler(config, itemRegistry, thingRegistry, thingManager, eventPublisher,
eventSubscriber, voiceManager, cronScheduler, componentContext.getBundleContext(), metadataRegistry);
eventSubscriber, voiceManager, audioHTTPServer, networkAddressService, cronScheduler,
componentContext.getBundleContext(), metadataRegistry);
delayedInit.call(this::init);
}

Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/openhab/automation/jrule/internal/JRuleLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ public static void warn(Logger logger, String logPrefix, String message, Object.
logger.warn(PREFIX_WARN_LOG, logPrefix, logMessage.getMessage());
}

public static void error(Logger logger, String logPrefix, Throwable t, String message, Object... parameters) {
final FormattingTuple logMessage = MessageFormatter.arrayFormat(message, parameters);
final FormattingTuple finalLogmessage = MessageFormatter.format(PREFIX_ERROR_LOG,
new String[] { logPrefix, logMessage.getMessage() });
error(logger, t, finalLogmessage.getMessage());
}

private static void error(Logger logger, Throwable t, String message) {
logger.error(message, t);
}

public static void error(Logger logger, String logPrefix, String message, Object... parameters) {
final FormattingTuple logMessage = MessageFormatter.arrayFormat(message, parameters);
logger.error(PREFIX_ERROR_LOG, logPrefix, logMessage.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
*/
public class JRuleCompiler {

private static final String ORG_OPENHAB_CORE = "org.openhab.core-";
private static final String ORG_OPENHAB_CORE_THING = "org.openhab.core.thing-";
private static final String JAVA_CLASS_PATH_PROPERTY = "java.class.path";
private static final String CLASSPATH_OPTION = "-classpath";
public static final String JAR_JRULE_NAME = "jrule.jar";
Expand Down Expand Up @@ -108,12 +110,12 @@ public void loadClassesFromJar(ClassLoader classLoader, File sourceFolder, Strin
loadClass(classLoader, relativePathToFullClassname(jarEntryName), createInstance);
}
} catch (IllegalArgumentException | SecurityException | IOException e) {
logError("Error loading classes from jarfile {} due to {}", jarItem.getAbsolutePath(), e);
logError(e, "Error loading classes from jarfile {}", jarItem.getAbsolutePath());
}
// Best effort
});
} catch (Exception e) {
logError("Error loading classes from jarfile: {}", e);
logError(e, "Error loading classes from jarfile: {}");
}
}

Expand Down Expand Up @@ -149,7 +151,7 @@ public void loadClass(ClassLoader classLoader, String className, boolean createI
final Object obj = loadedClass.getDeclaredConstructor().newInstance();
logDebug("Created instance: {} obj: {}", className, obj);
} catch (Exception x) {
logError("Could not create create instance using default constructor: {}: {}", className, x);
logError(x, "Could not create create instance using default constructor: {}", className);
}
}
}
Expand All @@ -169,7 +171,7 @@ public void loadClassesFromFolder(ClassLoader classLoader, File rootFolder, Stri
.map(this::relativePathToFullClassname).filter(e -> e.startsWith(onlyInRootPackage))
.collect(Collectors.toList());
} catch (IOException e) {
logError("Error loading classes in {} due to {}", rootFolder.getAbsolutePath(), e);
logError(e, "Error loading classes in {} due to {}", rootFolder.getAbsolutePath());
}

// classFiles is now in the form "packageRoot.subPackage.classname", filtered by prefix in onlyInRootPackage
Expand Down Expand Up @@ -202,14 +204,14 @@ private boolean compileGeneratedSource(File sourceFolder) {
.filter(f -> f.getFileName().toString().endsWith(JRuleConstants.JAVA_FILE_TYPE)).map(Path::toFile)
.collect(Collectors.toList());
} catch (IOException e) {
logError("Error listing source files in folder: {}", sourceFolder.getAbsolutePath(), e);
logError(e, "Error listing source files in folder: {}", sourceFolder.getAbsolutePath());
}
try (Stream<Path> paths = Files.walk(Paths.get(sourceFolder.toURI()))) {
javaClassFiles = paths.filter(Files::isRegularFile) // is a file
.filter(f -> f.getFileName().toString().endsWith(JRuleConstants.CLASS_FILE_TYPE)).map(Path::toFile)
.collect(Collectors.toList());
} catch (IOException e) {
logError("Error listing class files in folder: {}", sourceFolder.getAbsolutePath(), e);
logError(e, "Error listing class files in folder: {}", sourceFolder.getAbsolutePath());
}

Map<String, File> classFiles = new HashMap<>();
Expand Down Expand Up @@ -241,9 +243,12 @@ public boolean compile(List<File> javaSourceFiles, String classPath) {
final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
final List<String> optionList = new ArrayList<>();
optionList.add(CLASSPATH_OPTION);
String openhabCoreJar = getOpenhabCoreJar().map(s -> s + File.pathSeparator).orElse("");
final String openhabCoreJar = getOpenhabCoreJar().map(s -> s + File.pathSeparator).orElse("");
logDebug("Openhab-Core Jar: {}", openhabCoreJar);
String cp = openhabCoreJar + System.getProperty(JAVA_CLASS_PATH_PROPERTY) + File.pathSeparator + classPath;
final String openhabCoreThingJar = getOpenhabCoreThingJar().map(s -> s + File.pathSeparator).orElse("");
logDebug("Openhab-Core-Thing Jar: {}", openhabCoreThingJar);
String cp = openhabCoreJar + System.getProperty(JAVA_CLASS_PATH_PROPERTY) + File.pathSeparator
+ openhabCoreThingJar + System.getProperty(JAVA_CLASS_PATH_PROPERTY) + File.pathSeparator + classPath;
optionList.add(cp);
logDebug("Compiling classes using classpath: {}", cp);
javaSourceFiles.stream().filter(javaSourceFile -> javaSourceFile.exists() && javaSourceFile.canRead())
Expand All @@ -266,13 +271,21 @@ public boolean compile(List<File> javaSourceFiles, String classPath) {
}
fileManager.close();
} catch (Exception x) {
logError("Compiler threw error {}", x.toString());
logError(x, "Compiler threw error {}");
}

return false;
}

private Optional<String> getOpenhabCoreJar() {
return getOpenHABJar(ORG_OPENHAB_CORE);
}

private Optional<String> getOpenhabCoreThingJar() {
return getOpenHABJar(ORG_OPENHAB_CORE_THING);
}

private Optional<String> getOpenHABJar(String jarPrefix) {
if (!System.getProperties().containsKey(PROPERTY_KARAF_HOME_URI)
&& !System.getProperties().containsKey(PROPERTY_KARAF_DEFAULT_REPOSITORY)) {
logWarn("required system properties does not exist [{}]",
Expand All @@ -284,12 +297,13 @@ private Optional<String> getOpenhabCoreJar() {
+ System.getProperty(PROPERTY_KARAF_DEFAULT_REPOSITORY);
logDebug("Openhab Jars path: {}", openhabJars);
Optional<String> coreJarPath;

try (Stream<Path> stream = Files.walk(Paths.get(openhabJars))) {
coreJarPath = stream.filter(path -> path.getFileName().toString().endsWith(JRuleConstants.JAR_FILE_TYPE))
.filter(path -> path.getFileName().toString().startsWith("org.openhab.core-"))
.filter(path -> path.getFileName().toString().startsWith(jarPrefix))
.map(path -> path.toFile().getAbsolutePath()).findFirst();
} catch (IOException e) {
logError(e.getMessage());
logError(e, "Failed to get CoreJar");
return Optional.empty();
}
return coreJarPath;
Expand Down Expand Up @@ -323,7 +337,7 @@ public boolean compileRules() {
logWarn("Found no java rules to compile and use in folder {}", jRuleConfig.getRulesDirectory());
}
} catch (IOException e) {
logError("Error listing java files in folder: {}", jRuleConfig.getRulesDirectory(), e);
logError(e, "Error listing java files in folder: {}", jRuleConfig.getRulesDirectory());

}
return false;
Expand All @@ -334,7 +348,7 @@ public List<URL> getExtLibsAsUrls() {
final File[] extLibsFiles = getExtLibsAsFiles();
return Arrays.stream(extLibsFiles).map(this::getUrl).collect(Collectors.toList());
} catch (Exception x) {
logError("Failed to get extLib urls");
logError(x, "Failed to get extLib urls");
return new ArrayList<>();
}
}
Expand All @@ -344,7 +358,7 @@ public List<URL> getJarRulesAsUrls() {
final File[] jarRulesFiles = getJarRulesAsFiles();
return Arrays.stream(jarRulesFiles).map(this::getUrl).collect(Collectors.toList());
} catch (Exception x) {
logError("Failed to get jar-rules urls");
logError(x, "Failed to get jar-rules urls");
return new ArrayList<>();
}
}
Expand All @@ -353,7 +367,7 @@ private URL getUrl(File f) {
try {
return f.toURI().toURL();
} catch (MalformedURLException e) {
logError("Failed to convert to URL: {}", f.getAbsolutePath(), e);
logError(e, "Failed to convert to URL: {}", f.getAbsolutePath());
}
return null;
}
Expand Down Expand Up @@ -413,6 +427,10 @@ private void logError(String message, Object... parameters) {
JRuleLog.error(logger, LOG_NAME_COMPILER, message, parameters);
}

private void logError(Throwable t, String message, Object... parameters) {
JRuleLog.error(logger, LOG_NAME_COMPILER, t, message);
}

private void logWarn(String message, Object... parameters) {
JRuleLog.warn(logger, LOG_NAME_COMPILER, message, parameters);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -97,7 +98,7 @@
*/
public class JRuleEngine implements PropertyChangeListener {
public static final String MDC_KEY_TIMER = "timer";
private static final String[] EMPTY_LOG_TAGS = new String[0];
public static final String[] EMPTY_LOG_TAGS = new String[0];
private static final int AWAIT_TERMINATION_THREAD_SECONDS = 2;
private List<JRuleExecutionContext> contextList = new CopyOnWriteArrayList<>();
private JRuleTimerExecutor timerExecutor = new JRuleTimerExecutor(this);
Expand Down Expand Up @@ -135,6 +136,24 @@ public void add(JRule jRule, boolean enableRule) {
.forEach(method -> this.add(method, jRule, enableRule));
}

public void addDynamicWhenReceivedCommand(Method method, JRule jRule, String ruleName, List<String> items) {
JRuleModuleEntry ruleModuleEntry = new JRuleModuleEntry(jRule, method, ruleName);
List<JRulePreconditionContext> emptyPreconditionContextList = new ArrayList<>(0);
for (String itemName : items) {
JRuleItemReceivedCommandExecutionContext context = new JRuleItemReceivedCommandExecutionContext(jRule,
ruleName, JRuleEngine.EMPTY_LOG_TAGS, method, itemName, JRuleMemberOf.None, Optional.empty(),
emptyPreconditionContextList, Optional.empty(), null, null);

addToContext(context, false);
ruleLoadingStatistics.addItemStateTrigger();
ruleModuleEntry.addJRuleWhenItemReceivedCommand(context);

logInfo("Adding Dynamic Rule Name: {} item: {}", ruleName, itemName);
}
ruleLoadingStatistics.addRuleClass();
ruleProvider.add(ruleModuleEntry);
}

private void add(Method method, JRule jRule, boolean enableRule) {
logDebug("Adding rule method: {}", method.getName());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
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.audio.AudioHTTPServer;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.items.Item;
Expand All @@ -59,6 +60,7 @@
import org.openhab.core.items.events.ItemAddedEvent;
import org.openhab.core.items.events.ItemRemovedEvent;
import org.openhab.core.items.events.ItemUpdatedEvent;
import org.openhab.core.net.NetworkAddressService;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingManager;
Expand Down Expand Up @@ -119,8 +121,8 @@ public class JRuleHandler implements PropertyChangeListener {

public JRuleHandler(JRuleConfig config, ItemRegistry itemRegistry, ThingRegistry thingRegistry,
ThingManager thingManager, EventPublisher eventPublisher, JRuleEventSubscriber eventSubscriber,
VoiceManager voiceManager, CronScheduler cronScheduler, BundleContext bundleContext,
MetadataRegistry metadataRegistry) {
VoiceManager voiceManager, AudioHTTPServer audioHTTPServer, NetworkAddressService networkAddressService,
CronScheduler cronScheduler, BundleContext bundleContext, MetadataRegistry metadataRegistry) {
this.itemRegistry = itemRegistry;
this.thingRegistry = thingRegistry;
this.metadataRegistry = metadataRegistry;
Expand All @@ -136,19 +138,24 @@ public JRuleHandler(JRuleConfig config, ItemRegistry itemRegistry, ThingRegistry
actionGenerator = new JRuleActionClassGenerator(config);
compiler = new JRuleCompiler(config);

JRuleEventHandler jRuleEventHandler = JRuleEventHandler.get();
final JRuleEventHandler jRuleEventHandler = JRuleEventHandler.get();
jRuleEventHandler.setEventPublisher(eventPublisher);
jRuleEventHandler.setItemRegistry(itemRegistry);
eventSubscriber.addPropertyChangeListener(this);
JRuleVoiceHandler jRuleVoiceHandler = JRuleVoiceHandler.get();
final JRuleVoiceHandler jRuleVoiceHandler = JRuleVoiceHandler.get();
jRuleVoiceHandler.setVoiceManager(voiceManager);
JRuleTransformationHandler jRuleTransformationHandler = JRuleTransformationHandler.get();
jRuleVoiceHandler.setAudioHTTPServer(audioHTTPServer);
jRuleVoiceHandler.setNetworkAddressService(networkAddressService);
final JRuleTransformationHandler jRuleTransformationHandler = JRuleTransformationHandler.get();
jRuleTransformationHandler.setBundleContext(bundleContext);

JRuleThingHandler thingHandler = JRuleThingHandler.get();
final JRuleThingHandler thingHandler = JRuleThingHandler.get();
thingHandler.setThingManager(thingManager);
thingHandler.setThingRegistry(thingRegistry);

final JRuleItemHandler itemHandler = JRuleItemHandler.get();
itemHandler.setItemRegistry(itemRegistry);

logDebug("JRuleHandler()");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* 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 org.openhab.core.items.Item;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;

/**
* The {@link JRuleItemHandler} provides access to item Registry
*
* @author Joseph (Seaside) Hagberg - Initial contribution
*/
public class JRuleItemHandler {

private static volatile JRuleItemHandler instance = null;

private JRuleItemHandler() {
}

private ItemRegistry itemRegistry;

public void setItemRegistry(ItemRegistry itemRegistry) {
this.itemRegistry = itemRegistry;
}

public static JRuleItemHandler get() {
if (instance == null) {
synchronized (JRuleItemHandler.class) {
if (instance == null) {
instance = new JRuleItemHandler();
}
}
}
return instance;
}

public Item addToRegistry(Item item) {
return itemRegistry.add(item);
}

public boolean itemRegistryContainsItem(String itemName) {
return itemRegistry.get(itemName) != null;
}

public Item addNumberItem(String name, int value) {
final NumberItem numberItem = new NumberItem(name);
numberItem.setState(new DecimalType(value));
return itemRegistry.add(numberItem);
}

public Item addStringItem(String name, String value) {
final StringItem stringItem = new StringItem(name);
stringItem.setState(new StringType(value));
return itemRegistry.add(stringItem);
}
}
Loading

0 comments on commit a07c951

Please sign in to comment.