Repository: incubator-tamaya-sandbox Updated Branches: refs/heads/master 855230b79 -> 9731be7d5
TAMAYA-145: Using beanshell for expression evaluation. Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/commit/9731be7d Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/tree/9731be7d Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/diff/9731be7d Branch: refs/heads/master Commit: 9731be7d5cd6192ab4337da53c741601f7132362 Parents: 855230b Author: anatole <anat...@apache.org> Authored: Mon May 1 22:23:11 2017 +0200 Committer: anatole <anat...@apache.org> Committed: Mon May 1 22:23:29 2017 +0200 ---------------------------------------------------------------------- metamodel/pom.xml | 5 + .../tamaya/metamodel/EnabledPropertySource.java | 23 +--- .../internal/resolver/JavaResolver.java | 124 ++++++++++++------- .../internal/resolver/JavaResolverTest.java | 52 +++++--- metamodel/src/test/resources/tamaya-config.xml | 13 +- 5 files changed, 132 insertions(+), 85 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/9731be7d/metamodel/pom.xml ---------------------------------------------------------------------- diff --git a/metamodel/pom.xml b/metamodel/pom.xml index 0cfc48d..bfffea8 100644 --- a/metamodel/pom.xml +++ b/metamodel/pom.xml @@ -78,6 +78,11 @@ <scope>provided</scope> <optional>true</optional> </dependency> + <dependency> + <groupId>org.apache-extras.beanshell</groupId> + <artifactId>bsh</artifactId> + <version>2.0b6</version> + </dependency> <!--<dependency>--> <!--<groupId>org.apache.tamaya.ext</groupId>--> <!--<artifactId>tamaya-json</artifactId>--> http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/9731be7d/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySource.java ---------------------------------------------------------------------- diff --git a/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySource.java b/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySource.java index 7e04499..2dcf101 100644 --- a/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySource.java +++ b/metamodel/src/main/java/org/apache/tamaya/metamodel/EnabledPropertySource.java @@ -18,6 +18,7 @@ */ package org.apache.tamaya.metamodel; +import org.apache.tamaya.metamodel.internal.resolver.JavaResolver; import org.apache.tamaya.spi.PropertySource; import org.apache.tamaya.spi.PropertyValue; import org.apache.tamaya.spisupport.PropertySourceComparator; @@ -42,6 +43,7 @@ public final class EnabledPropertySource private String enabledExpression; private PropertySource wrapped; private boolean enabled; + private static final JavaResolver resolver = new JavaResolver(); public EnabledPropertySource(PropertySource wrapped, Map<String,String> context, String expression) { this.enabledExpression = Objects.requireNonNull(expression); @@ -51,25 +53,8 @@ public final class EnabledPropertySource protected boolean calculateEnabled(Map<String, String> context) { try { - ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine engine = manager.getEngineByName("nashorn"); - if(engine==null){ - engine = manager.getEngineByName("rhino"); - } - // init script engine - for(Map.Entry<String,String> entry: context.entrySet()) { - engine.put(entry.getKey(), entry.getValue()); - } - Object o = engine.eval(enabledExpression); - if(!(o instanceof Boolean)){ - LOG.severe("Enabled expression must evaluate to Boolean: '" - +enabledExpression+"', but was " + o + - ", property source will be disabled: " + - wrapped.getName()); - return false; - } - return (Boolean)o; - } catch (ScriptException e) { + return Boolean.TRUE.equals(resolver.evaluate(enabledExpression, context)); + } catch (Exception e) { LOG.severe("Invalid Boolean expression: '" +enabledExpression+"': " + e + ", property source will be disabled: " + wrapped.getName()); http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/9731be7d/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolver.java ---------------------------------------------------------------------- diff --git a/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolver.java b/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolver.java index 22d8618..2739da9 100644 --- a/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolver.java +++ b/metamodel/src/main/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolver.java @@ -18,28 +18,30 @@ */ package org.apache.tamaya.metamodel.internal.resolver; +import bsh.*; import org.apache.tamaya.metamodel.MetaContext; import org.apache.tamaya.metamodel.spi.SimpleResolver; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Objects; +import java.io.*; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Simple resolver for {@link MetaContext} entries that - * reads data from system and environment properties. + * reads data from system and environment properties and uses + * beanshell evaluation. * * Valid inputs are: * <ul> - * <li>{@code ${properties:sys:key?default=abcval} } reading a system property.</li> - * <li>{@code ${properties:env:key?default=abcval} } reading a environment property.</li> - * <li>{@code ${properties:ctx:[ctxName:]key?default=abcval} } reading a <i>default</i> MetaContext entry.</li> + * <li>{@code ${java:expression} }, whereas <i>expression</i> evaluates to the required type.</li> * </ul> - * - * Hereby the _default_ parameter defines the default value to be applied, if no value was found. */ public final class JavaResolver implements SimpleResolver{ + + private static final Logger LOG = Logger.getLogger(JavaResolver.class.getName()); + + @Override public String getResolverId() { return "java"; @@ -47,45 +49,81 @@ public final class JavaResolver implements SimpleResolver{ @Override public String evaluate(String expression) { - String[] mainParts = expression.split("#"); - if(mainParts.length<2){ - return null; - }else{ - try { - return evaluate(mainParts[0].trim(), mainParts[1].trim()); - } catch (Exception e) { - e.printStackTrace(); - } + try{ + return String.valueOf(evaluate(expression, null)); + }catch(Exception e){ + LOG.log(Level.WARNING, "Error evaluating expression: " + expression, e); + return "ERROR{"+expression+"}:"+e; } - return null; } - private String evaluate(String className, String memberName) throws Exception{ - Objects.requireNonNull(className); - Objects.requireNonNull(memberName); - Class clazz = Class.forName(className); - try { - Method method = clazz.getDeclaredMethod(memberName); - if (Modifier.isStatic(method.getModifiers())) { - if (!method.isAccessible()) { - method.setAccessible(true); + public Object evaluate(String expression, Map<String, String> context) throws UtilEvalError, EvalError { + BshClassManager bshClassManager = new BshClassManager(); + NameSpace namespace = new NameSpace(bshClassManager, "config"); + namespace.loadDefaultImports(); + namespace.importStatic(JavaResolver.class); + namespace.setVariable("contextprops", MetaContext.getInstance(), false); + namespace.setVariable("envprops", System.getenv(), false); + namespace.setVariable("sysprops", System.getProperties(), false); + if(context!=null){ + for(Map.Entry<String,String> en:context.entrySet()){ + namespace.setVariable(en.getKey(), en.getValue(), false); } - return (String) method.invoke(null); } - }catch(Exception e){ - // ignore - } - try { - Field field = clazz.getDeclaredField(memberName); - if (Modifier.isStatic(field.getModifiers())) { - if (!field.isAccessible()) { - field.setAccessible(true); - } - return (String) field.get(null); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PrintStream outStream = new PrintStream(out); + ByteArrayOutputStream err = new ByteArrayOutputStream(); + PrintStream errStream = new PrintStream(err); + Interpreter interpreter = new Interpreter(null, +// new BufferedReader( +// new InputStreamReader(System.in)), + outStream, errStream, + false, namespace); + return interpreter.eval(expression); + } + + public static String context(String key){ + return MetaContext.getInstance().getProperty(key); + } + + public static String env(String key){ + return System.getenv(key); + } + + public static String sys(String key){ + return System.getProperty(key); + } + + public static long TIME(){ + return System.currentTimeMillis(); + } + + public static Object[] eval(String command) throws IOException { + Process proc = Runtime.getRuntime().exec(command); + try(InputStream out = proc.getInputStream(); + InputStream err = proc.getErrorStream()){ + Object[] result = new Object[3]; + ByteArrayOutputStream sw = new ByteArrayOutputStream(); + byte[] buff = new byte[512]; + result[0] = proc.waitFor(); + int read = out.read(buff); + while(read > 0){ + sw.write(buff, 0, read); + out.read(buff); } - }catch(Exception e){ - // ignore + result[1] = sw.toString(); + read = err.read(buff); + while(read > 0){ + sw.write(buff, 0, read); + err.read(buff); + } + result[2] = sw.toString(); + return result; + } catch (InterruptedException e) { + return new Object[]{"","Process interrupted.", -1}; + } catch (Exception e){ + return new Object[]{"","Process failed: " + e, -1}; } - return null; } + } http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/9731be7d/metamodel/src/test/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolverTest.java ---------------------------------------------------------------------- diff --git a/metamodel/src/test/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolverTest.java b/metamodel/src/test/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolverTest.java index 55ca290..0c1bfb3 100644 --- a/metamodel/src/test/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolverTest.java +++ b/metamodel/src/test/java/org/apache/tamaya/metamodel/internal/resolver/JavaResolverTest.java @@ -18,9 +18,10 @@ */ package org.apache.tamaya.metamodel.internal.resolver; +import org.apache.tamaya.metamodel.MetaContext; import org.junit.Test; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; /** @@ -28,7 +29,8 @@ import static org.junit.Assert.*; */ public class JavaResolverTest { - private static final String TEST = "ResTest"; + public static final String TEST = "ResTest"; + private JavaResolver r = new JavaResolver(); @Test @@ -37,34 +39,46 @@ public class JavaResolverTest { } @Test - public void evaluate() throws Exception { - assertEquals(TEST, r.evaluate("org.apache.tamaya.metamodel.internal.resolver.JavaResolverTest#TEST")); - assertEquals(TEST, r.evaluate("org.apache.tamaya.metamodel.internal.resolver.JavaResolverTest#getTest4")); - assertEquals(TEST, r.evaluate("org.apache.tamaya.metamodel.internal.resolver.JavaResolverTest#getTest6")); - assertEquals(TEST, r.evaluate("org.apache.tamaya.metamodel.internal.resolver.JavaResolverTest#getTest7")); + public void evaluateDirect() throws Exception { + assertEquals("value", r.evaluate("\"value\"")); + assertEquals("1.1", r.evaluate("1.1")); + assertEquals("1", r.evaluate("1")); } - private String getTest5(){ - return TEST; - } - - String getTest2(){ - return TEST; + @Test + public void evaluateProperties() throws Exception { + assertEquals(System.getProperty("java.version"), r.evaluate("sys(\"java.version\")")); + String key = System.getenv().keySet().iterator().next(); + assertEquals(System.getenv(key), r.evaluate("env(\""+key+"\")")); + MetaContext.getInstance().setProperty("foo", "bar"); + assertEquals("bar", r.evaluate("context(\"foo\")")); } - public String getTest3(){ - return TEST; + @Test + public void evaluateExpression() throws Exception { + assertEquals("true", r.evaluate("env(\"STAGE\") == null")); + assertEquals("true", r.evaluate("sys(\"STAGE\") == null")); + System.setProperty("STAGE", "DEV2"); + assertEquals("false", r.evaluate("sys(\"STAGE\") == null")); + System.setProperty("STAGE", "DEV2"); + assertEquals("DEV2", r.evaluate("sys(\"STAGE\") == null?env(\"STAGE\"):sys(\"STAGE\")")); + assertEquals("DEV2", r.evaluate("if(sys(\"STAGE\") == null)return env(\"STAGE\"); else return sys(\"STAGE\");")); + System.getProperties().remove("STAGE"); + assertEquals("foo", r.evaluate("if(sys(\"STAGE\") == null)return \"foo\"; else return sys(\"STAGE\");")); } - static String getTest4(){ - return TEST; + @Test + public void evaluateSimple() throws Exception { + assertEquals(TEST, r.evaluate("org.apache.tamaya.metamodel.internal.resolver.JavaResolverTest.TEST")); + assertEquals(TEST, r.evaluate("new org.apache.tamaya.metamodel.internal.resolver.JavaResolverTest().getTest1()")); + assertEquals(TEST, r.evaluate("org.apache.tamaya.metamodel.internal.resolver.JavaResolverTest.getTest2()")); } - private static String getTest6(){ + public String getTest1(){ return TEST; } - public static String getTest7(){ + public static String getTest2(){ return TEST; } http://git-wip-us.apache.org/repos/asf/incubator-tamaya-sandbox/blob/9731be7d/metamodel/src/test/resources/tamaya-config.xml ---------------------------------------------------------------------- diff --git a/metamodel/src/test/resources/tamaya-config.xml b/metamodel/src/test/resources/tamaya-config.xml index 388d84c..bdb3269 100644 --- a/metamodel/src/test/resources/tamaya-config.xml +++ b/metamodel/src/test/resources/tamaya-config.xml @@ -22,7 +22,7 @@ <stage>${properties:system:STAGE?default=DEV}</stage> <configdir>${properties:system:configdir?default=.}</configdir> <app>${properties:system.APP?default=NONE}</app> - <context>${java:org.apache.tamaya.context.Context#id()}</context> + <context>${java:java.util.UUID.randomUUID()}</context> <company>Trivadis</company> <default-formats>yaml,json</default-formats> <default-refresh-period>5 SECOND</default-refresh-period> @@ -36,7 +36,7 @@ <!-- Configuration definition. --> <property-sources> - <env-properties enabled="${stage=TEST || stage=PTA || stage=PROD}"> + <env-properties enabled="${context('stage')=='TEST' || context('stage')=='PTA' || context('stage')=='PROD'}"> <filters> <Map target="ENV."/> <Mask policy="MULTIVALUE" @@ -53,8 +53,8 @@ <file location="config.xml" formats="xml-properties" refreshable="true" /> <resource location="/META-INF/application-config.yml"/> <ch.mypack.MyClassSource /> - <!--<include enabled="${stage==TEST}">TEST-config.xml</include>--> - <resources location="${configdir}/**/*.json" enabled="${configdir != null}" /> + <!--<include enabled="${CONTEXT('stage')==TEST}">TEST-config.xml</include>--> + <resources location="${context('configdir')}/**/*.json" enabled="${context('configdir') != null}" /> <url location="https://www.confdrive.com/cfg/customerId=1234" formats="json" refreshable="true"> @@ -72,5 +72,10 @@ <default-converters/> </property-converters> + <log> + "METACONTEXT: " + MetaContext.getInstance().getProperties() + "CONFIG : " + Configuration.getInstance().getProperties() + </log> + </configuration>