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..a24608df4cbec
--- /dev/null
+++ b/bundles/org.openhab.automation.jythonscripting/src/main/java/org/openhab/automation/jythonscripting/JythonScriptEngineFactory.java
@@ -0,0 +1,128 @@
+/**
+ * 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<>(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 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 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<>(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();
+ }
+}
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}]]>