From 36ab179f5bb46dead769975154250b1b1146f867 Mon Sep 17 00:00:00 2001 From: Wouter Born Date: Wed, 16 Dec 2020 23:26:25 +0100 Subject: [PATCH 1/2] Add Jython Scripting Also-by: Scott Rushworth Signed-off-by: Wouter Born --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + .../NOTICE | 13 ++ .../README.md | 5 + .../pom.xml | 31 ++++ .../src/main/feature/feature.xml | 9 ++ .../JythonScriptEngineFactory.java | 132 ++++++++++++++++++ .../jythonscripting/package-info.java | 21 +++ bundles/pom.xml | 1 + pom.xml | 4 +- 10 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 bundles/org.openhab.automation.jythonscripting/NOTICE create mode 100644 bundles/org.openhab.automation.jythonscripting/README.md create mode 100644 bundles/org.openhab.automation.jythonscripting/pom.xml create mode 100755 bundles/org.openhab.automation.jythonscripting/src/main/feature/feature.xml create mode 100755 bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/JythonScriptEngineFactory.java create mode 100644 bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/package-info.java diff --git a/CODEOWNERS b/CODEOWNERS index 3a06e0588bec2..96f873cd0c5c0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -6,6 +6,7 @@ # Add-on maintainers: /bundles/org.openhab.automation.groovyscripting/ @wborn +/bundles/org.openhab.automation.jythonscripting/ @openhab/add-ons-maintainers /bundles/org.openhab.binding.adorne/ @theiding /bundles/org.openhab.binding.airquality/ @kubawolanin /bundles/org.openhab.binding.airvisualnode/ @3cky diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index ad1e3db6bc2e2..94769faead16f 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -21,6 +21,11 @@ org.openhab.automation.groovyscripting ${project.version} + + org.openhab.addons.bundles + org.openhab.automation.jythonscripting + ${project.version} + org.openhab.addons.bundles org.openhab.binding.adorne diff --git a/bundles/org.openhab.automation.jythonscripting/NOTICE b/bundles/org.openhab.automation.jythonscripting/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.automation.jythonscripting/NOTICE @@ -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 diff --git a/bundles/org.openhab.automation.jythonscripting/README.md b/bundles/org.openhab.automation.jythonscripting/README.md new file mode 100644 index 0000000000000..ff6cab9e9a778 --- /dev/null +++ b/bundles/org.openhab.automation.jythonscripting/README.md @@ -0,0 +1,5 @@ +# Jython Scripting + +This addon provides a [Jython](https://www.jython.org/) 2.7.2 for use with scripted automation and eliminates the need to download Jython and create `EXTRA_JAVA_OPTS` entries for `bootclasspath`, `python.home` and `python.path`. +The `python.home` System property will be set to the path of the add-on. +The `python.path` System property will be set to `$OPENHAB_CONF/automation/lib/python`, but any existing `python.path` will be appended to it. diff --git a/bundles/org.openhab.automation.jythonscripting/pom.xml b/bundles/org.openhab.automation.jythonscripting/pom.xml new file mode 100644 index 0000000000000..9b31f9d09f957 --- /dev/null +++ b/bundles/org.openhab.automation.jythonscripting/pom.xml @@ -0,0 +1,31 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.0.0-SNAPSHOT + + + org.openhab.automation.jythonscripting + + openHAB Add-ons :: Automation :: Jython Scripting + + + + *blockhound*;resolution:=optional,com.cloudius.util;resolution:=optional,com.github.luben.zstd;resolution:=optional,com.informix.jdbc;resolution:=optional,com.jcraft.jzlib;resolution:=optional,com.ning.compress.*;resolution:=optional,com.oracle.svm.core.annotate;resolution:=optional,com.sun.management;resolution:=optional,custom_proxymaker.tests;resolution:=optional,jnr.*;resolution;resolution:=optional,*jpountz*;resolution:=optional,junit.framework;resolution:=optional,lzma.sdk.*;resolution:=optional,oracle.*;resolution:=optional,org.antlr.stringtemplate;resolution:=optional,org.apache.tools.*;resolution:=optional,org.brotli.dec;resolution:=optional,org.checkerframework.*;resolution:=optional,org.conscrypt;resolution:=optional,org.eclipse.jetty.*;resolution:=optional,org.hamcrest;resolution:=optional,org.jboss.marshalling;resolution:=optional,org.junit.*;resolution:=optional,org.python.apache.xml.resolver.*;resolution:=optional,org.python.google.*;resolution:=optional,org.python.netty.internal.tcnative;resolution:=optional,org.python.objectweb.asm.tree.*;resolution:=optional,org.python.proxies;resolution:=optional,org.tukaani.xz;resolution:=optional,sun.*;resolution:=optional + + + + + org.python + jython-standalone + 2.7.2 + compile + + + + diff --git a/bundles/org.openhab.automation.jythonscripting/src/main/feature/feature.xml b/bundles/org.openhab.automation.jythonscripting/src/main/feature/feature.xml new file mode 100755 index 0000000000000..9bec0190a9661 --- /dev/null +++ b/bundles/org.openhab.automation.jythonscripting/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.automation.jythonscripting/${project.version} + + diff --git a/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/JythonScriptEngineFactory.java b/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/JythonScriptEngineFactory.java new file mode 100755 index 0000000000000..844a1e8bcdb15 --- /dev/null +++ b/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/JythonScriptEngineFactory.java @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2010-2020 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.jythonscripting; + +import java.io.File; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import javax.script.ScriptEngine; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.OpenHAB; +import org.openhab.core.automation.module.script.AbstractScriptEngineFactory; +import org.openhab.core.automation.module.script.ScriptEngineFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; + +/** + * This is an implementation of {@link ScriptEngineFactory} for Jython. + * + * @author Scott Rushworth - Initial contribution + * @author Wouter Born - Initial contribution + */ +@Component(service = ScriptEngineFactory.class) +@NonNullByDefault +public class JythonScriptEngineFactory extends AbstractScriptEngineFactory { + + private static final String PYTHON_CACHEDIR = "python.cachedir"; + private static final String PYTHON_HOME = "python.home"; + private static final String PYTHON_PATH = "python.path"; + + private static final String DEFAULT_PYTHON_PATH = Paths + .get(OpenHAB.getConfigFolder(), "automation", "lib", "python").toString(); + + private static final String SCRIPT_TYPE = "py"; + private static final javax.script.ScriptEngineManager ENGINE_MANAGER = new javax.script.ScriptEngineManager(); + + @Activate + public JythonScriptEngineFactory() { + logger.debug("Loading JythonScriptEngineFactory"); + + String pythonHome = JythonScriptEngineFactory.class.getProtectionDomain().getCodeSource().getLocation() + .toString().replace("file:", ""); + System.setProperty(PYTHON_HOME, pythonHome); + + String existingPythonPath = System.getProperty(PYTHON_PATH); + if (existingPythonPath == null || existingPythonPath.isEmpty()) { + System.setProperty(PYTHON_PATH, DEFAULT_PYTHON_PATH); + } else if (!existingPythonPath.contains(DEFAULT_PYTHON_PATH)) { + Set newPythonPathList = new TreeSet<>( + new ArrayList<>(Arrays.asList(existingPythonPath.split(File.pathSeparator)))); + newPythonPathList.add(DEFAULT_PYTHON_PATH); + String newPythonPath = String.join(File.pathSeparator, newPythonPathList); + System.setProperty(PYTHON_PATH, newPythonPath); + } + + System.setProperty(PYTHON_CACHEDIR, + Paths.get(OpenHAB.getUserDataFolder(), "cache", "org.openhab.automation.jythonscripting", "cachedir") + .toString()); + + logPythonPaths(); + } + + private void logPythonPaths() { + logger.trace("{}: {}, {}: {}, {}: {}", // + PYTHON_HOME, System.getProperty(PYTHON_HOME), // + PYTHON_PATH, System.getProperty(PYTHON_PATH), // + PYTHON_CACHEDIR, System.getProperty(PYTHON_CACHEDIR)); + } + + @Override + public List getScriptTypes() { + List scriptTypes = new ArrayList<>(); + + for (javax.script.ScriptEngineFactory factory : ENGINE_MANAGER.getEngineFactories()) { + List extensions = factory.getExtensions(); + + if (extensions.contains(SCRIPT_TYPE)) { + scriptTypes.addAll(extensions); + scriptTypes.addAll(factory.getMimeTypes()); + } + } + return List.copyOf(scriptTypes); + } + + @Override + public @Nullable ScriptEngine createScriptEngine(String scriptType) { + ScriptEngine scriptEngine = ENGINE_MANAGER.getEngineByExtension(scriptType); + if (scriptEngine == null) { + scriptEngine = ENGINE_MANAGER.getEngineByMimeType(scriptType); + } + if (scriptEngine == null) { + scriptEngine = ENGINE_MANAGER.getEngineByName(scriptType); + } + return scriptEngine; + } + + @Deactivate + public void removePythonPath() { + logger.debug("Unloading JythonScriptEngineFactory"); + + String existingPythonPath = System.getProperty(PYTHON_PATH); + if (existingPythonPath != null && existingPythonPath.contains(DEFAULT_PYTHON_PATH)) { + Set newPythonPathList = new TreeSet<>( + new ArrayList<>(Arrays.asList(existingPythonPath.split(File.pathSeparator)))); + newPythonPathList.remove(DEFAULT_PYTHON_PATH); + String newPythonPath = String.join(File.pathSeparator, newPythonPathList); + System.setProperty(PYTHON_PATH, newPythonPath); + } + + System.clearProperty(PYTHON_HOME); + System.clearProperty(PYTHON_CACHEDIR); + + logPythonPaths(); + } +} diff --git a/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/package-info.java b/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/package-info.java new file mode 100644 index 0000000000000..80cb3d315426c --- /dev/null +++ b/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/package-info.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2010-2020 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.jythonscripting; + +/** + * Additional information for the Jython Scripting package + * + * @author Wouter Born - Initial contribution + */ diff --git a/bundles/pom.xml b/bundles/pom.xml index 714dcb5a91787..1d69ef8c099f1 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -19,6 +19,7 @@ org.openhab.automation.groovyscripting + org.openhab.automation.jythonscripting org.openhab.io.homekit org.openhab.io.hueemulation diff --git a/pom.xml b/pom.xml index 48e68d21f9b55..6c394d6ed33f8 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,7 @@ + -${.}/NOTICE, -${.}/*.xsd src/main/feature/feature.xml @@ -153,7 +154,8 @@ Import-Package: \\ ${bnd.exportpackage} -sources: false -contract: * --includeresource: ${bnd.includeresource}]]> +-includeresource: ${bnd.includeresource} +-fixupmessages: ${bnd.fixupmessages}]]> From 560a6bf9899300c313aab98924db41998a01a746 Mon Sep 17 00:00:00 2001 From: Wouter Born Date: Thu, 17 Dec 2020 09:11:41 +0100 Subject: [PATCH 2/2] Address review comments Signed-off-by: Wouter Born --- .../JythonScriptEngineFactory.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/JythonScriptEngineFactory.java b/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/JythonScriptEngineFactory.java index 844a1e8bcdb15..a24608df4cbec 100755 --- a/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/JythonScriptEngineFactory.java +++ b/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/JythonScriptEngineFactory.java @@ -63,16 +63,14 @@ public JythonScriptEngineFactory() { if (existingPythonPath == null || existingPythonPath.isEmpty()) { System.setProperty(PYTHON_PATH, DEFAULT_PYTHON_PATH); } else if (!existingPythonPath.contains(DEFAULT_PYTHON_PATH)) { - Set newPythonPathList = new TreeSet<>( - new ArrayList<>(Arrays.asList(existingPythonPath.split(File.pathSeparator)))); + Set newPythonPathList = new TreeSet<>(Arrays.asList(existingPythonPath.split(File.pathSeparator))); newPythonPathList.add(DEFAULT_PYTHON_PATH); - String newPythonPath = String.join(File.pathSeparator, newPythonPathList); - System.setProperty(PYTHON_PATH, newPythonPath); + System.setProperty(PYTHON_PATH, String.join(File.pathSeparator, newPythonPathList)); } - System.setProperty(PYTHON_CACHEDIR, - Paths.get(OpenHAB.getUserDataFolder(), "cache", "org.openhab.automation.jythonscripting", "cachedir") - .toString()); + System.setProperty(PYTHON_CACHEDIR, Paths + .get(OpenHAB.getUserDataFolder(), "cache", JythonScriptEngineFactory.class.getPackageName(), "cachedir") + .toString()); logPythonPaths(); } @@ -96,7 +94,7 @@ public List getScriptTypes() { scriptTypes.addAll(factory.getMimeTypes()); } } - return List.copyOf(scriptTypes); + return scriptTypes; } @Override @@ -117,11 +115,9 @@ public void removePythonPath() { String existingPythonPath = System.getProperty(PYTHON_PATH); if (existingPythonPath != null && existingPythonPath.contains(DEFAULT_PYTHON_PATH)) { - Set newPythonPathList = new TreeSet<>( - new ArrayList<>(Arrays.asList(existingPythonPath.split(File.pathSeparator)))); + Set newPythonPathList = new TreeSet<>(Arrays.asList(existingPythonPath.split(File.pathSeparator))); newPythonPathList.remove(DEFAULT_PYTHON_PATH); - String newPythonPath = String.join(File.pathSeparator, newPythonPathList); - System.setProperty(PYTHON_PATH, newPythonPath); + System.setProperty(PYTHON_PATH, String.join(File.pathSeparator, newPythonPathList)); } System.clearProperty(PYTHON_HOME);