changeset 531:229736339e0c

HttpMvelFilter improvements
author Devel 2
date Fri, 01 Sep 2017 09:59:19 +0200
parents ab132a470e21
children f78f71168bef
files stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFilterDirection.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMvelFilter.java stress-tester/src/main/java/com/passus/st/config/StringSourceNodeDefinition.java stress-tester/src/main/java/com/passus/st/config/StringToExecutableStatementValueTransformer.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMvelFilterTest.java stress-tester/src/test/java/com/passus/st/config/StringSourceNodeDefinitionTest.java
diffstat 6 files changed, 153 insertions(+), 210 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFilterDirection.java	Fri Sep 01 09:59:19 2017 +0200
@@ -0,0 +1,11 @@
+package com.passus.st.client.http.filter;
+
+/**
+ *
+ * @author Mirosław Hawrot
+ */
+public enum HttpFilterDirection {
+
+    IN, OUT, BOTH
+
+}
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMvelFilter.java	Thu Aug 31 15:32:31 2017 +0200
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMvelFilter.java	Fri Sep 01 09:59:19 2017 +0200
@@ -1,50 +1,37 @@
 package com.passus.st.client.http.filter;
 
-import com.passus.commons.ConversionException;
+import com.passus.commons.Assert;
 import com.passus.commons.annotations.Plugin;
-import com.passus.config.CMapNode;
-import com.passus.config.CNode;
-import com.passus.config.CTupleNode;
-import com.passus.config.CValueNode;
 import com.passus.config.Configuration;
-import com.passus.config.ValueTransformer;
 import com.passus.config.annotations.NodeDefinitionCreate;
 import static com.passus.config.schema.ConfigurationSchemaBuilder.mapDef;
-import static com.passus.config.schema.ConfigurationSchemaBuilder.mixedDef;
 import static com.passus.config.schema.ConfigurationSchemaBuilder.tupleDef;
-import static com.passus.config.schema.ConfigurationSchemaBuilder.valueDef;
-import com.passus.config.schema.MixedNodeDefinition;
+import static com.passus.config.schema.ConfigurationSchemaBuilder.enumDef;
 import com.passus.config.schema.NodeDefinition;
 import com.passus.config.schema.NodeDefinitionCreator;
-import com.passus.config.schema.NodeTransformer;
-import com.passus.config.validation.Errors;
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpResponse;
 import com.passus.st.client.http.HttpFlowContext;
+import static com.passus.st.client.http.filter.HttpFilter.ACCEPT;
+import static com.passus.st.client.http.filter.HttpFilter.DENY;
+import static com.passus.st.client.http.filter.HttpFilter.DUNNO;
+import com.passus.st.config.StringSourceNodeDefinition;
+import com.passus.st.config.StringToExecutableStatementValueTransformer;
 import com.passus.st.plugin.PluginConstants;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Collection;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.mvel2.CompileException;
-import org.mvel2.MVEL;
 import org.mvel2.compiler.ExecutableStatement;
 import org.mvel2.integration.VariableResolverFactory;
 import org.mvel2.integration.impl.CachingMapVariableResolverFactory;
-import org.mvel2.integration.impl.MapVariableResolverFactory;
 
 /**
  *
- * @author mikolaj.podbielski
+ * @author Mirosław Hawrot
  */
+@NodeDefinitionCreate(HttpMvelFilter.HttpMvelFilterNodeDefCreator.class)
 @Plugin(name = HttpMvelFilter.TYPE, category = PluginConstants.CATEGORY_HTTP_FILTER)
-@NodeDefinitionCreate(HttpMvelFilter.NodeDefCreator.class)
 public class HttpMvelFilter extends HttpFilter {
 
     public static final String TYPE = "mvel";
@@ -52,58 +39,61 @@
     private static final Logger LOGGER = LogManager.getLogger(HttpMvelFilter.class);
 
     private VariableResolverFactory globalFactory;
-    private ExecutableStatement outbound;
-    private ExecutableStatement inbound;
 
-    VariableResolverFactory getGlobalFactory() {
+    private ExecutableStatement statement;
+
+    private HttpFilterDirection direction = HttpFilterDirection.OUT;
+
+    public HttpMvelFilter() {
+    }
+
+    public HttpMvelFilter(ExecutableStatement statement, HttpFilterDirection direction) {
+        Assert.notNull(statement, "statement");
+        Assert.notNull(direction, "direction");
+        this.statement = statement;
+        this.direction = direction;
+    }
+
+    public VariableResolverFactory getGlobalFactory() {
         return globalFactory;
     }
 
-    void setGlobalFactory(VariableResolverFactory globalFactory) {
+    public void setGlobalFactory(VariableResolverFactory globalFactory) {
         this.globalFactory = globalFactory;
     }
 
-    ExecutableStatement getOutbound() {
-        return outbound;
+    public HttpFilterDirection getDirection() {
+        return direction;
     }
 
-    void setOutbound(ExecutableStatement outbound) {
-        this.outbound = outbound;
+    public void setDirection(HttpFilterDirection direction) {
+        Assert.notNull(direction, "direction");
+        this.direction = direction;
     }
 
-    ExecutableStatement getInbound() {
-        return inbound;
+    public ExecutableStatement getStatement() {
+        return statement;
     }
 
-    void setInbound(ExecutableStatement inbound) {
-        this.inbound = inbound;
+    public void setStatement(ExecutableStatement statement) {
+        this.statement = statement;
     }
 
     @Override
     public void configure(Configuration config) {
-        outbound = (ExecutableStatement) config.get("outbound", null);
-        inbound = (ExecutableStatement) config.get("inbound", null);
+        direction = (HttpFilterDirection) config.get("dir", HttpFilterDirection.OUT);
+        statement = (ExecutableStatement) config.get("script");
     }
 
-    @Override
-    public int filterOutbound(HttpRequest request, HttpResponse resp, HttpFlowContext context) {
-        return filter(outbound, request, resp, context);
-    }
-
-    @Override
-    public int filterInbound(HttpRequest request, HttpResponse resp, HttpFlowContext context) {
-        return filter(inbound, request, resp, context);
-    }
-
-    private int filter(ExecutableStatement expression, HttpRequest request, HttpResponse resp, HttpFlowContext context) {
-        if (expression == null) {
+    private int filter(HttpRequest req, HttpResponse resp, HttpFlowContext context) {
+        if (statement == null) {
             return DUNNO;
         }
 
         Map<String, Object> vars = new HashMap<>(3);
-        vars.put("$req", request);
+        vars.put("$req", req);
         vars.put("$resp", resp);
-        vars.put("$ctx", context);
+        vars.put("$context", context);
 
         CachingMapVariableResolverFactory factory = new CachingMapVariableResolverFactory(vars);
         if (globalFactory != null) {
@@ -111,146 +101,58 @@
         }
 
         try {
-            Object out = expression.getValue(null, factory);
+            Object out = statement.getValue(null, factory);
             if (out instanceof Integer) {
                 int value = (Integer) out;
-                if (value == DENY || value == DUNNO || value == ACCEPT) {
-                    return value;
+                if (value < 0) {
+                    return DENY;
+                } else if (value > 0) {
+                    return ACCEPT;
                 }
+
+                return DUNNO;
             }
         } catch (Throwable th) {
             LOGGER.trace(th);
         }
+
         return DUNNO;
     }
 
-    public static ExecutableStatement compile(String expression) {
-        try {
-            return (ExecutableStatement) MVEL.compileExpression(expression);
-        } catch (Exception ex) {
-            LOGGER.warn("Compilation error:\n" + ex.getMessage());
-            throw ex;
+    @Override
+    public int filterOutbound(HttpRequest req, HttpResponse resp, HttpFlowContext context) {
+        if (direction == HttpFilterDirection.BOTH || direction == HttpFilterDirection.OUT) {
+            return filter(req, resp, context);
         }
-    }
-
-    public static void validate(String expression) throws CompileException {
-        MVEL.compileExpression(expression);
-    }
 
-    public static int validate(Collection<String> includes) throws CompileException, RuntimeException {
-        Map variables = new HashMap();
-        MapVariableResolverFactory globalFactory = new MapVariableResolverFactory(variables);
-        for (String include : includes) {
-            ExecutableStatement es = (ExecutableStatement) MVEL.compileExpression(include);
-            es.getValue(null, globalFactory);
-        }
-        return variables.size();
-    }
-
-    public static VariableResolverFactory global(Collection<String> includes) {
-        MapVariableResolverFactory globalFactory = new MapVariableResolverFactory();
-//        GlobalExpressionList compiledGlobalExpressions;
-        for (String include : includes) {
-            if (include != null) {
-//                GlobalExpression globalExpression = compiledGlobalExpressions.getByName(include);
-//                if (globalExpression != null) {
-//                    ExecutableStatement compiledGlobal = globalExpression.getCompiled();
-//                    compiledGlobal.getValue(null, globalFactory);
-//                }
-            }
-        }
-        return globalFactory;
+        return DUNNO;
     }
 
     @Override
-    public HttpMvelFilter instanceForWorker(int index) {
-        HttpMvelFilter filter = new HttpMvelFilter();
-        filter.globalFactory = globalFactory;
-        filter.outbound = outbound;
-        filter.inbound = inbound;
-        return filter;
+    public int filterInbound(HttpRequest req, HttpResponse resp, HttpFlowContext context) {
+        if (direction == HttpFilterDirection.BOTH || direction == HttpFilterDirection.IN) {
+            return filter(req, resp, context);
+        }
+
+        return DUNNO;
     }
 
-    // TODO: replace ScriptNodeTransformer and ScriptTransformer with custom NodeDefinition
-    public static class NodeDefCreator implements NodeDefinitionCreator {
+    @Override
+    public HttpFilter instanceForWorker(int index) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public static final class HttpMvelFilterNodeDefCreator implements NodeDefinitionCreator {
 
         @Override
         public NodeDefinition create() {
-            MixedNodeDefinition scriptDef = mixedDef(
-                    valueDef().setTransformer(new ScriptTransformer()),
-                    mapDef(tupleDef("file", valueDef())).setTransformer(new ScriptNodeTransformer())
-            );
-
             return mapDef(
-                    //                    tupleDef("includes", null),
-                    tupleDef("outbound", scriptDef),
-                    tupleDef("inbound", scriptDef)
+                    tupleDef("dir", enumDef(HttpFilterDirection.class)).setRequired(false),
+                    tupleDef("script", new StringSourceNodeDefinition()
+                            .setTransformer(new StringToExecutableStatementValueTransformer())
+                    )
             );
         }
 
     }
-
-    private static class ScriptNodeTransformer implements NodeTransformer<CValueNode> {
-
-        @Override
-        public CValueNode transform(CNode node, Errors errors) {
-            CMapNode mapNode = (CMapNode) node;
-
-            List<CTupleNode> tuples = mapNode.getChildren();
-            if (tuples.size() != 1) {
-                errors.reject(node, "Node should contain one child.");
-                return null;
-            }
-
-            String script = null;
-
-            CTupleNode tupleNode = tuples.get(0);
-            CValueNode valueNode = (CValueNode) tupleNode.getNode();
-            switch (tupleNode.getName()) {
-                case "file":
-                    String fileName = (String) valueNode.getValue();
-                    try {
-                        script = new String(Files.readAllBytes(Paths.get(new File(fileName).toURI())));
-                    } catch (IOException ex) {
-                        errors.reject(valueNode, "Could not read file \"%s\".", fileName);
-                    }
-                    break;
-                case "script":
-                    script = (String) valueNode.getValue();
-                    break;
-                default:
-                    errors.reject(tupleNode, "Invalid key.");
-            }
-
-            if (script != null) {
-                ExecutableStatement es = compile(script);
-                return new CValueNode(es);
-            } else {
-                return new CValueNode(null);
-            }
-        }
-
-        @Override
-        public CValueNode reverseTransform(CNode node, Errors errors) {
-            throw new UnsupportedOperationException("Not supported yet.");
-        }
-
-    }
-
-    private static class ScriptTransformer implements ValueTransformer {
-
-        @Override
-        public Object transform(Object obj) throws ConversionException {
-            if (obj instanceof String) {
-                return compile((String) obj);
-            }
-            throw new ConversionException("Invalid token store type.");
-        }
-
-        @Override
-        public Object reverseTransform(Object obj) throws ConversionException {
-            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
-        }
-
-    }
 }
--- a/stress-tester/src/main/java/com/passus/st/config/StringSourceNodeDefinition.java	Thu Aug 31 15:32:31 2017 +0200
+++ b/stress-tester/src/main/java/com/passus/st/config/StringSourceNodeDefinition.java	Fri Sep 01 09:59:19 2017 +0200
@@ -1,5 +1,6 @@
 package com.passus.st.config;
 
+import com.passus.commons.ConversionException;
 import com.passus.commons.utils.ArrayUtils;
 import com.passus.config.CMapNode;
 import com.passus.config.CNode;
@@ -8,10 +9,12 @@
 import static com.passus.config.ConfigurationUtils.extractString;
 import static com.passus.config.ConfigurationUtils.validateType;
 import com.passus.config.NodeType;
+import com.passus.config.ValueTransformer;
 import com.passus.config.schema.NodeDefinition;
 import static com.passus.config.schema.validation.NodeValidationMessages.ONE_CHILD_REQUIRED_MSG;
 import static com.passus.config.schema.validation.NodeValidationMessages.TUPLE_NOT_DEFINED_MSG;
 import com.passus.config.validation.Errors;
+import static com.passus.config.validation.ValidationDefaultMessages.GENERAL_CONVERSION_ERROR;
 import static com.passus.config.validation.ValidationDefaultMessages.VALUE_SHOULD_NOT_BE_NULL_MSG;
 import java.io.File;
 import java.io.IOException;
@@ -25,10 +28,10 @@
  */
 public class StringSourceNodeDefinition extends NodeDefinition<StringSourceNodeDefinition> {
 
-    public static final StringSourceNodeDefinition INSTANCE = new StringSourceNodeDefinition();
-    
     private final static Set<Class<? extends CNode>> SUPPORTED = ArrayUtils.asUnmodifiableSet(CValueNode.class, CMapNode.class);
 
+    private ValueTransformer transformer;
+
     @Override
     public Set<Class<? extends CNode>> getSupportedNode() {
         return SUPPORTED;
@@ -69,11 +72,21 @@
         }
     }
 
+    public ValueTransformer getTransformer() {
+        return transformer;
+    }
+
+    public StringSourceNodeDefinition setTransformer(ValueTransformer transformer) {
+        this.transformer = transformer;
+        return this;
+    }
+
     @Override
     protected CNode doTransform(CNode node, Errors errors, boolean reverse) {
         if (reverse) {
             throw new UnsupportedOperationException("Not supported yet.");
         } else {
+            CValueNode outNode = null;
             if (node.getType() == NodeType.MAP) {
                 CMapNode mapNode = (CMapNode) node;
                 CTupleNode tuple = mapNode.getFirstChild();
@@ -81,7 +94,7 @@
                 try {
                     errors.pushNestedPath(tuple.getName());
                     if (tupleName.equalsIgnoreCase("$text")) {
-                        return tuple.getNode();
+                        outNode = (CValueNode) tuple.getNode();
                     } else if (tupleName.equalsIgnoreCase("$file")) {
                         String fileName = extractString(tuple, errors);
                         if (fileName != null) {
@@ -93,7 +106,7 @@
                             } else {
                                 try {
                                     String content = FileUtils.readFileToString(file);
-                                    return new CValueNode(content);
+                                    outNode = new CValueNode(content);
                                 } catch (IOException ex) {
                                     errors.reject(tuple.getNode(), "Error occured during reading the file.");
                                 }
@@ -103,6 +116,21 @@
                 } finally {
                     errors.popNestedPath();
                 }
+            } else if (node.getType() == NodeType.VALUE) {
+                outNode = (CValueNode) node;
+            }
+
+            if (outNode != null) {
+                if (transformer != null) {
+                    try {
+                        Object newValue = transformer.transform(outNode.getValue());
+                        outNode.setValue(newValue);
+                    } catch (ConversionException e) {
+                        errors.reject(node, GENERAL_CONVERSION_ERROR);
+                    }
+                }
+
+                return outNode;
             }
 
             return node;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/config/StringToExecutableStatementValueTransformer.java	Fri Sep 01 09:59:19 2017 +0200
@@ -0,0 +1,33 @@
+package com.passus.st.config;
+
+import com.passus.commons.ConversionException;
+import com.passus.config.ValueTransformer;
+import org.mvel2.MVEL;
+import org.mvel2.compiler.ExecutableStatement;
+
+/**
+ *
+ * @author Mirosław Hawrot
+ */
+public class StringToExecutableStatementValueTransformer implements ValueTransformer {
+
+    @Override
+    public Object transform(Object obj) throws ConversionException {
+        if (obj == null) {
+            return null;
+        }
+
+        String str = obj.toString();
+        try {
+            return (ExecutableStatement) MVEL.compileExpression(str);
+        } catch (Exception ex) {
+            throw new ConversionException("Compilation error:\n" + ex.getMessage());
+        }
+    }
+
+    @Override
+    public Object reverseTransform(Object obj) throws ConversionException {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+}
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMvelFilterTest.java	Thu Aug 31 15:32:31 2017 +0200
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMvelFilterTest.java	Fri Sep 01 09:59:19 2017 +0200
@@ -5,10 +5,6 @@
 import com.passus.net.http.HttpMethod;
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpRequestBuilder;
-import com.passus.net.http.HttpResponse;
-import com.passus.net.http.HttpResponseBuilder;
-import com.passus.st.ParametersBag;
-import com.passus.st.client.http.HttpFlowContext;
 import java.io.File;
 import java.util.List;
 import org.mvel2.MVEL;
@@ -30,7 +26,7 @@
                 + "$req.getUri().toString().equals(\"/index.html\") ? 1 : 0";
 
         HttpMvelFilter filter = new HttpMvelFilter();
-        filter.setOutbound(es(expression));
+        filter.setStatement(es(expression));
 
         int result = filter.filterOutbound(req, null, null);
 
@@ -39,41 +35,13 @@
     }
 
     @Test
-    public void testFilterInbound() {
-        HttpRequest req = HttpRequestBuilder.get("http://example.com/index.html").build();
-        HttpResponse resp = HttpResponseBuilder.ok().cookie("id", "123").build();
-        HttpFlowContext mockContext = HttpFilterTestUtils.createMockContext();
-        HttpFilterTestUtils.tagMessages(req, resp);
-
-        String expression = "hlp = com.passus.net.http.HttpMessageHelper.STRICT;\n"
-                + "c = hlp.getCookie($resp, \"id\");\n"
-                + "if (c != null) {\n"
-                + "   $ctx.scopes().getSession($req).set(\"token\", c.getValue());\n"
-                + "}";
-
-        HttpMvelFilter filter = new HttpMvelFilter();
-        filter.setInbound(es(expression));
-
-        int result = filter.filterInbound(req, resp, mockContext);
-        ParametersBag session = mockContext.scopes().getSession(req);
-
-        assertEquals(0, result);
-        assertEquals("123", session.get("token").toString());
-    }
-
-//    @Test
-//    public void testGlobal() {
-//    }
-    @Test
     public void testConfigure() throws Exception {
         File file = ResourceUtils.getFile("mvel/return1.mvel");
 
         String filterConfig = "filters:\n"
                 + "  - type: mvel\n"
-                + "    outbound:\n"
-                + "      \"return -1\"\n"
-                + "    inbound:\n"
-                + "      file: '" + file.getAbsolutePath() + "'";
+                + "    dir: out\n"
+                + "    script: return -1\n";
 
         Errors errors = new Errors();
         List<HttpFilter> filters = HttpFiltersConfigurator.getFilters(filterConfig, errors);
@@ -85,7 +53,7 @@
 
         HttpMvelFilter filter = (HttpMvelFilter) filters.get(0);
         assertEquals(-1, filter.filterOutbound(null, null, null));
-        assertEquals(1, filter.filterInbound(null, null, null));
+        assertEquals(0, filter.filterInbound(null, null, null));
     }
 
     private static ExecutableStatement es(String expression) {
--- a/stress-tester/src/test/java/com/passus/st/config/StringSourceNodeDefinitionTest.java	Thu Aug 31 15:32:31 2017 +0200
+++ b/stress-tester/src/test/java/com/passus/st/config/StringSourceNodeDefinitionTest.java	Fri Sep 01 09:59:19 2017 +0200
@@ -85,10 +85,11 @@
                 + "  content3: {$file: \"" + mvelFile.getAbsolutePath().replace("\\", "\\\\") + "\"}\n"
         );
 
+        StringSourceNodeDefinition strSourceDef = new StringSourceNodeDefinition();
         KeyNameVaryListNodeDefinition listDef = keyNameVaryListDef()
-                .add("content1", StringSourceNodeDefinition.INSTANCE)
-                .add("content2", StringSourceNodeDefinition.INSTANCE)
-                .add("content3", StringSourceNodeDefinition.INSTANCE);
+                .add("content1", strSourceDef)
+                .add("content2", strSourceDef)
+                .add("content3", strSourceDef);
 
         MapNodeDefinition rootNodeDef = mapDef(
                 tupleDef("list", listDef)