diff --git a/rhino-tools/src/main/java/org/mozilla/javascript/tools/shell/Main.java b/rhino-tools/src/main/java/org/mozilla/javascript/tools/shell/Main.java index 1393d17ef6..70a9aab04a 100644 --- a/rhino-tools/src/main/java/org/mozilla/javascript/tools/shell/Main.java +++ b/rhino-tools/src/main/java/org/mozilla/javascript/tools/shell/Main.java @@ -32,6 +32,7 @@ import org.mozilla.javascript.JavaScriptException; import org.mozilla.javascript.Kit; import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.RhinoConfig; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Script; import org.mozilla.javascript.Scriptable; @@ -132,7 +133,7 @@ public void quit(Context cx, int exitCode) { */ public static void main(String args[]) { try { - if (Boolean.getBoolean("rhino.use_java_policy_security")) { + if (RhinoConfig.getBoolean("rhino.use_java_policy_security")) { initJavaPolicySecuritySupport(); } } catch (SecurityException ex) { diff --git a/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java b/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java new file mode 100644 index 0000000000..8d170325b7 --- /dev/null +++ b/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java @@ -0,0 +1,167 @@ +package org.mozilla.javascript; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Comparator; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; + +/** + * Utility class to read current rhino configuration. + * + *

Rhino properties typically begins with "rhino." (properties) or "RHINO_" (env) + * + *

The configuration is read from these locations: + * + *

    + *
  1. rhino.config file from current class' classpath + *
  2. rhino.config file from current threas's classpath + *
  3. rhino.config file from current directory + *
  4. System-properties starting with "rhino." + *
  5. env variables starting with "RHINO_" (underscores are replaced by '.' and string is + *
+ * + * The config files are in UTF-8 format and all keys in this configuration are case-insensitive and + * dot/underscore-insensitive. + * + *

This means, "rhino.use_java_policy_security" is equvalent to "RHINO_USE_JAVA_POLICY_SECURITY" + * + * @author Roland Praml, Foconis Analytics GmbH + */ +public class RhinoConfig { + private static final Map PROPERTIES = + AccessController.doPrivileged((PrivilegedAction>) () -> init()); + + private static Map init() { + // we have to add a comparator at least for environment: TODO: can this done simpler + Comparator comparator = + (s1, s2) -> { + s1 = s1.toLowerCase(Locale.ROOT).replace('_', '.'); + s2 = s2.toLowerCase(Locale.ROOT).replace('_', '.'); + return s1.compareTo(s2); + }; + Map map = new TreeMap<>(comparator); + + // load from classpaths + map.putAll(loadFromClasspath(RhinoConfig.class.getClassLoader())); + map.putAll(loadFromClasspath(Thread.currentThread().getContextClassLoader())); + map.putAll(loadFromFile(new File("rhino.config"))); + copyMap(System.getProperties(), map); + copyMap(System.getenv(), map); + System.out.println("Current config: " + map); + return map; + } + + /** Copies all rhino relevant properties. */ + private static void copyMap(Map src, Map dst) { + for (Map.Entry entry : src.entrySet()) { + if (entry.getKey() instanceof String && entry.getValue() instanceof String) { + String key = (String) entry.getKey(); + if (key.startsWith("rhino.") || key.startsWith("RHINO_")) { + dst.put(key, (String) entry.getValue()); + } + } + } + } + + @SuppressWarnings("unchecked") + private static Map loadFromFile(File config) { + if (config.exists()) { + try (InputStream in = new FileInputStream(config)) { + if (in != null) { + Properties props = new Properties(); + props.load(new InputStreamReader(in, StandardCharsets.UTF_8)); + System.out.println( + "Loaded rhino.config from " + + config.getAbsolutePath()); // TODO: remove these prints + return (Map) props; + } + } catch (IOException e) { + System.err.println( + "Error loading rhino.config from " + + config.getAbsolutePath() + + ": " + + e.getMessage()); + } + } + return Collections.emptyMap(); + } + + @SuppressWarnings("unchecked") + private static Map loadFromClasspath(ClassLoader cl) { + if (cl != null) { + try (InputStream in = cl.getResourceAsStream("rhino.config")) { + if (in != null) { + Properties props = new Properties(); + props.load(new InputStreamReader(in, StandardCharsets.UTF_8)); + System.out.println( + "Loaded " + + props.size() + + " proerties from rhino.config in classpath"); // TODO: remove + // these prints + return (Map) props; + } + } catch (IOException e) { + System.err.println("Error loading rhino.config from classpath: " + e.getMessage()); + } + } + return Collections.emptyMap(); + } + + /** Replacement for {@link System#getProperty(String)}. */ + public static String getProperty(String key) { + return PROPERTIES.get(key); + } + + /** Replacement for {@link System#getProperty(String, String)}. */ + public static String getProperty(String key, String defaultValue) { + String val = getProperty(key); + return (val == null) ? defaultValue : val; + } + + /** Replacement for {@link Boolean#getBoolean(String)}. */ + public static boolean getBoolean(String name) { + boolean result = false; + try { + result = Boolean.parseBoolean(getProperty(name)); + } catch (IllegalArgumentException | NullPointerException e) { + } + return result; + } + + /** Replacement for {@link Integer#getInteger(String, Integer)}. */ + public static Integer getInteger(String nm, Integer val) { + String v = null; + try { + v = getProperty(nm); + } catch (IllegalArgumentException | NullPointerException e) { + } + if (v != null) { + try { + return Integer.decode(v); + } catch (NumberFormatException e) { + } + } + return val; + } + + /** Replacement for {@link Integer#getInteger(String, int)}. */ + public static Integer getInteger(String nm, int val) { + Integer result = getInteger(nm, null); + return (result == null) ? Integer.valueOf(val) : result; + } + + /** Replacement for {@link Integer#getInteger(String)}. */ + public static Integer getInteger(String nm) { + return getInteger(nm, null); + } +} diff --git a/rhino/src/main/java/org/mozilla/javascript/RhinoException.java b/rhino/src/main/java/org/mozilla/javascript/RhinoException.java index 8bccbf6c2e..8b0f2d6175 100644 --- a/rhino/src/main/java/org/mozilla/javascript/RhinoException.java +++ b/rhino/src/main/java/org/mozilla/javascript/RhinoException.java @@ -10,7 +10,6 @@ import java.io.FilenameFilter; import java.io.PrintStream; import java.io.PrintWriter; -import java.security.AccessControlException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; @@ -221,11 +220,11 @@ static String formatStackTrace(ScriptStackElement[] stack, String message) { /** * Get a string representing the script stack of this exception. * - * @deprecated the filter argument is ignored as we are able to recognize script stack elements - * by our own. Use #getScriptStackTrace() instead. * @param filter ignored * @return a script stack dump * @since 1.6R6 + * @deprecated the filter argument is ignored as we are able to recognize script stack elements + * by our own. Use #getScriptStackTrace() instead. */ @Deprecated public String getScriptStackTrace(FilenameFilter filter) { @@ -387,20 +386,15 @@ public static StackStyle getStackStyle() { // Allow us to override default stack style for debugging. static { - try { - String style = System.getProperty("rhino.stack.style"); - if (style != null) { - if ("Rhino".equalsIgnoreCase(style)) { - stackStyle = StackStyle.RHINO; - } else if ("Mozilla".equalsIgnoreCase(style)) { - stackStyle = StackStyle.MOZILLA; - } else if ("V8".equalsIgnoreCase(style)) { - stackStyle = StackStyle.V8; - } + String style = RhinoConfig.getProperty("rhino.stack.style"); + if (style != null) { + if ("Rhino".equalsIgnoreCase(style)) { + stackStyle = StackStyle.RHINO; + } else if ("Mozilla".equalsIgnoreCase(style)) { + stackStyle = StackStyle.MOZILLA; + } else if ("V8".equalsIgnoreCase(style)) { + stackStyle = StackStyle.V8; } - } catch (AccessControlException ace) { - // ignore. We will land here, if SecurityManager is in place and error is lazily - // initialized } } } diff --git a/tests/rhino.config b/tests/rhino.config new file mode 100644 index 0000000000..6bdef9fb3f --- /dev/null +++ b/tests/rhino.config @@ -0,0 +1 @@ +rhino.helloFromFile=true diff --git a/tests/src/test/resources/rhino.config b/tests/src/test/resources/rhino.config new file mode 100644 index 0000000000..e8c42e5aa6 --- /dev/null +++ b/tests/src/test/resources/rhino.config @@ -0,0 +1 @@ +rhino.helloFromClasspath=true \ No newline at end of file