/* * RHQ Management Platform * Copyright 2012, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.scripting.python; import java.io.FilePermission; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URI; import java.security.AccessControlException; import java.security.Permissions; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptException; import org.testng.Assert; import org.testng.annotations.Test; import org.rhq.scripting.ScriptSourceProvider; /** * @author Lukas Krejci */ @Test public class PythonScriptEngineInitializerTest { public static class Tester { private int cnt = 0; public void increment() { ++cnt; } public int getInvocationCoung() { return cnt; } } private static final String EXPECTED_OUTPUT = "kachny"; public static class SourceProvider implements ScriptSourceProvider { @Override public Reader getScriptSource(URI scriptUri) { if (scriptUri.toString().equals("test/test_module.py")) { return new StringReader("print '" + EXPECTED_OUTPUT + "'"); } return null; } } public void testEngineInitialization() throws Exception { PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer(); ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), null); //just some code to test out this is python engine.eval("from java.util import HashMap\nHashMap()"); } public void testMethodIndirection() throws Exception { PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer(); ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), null); Bindings bindings = engine.createBindings(); Tester tester = new Tester(); bindings.put("tester", tester); engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE); engine.eval("tester.increment()"); Assert.assertEquals(tester.getInvocationCoung(), 1, "Unexpected number of tester invocations."); Map<String, Set<Method>> methods = getMethodsByName(Tester.class); for (Set<Method> ms : methods.values()) { Set<String> fns = initializer.generateIndirectionMethods("tester", ms); for (String fn : fns) { engine.eval(fn); } } engine.eval("increment()"); Assert.assertEquals(tester.getInvocationCoung(), 2, "Unexpected number of tester invocations after calling an indirected method."); } public void testSecuredEngine() throws Exception { PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer(); //jython seems to need these two.. Permissions perms = new Permissions(); perms.add(new RuntimePermission("createClassLoader")); perms.add(new RuntimePermission("getProtectionDomain")); //add permission to read files so that modules can be loaded, but writing should fail perms.add(new FilePermission("<<ALL FILES>>", "read")); ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), perms); try { engine.eval("import os\nfp = open('pom.xml', 'w')"); Assert.fail("Opening a file for writing should have failed with a security exception."); } catch (ScriptException e) { checkIsCausedByAccessControlException(e); } } public void testSourceProvider() throws Exception { PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer(); ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), null); StringWriter wrt = new StringWriter(); engine.getContext().setWriter(wrt); initializer.installScriptSourceProvider(engine, new SourceProvider()); engine .eval("import sys\nsys.path.append('__rhq__:test-unsupported/')\nsys.path.append('__rhq__:test/')\nimport test_module"); Assert.assertEquals(wrt.toString(), EXPECTED_OUTPUT + "\n", "Unexpected output from a custom module."); } private void checkIsCausedByAccessControlException(Throwable e) { Throwable ex = e; while (ex != null) { if (ex instanceof AccessControlException) { return; } ex = ex.getCause(); } Assert.fail("Expected an AccessControlException but the exception doesn't seem to be caused by it.", e); } private static Map<String, Set<Method>> getMethodsByName(Class<?> cls) { Map<String, Set<Method>> ret = new HashMap<String, Set<Method>>(); for (Method m : cls.getDeclaredMethods()) { int mods = m.getModifiers(); if (Modifier.isStatic(mods) || !Modifier.isPublic(mods)) { continue; } Set<Method> methods = ret.get(m.getName()); if (methods == null) { methods = new HashSet<Method>(); ret.put(m.getName(), methods); } methods.add(m); } return ret; } }