summaryrefslogtreecommitdiffstats
path: root/java/com/google/gerrit/pgm/JythonShell.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/gerrit/pgm/JythonShell.java')
-rw-r--r--java/com/google/gerrit/pgm/JythonShell.java229
1 files changed, 229 insertions, 0 deletions
diff --git a/java/com/google/gerrit/pgm/JythonShell.java b/java/com/google/gerrit/pgm/JythonShell.java
new file mode 100644
index 0000000000..88f7b5dbc5
--- /dev/null
+++ b/java/com/google/gerrit/pgm/JythonShell.java
@@ -0,0 +1,229 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.pgm;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.launcher.GerritLauncher;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Properties;
+
+public class JythonShell {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private static final String STARTUP_RESOURCE = "com/google/gerrit/pgm/Startup.py";
+ private static final String STARTUP_FILE = "Startup.py";
+
+ private Class<?> console;
+ private Class<?> pyObject;
+ private Class<?> pySystemState;
+ private Object shell;
+ private ArrayList<String> injectedVariables;
+
+ public JythonShell() {
+ Properties env = new Properties();
+ // Let us inspect private class members
+ env.setProperty("python.security.respectJavaAccessibility", "false");
+
+ File home = GerritLauncher.getHomeDirectory();
+ if (home != null) {
+ env.setProperty("python.cachedir", new File(home, "jythoncache").getPath());
+ }
+
+ // For package introspection and "import com.google" to work,
+ // Jython needs to inspect actual .jar files (not just classloader)
+ StringBuilder classPath = new StringBuilder();
+ final ClassLoader cl = getClass().getClassLoader();
+ if (cl instanceof java.net.URLClassLoader) {
+ @SuppressWarnings("resource")
+ URLClassLoader ucl = (URLClassLoader) cl;
+ for (URL u : ucl.getURLs()) {
+ if ("file".equals(u.getProtocol())) {
+ if (classPath.length() > 0) {
+ classPath.append(java.io.File.pathSeparatorChar);
+ }
+ classPath.append(u.getFile());
+ }
+ }
+ }
+ env.setProperty("java.class.path", classPath.toString());
+
+ console = findClass("org.python.util.InteractiveConsole");
+ pyObject = findClass("org.python.core.PyObject");
+ pySystemState = findClass("org.python.core.PySystemState");
+
+ runMethod(
+ pySystemState,
+ pySystemState,
+ "initialize",
+ new Class<?>[] {Properties.class, Properties.class},
+ new Object[] {null, env});
+
+ try {
+ shell = console.getConstructor(new Class<?>[] {}).newInstance();
+ logger.atInfo().log("Jython shell instance created.");
+ } catch (InstantiationException
+ | IllegalAccessException
+ | IllegalArgumentException
+ | InvocationTargetException
+ | NoSuchMethodException
+ | SecurityException e) {
+ throw noInterpreter(e);
+ }
+ injectedVariables = new ArrayList<>();
+ set("Shell", this);
+ }
+
+ protected Object runMethod0(
+ Class<?> klazz, Object instance, String name, Class<?>[] sig, Object[] args)
+ throws InvocationTargetException {
+ try {
+ Method m;
+ m = klazz.getMethod(name, sig);
+ return m.invoke(instance, args);
+ } catch (NoSuchMethodException
+ | IllegalAccessException
+ | IllegalArgumentException
+ | SecurityException e) {
+ throw cannotStart(e);
+ }
+ }
+
+ protected Object runMethod(
+ Class<?> klazz, Object instance, String name, Class<?>[] sig, Object[] args) {
+ try {
+ return runMethod0(klazz, instance, name, sig, args);
+ } catch (InvocationTargetException e) {
+ throw cannotStart(e);
+ }
+ }
+
+ protected Object runInterpreter(String name, Class<?>[] sig, Object[] args) {
+ return runMethod(console, shell, name, sig, args);
+ }
+
+ protected String getDefaultBanner() {
+ return (String) runInterpreter("getDefaultBanner", new Class<?>[] {}, new Object[] {});
+ }
+
+ protected void printInjectedVariable(String id) {
+ runInterpreter(
+ "exec",
+ new Class<?>[] {String.class},
+ new Object[] {"print '\"%s\" is \"%s\"' % (\"" + id + "\", " + id + ")"});
+ }
+
+ public void run() {
+ for (String key : injectedVariables) {
+ printInjectedVariable(key);
+ }
+ reload();
+ runInterpreter(
+ "interact",
+ new Class<?>[] {String.class, pyObject},
+ new Object[] {
+ getDefaultBanner()
+ + " running for Gerrit "
+ + com.google.gerrit.common.Version.getVersion(),
+ null,
+ });
+ }
+
+ public void set(String key, Object content) {
+ runInterpreter("set", new Class<?>[] {String.class, Object.class}, new Object[] {key, content});
+ injectedVariables.add(key);
+ }
+
+ private static Class<?> findClass(String klazzname) {
+ try {
+ return Class.forName(klazzname);
+ } catch (ClassNotFoundException e) {
+ throw noShell("Class " + klazzname + " not found", e);
+ }
+ }
+
+ public void reload() {
+ execResource(STARTUP_RESOURCE);
+ execFile(GerritLauncher.getHomeDirectory(), STARTUP_FILE);
+ }
+
+ protected void execResource(String p) {
+ try (InputStream in = JythonShell.class.getClassLoader().getResourceAsStream(p)) {
+ if (in != null) {
+ execStream(in, "resource " + p);
+ } else {
+ logger.atSevere().log("Cannot load resource %s", p);
+ }
+ } catch (IOException e) {
+ logger.atSevere().withCause(e).log(e.getMessage());
+ }
+ }
+
+ protected void execFile(File parent, String p) {
+ try {
+ File script = new File(parent, p);
+ if (script.canExecute()) {
+ runMethod0(
+ console,
+ shell,
+ "execfile",
+ new Class<?>[] {String.class},
+ new Object[] {script.getAbsolutePath()});
+ } else {
+ logger.atInfo().log(
+ "User initialization file %s is not found or not executable", script.getAbsolutePath());
+ }
+ } catch (InvocationTargetException e) {
+ logger.atSevere().withCause(e).log("Exception occurred while loading file %s", p);
+ } catch (SecurityException e) {
+ logger.atSevere().withCause(e).log("SecurityException occurred while loading file %s", p);
+ }
+ }
+
+ protected void execStream(InputStream in, String p) {
+ try {
+ runMethod0(
+ console,
+ shell,
+ "execfile",
+ new Class<?>[] {InputStream.class, String.class},
+ new Object[] {in, p});
+ } catch (InvocationTargetException e) {
+ logger.atSevere().withCause(e).log("Exception occurred while loading %s", p);
+ }
+ }
+
+ private static UnsupportedOperationException noShell(String m, Throwable why) {
+ final String prefix = "Cannot create Jython shell: ";
+ final String postfix = "\n (You might need to install jython.jar in the lib directory)";
+ return new UnsupportedOperationException(prefix + m + postfix, why);
+ }
+
+ private static UnsupportedOperationException noInterpreter(Throwable why) {
+ final String msg = "Cannot create Python interpreter";
+ return noShell(msg, why);
+ }
+
+ private static UnsupportedOperationException cannotStart(Throwable why) {
+ final String msg = "Cannot start Jython shell";
+ return new UnsupportedOperationException(msg, why);
+ }
+}