mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[jrubyscripting] JRuby Scripting initial contribution (#11538)
Also-by: Jimmy Tanagra <jimmy@tanagra.id.au> Signed-off-by: Brian O'Connell <broconne@gmail.com>
This commit is contained in:
parent
4d8c6b304b
commit
633582522c
@ -6,6 +6,7 @@
|
||||
|
||||
# Add-on maintainers:
|
||||
/bundles/org.openhab.automation.groovyscripting/ @wborn
|
||||
/bundles/org.openhab.automation.jrubyscripting/ @boc-tothefuture
|
||||
/bundles/org.openhab.automation.jsscripting/ @jpg0
|
||||
/bundles/org.openhab.automation.jythonscripting/ @openhab/add-ons-maintainers
|
||||
/bundles/org.openhab.automation.pidcontroller/ @fwolter
|
||||
|
@ -21,6 +21,11 @@
|
||||
<artifactId>org.openhab.automation.groovyscripting</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.automation.jrubyscripting</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.automation.jsscripting</artifactId>
|
||||
|
13
bundles/org.openhab.automation.jrubyscripting/NOTICE
Normal file
13
bundles/org.openhab.automation.jrubyscripting/NOTICE
Normal file
@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
72
bundles/org.openhab.automation.jrubyscripting/README.md
Normal file
72
bundles/org.openhab.automation.jrubyscripting/README.md
Normal file
@ -0,0 +1,72 @@
|
||||
# JRuby Scripting
|
||||
|
||||
This add-on provides [JRuby](https://www.jruby.org/) 9.3.1 that can be used as a scripting language within automation rules.
|
||||
|
||||
## JRuby Scripting Configuration
|
||||
|
||||
After installing this add-on, you will find configuration options in the openHAB portal under _Settings -> Other Services -> JRuby Scripting_.
|
||||
|
||||
Alternatively, JRuby configuration parameters may be set by creating a `jruby.cfg` file in `conf/services/`
|
||||
|
||||
| Parameter | Default | Description |
|
||||
| ----------------------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| org.openhab.automation.jrubyscripting:gem_home | $OPENHAB_CONF/scripts/lib/ruby/gem_home | Location ruby gems will be installed and loaded, directory will be created if missing and gem installs are specified |
|
||||
| org.openhab.automation.jrubyscripting:rubylib | $OPENHAB_CONF/automation/lib/ruby/ | Search path for user libraries. Separate each path with a colon (semicolon in Windows). |
|
||||
| org.openhab.automation.jrubyscripting:local_context | singlethread | The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See [this](https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type) for options and details |
|
||||
| org.openhab.automation.jrubyscripting:local_variables | transient | Defines how variables are shared between Ruby and Java. See [this](https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options) for options and details |
|
||||
| org.openhab.automation.jrubyscripting:gems | | Comma separated list of [Ruby Gems](https://rubygems.org/) to install. |
|
||||
|
||||
## Ruby Gems
|
||||
|
||||
This automation add-on will install user specified gems and make them available on the library search path.
|
||||
Gem versions may be specified using the standard ruby gem_name=version format.
|
||||
The version number follows the [pessimistic version constraint](https://guides.rubygems.org/patterns/#pessimistic-version-constraint) syntax.
|
||||
|
||||
For example this configuration will install version 4 or higher of the [openHAB JRuby Scripting Library](https://boc-tothefuture.github.io/openhab-jruby/).
|
||||
|
||||
```text
|
||||
org.openhab.automation.jrubyscripting:gems=openhab-scripting=~>4.0
|
||||
```
|
||||
|
||||
## Creating JRuby Scripts
|
||||
|
||||
When this add-on is installed, you can select JRuby as a scripting language when creating a script action within the rule editor of the UI.
|
||||
|
||||
Alternatively, you can create scripts in the `automation/jsr223/ruby/personal` configuration directory.
|
||||
If you create an empty file called `test.rb`, you will see a log line with information similar to:
|
||||
|
||||
```text
|
||||
... [INFO ] [.a.m.s.r.i.l.ScriptFileWatcher:150 ] - Loading script 'test.rb'
|
||||
```
|
||||
|
||||
To enable debug logging, use the [console logging]({{base}}/administration/logging.html) commands to
|
||||
enable debug logging for the automation functionality:
|
||||
|
||||
```text
|
||||
log:set DEBUG org.openhab.core.automation
|
||||
log:set DEBUG org.openhab.automation.jrubyscripting
|
||||
```
|
||||
|
||||
## Imports
|
||||
|
||||
All [ScriptExtensions]({{base}}/configuration/jsr223.html#scriptextension-objects-all-jsr223-languages) are available in JRuby with the following exceptions/modifications:
|
||||
|
||||
- The File variable, referencing java.io.File is not available as it conflicts with Ruby's File class preventing Ruby from initializing
|
||||
- Globals scriptExtension, automationManager, ruleRegistry, items, voice, rules, things, events, itemRegistry, ir, actions, se, audio, lifecycleTracker are prepended with a $ (e.g. $automationManager) making them available as a global objects in Ruby.
|
||||
|
||||
## Script Examples
|
||||
|
||||
JRuby scripts provide access to almost all the functionality in an openHAB runtime environment.
|
||||
As a simple example, the following script logs "Hello, World!".
|
||||
Note that `puts` will usually not work since the output has no terminal to display the text.
|
||||
The openHAB server uses the [SLF4J](https://www.slf4j.org/) library for logging.
|
||||
|
||||
```ruby
|
||||
require 'java'
|
||||
java_import org.slf4j.LoggerFactory
|
||||
|
||||
LoggerFactory.getLogger("org.openhab.core.automation.examples").info("Hello world!")
|
||||
```
|
||||
|
||||
JRuby can [import Java classes](https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby).
|
||||
Depending on the openHAB logging configuration, you may need to prefix logger names with `org.openhab.core.automation` for them to show up in the log file (or you modify the logging configuration).
|
31
bundles/org.openhab.automation.jrubyscripting/pom.xml
Normal file
31
bundles/org.openhab.automation.jrubyscripting/pom.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.automation.jrubyscripting</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Automation :: JRuby Scripting</name>
|
||||
|
||||
<properties>
|
||||
<bnd.importpackage>com.sun.nio.*;resolution:=optional,com.sun.security.*;resolution:=optional,org.apache.tools.ant.*;resolution:=optional,org.bouncycastle.*;resolution:=optional,org.joda.*;resolution:=optional,sun.management.*;resolution:=optional,sun.nio.*;resolution:=optional</bnd.importpackage>
|
||||
<jruby.version>9.3.1.0</jruby.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jruby</groupId>
|
||||
<artifactId>jruby-complete</artifactId>
|
||||
<version>${jruby.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.automation.jrubyscripting-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-automation-jrubyscripting" description="JRuby Scripting" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.automation.jrubyscripting/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,291 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.jrubyscripting.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.OpenHAB;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Processes JRuby Configuration Parameters.
|
||||
*
|
||||
* @author Brian O'Connell - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class JRubyScriptEngineConfiguration {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(JRubyScriptEngineConfiguration.class);
|
||||
|
||||
private static final Path DEFAULT_GEM_HOME = Paths.get(OpenHAB.getConfigFolder(), "scripts", "lib", "ruby",
|
||||
"gem_home");
|
||||
|
||||
private static final Path DEFAULT_RUBYLIB = Paths.get(OpenHAB.getConfigFolder(), "automation", "lib", "ruby");
|
||||
|
||||
private static final String GEM_HOME = "gem_home";
|
||||
|
||||
// Map of configuration parameters
|
||||
private static final Map<String, OptionalConfigurationElement> CONFIGURATION_PARAMETERS = Map.ofEntries(
|
||||
Map.entry("local_context",
|
||||
new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.SYSTEM_PROPERTY)
|
||||
.mappedTo("org.jruby.embed.localcontext.scope").defaultValue("singlethread").build()),
|
||||
|
||||
Map.entry("local_variable",
|
||||
new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.SYSTEM_PROPERTY)
|
||||
.mappedTo("org.jruby.embed.localvariable.behavior").defaultValue("transient").build()),
|
||||
|
||||
Map.entry(GEM_HOME,
|
||||
new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT)
|
||||
.mappedTo("GEM_HOME").defaultValue(DEFAULT_GEM_HOME.toString()).build()),
|
||||
|
||||
Map.entry("rubylib",
|
||||
new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT)
|
||||
.mappedTo("RUBYLIB").defaultValue(DEFAULT_RUBYLIB.toString()).build()),
|
||||
|
||||
Map.entry("gems", new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.GEM).build()));
|
||||
|
||||
private static final Map<OptionalConfigurationElement.Type, List<OptionalConfigurationElement>> CONFIGURATION_TYPE_MAP = CONFIGURATION_PARAMETERS
|
||||
.values().stream().collect(Collectors.groupingBy(v -> v.type));
|
||||
|
||||
/**
|
||||
* Update configuration
|
||||
*
|
||||
* @param config Configuration parameters to apply to ScriptEngine
|
||||
* @param factory ScriptEngineFactory to configure
|
||||
*/
|
||||
void update(Map<String, Object> config, ScriptEngineFactory factory) {
|
||||
logger.trace("JRuby Script Engine Configuration: {}", config);
|
||||
config.forEach(this::processConfigValue);
|
||||
configureScriptEngine(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply configuration key/value to known configuration parameters
|
||||
*
|
||||
* @param key Configuration key
|
||||
* @param value Configuration value
|
||||
*/
|
||||
private void processConfigValue(String key, Object value) {
|
||||
OptionalConfigurationElement configurationElement = CONFIGURATION_PARAMETERS.get(key);
|
||||
if (configurationElement != null) {
|
||||
configurationElement.setValue(value.toString());
|
||||
} else {
|
||||
logger.debug("Ignoring unexpected configuration key: {}", key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the ScriptEngine
|
||||
*
|
||||
* @param factory Script Engine to configure
|
||||
*/
|
||||
void configureScriptEngine(ScriptEngineFactory factory) {
|
||||
configureSystemProperties(CONFIGURATION_TYPE_MAP.getOrDefault(OptionalConfigurationElement.Type.SYSTEM_PROPERTY,
|
||||
Collections.<OptionalConfigurationElement> emptyList()));
|
||||
|
||||
ScriptEngine engine = factory.getScriptEngine();
|
||||
|
||||
configureRubyEnvironment(CONFIGURATION_TYPE_MAP.getOrDefault(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT,
|
||||
Collections.<OptionalConfigurationElement> emptyList()), engine);
|
||||
|
||||
configureGems(CONFIGURATION_TYPE_MAP.getOrDefault(OptionalConfigurationElement.Type.GEM,
|
||||
Collections.<OptionalConfigurationElement> emptyList()), engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes Gem home directory if it does not exist
|
||||
*/
|
||||
private void ensureGemHomeExists() {
|
||||
OptionalConfigurationElement gemHomeConfigElement = CONFIGURATION_PARAMETERS.get(GEM_HOME);
|
||||
if (gemHomeConfigElement != null) {
|
||||
Optional<String> gemHome = gemHomeConfigElement.getValue();
|
||||
if (gemHome.isPresent()) {
|
||||
File gemHomeDirectory = new File(gemHome.get());
|
||||
if (!gemHomeDirectory.exists()) {
|
||||
logger.debug("gem_home directory does not exist, creating");
|
||||
if (!gemHomeDirectory.mkdirs()) {
|
||||
logger.debug("Error creating gem_home direcotry");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Gem install requested without gem_home specified, not ensuring gem_home path exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a gems in ScriptEngine
|
||||
*
|
||||
* @param gemsDirectives List of gems to install
|
||||
* @param engine Engine to install gems
|
||||
*/
|
||||
private synchronized void configureGems(List<OptionalConfigurationElement> gemDirectives, ScriptEngine engine) {
|
||||
for (OptionalConfigurationElement gemDirective : gemDirectives) {
|
||||
if (gemDirective.getValue().isPresent()) {
|
||||
ensureGemHomeExists();
|
||||
|
||||
String[] gems = gemDirective.getValue().get().split(",");
|
||||
for (String gem : gems) {
|
||||
gem = gem.trim();
|
||||
String gemCommand;
|
||||
if (gem.contains("=")) {
|
||||
String[] gemParts = gem.split("=");
|
||||
gem = gemParts[0];
|
||||
String version = gemParts[1];
|
||||
gemCommand = "Gem.install('" + gem + "',version='" + version + "')\n";
|
||||
} else {
|
||||
gemCommand = "Gem.install('" + gem + "')\n";
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug("Installing Gem: {} ", gem);
|
||||
logger.trace("Gem install code:\n{}\n", gemCommand);
|
||||
engine.eval(gemCommand);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error installing Gem", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Ruby gem property has no value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the base Ruby Environment
|
||||
*
|
||||
* @param engine Engine to configure
|
||||
*/
|
||||
public ScriptEngine configureRubyEnvironment(ScriptEngine engine) {
|
||||
configureRubyEnvironment(CONFIGURATION_TYPE_MAP.getOrDefault(OptionalConfigurationElement.Type.RUBY_ENVIRONMENT,
|
||||
Collections.<OptionalConfigurationElement> emptyList()), engine);
|
||||
return engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the optional elements of the Ruby Environment
|
||||
*
|
||||
* @param optionalConfigurationElements Optional elements to configure in the ruby environment
|
||||
* @param engine Engine in which to configure environment
|
||||
*/
|
||||
private void configureRubyEnvironment(List<OptionalConfigurationElement> optionalConfigurationElements,
|
||||
ScriptEngine engine) {
|
||||
for (OptionalConfigurationElement configElement : optionalConfigurationElements) {
|
||||
String environmentProperty = configElement.mappedTo().get();
|
||||
if (configElement.getValue().isPresent()) {
|
||||
String environmentSetting = "ENV['" + environmentProperty + "']='" + configElement.getValue().get()
|
||||
+ "'";
|
||||
try {
|
||||
logger.trace("Setting Ruby environment with code: {} ", environmentSetting);
|
||||
engine.eval(environmentSetting);
|
||||
} catch (ScriptException e) {
|
||||
logger.error("Error setting ruby environment", e);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Ruby environment property ({}) has no value", environmentProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure system properties
|
||||
*
|
||||
* @param optionalConfigurationElements Optional system properties to configure
|
||||
*/
|
||||
private void configureSystemProperties(List<OptionalConfigurationElement> optionalConfigurationElements) {
|
||||
for (OptionalConfigurationElement configElement : optionalConfigurationElements) {
|
||||
String systemProperty = configElement.mappedTo().get();
|
||||
if (configElement.getValue().isPresent()) {
|
||||
String propertyValue = configElement.getValue().get();
|
||||
logger.trace("Setting system property ({}) to ({})", systemProperty, propertyValue);
|
||||
System.setProperty(systemProperty, propertyValue);
|
||||
} else {
|
||||
logger.warn("System property ({}) has no value", systemProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner static companion class for configuration elements
|
||||
*/
|
||||
private static class OptionalConfigurationElement {
|
||||
|
||||
private final Optional<String> defaultValue;
|
||||
private final Optional<String> mappedTo;
|
||||
private final Type type;
|
||||
private Optional<String> value;
|
||||
|
||||
private OptionalConfigurationElement(Type type, @Nullable String mappedTo, @Nullable String defaultValue) {
|
||||
this.type = type;
|
||||
this.defaultValue = Optional.ofNullable(defaultValue);
|
||||
this.mappedTo = Optional.ofNullable(mappedTo);
|
||||
value = Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<String> getValue() {
|
||||
return value.or(() -> defaultValue);
|
||||
}
|
||||
|
||||
private void setValue(String value) {
|
||||
this.value = Optional.of(value);
|
||||
}
|
||||
|
||||
private Optional<String> mappedTo() {
|
||||
return mappedTo;
|
||||
}
|
||||
|
||||
private enum Type {
|
||||
SYSTEM_PROPERTY,
|
||||
RUBY_ENVIRONMENT,
|
||||
GEM
|
||||
}
|
||||
|
||||
private static class Builder {
|
||||
private final Type type;
|
||||
private @Nullable String defaultValue = null;
|
||||
private @Nullable String mappedTo = null;
|
||||
|
||||
private Builder(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private Builder mappedTo(String mappedTo) {
|
||||
this.mappedTo = mappedTo;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Builder defaultValue(String value) {
|
||||
this.defaultValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
private OptionalConfigurationElement build() {
|
||||
return new OptionalConfigurationElement(type, mappedTo, defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.jrubyscripting.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.automation.module.script.AbstractScriptEngineFactory;
|
||||
import org.openhab.core.automation.module.script.ScriptEngineFactory;
|
||||
import org.openhab.core.config.core.ConfigurableService;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
|
||||
/**
|
||||
* This is an implementation of a {@link ScriptEngineFactory} for Ruby.
|
||||
*
|
||||
* @author Brian O'Connell - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ScriptEngineFactory.class, configurationPid = "org.openhab.automation.jrubyscripting")
|
||||
@ConfigurableService(category = "automation", label = "JRuby Scripting", description_uri = "automation:jruby")
|
||||
public class JRubyScriptEngineFactory extends AbstractScriptEngineFactory {
|
||||
|
||||
private final JRubyScriptEngineConfiguration configuration = new JRubyScriptEngineConfiguration();
|
||||
|
||||
// Filter out the File entry to prevent shadowing the Ruby File class which breaks Ruby in spectacularly
|
||||
// difficult ways to debug.
|
||||
private static final Set<String> FILTERED_PRESETS = Set.of("File");
|
||||
private static final Set<String> INSTANCE_PRESETS = Set.of();
|
||||
private static final Set<String> GLOBAL_PRESETS = Set.of("scriptExtension", "automationManager", "ruleRegistry",
|
||||
"items", "voice", "rules", "things", "events", "itemRegistry", "ir", "actions", "se", "audio",
|
||||
"lifecycleTracker");
|
||||
|
||||
private final javax.script.ScriptEngineFactory factory = new org.jruby.embed.jsr223.JRubyEngineFactory();
|
||||
|
||||
private final List<String> scriptTypes = Stream
|
||||
.concat(factory.getExtensions().stream(), factory.getMimeTypes().stream())
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
|
||||
// Adds @ in front of a set of variables so that Ruby recogonizes them as instance variables
|
||||
private static Map.Entry<String, Object> mapInstancePresets(Map.Entry<String, Object> entry) {
|
||||
if (INSTANCE_PRESETS.contains(entry.getKey())) {
|
||||
return Map.entry("@" + entry.getKey(), entry.getValue());
|
||||
} else {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds $ in front of a set of variables so that Ruby recogonizes them as global variables
|
||||
private static Map.Entry<String, Object> mapGlobalPresets(Map.Entry<String, Object> entry) {
|
||||
if (GLOBAL_PRESETS.contains(entry.getKey())) {
|
||||
return Map.entry("$" + entry.getKey(), entry.getValue());
|
||||
} else {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
// The activate call activates the automation and sets its configuration
|
||||
@Activate
|
||||
protected void activate(Map<String, Object> config) {
|
||||
configuration.update(config, factory);
|
||||
}
|
||||
|
||||
// The modified call updates configuration for the automation
|
||||
@Modified
|
||||
protected void modified(Map<String, Object> config) {
|
||||
configuration.update(config, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getScriptTypes() {
|
||||
return scriptTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
|
||||
// Empty comments prevent the formatter from breaking up the correct streams chaining
|
||||
Map<String, Object> filteredScopeValues = //
|
||||
scopeValues //
|
||||
.entrySet() //
|
||||
.stream() //
|
||||
.filter(map -> !FILTERED_PRESETS.contains(map.getKey())) //
|
||||
.map(JRubyScriptEngineFactory::mapInstancePresets) //
|
||||
.map(JRubyScriptEngineFactory::mapGlobalPresets) //
|
||||
.collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); //
|
||||
|
||||
super.scopeValues(scriptEngine, filteredScopeValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ScriptEngine createScriptEngine(String scriptType) {
|
||||
return scriptTypes.contains(scriptType) ? configuration.configureRubyEnvironment(factory.getScriptEngine())
|
||||
: null;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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
|
||||
*/
|
||||
|
||||
@org.osgi.annotation.bundle.Header(name = org.osgi.framework.Constants.DYNAMICIMPORT_PACKAGE, value = "*")
|
||||
package org.openhab.automation.jrubyscripting.internal;
|
||||
|
||||
/**
|
||||
* Additional information for the JRuby Scripting package
|
||||
*
|
||||
* @author Brian O'Connell - Initial contribution
|
||||
*/
|
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
<config-description uri="automation:jruby">
|
||||
|
||||
<parameter-group name="system">
|
||||
<label>System Properties</label>
|
||||
<description>This group defines JRuby system properties.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter-group>
|
||||
|
||||
<parameter-group name="environment">
|
||||
<label>Ruby Environment</label>
|
||||
<description>This group defines Ruby's environment.</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter-group>
|
||||
|
||||
<parameter-group name="gems">
|
||||
<label>Ruby Gems</label>
|
||||
<description>This group defines the list of Ruby Gems to install.</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter-group>
|
||||
|
||||
<parameter name="local_context" type="text" required="false" groupName="system">
|
||||
<label>Context Instance Type</label>
|
||||
<description>The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See
|
||||
https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type for options and details.</description>
|
||||
<default>singlethread</default>
|
||||
<options>
|
||||
<option value="singleton">Singleton</option>
|
||||
<option value="threadsafe">ThreadSafe</option>
|
||||
<option value="singlethread">SingleThread</option>
|
||||
<option value="concurrent">Concurrent</option>
|
||||
</options>
|
||||
</parameter>
|
||||
|
||||
<parameter name="local_variable" type="text" required="false" groupName="system">
|
||||
<label>Local Variable Behavior</label>
|
||||
<description>Defines how variables are shared between Ruby and Java. See
|
||||
https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options for options and details.</description>
|
||||
<default>transient</default>
|
||||
<options>
|
||||
<option value="transient">Transient</option>
|
||||
<option value="persistent">Persistent</option>
|
||||
<option value="global">Global</option>
|
||||
</options>
|
||||
</parameter>
|
||||
|
||||
<parameter name="gem_home" type="text" required="false" groupName="environment">
|
||||
<label>GEM_HOME</label>
|
||||
<description>Location Ruby Gems will be installed and loaded, directory will be created if missing and gem installs
|
||||
are specified</description>
|
||||
<default></default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="rubylib" type="text" required="false" groupName="environment">
|
||||
<label>RUBYLIB</label>
|
||||
<description>Search path for user libraries. Separate each path with a colon (semicolon in Windows).</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="gems" type="text" required="false" groupName="gems">
|
||||
<label>Ruby Gems</label>
|
||||
<description>Comma separated list of Ruby Gems to install.</description>
|
||||
</parameter>
|
||||
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,30 @@
|
||||
|
||||
# service
|
||||
|
||||
service.automation.jrubyscripting.label = JRuby Scripting
|
||||
|
||||
# bundle config
|
||||
|
||||
automation.config.jruby.gem_home.label = GEM_HOME
|
||||
automation.config.jruby.gem_home.description = Location Ruby Gems will be installed and loaded, directory will be created if missing and gem installs are specified
|
||||
automation.config.jruby.gems.label = Ruby Gems
|
||||
automation.config.jruby.gems.description = Comma separated list of Ruby Gems to install.
|
||||
automation.config.jruby.group.environment.label = Ruby Environment
|
||||
automation.config.jruby.group.environment.description = This group defines Ruby's environment.
|
||||
automation.config.jruby.group.gems.label = Ruby Gems
|
||||
automation.config.jruby.group.gems.description = This group defines the list of Ruby Gems to install.
|
||||
automation.config.jruby.group.system.label = System Properties
|
||||
automation.config.jruby.group.system.description = This group defines JRuby system properties.
|
||||
automation.config.jruby.local_context.label = Context Instance Type
|
||||
automation.config.jruby.local_context.description = The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type for options and details.
|
||||
automation.config.jruby.local_context.option.singleton = Singleton
|
||||
automation.config.jruby.local_context.option.threadsafe = ThreadSafe
|
||||
automation.config.jruby.local_context.option.singlethread = SingleThread
|
||||
automation.config.jruby.local_context.option.concurrent = Concurrent
|
||||
automation.config.jruby.local_variable.label = Local Variable Behavior
|
||||
automation.config.jruby.local_variable.description = Defines how variables are shared between Ruby and Java. See https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options for options and details.
|
||||
automation.config.jruby.local_variable.option.transient = Transient
|
||||
automation.config.jruby.local_variable.option.persistent = Persistent
|
||||
automation.config.jruby.local_variable.option.global = Global
|
||||
automation.config.jruby.rubylib.label = RUBYLIB
|
||||
automation.config.jruby.rubylib.description = Search path for user libraries. Separate each path with a colon (semicolon in Windows).
|
@ -19,6 +19,7 @@
|
||||
<modules>
|
||||
<!-- automation -->
|
||||
<module>org.openhab.automation.groovyscripting</module>
|
||||
<module>org.openhab.automation.jrubyscripting</module>
|
||||
<module>org.openhab.automation.jsscripting</module>
|
||||
<module>org.openhab.automation.jythonscripting</module>
|
||||
<module>org.openhab.automation.pidcontroller</module>
|
||||
|
Loading…
Reference in New Issue
Block a user