--- /dev/null
+<?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>
--- /dev/null
+/**
+ * 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<String> newPythonPathList = new TreeSet<>(Arrays.asList(existingPythonPath.split(File.pathSeparator)));
+ newPythonPathList.add(DEFAULT_PYTHON_PATH);
+ System.setProperty(PYTHON_PATH, String.join(File.pathSeparator, newPythonPathList));
+ }
+
+ System.setProperty(PYTHON_CACHEDIR, Paths
+ .get(OpenHAB.getUserDataFolder(), "cache", JythonScriptEngineFactory.class.getPackageName(), "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 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<String> newPythonPathList = new TreeSet<>(Arrays.asList(existingPythonPath.split(File.pathSeparator)));
+ newPythonPathList.remove(DEFAULT_PYTHON_PATH);
+ System.setProperty(PYTHON_PATH, String.join(File.pathSeparator, newPythonPathList));
+ }
+
+ System.clearProperty(PYTHON_HOME);
+ System.clearProperty(PYTHON_CACHEDIR);
+
+ logPythonPaths();
+ }
+}