Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Jython Scripting #9404

Merged
merged 2 commits into from
Dec 17, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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.jythonscripting</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.adorne</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.automation.jythonscripting/NOTICE
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions bundles/org.openhab.automation.jythonscripting/README.md
Original file line number Diff line number Diff line change
@@ -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.
31 changes: 31 additions & 0 deletions bundles/org.openhab.automation.jythonscripting/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.0.0-SNAPSHOT</version>
</parent>

<artifactId>org.openhab.automation.jythonscripting</artifactId>

<name>openHAB Add-ons :: Automation :: Jython Scripting</name>

<properties>
<bnd.fixupmessages><![CDATA["Classes found in the wrong directory","The default package '.' is not permitted by the Import-Package syntax"; restrict:=error; is:=warning]]></bnd.fixupmessages>
<bnd.importpackage>*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</bnd.importpackage>
</properties>

<dependencies>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.2</version>
<scope>compile</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.automation.jythonscripting-${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-jythonscripting" description="Jython Scripting" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.automation.jythonscripting/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for testing! The issue is that the org.openhab.core.automation.module.script.ScriptEngineFactory.ENGINE_MANAGER has a different classloader. So it only detects engines that are on the boot classpath (like Nashorn). I ran into the same issue with the Groovy add-on. I looked into ways to get engines registered in the ScriptEngineFactory.ENGINE_MANAGER. IIRC it wasn't possible to call methods on ScriptEngineFactory.ENGINE_MANAGER to (un)register engines provided by OSGi bundles.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any concerns regarding SecurityException which might be thrown by this method?

We don't seem to handle any SecurityExceptions in openhab-core either when modifying System properties:

https://github.com/openhab/openhab-core/search?q=System.setProperty

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to handle it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay. Sounds reasonable. Thanks for clarification.

Do we need to handle it?

I think it is okay to not handle it. If the exceptions will be thrown there might be a bigger issue regarding permissions and other stuff will fail too.


@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<String> newPythonPathList = new TreeSet<>(
new ArrayList<>(Arrays.asList(existingPythonPath.split(File.pathSeparator))));
wborn marked this conversation as resolved.
Show resolved Hide resolved
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<String> getScriptTypes() {
List<String> scriptTypes = new ArrayList<>();

for (javax.script.ScriptEngineFactory factory : ENGINE_MANAGER.getEngineFactories()) {
List<String> extensions = factory.getExtensions();

if (extensions.contains(SCRIPT_TYPE)) {
scriptTypes.addAll(extensions);
scriptTypes.addAll(factory.getMimeTypes());
}
}
return List.copyOf(scriptTypes);
cpmeister marked this conversation as resolved.
Show resolved Hide resolved
}

@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<String> 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);
cpmeister marked this conversation as resolved.
Show resolved Hide resolved
}

System.clearProperty(PYTHON_HOME);
System.clearProperty(PYTHON_CACHEDIR);

logPythonPaths();
}
}
Original file line number Diff line number Diff line change
@@ -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
*/
1 change: 1 addition & 0 deletions bundles/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<modules>
<!-- automation -->
<module>org.openhab.automation.groovyscripting</module>
<module>org.openhab.automation.jythonscripting</module>
<!-- io -->
<module>org.openhab.io.homekit</module>
<module>org.openhab.io.hueemulation</module>
Expand Down
4 changes: 3 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@

<bnd.importpackage/>
<bnd.exportpackage/>
<bnd.fixupmessages/>
<bnd.includeresource>-${.}/NOTICE, -${.}/*.xsd</bnd.includeresource>

<feature.directory>src/main/feature/feature.xml</feature.directory>
Expand Down Expand Up @@ -153,7 +154,8 @@ Import-Package: \\
${bnd.exportpackage}
-sources: false
-contract: *
-includeresource: ${bnd.includeresource}]]></bnd>
-includeresource: ${bnd.includeresource}
-fixupmessages: ${bnd.fixupmessages}]]></bnd>
cpmeister marked this conversation as resolved.
Show resolved Hide resolved
<!-- -dsannotations-options: norequirements -->
<!-- Bundle-SymbolicName: ${project.groupId}.${project.artifactId} -->
</configuration>
Expand Down