changeset 1020:4678815d3051

Classes from package com.passus.st.client.filter moved to com.passus.st.filter
author Devel 2
date Thu, 26 Mar 2020 13:27:52 +0100
parents b2e6ddf33fff
children 7a4efb7ff5b4
files stress-tester/src/main/java/com/passus/st/CliHelper.java stress-tester/src/main/java/com/passus/st/ConverterHttpClient.java stress-tester/src/main/java/com/passus/st/ConverterMain.java stress-tester/src/main/java/com/passus/st/Main.java stress-tester/src/main/java/com/passus/st/Main2.java stress-tester/src/main/java/com/passus/st/ReaderMain.java stress-tester/src/main/java/com/passus/st/client/FilterAware.java stress-tester/src/main/java/com/passus/st/client/Flow.java stress-tester/src/main/java/com/passus/st/client/FlowExecutor.java stress-tester/src/main/java/com/passus/st/client/FlowWorker.java stress-tester/src/main/java/com/passus/st/client/dns/filter/DnsFilter.java stress-tester/src/main/java/com/passus/st/client/dns/filter/DnsRecordTypeFilter.java stress-tester/src/main/java/com/passus/st/client/filter/CounterFilter.java stress-tester/src/main/java/com/passus/st/client/filter/CounterListener.java stress-tester/src/main/java/com/passus/st/client/filter/DefaultFlowFilterFactory.java stress-tester/src/main/java/com/passus/st/client/filter/FilterDirection.java stress-tester/src/main/java/com/passus/st/client/filter/FlowFilter.java stress-tester/src/main/java/com/passus/st/client/filter/FlowFilterChain.java stress-tester/src/main/java/com/passus/st/client/filter/FlowFilterFactory.java stress-tester/src/main/java/com/passus/st/client/filter/FlowFilterNodeDefinitionCreator.java stress-tester/src/main/java/com/passus/st/client/filter/FlowFilterPluginFactory.java stress-tester/src/main/java/com/passus/st/client/filter/FlowFiltersConfigurator.java stress-tester/src/main/java/com/passus/st/client/filter/MatchFilter.java stress-tester/src/main/java/com/passus/st/client/filter/MessagePredicate.java stress-tester/src/main/java/com/passus/st/client/filter/MessagePredicateNodeDefinition.java stress-tester/src/main/java/com/passus/st/client/filter/MessageWrapper.java stress-tester/src/main/java/com/passus/st/client/filter/MvelFilter.java stress-tester/src/main/java/com/passus/st/client/filter/SequenceFilter.java stress-tester/src/main/java/com/passus/st/client/filter/SequenceListener.java stress-tester/src/main/java/com/passus/st/client/http/ReporterDestination.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpAbstractLoginFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpBasicAuthLoginFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpDigestAuthLoginFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFiltersUtils.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFormLoginFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpLogoutFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMarkFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationOperations.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageWrapper.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpScopeModificationFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpVarsFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpZoneFilter.java stress-tester/src/main/java/com/passus/st/client/netflow/filter/NetflowFilter.java stress-tester/src/main/java/com/passus/st/client/netflow/filter/NetflowVersionFilter.java stress-tester/src/main/java/com/passus/st/filter/CounterFilter.java stress-tester/src/main/java/com/passus/st/filter/CounterListener.java stress-tester/src/main/java/com/passus/st/filter/DefaultFlowFilterFactory.java stress-tester/src/main/java/com/passus/st/filter/FilterDirection.java stress-tester/src/main/java/com/passus/st/filter/FlowFilter.java stress-tester/src/main/java/com/passus/st/filter/FlowFilterChain.java stress-tester/src/main/java/com/passus/st/filter/FlowFilterFactory.java stress-tester/src/main/java/com/passus/st/filter/FlowFilterNodeDefinitionCreator.java stress-tester/src/main/java/com/passus/st/filter/FlowFilterPluginFactory.java stress-tester/src/main/java/com/passus/st/filter/FlowFiltersConfigurator.java stress-tester/src/main/java/com/passus/st/filter/MatchFilter.java stress-tester/src/main/java/com/passus/st/filter/MessagePredicate.java stress-tester/src/main/java/com/passus/st/filter/MessagePredicateNodeDefinition.java stress-tester/src/main/java/com/passus/st/filter/MessageWrapper.java stress-tester/src/main/java/com/passus/st/filter/MvelFilter.java stress-tester/src/main/java/com/passus/st/filter/SequenceFilter.java stress-tester/src/main/java/com/passus/st/filter/SequenceListener.java stress-tester/src/main/java/com/passus/st/job/TestJob.java stress-tester/src/test/java/com/passus/st/ConverterHttpClientTest.java stress-tester/src/test/java/com/passus/st/FilterScanner.java stress-tester/src/test/java/com/passus/st/client/FlowFilterChainTest.java stress-tester/src/test/java/com/passus/st/client/dns/filter/DnsRecordTypeFilterTest.java stress-tester/src/test/java/com/passus/st/client/filter/CounterFilterTest.java stress-tester/src/test/java/com/passus/st/client/filter/FilterTestUtils.java stress-tester/src/test/java/com/passus/st/client/filter/MatchFilterTest.java stress-tester/src/test/java/com/passus/st/client/filter/MvelFilterTest.java stress-tester/src/test/java/com/passus/st/client/filter/SequenceFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpBasicAuthLoginFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpCsrfFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpCsrfFormFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpDigestAuthLoginFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpFiltersConfiguratorTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpFiltersNodeDefinitionCreatorTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpFormLoginFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpHostRewriterFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpLogoutFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMarkFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformerTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageWrapperTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpScopeModificationFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpSessionBlockerFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpSessionCookieFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpZoneFilterTest.java stress-tester/src/test/java/com/passus/st/client/netflow/filter/NetflowVersionFilterTest.java stress-tester/src/test/java/com/passus/st/filter/CounterFilterTest.java stress-tester/src/test/java/com/passus/st/filter/FilterTestUtils.java stress-tester/src/test/java/com/passus/st/filter/HttpMessageValueExtractorTest.java stress-tester/src/test/java/com/passus/st/filter/HttpMessageWrapperDynamicExtractorTest.java stress-tester/src/test/java/com/passus/st/filter/HttpMessageWrapperStaticExtractorTest.java stress-tester/src/test/java/com/passus/st/filter/MatchFilterTest.java stress-tester/src/test/java/com/passus/st/filter/MvelFilterTest.java stress-tester/src/test/java/com/passus/st/filter/SequenceFilterTest.java stress-tester/src/test/java/com/passus/st/vars/VarsConfiguratorTest.java
diffstat 100 files changed, 2008 insertions(+), 2023 deletions(-) [+]
line wrap: on
line diff
--- a/stress-tester/src/main/java/com/passus/st/CliHelper.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/CliHelper.java	Thu Mar 26 13:27:52 2020 +0100
@@ -10,8 +10,8 @@
 import com.passus.net.PortRangeSet;
 import com.passus.net.http.session.HttpSessionAnalyzer;
 import com.passus.st.client.FlowExecutor;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import com.passus.st.client.http.filter.HttpVarsFilter;
 import com.passus.st.emitter.Emitter;
 import com.passus.st.emitter.PassThroughSessionMapper;
--- a/stress-tester/src/main/java/com/passus/st/ConverterHttpClient.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/ConverterHttpClient.java	Thu Mar 26 13:27:52 2020 +0100
@@ -3,8 +3,8 @@
 import com.passus.commons.Assert;
 import com.passus.commons.service.Service;
 import com.passus.st.client.*;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFilterChain;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFilterChain;
 import com.passus.st.client.http.HttpScopes;
 import com.passus.st.client.http.filter.HttpFilter;
 import com.passus.st.client.http.filter.HttpFlowUtils;
--- a/stress-tester/src/main/java/com/passus/st/ConverterMain.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/ConverterMain.java	Thu Mar 26 13:27:52 2020 +0100
@@ -1,7 +1,7 @@
 package com.passus.st;
 
 import com.passus.config.ConfigurationContext;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 import com.passus.st.reader.nc.HttpWriteMode;
 import com.passus.st.source.NcEventDestination;
 import com.passus.st.source.PcapSessionEventSource;
--- a/stress-tester/src/main/java/com/passus/st/Main.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/Main.java	Thu Mar 26 13:27:52 2020 +0100
@@ -5,7 +5,7 @@
 import com.passus.commons.service.Registry;
 import com.passus.config.ConfigurationContext;
 import com.passus.st.client.*;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 import com.passus.st.client.http.*;
 import com.passus.st.emitter.SessionMapper;
 import com.passus.st.emitter.nio.NioEmitter;
--- a/stress-tester/src/main/java/com/passus/st/Main2.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/Main2.java	Thu Mar 26 13:27:52 2020 +0100
@@ -5,7 +5,7 @@
 import com.passus.commons.service.Registry;
 import com.passus.config.ConfigurationContext;
 import com.passus.st.client.*;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 import com.passus.st.client.http.*;
 import com.passus.st.emitter.SessionMapper;
 import com.passus.st.emitter.nio.NioEmitter;
--- a/stress-tester/src/main/java/com/passus/st/ReaderMain.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/ReaderMain.java	Thu Mar 26 13:27:52 2020 +0100
@@ -6,7 +6,7 @@
 import com.passus.st.client.Event;
 import com.passus.st.client.SessionPayloadEvent;
 import com.passus.st.client.SessionStatusEvent;
-import com.passus.st.client.filter.MessagePredicate;
+import com.passus.st.filter.MessagePredicate;
 import com.passus.st.emitter.SessionInfo;
 import com.passus.st.filter.Transformers;
 import com.passus.st.source.EventSource;
--- a/stress-tester/src/main/java/com/passus/st/client/FilterAware.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/FilterAware.java	Thu Mar 26 13:27:52 2020 +0100
@@ -1,6 +1,6 @@
 package com.passus.st.client;
 
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 
 import java.util.Collection;
 import java.util.List;
--- a/stress-tester/src/main/java/com/passus/st/client/Flow.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/Flow.java	Thu Mar 26 13:27:52 2020 +0100
@@ -1,7 +1,7 @@
 package com.passus.st.client;
 
 import com.passus.config.Configurable;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 import com.passus.st.emitter.Emitter;
 import com.passus.st.metric.MetricSource;
 
--- a/stress-tester/src/main/java/com/passus/st/client/FlowExecutor.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/FlowExecutor.java	Thu Mar 26 13:27:52 2020 +0100
@@ -13,9 +13,9 @@
 import com.passus.config.validation.EnumValidator;
 import com.passus.config.validation.Errors;
 import com.passus.config.validation.LongValidator;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFilterChain;
-import com.passus.st.client.filter.FlowFilterNodeDefinitionCreator;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFilterChain;
+import com.passus.st.filter.FlowFilterNodeDefinitionCreator;
 import com.passus.st.emitter.Emitter;
 import com.passus.st.metric.MetricSource;
 import com.passus.st.metric.MetricsContainer;
--- a/stress-tester/src/main/java/com/passus/st/client/FlowWorker.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/FlowWorker.java	Thu Mar 26 13:27:52 2020 +0100
@@ -3,7 +3,7 @@
 import com.passus.commons.Assert;
 import com.passus.commons.time.TimeAware;
 import com.passus.commons.time.TimeGenerator;
-import com.passus.st.client.filter.FlowFilterChain;
+import com.passus.st.filter.FlowFilterChain;
 import com.passus.st.emitter.Emitter;
 import com.passus.st.emitter.EmitterHandler;
 import com.passus.st.emitter.SessionInfo;
--- a/stress-tester/src/main/java/com/passus/st/client/dns/filter/DnsFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/dns/filter/DnsFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -2,7 +2,7 @@
 
 import com.passus.net.dns.DnsRecord;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 
 public abstract class DnsFilter implements FlowFilter {
 
--- a/stress-tester/src/main/java/com/passus/st/client/dns/filter/DnsRecordTypeFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/dns/filter/DnsRecordTypeFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -9,7 +9,7 @@
 import com.passus.net.dns.DnsRecord;
 import com.passus.net.dns.DnsRecordType;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 import com.passus.st.plugin.PluginConstants;
 
 import java.util.Collections;
--- a/stress-tester/src/main/java/com/passus/st/client/filter/CounterFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.annotations.Plugin;
-import com.passus.config.Configuration;
-import com.passus.config.ConfigurationContext;
-import com.passus.config.annotations.NodeDefinitionCreate;
-import com.passus.config.schema.NodeDefinition;
-import com.passus.config.schema.NodeDefinitionCreator;
-import com.passus.st.client.FlowContext;
-import com.passus.st.plugin.PluginConstants;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
-
-/**
- *
- * @author mikolaj.podbielski
- */
-@NodeDefinitionCreate(CounterFilter.NodeDefCreator.class)
-@Plugin(name = CounterFilter.TYPE, category = PluginConstants.CATEGORY_FLOW_FILTER)
-public class CounterFilter implements FlowFilter {
-
-    public static final String TYPE = "counter";
-
-    private static final AtomicInteger ID = new AtomicInteger();
-
-    private MessagePredicate predicate;
-    private String name;
-    private int limit;
-    private CounterListener listener = (e) -> {
-    };
-
-    private int count;
-
-    MessagePredicate getPredicate() {
-        return predicate;
-    }
-
-    void setPredicate(MessagePredicate predicate) {
-        this.predicate = predicate;
-    }
-
-    String getName() {
-        return name;
-    }
-
-    void setName(String name) {
-        this.name = name;
-    }
-
-    int getLimit() {
-        return limit;
-    }
-
-    void setLimit(int limit) {
-        this.limit = limit;
-    }
-
-    CounterListener getListener() {
-        return listener;
-    }
-
-    void setListener(CounterListener listener) {
-        this.listener = listener;
-    }
-
-    @Override
-    public void configure(Configuration config, ConfigurationContext context) {
-        predicate = (MessagePredicate) config.get("applyIf");
-        limit = config.getInteger("limit");
-        name = config.getString("name", "counter_" + ID.getAndIncrement());
-        // TODO:
-        listener = System.out::println;
-    }
-
-    @Override
-    public int filterInbound(Object req, Object resp, FlowContext context) {
-        if (predicate.test(req, resp, context)) {
-            if (++count >= limit) {
-                listener.limitReached(new CounterListener.Event(this));
-            }
-        }
-        return DUNNO;
-    }
-
-    @Override
-    public CounterFilter instanceForWorker(int index) {
-        CounterFilter filter = new CounterFilter();
-        filter.predicate = predicate;
-        filter.name = name;
-        filter.limit = limit;
-        filter.listener = listener;
-        return filter;
-    }
-
-    public static class NodeDefCreator implements NodeDefinitionCreator {
-
-        @Override
-        public NodeDefinition create() {
-            return mapDef(
-                    tupleDef("applyIf", new MessagePredicateNodeDefinition()),
-                    tupleDef("limit", valueDefInteger()),
-                    tupleDef("name", valueDef()).setRequired(false)
-            );
-        }
-
-    }
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/CounterListener.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-package com.passus.st.client.filter;
-
-/**
- *
- * @author mikolaj.podbielski
- */
-public interface CounterListener {
-    
-    public void limitReached(Event event);
-    
-    public static final class Event {
-        private final CounterFilter filter;
-//        HttpRequest req
-
-        public Event(CounterFilter filter) {
-            this.filter = filter;
-        }
-        
-    }
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/DefaultFlowFilterFactory.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.net.http.HttpMessage;
-import com.passus.net.http.HttpRequest;
-import com.passus.net.http.HttpResponse;
-import com.passus.st.client.FlowContext;
-import com.passus.st.client.http.HttpFlowConst;
-import com.passus.st.client.http.HttpFlowContext;
-import com.passus.st.client.http.filter.HttpFilterMessageWrapper;
-import com.passus.st.client.http.filter.HttpFilterRequestWrapper;
-import com.passus.st.client.http.filter.HttpFilterResponseWrapper;
-import com.passus.st.client.http.filter.HttpMessageWrapper;
-
-import static com.passus.st.Protocols.HTTP;
-
-public final class DefaultFlowFilterFactory implements FlowFilterFactory {
-
-    @Override
-    public MessageWrapper createWrapper(Object req, Object resp, FlowContext context) {
-        if (req instanceof HttpRequest || resp instanceof HttpResponse) {
-            return createHttpMessageWrapper((HttpRequest) req, (HttpResponse) resp, context);
-        } else {
-            return new MessageWrapper(req, resp, context);
-        }
-    }
-
-    @Override
-    public MessageWrapper createWrapper(Object obj) {
-        if (obj instanceof HttpMessage) {
-            HttpMessage message = (HttpMessage) obj;
-            if (message.isRequest()) {
-                return createHttpMessageWrapper((HttpRequest) message, null, null);
-            } else {
-                return createHttpMessageWrapper(null, (HttpResponse) message, null);
-            }
-        } else {
-            return new MessageWrapper(obj, null, null);
-        }
-    }
-
-    public HttpMessageWrapper createHttpMessageWrapper(HttpRequest req, HttpResponse resp, FlowContext context) {
-        HttpFilterRequestWrapper reqWrapper = (HttpFilterRequestWrapper) createHttpMessageWrapper(req);
-        HttpFilterResponseWrapper respWrapper = (HttpFilterResponseWrapper) createHttpMessageWrapper(resp);
-
-        HttpFlowContext httpContext;
-        HttpFilterResponseWrapper origResp;
-        if (context != null) {
-            httpContext = context.getParamValue(HttpFlowConst.PARAM_HTTP_CONTEXT);
-            origResp = (HttpFilterResponseWrapper) createHttpMessageWrapper(httpContext.origResponse());
-        } else {
-            httpContext = null;
-            origResp = null;
-        }
-
-        return new HttpMessageWrapper(reqWrapper, respWrapper, context, origResp, httpContext);
-    }
-
-    public HttpFilterMessageWrapper createHttpMessageWrapper(HttpMessage message) {
-        if (message == null) {
-            return null;
-        }
-
-        if (message.isRequest()) {
-            return new HttpFilterRequestWrapper((HttpRequest) message);
-        } else {
-            return new HttpFilterResponseWrapper((HttpResponse) message);
-        }
-    }
-
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/FilterDirection.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-package com.passus.st.client.filter;
-
-/**
- *
- * @author Mirosław Hawrot
- */
-public enum FilterDirection {
-
-    IN, OUT, BOTH
-
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/FlowFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.config.Configurable;
-import com.passus.config.Configuration;
-import com.passus.config.ConfigurationContext;
-import com.passus.st.client.FlowContext;
-
-public interface FlowFilter extends Configurable {
-
-    int DENY = -1;
-
-    int DUNNO = 0;
-
-    int ACCEPT = 1;
-
-    default void reset() {
-
-    }
-
-    @Override
-    default void configure(Configuration config, ConfigurationContext context) {
-
-    }
-
-    default int filterInbound(Object req, Object resp, FlowContext context) {
-        return DUNNO;
-    }
-
-    default int filterOutbound(Object req, Object resp, FlowContext context) {
-        return DUNNO;
-    }
-
-    FlowFilter instanceForWorker(int index);
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/FlowFilterChain.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.filter.Filter;
-import com.passus.st.client.FlowContext;
-
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-public class FlowFilterChain {
-
-    protected final LinkedList<FlowFilter> filters = new LinkedList<>();
-
-    public List<FlowFilter> getFilters() {
-        return Collections.unmodifiableList(filters);
-    }
-
-    @SuppressWarnings("unchecked")
-    public void addFilter(FlowFilter filter) {
-        filters.add(filter);
-    }
-
-    public void clear() {
-        filters.clear();
-    }
-
-    public boolean isEmpty() {
-        return filters.isEmpty();
-    }
-
-    @SuppressWarnings("unchecked")
-    public int filterOutbound(Object request, Object resp, FlowContext context) {
-        Iterator<FlowFilter> it = filters.iterator();
-        while (it.hasNext()) {
-            FlowFilter f = it.next();
-            int res = f.filterOutbound(request, resp, context);
-            if (res != Filter.DUNNO) {
-                return res;
-            }
-        }
-
-        return Filter.ACCEPT;
-    }
-
-    @SuppressWarnings("unchecked")
-    public int filterInbound(Object request, Object resp, FlowContext context) {
-        Iterator<FlowFilter> it = filters.iterator();
-        while (it.hasNext()) {
-            FlowFilter f = it.next();
-            int res = f.filterInbound(request, resp, context);
-            if (res != Filter.DUNNO) {
-                return res;
-            }
-        }
-
-        return Filter.ACCEPT;
-    }
-
-    public void reset() {
-        Iterator<FlowFilter> it = filters.iterator();
-        while (it.hasNext()) {
-            FlowFilter f = it.next();
-            f.reset();
-        }
-    }
-
-    public FlowFilterChain instanceForWorker(int index) {
-        FlowFilterChain copy = new FlowFilterChain();
-
-        Iterator<FlowFilter> it = filters.iterator();
-        while (it.hasNext()) {
-            FlowFilter f = it.next();
-            copy.addFilter(f.instanceForWorker(index));
-        }
-
-        return copy;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("FlowFilterChain@").append(System.identityHashCode(this));
-        sb.append(" {");
-
-        Iterator<FlowFilter> it = filters.iterator();
-        while (it.hasNext()) {
-            FlowFilter f = it.next();
-            sb.append(f).append(", ");
-        }
-        sb.append("}");
-
-        return sb.toString();
-    }
-
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/FlowFilterFactory.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.net.http.HttpRequest;
-import com.passus.net.http.HttpResponse;
-import com.passus.st.client.FlowContext;
-import com.passus.st.client.http.filter.HttpMessageWrapper;
-
-public interface FlowFilterFactory {
-
-    FlowFilterFactory DEFAULT_FACTORY = new DefaultFlowFilterFactory();
-
-    MessageWrapper createWrapper(Object req, Object resp, FlowContext context);
-
-    MessageWrapper createWrapper(Object obj);
-
-    HttpMessageWrapper createHttpMessageWrapper(HttpRequest req, HttpResponse resp, FlowContext context);
-
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/FlowFilterNodeDefinitionCreator.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.config.schema.DynaKeyValueVaryListNodeDefinition;
-import com.passus.config.schema.NodeDefinition;
-import com.passus.config.schema.NodeDefinitionCreator;
-
-/**
- * @author Mirosław Hawrot
- */
-public class FlowFilterNodeDefinitionCreator implements NodeDefinitionCreator {
-
-    public static NodeDefinition createFiltersList(boolean transformToObject) {
-        return new DynaKeyValueVaryListNodeDefinition("type", FlowFilterPluginFactory.getInstance())
-                .setTransformToPluginObject(transformToObject);
-    }
-
-    @Override
-    public NodeDefinition create() {
-        return createFiltersList(true);
-    }
-
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/FlowFilterPluginFactory.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.plugin.PluginFactory;
-import com.passus.st.plugin.PluginConstants;
-
-/**
- * @author Mirosław Hawrot
- */
-public final class FlowFilterPluginFactory extends PluginFactory<FlowFilter> {
-
-    private static FlowFilterPluginFactory instance;
-
-    public FlowFilterPluginFactory() {
-        super(PluginConstants.CATEGORY_FLOW_FILTER, FlowFilter.class);
-    }
-
-    public static synchronized FlowFilterPluginFactory getInstance() {
-        if (instance == null) {
-            instance = new FlowFilterPluginFactory();
-        }
-
-        return instance;
-    }
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/FlowFiltersConfigurator.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.annotations.Plugin;
-import com.passus.config.*;
-import com.passus.config.schema.NodeDefinition;
-import com.passus.config.validation.Errors;
-import com.passus.st.plugin.PluginConstants;
-import com.passus.st.utils.ConfigurationContextConsts;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * @author Mirosław Hawrot
- */
-@Plugin(name = FlowFiltersConfigurator.DOMAIN, category = PluginConstants.CATEGORY_DOMAIN_CONFIGURATOR)
-public class FlowFiltersConfigurator implements DomainConfigurator {
-
-    public static final String DOMAIN = "filters";
-
-    private static final FlowFilterNodeDefinitionCreator CREATOR = new FlowFilterNodeDefinitionCreator();
-
-    private final NodeDefinition nodeDef = CREATOR.create();
-
-    @Override
-    public String getDomain() {
-        return DOMAIN;
-    }
-
-    public static List<FlowFilter> getFilters(File configFile, Errors errors, ConfigurationContext context) throws IOException, NodeException {
-        Configuration config = YamlConfigurationReader.readFromFile(configFile);
-        return getFilters(config, errors, context);
-    }
-
-    public static List<FlowFilter> getFilters(String configStr, Errors errors, ConfigurationContext context) throws IOException, NodeException {
-        Configuration config = YamlConfigurationReader.readFromString(configStr);
-        return getFilters(config, errors, context);
-    }
-
-    public static List<FlowFilter> getFilters(Configuration config, Errors errors, ConfigurationContext context) {
-        NodeDefinition nodeDef = CREATOR.create();
-        return ConfigurationUtils.getDomainObjects(config, errors, context, DOMAIN, nodeDef);
-    }
-
-    @Override
-    public void validate(Configuration config, Errors errors, ConfigurationContext context) {
-        CCompositeNode rootNode = config.getRootNode();
-        nodeDef.validate(rootNode, errors, context);
-    }
-
-    @Override
-    public void configure(Configuration config, Errors errors, ConfigurationContext context) {
-        CCompositeNode rootNode = config.getRootNode();
-        nodeDef.transform(rootNode, errors, context);
-        if (!errors.hasError()) {
-            try {
-                List<FlowFilter> filters = ConfigurationUtils.convertToList(rootNode, FlowFilter.class);
-                context.add(ConfigurationContextConsts.FLOW_FILTERS, filters);
-            } catch (Exception e) {
-                throw new RuntimeException(e.getMessage(), e);
-            }
-
-        }
-    }
-
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/MatchFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.Assert;
-import com.passus.commons.annotations.Plugin;
-import com.passus.config.Configuration;
-import com.passus.config.ConfigurationContext;
-import com.passus.config.annotations.NodeDefinitionCreate;
-import com.passus.config.schema.NodeDefinition;
-import com.passus.config.schema.NodeDefinitionCreator;
-import com.passus.st.client.FlowContext;
-import com.passus.st.plugin.PluginConstants;
-
-import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
-
-/**
- * @author Mirosław Hawrot
- */
-@NodeDefinitionCreate(MatchFilter.MatchFilterNodeDefCreator.class)
-@Plugin(name = MatchFilter.TYPE, category = PluginConstants.CATEGORY_FLOW_FILTER)
-public class MatchFilter implements FlowFilter {
-
-    public static final String TYPE = "matcher";
-
-    private MessagePredicate predicate;
-
-    private boolean acceptOnMatch = true;
-
-    private final FlowFilterFactory filterFactory;
-
-    public MatchFilter() {
-        this(FlowFilterFactory.DEFAULT_FACTORY);
-    }
-
-    public MatchFilter(FlowFilterFactory filterFactory) {
-        Assert.notNull(filterFactory, "filterFactory");
-        this.filterFactory = filterFactory;
-    }
-
-    public MessagePredicate getPredicate() {
-        return predicate;
-    }
-
-    public void setPredicate(MessagePredicate predicate) {
-        this.predicate = predicate;
-    }
-
-    public boolean isAcceptOnMatch() {
-        return acceptOnMatch;
-    }
-
-    public void setAcceptOnMatch(boolean acceptOnMatch) {
-        this.acceptOnMatch = acceptOnMatch;
-    }
-
-    @Override
-    public void configure(Configuration config, ConfigurationContext context) {
-        predicate = (MessagePredicate) config.get("matches");
-        acceptOnMatch = config.getBoolean("acceptOnMatch", true);
-    }
-
-    @Override
-    public int filterOutbound(Object request, Object resp, FlowContext context) {
-        if (predicate == null) {
-            return DUNNO;
-        }
-
-        boolean match = predicate.test(filterFactory.createWrapper(request, resp, context));
-        if (!match) {
-            return DUNNO;
-        }
-
-        return acceptOnMatch ? ACCEPT : DENY;
-    }
-
-    @Override
-    public MatchFilter instanceForWorker(int index) {
-        MatchFilter filter = new MatchFilter();
-        filter.acceptOnMatch = acceptOnMatch;
-        filter.predicate = predicate;
-        return filter;
-    }
-
-    public static class MatchFilterNodeDefCreator implements NodeDefinitionCreator {
-
-        @Override
-        public NodeDefinition create() {
-            return mapDef(
-                    tupleDef("acceptOnMatch", valueDefBool()).setRequired(false),
-                    tupleDef("matches", new MessagePredicateNodeDefinition())
-            );
-        }
-
-    }
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/MessagePredicate.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.Assert;
-import com.passus.commons.ConversionException;
-import com.passus.config.NodeException;
-import com.passus.st.client.FlowContext;
-import com.passus.st.filter.Transformers;
-
-import java.io.IOException;
-import java.util.function.Predicate;
-
-import static com.passus.st.client.filter.FlowFilterFactory.DEFAULT_FACTORY;
-
-public class MessagePredicate<T> implements Predicate<T> {
-
-    protected final Predicate predicate;
-
-    protected final FlowFilterFactory filterFactory;
-
-    public MessagePredicate(Predicate predicate) {
-        this(predicate, DEFAULT_FACTORY);
-    }
-
-    public MessagePredicate(Predicate predicate, FlowFilterFactory filterFactory) {
-        Assert.notNull(predicate, "predicate");
-        Assert.notNull(filterFactory, "filterFactory");
-        this.predicate = predicate;
-        this.filterFactory = filterFactory;
-    }
-
-    public Predicate getPredicate() {
-        return predicate;
-    }
-
-    public boolean test(Object req, Object resp, FlowContext context) {
-        return test(filterFactory.createWrapper(req, resp, context));
-    }
-
-    public boolean test(MessageWrapper wrapper) {
-        return predicate.test(wrapper);
-    }
-
-    @Override
-    public boolean test(T message) {
-        return predicate.test(filterFactory.createWrapper(message));
-    }
-
-    @Override
-    public String toString() {
-        return predicate.toString();
-    }
-
-    public static MessagePredicate parse(String content) throws ConversionException, IOException, NodeException {
-        Predicate predicate = Transformers.PREDICATE.transform(content);
-        return new MessagePredicate(predicate);
-    }
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/MessagePredicateNodeDefinition.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.ConversionException;
-import com.passus.commons.utils.ArrayUtils;
-import com.passus.config.*;
-import com.passus.config.schema.NodeDefinition;
-import com.passus.config.validation.Errors;
-import com.passus.filter.config.PredicateNodeTransformer;
-import com.passus.lookup.filter.LookupKeyExistsLeftOperatorTransformer;
-import com.passus.lookup.filter.LookupValueExtractorTransformer;
-import com.passus.st.filter.Transformers;
-
-import java.util.Set;
-import java.util.function.Predicate;
-
-import static com.passus.config.validation.ValidationDefaultMessages.GENERAL_CONVERSION_ERROR;
-
-/**
- * @author Mirosław Hawrot
- */
-public class MessagePredicateNodeDefinition extends NodeDefinition<MessagePredicateNodeDefinition> {
-
-    private static final PredicateNodeTransformer TRANSFORMER = Transformers.PREDICATE;
-
-    private final static Set<Class<? extends CNode>> SUPPORTED = ArrayUtils.asUnmodifiableSet(CListNode.class, CMapNode.class);
-
-    @Override
-    public Set<Class<? extends CNode>> getSupportedNode() {
-        return SUPPORTED;
-    }
-
-    @Override
-    protected void doValidate(CNode node, Errors errors, ConfigurationContext context) {
-        try {
-            // TODO: refactor
-            LookupKeyExistsLeftOperatorTransformer lookupKeyExistsTransformer = Transformers.lookupKeyExistsTransformer(context);
-            LookupValueExtractorTransformer lookupValueTransformer = Transformers.lookupValueTransformer(context);
-            TRANSFORMER.addLeftOperatorTransformer(lookupKeyExistsTransformer);
-            TRANSFORMER.addValueExtractorTransformer(lookupValueTransformer);
-            TRANSFORMER.transform(node);
-            TRANSFORMER.removeLeftOperatorTransformer(lookupKeyExistsTransformer);
-            TRANSFORMER.removeValueExtractorTransformer(lookupValueTransformer);
-        } catch (NodeConversionException ex) {
-            errors.reject(node, ex.getMessage());
-        } catch (ConversionException ex) {
-            errors.reject(node, GENERAL_CONVERSION_ERROR);
-        }
-    }
-
-    @Override
-    protected CNode doTransform(CNode node, Errors errors, ConfigurationContext context, boolean reverse) {
-        if (reverse) {
-            throw new UnsupportedOperationException("Not supported yet.");
-        } else {
-            try {
-                // TODO: refactor
-                LookupKeyExistsLeftOperatorTransformer lookupKeyExistsTransformer = Transformers.lookupKeyExistsTransformer(context);
-                LookupValueExtractorTransformer lookupValueTransformer = Transformers.lookupValueTransformer(context);
-                TRANSFORMER.addLeftOperatorTransformer(lookupKeyExistsTransformer);
-                TRANSFORMER.addValueExtractorTransformer(lookupValueTransformer);
-                Predicate predicate = TRANSFORMER.transform(node);
-                TRANSFORMER.removeLeftOperatorTransformer(lookupKeyExistsTransformer);
-                TRANSFORMER.removeValueExtractorTransformer(lookupValueTransformer);
-                return new CValueNode(new MessagePredicate(predicate));
-            } catch (NodeConversionException ex) {
-                errors.reject(node, ex.getMessage());
-            } catch (ConversionException ex) {
-                errors.reject(node, GENERAL_CONVERSION_ERROR);
-            }
-        }
-
-        return null;
-    }
-
-    @Override
-    protected boolean doEquals(CNode node1, CNode node2) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/MessageWrapper.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.st.client.FlowContext;
-
-public class MessageWrapper<R, S> {
-
-    protected final R req;
-
-    protected final S resp;
-
-    protected final FlowContext context;
-
-    public MessageWrapper(R req, S resp, FlowContext context) {
-        this.req = req;
-        this.resp = resp;
-        this.context = context;
-    }
-
-    public R getReq() {
-        return req;
-    }
-
-    public S getResp() {
-        return resp;
-    }
-
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/MvelFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.Assert;
-import com.passus.commons.annotations.Plugin;
-import com.passus.config.Configuration;
-import com.passus.config.ConfigurationContext;
-import com.passus.config.annotations.NodeDefinitionCreate;
-import com.passus.config.schema.NodeDefinition;
-import com.passus.config.schema.NodeDefinitionCreator;
-import com.passus.net.http.HttpRequest;
-import com.passus.st.client.FlowContext;
-import com.passus.st.client.http.HttpScopes;
-import com.passus.st.client.http.filter.HttpFilter;
-import com.passus.st.config.StringSourceNodeDefinition;
-import com.passus.st.config.StringToExecutableStatementValueTransformer;
-import com.passus.st.plugin.PluginConstants;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.mvel2.compiler.ExecutableStatement;
-import org.mvel2.integration.VariableResolverFactory;
-import org.mvel2.integration.impl.CachingMapVariableResolverFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
-import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
-
-/**
- * @author Mirosław Hawrot
- */
-@NodeDefinitionCreate(MvelFilter.MvelFilterNodeDefCreator.class)
-@Plugin(name = MvelFilter.TYPE, category = PluginConstants.CATEGORY_FLOW_FILTER)
-public class MvelFilter implements FlowFilter {
-
-    public static final String TYPE = "mvel";
-
-    private static final Logger LOGGER = LogManager.getLogger(MvelFilter.class);
-
-    private VariableResolverFactory globalFactory;
-
-    private ExecutableStatement statement;
-
-    private FilterDirection direction = FilterDirection.OUT;
-
-    public MvelFilter() {
-    }
-
-    public MvelFilter(ExecutableStatement statement, FilterDirection direction) {
-        Assert.notNull(statement, "statement");
-        Assert.notNull(direction, "direction");
-        this.statement = statement;
-        this.direction = direction;
-    }
-
-    public VariableResolverFactory getGlobalFactory() {
-        return globalFactory;
-    }
-
-    public void setGlobalFactory(VariableResolverFactory globalFactory) {
-        this.globalFactory = globalFactory;
-    }
-
-    public FilterDirection getDirection() {
-        return direction;
-    }
-
-    public void setDirection(FilterDirection direction) {
-        Assert.notNull(direction, "direction");
-        this.direction = direction;
-    }
-
-    public ExecutableStatement getStatement() {
-        return statement;
-    }
-
-    public void setStatement(ExecutableStatement statement) {
-        this.statement = statement;
-    }
-
-    @Override
-    public void configure(Configuration config, ConfigurationContext context) {
-        direction = config.get("dir", FilterDirection.OUT);
-        statement = (ExecutableStatement) config.get("script");
-    }
-
-    private int filter(Object req, Object resp, FlowContext context) {
-        if (statement == null) {
-            return DUNNO;
-        }
-
-        Map<String, Object> vars = new HashMap<>(3);
-        vars.put("$req", req);
-        vars.put("$resp", resp);
-        vars.put("$context", context);
-        if (context != null && req instanceof HttpRequest) {
-            HttpRequest httpReq = (HttpRequest) req;
-            HttpScopes scopes = extractHttpContext(context).scopes();
-            vars.put("$extractHttpContext", scopes);
-            vars.put("$httpSession", scopes.getSession(httpReq));
-        }
-
-        CachingMapVariableResolverFactory factory = new CachingMapVariableResolverFactory(vars);
-        if (globalFactory != null) {
-            factory.setNextFactory(globalFactory);
-        }
-
-        try {
-            Object out = statement.getValue(null, factory);
-            if (out instanceof Integer) {
-                int value = (Integer) out;
-                if (value < 0) {
-                    return DENY;
-                } else if (value > 0) {
-                    return ACCEPT;
-                }
-
-                return DUNNO;
-            }
-        } catch (Throwable th) {
-            LOGGER.trace(th);
-        }
-
-        return DUNNO;
-    }
-
-    @Override
-    public int filterOutbound(Object req, Object resp, FlowContext context) {
-        if (direction == FilterDirection.BOTH || direction == FilterDirection.OUT) {
-            return filter(req, resp, context);
-        }
-
-        return DUNNO;
-    }
-
-    @Override
-    public int filterInbound(Object req, Object resp, FlowContext context) {
-        if (direction == FilterDirection.BOTH || direction == FilterDirection.IN) {
-            return filter(req, resp, context);
-        }
-
-        return DUNNO;
-    }
-
-    @Override
-    public FlowFilter instanceForWorker(int index) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public static final class MvelFilterNodeDefCreator implements NodeDefinitionCreator {
-
-        @Override
-        public NodeDefinition create() {
-            return mapDef(
-                    tupleDef("dir", enumDef(FilterDirection.class)).setRequired(false),
-                    tupleDef("script", new StringSourceNodeDefinition()
-                            .setTransformer(new StringToExecutableStatementValueTransformer())
-                    )
-            );
-        }
-
-    }
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/SequenceFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,482 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.Assert;
-import com.passus.commons.annotations.Plugin;
-import com.passus.commons.metric.MapMetric;
-import com.passus.commons.service.Registry;
-import com.passus.commons.time.SystemTimeGenerator;
-import com.passus.commons.time.TimeAware;
-import com.passus.commons.time.TimeGenerator;
-import com.passus.config.*;
-import com.passus.config.annotations.NodeDefinitionCreate;
-import com.passus.config.schema.*;
-import com.passus.config.validation.Errors;
-import com.passus.filter.ValueExtractor;
-import com.passus.filter.ValueExtractorParser;
-import com.passus.filter.config.PredicateNodeTransformer;
-import com.passus.st.client.FlowContext;
-import com.passus.st.client.http.ReporterDestination;
-import com.passus.st.filter.Transformers;
-import com.passus.st.plugin.PluginConstants;
-
-import java.io.Serializable;
-import java.text.ParseException;
-import java.util.*;
-import java.util.function.Predicate;
-
-import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
-
-/**
- * @author mikolaj.podbielski
- */
-@NodeDefinitionCreate(SequenceFilter.NodeDefCreator.class)
-@Plugin(name = SequenceFilter.TYPE, category = PluginConstants.CATEGORY_FLOW_FILTER)
-public class SequenceFilter implements FlowFilter, TimeAware {
-
-    public static class SequenceItem {
-
-        private final Predicate predicate;
-        private boolean mustOccur = true;
-        private long time = 10_000;
-        private int num;
-        private String alias;
-        private String[] aliases;
-
-        public SequenceItem(Predicate predicate, int num) {
-            this(predicate);
-            this.num = num;
-        }
-
-        public SequenceItem(Predicate predicate) {
-            if (predicate == null) {
-                throw new NullPointerException();
-            }
-            this.predicate = predicate;
-        }
-
-        public Predicate getPredicate() {
-            return predicate;
-        }
-
-        public int getNum() {
-            return num;
-        }
-
-        public void setNum(int num) {
-            this.num = num;
-        }
-
-        public long getTime() {
-            return time;
-        }
-
-        public void setTime(long time) {
-            if (time <= 0) {
-                throw new IllegalArgumentException("Time must be greater than zero.");
-            }
-            this.time = time;
-        }
-
-        public String getAlias() {
-            return alias;
-        }
-
-        public void setAlias(String alias) {
-            this.alias = alias;
-            aliases = null;
-        }
-
-        public boolean isMustOccur() {
-            return mustOccur;
-        }
-
-        public void setMustOccur(boolean mustOccur) {
-            this.mustOccur = mustOccur;
-        }
-
-        private boolean match(Map<String, Object> value) {
-            return predicate.test(value);
-        }
-
-        private String[] getAliases() {
-            if (aliases == null) {
-                if (alias != null) {
-                    aliases = new String[2];
-                    aliases[1] = alias;
-                } else {
-                    aliases = new String[1];
-                }
-
-                aliases[0] = "_i" + num;
-            }
-
-            return aliases;
-        }
-    }
-
-    private static class SeqChainItem {
-
-        private final SequenceItem seq;
-        private SeqChainItem next;
-        private final long startTime;
-        private final long endTime;
-        private boolean active = true;
-
-        public SeqChainItem(SequenceItem seq, long startTime) {
-            this.seq = seq;
-            this.startTime = startTime;
-            this.endTime = startTime + seq.time;
-        }
-
-        public void setNext(SeqChainItem next) {
-            this.next = next;
-        }
-
-        public long getStartTime() {
-            return startTime;
-        }
-
-        public long getEndTime() {
-            return endTime;
-        }
-
-        public boolean match(Map<String, Object> value) {
-            return seq.match(value);
-        }
-
-        public SeqChainItem getNext() {
-            return next;
-        }
-
-        public boolean inTimeRange(long time) {
-            return (startTime <= time && time < endTime);
-        }
-    }
-
-    private static class SeqChain {
-
-        private SeqChainItem seqItem;
-
-        private final Map<String, Object> valueMap;
-//        private Filterable value = new FilterableMap();
-//        private FlowMarker flowMarker;
-
-        public SeqChain(SequenceItem[] seqItems, long startTime, Map<String, Object> valueMap) {
-            this.valueMap = valueMap;
-
-            SeqChainItem curItem = new SeqChainItem(seqItems[1], startTime);
-            seqItem = curItem;
-
-            for (int i = 2; i < seqItems.length; i++) {
-                SeqChainItem nexItem = new SeqChainItem(seqItems[i], curItem.getStartTime());
-                curItem.setNext(nexItem);
-                curItem = nexItem;
-            }
-        }
-
-        public void updateValue(MessageWrapper wrapper) {
-            valueMap.put("req", wrapper.getReq());
-            valueMap.put("resp", wrapper.getResp());
-        }
-
-        public void persistsValue(MessageWrapper wrapper, String[] aliases) {
-            for (String alias : aliases) {
-                valueMap.put(alias, wrapper);
-            }
-        }
-
-        public boolean hasNext() {
-            return (seqItem != null && seqItem.getNext() != null);
-        }
-
-        public boolean next() {
-            if (seqItem == null) {
-                return false;
-            }
-
-            seqItem = seqItem.getNext();
-            return true;
-        }
-
-        public boolean rewind(long time) {
-            if (seqItem == null) {
-                return false;
-            }
-
-            while (hasNext()) {
-                if (seqItem.inTimeRange(time)) {
-                    return true;
-                } else if (seqItem.seq.mustOccur) {
-                    return false;
-                }
-
-                next();
-            }
-
-            return false;
-        }
-    }
-
-    public static final String TYPE = "sequence";
-
-    private final List<SeqChain> chains = new ArrayList<>();
-    private SequenceListener listener = (e) -> {
-    };
-    private SequenceItem[] seqItems;
-    private Map<String, ValueExtractor> values = Collections.EMPTY_MAP;
-
-    private final FlowFilterFactory filterFactory;
-
-    private TimeGenerator timeGenerator = new SystemTimeGenerator();
-
-    public SequenceFilter() {
-        this(FlowFilterFactory.DEFAULT_FACTORY);
-    }
-
-    public SequenceFilter(FlowFilterFactory filterFactory) {
-        Assert.notNull(filterFactory, "filterFactory");
-        this.filterFactory = filterFactory;
-    }
-
-    public SequenceListener getListener() {
-        return listener;
-    }
-
-    public void setListener(SequenceListener listener) {
-        this.listener = listener;
-    }
-
-    public SequenceItem[] getSeqItems() {
-        return seqItems;
-    }
-
-    public void setSeqItems(SequenceItem[] seqItems) {
-        this.seqItems = seqItems;
-    }
-
-    public Map<String, ValueExtractor> getValues() {
-        return values;
-    }
-
-    public void setValues(Map<String, ValueExtractor> values) {
-        this.values = values;
-    }
-
-    @Override
-    public TimeGenerator getTimeGenerator() {
-        return timeGenerator;
-    }
-
-    @Override
-    public void setTimeGenerator(TimeGenerator timeGenerator) {
-        Assert.notNull(timeGenerator, "timeGenerator");
-        this.timeGenerator = timeGenerator;
-    }
-
-    private void processValue(long now, MessageWrapper value) {
-        Iterator<SeqChain> iterator = chains.iterator();
-        while (iterator.hasNext()) {
-            SeqChain chain = iterator.next();
-
-            if (chain.seqItem.startTime <= now) {
-                if (!chain.seqItem.inTimeRange(now) && !chain.rewind(now)) {
-                    chain.persistsValue(value, chain.seqItem.seq.getAliases());
-                    fireEvent(chain);
-                    iterator.remove();
-                } else if (chain.seqItem.active) {
-                    chain.updateValue(value);
-
-                    if (chain.seqItem.match(chain.valueMap)) {
-                        if (!chain.seqItem.seq.mustOccur) {
-                            iterator.remove();
-                        } else {
-                            chain.persistsValue(value, chain.seqItem.seq.getAliases());
-                            if (!chain.hasNext()) {
-                                fireEvent(chain);
-                                iterator.remove();
-                            } else if (!chain.next()) {
-                                fireEvent(chain);
-                                iterator.remove();
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        final Map<String, Object> valueMap = new HashMap<>();
-        valueMap.put("req", value.getReq());
-        valueMap.put("resp", value.getResp());
-
-        if (seqItems[0].match(valueMap)) {
-            SeqChain chain = new SeqChain(seqItems, now, valueMap);
-            chain.persistsValue(value, seqItems[0].getAliases());
-            chains.add(chain);
-        }
-    }
-
-    private void fireEvent(SeqChain chain) {
-        Map<String, Serializable> result;
-
-        if (values.isEmpty()) {
-            result = Collections.emptyMap();
-        } else {
-            result = new HashMap<>();
-            Map<String, Object> persisted = chain.valueMap;
-            for (Map.Entry<String, ValueExtractor> e : values.entrySet()) {
-                Object value = e.getValue().extract(persisted);
-                if (value instanceof Serializable) {
-                    result.put(e.getKey(), (Serializable) value);
-                }
-            }
-        }
-
-        listener.sequenceDetected(new MapMetric("sequence", result));
-    }
-
-    @Override
-    public int filterInbound(Object req, Object resp, FlowContext context) {
-        MessageWrapper wrapper = filterFactory.createWrapper(req, resp, context);
-        processValue(timeGenerator.currentTimeMillis(), wrapper);
-        return DUNNO;
-    }
-
-    @Override
-    public void reset() {
-        chains.clear();
-    }
-
-    @Override
-    public SequenceFilter instanceForWorker(int index) {
-        SequenceFilter filter = new SequenceFilter();
-        filter.listener = listener;
-        filter.seqItems = seqItems;
-        filter.values = values;
-        return filter;
-    }
-
-    @Override
-    public void configure(Configuration config, ConfigurationContext context) {
-        List<SequenceItem> itemsList = (List<SequenceItem>) config.get("sequence");
-        seqItems = itemsList.toArray(new SequenceItem[itemsList.size()]);
-        for (int i = 0; i < seqItems.length; ++i) {
-            seqItems[i].setNum(i);
-        }
-
-        values = (Map<String, ValueExtractor>) config.get("values", Collections.EMPTY_MAP);
-        ReporterDestination reporterDestination = Registry.getInstance().get(
-                ReporterDestination.SERVICE_NAME, ReporterDestination.class);
-        if (reporterDestination != null) {
-            listener = reporterDestination;
-        }
-    }
-
-    public static class NodeDefCreator implements NodeDefinitionCreator {
-
-        @Override
-        public NodeDefinition create() {
-            MapNodeDefinition elementDef = mapDef(
-                    tupleDef("match", new MessagePredicateNodeDefinition()),
-                    tupleDef("mustOccur", valueDef(Boolean.class)).setRequired(false),
-                    tupleDef("time", valueDef(Long.class)).setRequired(false),
-                    tupleDef("alias", valueDef()).setRequired(false)
-            );
-            elementDef.setTransformer(new SequencesNodeTransformer());
-            // TODO: mustOccur and time required in all steps but first
-
-            return mapDef(
-                    tupleDef("sequence", new ListNodeDefinition(elementDef).setMinNumberOfValues(2)),
-                    tupleDef("values", new MappingNodeDefinition().setTransformer(new ValuesNodeTransformer())).setRequired(false)
-            );
-        }
-
-    }
-
-    private static class SequencesNodeTransformer implements NodeTransformer<CNode> {
-
-        private static final PredicateNodeTransformer TRANSFORMER = Transformers.PREDICATE;
-
-        @Override
-        public CNode transform(CNode node, Errors errors, ConfigurationContext context) {
-            try {
-                CMapNode mapNode = (CMapNode) node;
-                List<CTupleNode> tupleNodes = mapNode.getChildren();
-
-                Predicate predicate = null;
-                boolean mustOccur = true;
-                long time = 0;
-                String alias = null;
-                for (CTupleNode tupleNode : tupleNodes) {
-                    String name = tupleNode.getName();
-                    CNode valueNode = tupleNode.getNode();
-                    switch (name) {
-                        case "match":
-                            predicate = TRANSFORMER.transform(valueNode);
-                            break;
-                        case "mustOccur":
-                            mustOccur = (Boolean) getValue(valueNode);
-                            break;
-                        case "time":
-                            time = (Long) getValue(valueNode);
-                            break;
-                        case "alias":
-                            alias = (String) getValue(valueNode);
-                            break;
-                    }
-                }
-                SequenceItem item = new SequenceItem(predicate);
-                if (time > 0) {
-                    item.setTime(time);
-                }
-                item.setMustOccur(mustOccur);
-                item.setAlias(alias);
-                return new CValueNode(item);
-            } catch (Exception ex) {
-                return node;
-            }
-        }
-
-        @Override
-        public CNode reverseTransform(CNode node, Errors errors, ConfigurationContext context) {
-            throw new UnsupportedOperationException("Not supported yet.");
-        }
-
-    }
-
-    private static class ValuesNodeTransformer implements NodeTransformer<CNode> {
-
-        @Override
-        public CNode transform(CNode node, Errors errors, ConfigurationContext context) {
-            Map<String, ValueExtractor> result = new HashMap<>();
-
-            CMapNode mapNode = (CMapNode) node;
-            List<CTupleNode> tupleNodes = mapNode.getChildren();
-            for (CTupleNode tupleNode : tupleNodes) {
-                String name = tupleNode.getName();
-                CValueNode valueNode = (CValueNode) tupleNode.getNode();
-                String value = (String) valueNode.getValue();
-                try {
-                    errors.pushNestedPath(name);
-                    ValueExtractor extractor = ValueExtractorParser.DEFAULT.parse(value);
-                    result.put(name, extractor);
-                } catch (ParseException ex) {
-                    errors.reject(tupleNode, "Invalid expression: \"%s\"", value);
-                } finally {
-                    errors.popNestedPath();
-                }
-            }
-            return new CValueNode(result);
-        }
-
-        @Override
-        public CNode reverseTransform(CNode node, Errors errors, ConfigurationContext context) {
-            throw new UnsupportedOperationException("Impossible."); // ValueExtractor
-        }
-
-    }
-
-    private static Object getValue(CNode node) {
-        CValueNode valueNode = (CValueNode) node;
-        return valueNode.getValue();
-    }
-}
--- a/stress-tester/src/main/java/com/passus/st/client/filter/SequenceListener.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.metric.MapMetric;
-
-/**
- *
- * @author mikolaj.podbielski
- */
-public interface SequenceListener {
-
-    void sequenceDetected(MapMetric sequence);
-}
--- a/stress-tester/src/main/java/com/passus/st/client/http/ReporterDestination.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/ReporterDestination.java	Thu Mar 26 13:27:52 2020 +0100
@@ -9,7 +9,7 @@
 import com.passus.net.http.HttpMessage;
 import com.passus.st.client.ClientListener;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.SequenceListener;
+import com.passus.st.filter.SequenceListener;
 import com.passus.st.metric.MetricsCollectionHandler;
 
 import java.util.Arrays;
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpAbstractLoginFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpAbstractLoginFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -17,9 +17,9 @@
 import com.passus.st.client.credentials.CredentialsProvider.ProviderContext;
 import com.passus.st.client.credentials.CredentialsProviderFactory;
 import com.passus.st.client.credentials.MultiCredentialsProviderTransformer;
-import com.passus.st.client.filter.FlowFilterFactory;
-import com.passus.st.client.filter.MessagePredicate;
-import com.passus.st.client.filter.MessagePredicateNodeDefinition;
+import com.passus.st.filter.FlowFilterFactory;
+import com.passus.st.filter.MessagePredicate;
+import com.passus.st.filter.MessagePredicateNodeDefinition;
 
 import java.util.Collection;
 
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpBasicAuthLoginFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpBasicAuthLoginFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -16,7 +16,7 @@
 import static com.passus.st.client.http.HttpConsts.PARAM_USERNAME;
 import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
 
-import com.passus.st.client.filter.MessagePredicate;
+import com.passus.st.filter.MessagePredicate;
 import com.passus.st.client.http.HttpScopes;
 import com.passus.st.plugin.PluginConstants;
 import java.nio.charset.Charset;
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpDigestAuthLoginFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpDigestAuthLoginFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -20,7 +20,7 @@
 import static com.passus.st.client.http.HttpConsts.PARAM_USERNAME;
 import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
 
-import com.passus.st.client.filter.MessagePredicate;
+import com.passus.st.filter.MessagePredicate;
 import com.passus.st.client.http.HttpFlowContext;
 import com.passus.st.plugin.PluginConstants;
 import java.util.HashMap;
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -3,7 +3,7 @@
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpResponse;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 
 /**
  * @author Mirosław Hawrot
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFiltersUtils.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFiltersUtils.java	Thu Mar 26 13:27:52 2020 +0100
@@ -5,9 +5,8 @@
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpResponse;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilterFactory;
-import com.passus.st.client.filter.MessageWrapper;
-import com.passus.st.client.http.HttpFlowContext;
+import com.passus.st.filter.FlowFilterFactory;
+import com.passus.st.filter.MessageWrapper;
 
 /**
  *
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFormLoginFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFormLoginFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -11,7 +11,7 @@
 import com.passus.st.client.credentials.Credentials;
 import com.passus.st.client.credentials.CredentialsProvider;
 import com.passus.st.client.credentials.CredentialsProvider.ProviderContext;
-import com.passus.st.client.filter.MessagePredicate;
+import com.passus.st.filter.MessagePredicate;
 import com.passus.st.client.http.HttpScopes;
 import com.passus.st.plugin.PluginConstants;
 import org.apache.logging.log4j.LogManager;
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpLogoutFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpLogoutFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -10,8 +10,8 @@
 import com.passus.net.http.HttpResponse;
 import com.passus.st.ParametersBag;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.MessagePredicate;
-import com.passus.st.client.filter.MessagePredicateNodeDefinition;
+import com.passus.st.filter.MessagePredicate;
+import com.passus.st.filter.MessagePredicateNodeDefinition;
 import com.passus.st.client.http.HttpScopes;
 import com.passus.st.plugin.PluginConstants;
 
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMarkFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMarkFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -13,9 +13,9 @@
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpResponse;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.MessagePredicate;
-import com.passus.st.client.filter.MessagePredicateNodeDefinition;
-import com.passus.st.client.filter.MessageWrapper;
+import com.passus.st.filter.MessagePredicate;
+import com.passus.st.filter.MessagePredicateNodeDefinition;
+import com.passus.st.filter.MessageWrapper;
 import com.passus.st.filter.Transformers;
 import com.passus.st.plugin.PluginConstants;
 
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -11,10 +11,10 @@
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpResponse;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.*;
 import com.passus.st.client.http.filter.HttpMessageModificationOperations.Operation;
 import com.passus.st.config.FieldValueExtractorTransformerNodeDefCreator;
 import com.passus.st.config.HeaderOperationNodeDefinition;
+import com.passus.st.filter.*;
 import com.passus.st.plugin.PluginConstants;
 import com.passus.st.validation.HttpValidators;
 import org.apache.logging.log4j.LogManager;
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationOperations.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationOperations.java	Thu Mar 26 13:27:52 2020 +0100
@@ -6,7 +6,7 @@
 import com.passus.filter.ValueExtractor;
 import com.passus.net.http.*;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilterFactory;
+import com.passus.st.filter.FlowFilterFactory;
 import com.passus.st.extractor.ContentReplacer;
 import com.passus.st.extractor.RegexValueExtractor;
 import org.apache.logging.log4j.LogManager;
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageWrapper.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageWrapper.java	Thu Mar 26 13:27:52 2020 +0100
@@ -5,7 +5,7 @@
 import com.passus.net.http.HttpResponse;
 import com.passus.st.ParametersBag;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.MessageWrapper;
+import com.passus.st.filter.MessageWrapper;
 import com.passus.st.client.http.HttpFlowContext;
 import com.passus.st.client.http.HttpScopes;
 import com.passus.st.emitter.SessionInfo;
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpScopeModificationFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpScopeModificationFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -13,9 +13,9 @@
 import com.passus.net.http.HttpResponse;
 import com.passus.st.ParametersBag;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.*;
 import com.passus.st.config.FieldValueExtractorTransformerNodeDefCreator;
 import com.passus.st.config.HeaderOperationNodeDefinition;
+import com.passus.st.filter.*;
 import com.passus.st.plugin.PluginConstants;
 
 import java.util.ArrayList;
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpVarsFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpVarsFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -9,7 +9,7 @@
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpResponse;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilterFactory;
+import com.passus.st.filter.FlowFilterFactory;
 import com.passus.st.vars.Var;
 import com.passus.st.vars.VarsCompiler;
 import com.passus.st.vars.VarsExecutor;
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpZoneFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpZoneFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -12,8 +12,8 @@
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpResponse;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilterFactory;
-import com.passus.st.client.filter.MessagePredicate;
+import com.passus.st.filter.FlowFilterFactory;
+import com.passus.st.filter.MessagePredicate;
 import com.passus.st.filter.Transformers;
 import com.passus.st.plugin.PluginConstants;
 
--- a/stress-tester/src/main/java/com/passus/st/client/netflow/filter/NetflowFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/netflow/filter/NetflowFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -2,7 +2,7 @@
 
 import com.passus.net.netflow.Netflow;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 
 public abstract class NetflowFilter implements FlowFilter {
 
--- a/stress-tester/src/main/java/com/passus/st/client/netflow/filter/NetflowVersionFilter.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/netflow/filter/NetflowVersionFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -9,7 +9,7 @@
 import com.passus.config.schema.NodeDefinitionCreator;
 import com.passus.net.netflow.Netflow;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 import com.passus.st.plugin.PluginConstants;
 
 import java.util.HashSet;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/CounterFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,109 @@
+package com.passus.st.filter;
+
+import com.passus.commons.annotations.Plugin;
+import com.passus.config.Configuration;
+import com.passus.config.ConfigurationContext;
+import com.passus.config.annotations.NodeDefinitionCreate;
+import com.passus.config.schema.NodeDefinition;
+import com.passus.config.schema.NodeDefinitionCreator;
+import com.passus.st.client.FlowContext;
+import com.passus.st.plugin.PluginConstants;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+@NodeDefinitionCreate(CounterFilter.NodeDefCreator.class)
+@Plugin(name = CounterFilter.TYPE, category = PluginConstants.CATEGORY_FLOW_FILTER)
+public class CounterFilter implements FlowFilter {
+
+    public static final String TYPE = "counter";
+
+    private static final AtomicInteger ID = new AtomicInteger();
+
+    private MessagePredicate predicate;
+    private String name;
+    private int limit;
+    private CounterListener listener = (e) -> {
+    };
+
+    private int count;
+
+    MessagePredicate getPredicate() {
+        return predicate;
+    }
+
+    void setPredicate(MessagePredicate predicate) {
+        this.predicate = predicate;
+    }
+
+    String getName() {
+        return name;
+    }
+
+    void setName(String name) {
+        this.name = name;
+    }
+
+    int getLimit() {
+        return limit;
+    }
+
+    void setLimit(int limit) {
+        this.limit = limit;
+    }
+
+    CounterListener getListener() {
+        return listener;
+    }
+
+    void setListener(CounterListener listener) {
+        this.listener = listener;
+    }
+
+    @Override
+    public void configure(Configuration config, ConfigurationContext context) {
+        predicate = (MessagePredicate) config.get("applyIf");
+        limit = config.getInteger("limit");
+        name = config.getString("name", "counter_" + ID.getAndIncrement());
+        // TODO:
+        listener = System.out::println;
+    }
+
+    @Override
+    public int filterInbound(Object req, Object resp, FlowContext context) {
+        if (predicate.test(req, resp, context)) {
+            if (++count >= limit) {
+                listener.limitReached(new CounterListener.Event(this));
+            }
+        }
+        return DUNNO;
+    }
+
+    @Override
+    public CounterFilter instanceForWorker(int index) {
+        CounterFilter filter = new CounterFilter();
+        filter.predicate = predicate;
+        filter.name = name;
+        filter.limit = limit;
+        filter.listener = listener;
+        return filter;
+    }
+
+    public static class NodeDefCreator implements NodeDefinitionCreator {
+
+        @Override
+        public NodeDefinition create() {
+            return mapDef(
+                    tupleDef("applyIf", new MessagePredicateNodeDefinition()),
+                    tupleDef("limit", valueDefInteger()),
+                    tupleDef("name", valueDef()).setRequired(false)
+            );
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/CounterListener.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,20 @@
+package com.passus.st.filter;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+public interface CounterListener {
+    
+    public void limitReached(Event event);
+    
+    public static final class Event {
+        private final CounterFilter filter;
+//        HttpRequest req
+
+        public Event(CounterFilter filter) {
+            this.filter = filter;
+        }
+        
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/DefaultFlowFilterFactory.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,68 @@
+package com.passus.st.filter;
+
+import com.passus.net.http.HttpMessage;
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpResponse;
+import com.passus.st.client.FlowContext;
+import com.passus.st.client.http.HttpFlowConst;
+import com.passus.st.client.http.HttpFlowContext;
+import com.passus.st.client.http.filter.HttpFilterMessageWrapper;
+import com.passus.st.client.http.filter.HttpFilterRequestWrapper;
+import com.passus.st.client.http.filter.HttpFilterResponseWrapper;
+import com.passus.st.client.http.filter.HttpMessageWrapper;
+
+public final class DefaultFlowFilterFactory implements FlowFilterFactory {
+
+    @Override
+    public MessageWrapper createWrapper(Object req, Object resp, FlowContext context) {
+        if (req instanceof HttpRequest || resp instanceof HttpResponse) {
+            return createHttpMessageWrapper((HttpRequest) req, (HttpResponse) resp, context);
+        } else {
+            return new MessageWrapper(req, resp, context);
+        }
+    }
+
+    @Override
+    public MessageWrapper createWrapper(Object obj) {
+        if (obj instanceof HttpMessage) {
+            HttpMessage message = (HttpMessage) obj;
+            if (message.isRequest()) {
+                return createHttpMessageWrapper((HttpRequest) message, null, null);
+            } else {
+                return createHttpMessageWrapper(null, (HttpResponse) message, null);
+            }
+        } else {
+            return new MessageWrapper(obj, null, null);
+        }
+    }
+
+    public HttpMessageWrapper createHttpMessageWrapper(HttpRequest req, HttpResponse resp, FlowContext context) {
+        HttpFilterRequestWrapper reqWrapper = (HttpFilterRequestWrapper) createHttpMessageWrapper(req);
+        HttpFilterResponseWrapper respWrapper = (HttpFilterResponseWrapper) createHttpMessageWrapper(resp);
+
+        HttpFlowContext httpContext;
+        HttpFilterResponseWrapper origResp;
+        if (context != null) {
+            httpContext = context.getParamValue(HttpFlowConst.PARAM_HTTP_CONTEXT);
+            origResp = (HttpFilterResponseWrapper) createHttpMessageWrapper(httpContext.origResponse());
+        } else {
+            httpContext = null;
+            origResp = null;
+        }
+
+        return new HttpMessageWrapper(reqWrapper, respWrapper, context, origResp, httpContext);
+    }
+
+    public HttpFilterMessageWrapper createHttpMessageWrapper(HttpMessage message) {
+        if (message == null) {
+            return null;
+        }
+
+        if (message.isRequest()) {
+            return new HttpFilterRequestWrapper((HttpRequest) message);
+        } else {
+            return new HttpFilterResponseWrapper((HttpResponse) message);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/FilterDirection.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,11 @@
+package com.passus.st.filter;
+
+/**
+ *
+ * @author Mirosław Hawrot
+ */
+public enum FilterDirection {
+
+    IN, OUT, BOTH
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/FlowFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,34 @@
+package com.passus.st.filter;
+
+import com.passus.config.Configurable;
+import com.passus.config.Configuration;
+import com.passus.config.ConfigurationContext;
+import com.passus.st.client.FlowContext;
+
+public interface FlowFilter extends Configurable {
+
+    int DENY = -1;
+
+    int DUNNO = 0;
+
+    int ACCEPT = 1;
+
+    default void reset() {
+
+    }
+
+    @Override
+    default void configure(Configuration config, ConfigurationContext context) {
+
+    }
+
+    default int filterInbound(Object req, Object resp, FlowContext context) {
+        return DUNNO;
+    }
+
+    default int filterOutbound(Object req, Object resp, FlowContext context) {
+        return DUNNO;
+    }
+
+    FlowFilter instanceForWorker(int index);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/FlowFilterChain.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,96 @@
+package com.passus.st.filter;
+
+import com.passus.filter.Filter;
+import com.passus.st.client.FlowContext;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class FlowFilterChain {
+
+    protected final LinkedList<FlowFilter> filters = new LinkedList<>();
+
+    public List<FlowFilter> getFilters() {
+        return Collections.unmodifiableList(filters);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void addFilter(FlowFilter filter) {
+        filters.add(filter);
+    }
+
+    public void clear() {
+        filters.clear();
+    }
+
+    public boolean isEmpty() {
+        return filters.isEmpty();
+    }
+
+    @SuppressWarnings("unchecked")
+    public int filterOutbound(Object request, Object resp, FlowContext context) {
+        Iterator<FlowFilter> it = filters.iterator();
+        while (it.hasNext()) {
+            FlowFilter f = it.next();
+            int res = f.filterOutbound(request, resp, context);
+            if (res != Filter.DUNNO) {
+                return res;
+            }
+        }
+
+        return Filter.ACCEPT;
+    }
+
+    @SuppressWarnings("unchecked")
+    public int filterInbound(Object request, Object resp, FlowContext context) {
+        Iterator<FlowFilter> it = filters.iterator();
+        while (it.hasNext()) {
+            FlowFilter f = it.next();
+            int res = f.filterInbound(request, resp, context);
+            if (res != Filter.DUNNO) {
+                return res;
+            }
+        }
+
+        return Filter.ACCEPT;
+    }
+
+    public void reset() {
+        Iterator<FlowFilter> it = filters.iterator();
+        while (it.hasNext()) {
+            FlowFilter f = it.next();
+            f.reset();
+        }
+    }
+
+    public FlowFilterChain instanceForWorker(int index) {
+        FlowFilterChain copy = new FlowFilterChain();
+
+        Iterator<FlowFilter> it = filters.iterator();
+        while (it.hasNext()) {
+            FlowFilter f = it.next();
+            copy.addFilter(f.instanceForWorker(index));
+        }
+
+        return copy;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("FlowFilterChain@").append(System.identityHashCode(this));
+        sb.append(" {");
+
+        Iterator<FlowFilter> it = filters.iterator();
+        while (it.hasNext()) {
+            FlowFilter f = it.next();
+            sb.append(f).append(", ");
+        }
+        sb.append("}");
+
+        return sb.toString();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/FlowFilterFactory.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,18 @@
+package com.passus.st.filter;
+
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpResponse;
+import com.passus.st.client.FlowContext;
+import com.passus.st.client.http.filter.HttpMessageWrapper;
+
+public interface FlowFilterFactory {
+
+    FlowFilterFactory DEFAULT_FACTORY = new DefaultFlowFilterFactory();
+
+    MessageWrapper createWrapper(Object req, Object resp, FlowContext context);
+
+    MessageWrapper createWrapper(Object obj);
+
+    HttpMessageWrapper createHttpMessageWrapper(HttpRequest req, HttpResponse resp, FlowContext context);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/FlowFilterNodeDefinitionCreator.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,22 @@
+package com.passus.st.filter;
+
+import com.passus.config.schema.DynaKeyValueVaryListNodeDefinition;
+import com.passus.config.schema.NodeDefinition;
+import com.passus.config.schema.NodeDefinitionCreator;
+
+/**
+ * @author Mirosław Hawrot
+ */
+public class FlowFilterNodeDefinitionCreator implements NodeDefinitionCreator {
+
+    public static NodeDefinition createFiltersList(boolean transformToObject) {
+        return new DynaKeyValueVaryListNodeDefinition("type", FlowFilterPluginFactory.getInstance())
+                .setTransformToPluginObject(transformToObject);
+    }
+
+    @Override
+    public NodeDefinition create() {
+        return createFiltersList(true);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/FlowFilterPluginFactory.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,24 @@
+package com.passus.st.filter;
+
+import com.passus.commons.plugin.PluginFactory;
+import com.passus.st.plugin.PluginConstants;
+
+/**
+ * @author Mirosław Hawrot
+ */
+public final class FlowFilterPluginFactory extends PluginFactory<FlowFilter> {
+
+    private static FlowFilterPluginFactory instance;
+
+    public FlowFilterPluginFactory() {
+        super(PluginConstants.CATEGORY_FLOW_FILTER, FlowFilter.class);
+    }
+
+    public static synchronized FlowFilterPluginFactory getInstance() {
+        if (instance == null) {
+            instance = new FlowFilterPluginFactory();
+        }
+
+        return instance;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/FlowFiltersConfigurator.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,67 @@
+package com.passus.st.filter;
+
+import com.passus.commons.annotations.Plugin;
+import com.passus.config.*;
+import com.passus.config.schema.NodeDefinition;
+import com.passus.config.validation.Errors;
+import com.passus.st.plugin.PluginConstants;
+import com.passus.st.utils.ConfigurationContextConsts;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Mirosław Hawrot
+ */
+@Plugin(name = FlowFiltersConfigurator.DOMAIN, category = PluginConstants.CATEGORY_DOMAIN_CONFIGURATOR)
+public class FlowFiltersConfigurator implements DomainConfigurator {
+
+    public static final String DOMAIN = "filters";
+
+    private static final FlowFilterNodeDefinitionCreator CREATOR = new FlowFilterNodeDefinitionCreator();
+
+    private final NodeDefinition nodeDef = CREATOR.create();
+
+    @Override
+    public String getDomain() {
+        return DOMAIN;
+    }
+
+    public static List<FlowFilter> getFilters(File configFile, Errors errors, ConfigurationContext context) throws IOException, NodeException {
+        Configuration config = YamlConfigurationReader.readFromFile(configFile);
+        return getFilters(config, errors, context);
+    }
+
+    public static List<FlowFilter> getFilters(String configStr, Errors errors, ConfigurationContext context) throws IOException, NodeException {
+        Configuration config = YamlConfigurationReader.readFromString(configStr);
+        return getFilters(config, errors, context);
+    }
+
+    public static List<FlowFilter> getFilters(Configuration config, Errors errors, ConfigurationContext context) {
+        NodeDefinition nodeDef = CREATOR.create();
+        return ConfigurationUtils.getDomainObjects(config, errors, context, DOMAIN, nodeDef);
+    }
+
+    @Override
+    public void validate(Configuration config, Errors errors, ConfigurationContext context) {
+        CCompositeNode rootNode = config.getRootNode();
+        nodeDef.validate(rootNode, errors, context);
+    }
+
+    @Override
+    public void configure(Configuration config, Errors errors, ConfigurationContext context) {
+        CCompositeNode rootNode = config.getRootNode();
+        nodeDef.transform(rootNode, errors, context);
+        if (!errors.hasError()) {
+            try {
+                List<FlowFilter> filters = ConfigurationUtils.convertToList(rootNode, FlowFilter.class);
+                context.add(ConfigurationContextConsts.FLOW_FILTERS, filters);
+            } catch (Exception e) {
+                throw new RuntimeException(e.getMessage(), e);
+            }
+
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/MatchFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,94 @@
+package com.passus.st.filter;
+
+import com.passus.commons.Assert;
+import com.passus.commons.annotations.Plugin;
+import com.passus.config.Configuration;
+import com.passus.config.ConfigurationContext;
+import com.passus.config.annotations.NodeDefinitionCreate;
+import com.passus.config.schema.NodeDefinition;
+import com.passus.config.schema.NodeDefinitionCreator;
+import com.passus.st.client.FlowContext;
+import com.passus.st.plugin.PluginConstants;
+
+import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
+
+/**
+ * @author Mirosław Hawrot
+ */
+@NodeDefinitionCreate(MatchFilter.MatchFilterNodeDefCreator.class)
+@Plugin(name = MatchFilter.TYPE, category = PluginConstants.CATEGORY_FLOW_FILTER)
+public class MatchFilter implements FlowFilter {
+
+    public static final String TYPE = "matcher";
+
+    private MessagePredicate predicate;
+
+    private boolean acceptOnMatch = true;
+
+    private final FlowFilterFactory filterFactory;
+
+    public MatchFilter() {
+        this(FlowFilterFactory.DEFAULT_FACTORY);
+    }
+
+    public MatchFilter(FlowFilterFactory filterFactory) {
+        Assert.notNull(filterFactory, "filterFactory");
+        this.filterFactory = filterFactory;
+    }
+
+    public MessagePredicate getPredicate() {
+        return predicate;
+    }
+
+    public void setPredicate(MessagePredicate predicate) {
+        this.predicate = predicate;
+    }
+
+    public boolean isAcceptOnMatch() {
+        return acceptOnMatch;
+    }
+
+    public void setAcceptOnMatch(boolean acceptOnMatch) {
+        this.acceptOnMatch = acceptOnMatch;
+    }
+
+    @Override
+    public void configure(Configuration config, ConfigurationContext context) {
+        predicate = (MessagePredicate) config.get("matches");
+        acceptOnMatch = config.getBoolean("acceptOnMatch", true);
+    }
+
+    @Override
+    public int filterOutbound(Object request, Object resp, FlowContext context) {
+        if (predicate == null) {
+            return DUNNO;
+        }
+
+        boolean match = predicate.test(filterFactory.createWrapper(request, resp, context));
+        if (!match) {
+            return DUNNO;
+        }
+
+        return acceptOnMatch ? ACCEPT : DENY;
+    }
+
+    @Override
+    public MatchFilter instanceForWorker(int index) {
+        MatchFilter filter = new MatchFilter();
+        filter.acceptOnMatch = acceptOnMatch;
+        filter.predicate = predicate;
+        return filter;
+    }
+
+    public static class MatchFilterNodeDefCreator implements NodeDefinitionCreator {
+
+        @Override
+        public NodeDefinition create() {
+            return mapDef(
+                    tupleDef("acceptOnMatch", valueDefBool()).setRequired(false),
+                    tupleDef("matches", new MessagePredicateNodeDefinition())
+            );
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/MessagePredicate.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,56 @@
+package com.passus.st.filter;
+
+import com.passus.commons.Assert;
+import com.passus.commons.ConversionException;
+import com.passus.config.NodeException;
+import com.passus.st.client.FlowContext;
+
+import java.io.IOException;
+import java.util.function.Predicate;
+
+import static com.passus.st.filter.FlowFilterFactory.DEFAULT_FACTORY;
+
+public class MessagePredicate<T> implements Predicate<T> {
+
+    protected final Predicate predicate;
+
+    protected final FlowFilterFactory filterFactory;
+
+    public MessagePredicate(Predicate predicate) {
+        this(predicate, DEFAULT_FACTORY);
+    }
+
+    public MessagePredicate(Predicate predicate, FlowFilterFactory filterFactory) {
+        Assert.notNull(predicate, "predicate");
+        Assert.notNull(filterFactory, "filterFactory");
+        this.predicate = predicate;
+        this.filterFactory = filterFactory;
+    }
+
+    public Predicate getPredicate() {
+        return predicate;
+    }
+
+    public boolean test(Object req, Object resp, FlowContext context) {
+        return test(filterFactory.createWrapper(req, resp, context));
+    }
+
+    public boolean test(MessageWrapper wrapper) {
+        return predicate.test(wrapper);
+    }
+
+    @Override
+    public boolean test(T message) {
+        return predicate.test(filterFactory.createWrapper(message));
+    }
+
+    @Override
+    public String toString() {
+        return predicate.toString();
+    }
+
+    public static MessagePredicate parse(String content) throws ConversionException, IOException, NodeException {
+        Predicate predicate = Transformers.PREDICATE.transform(content);
+        return new MessagePredicate(predicate);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/MessagePredicateNodeDefinition.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,79 @@
+package com.passus.st.filter;
+
+import com.passus.commons.ConversionException;
+import com.passus.commons.utils.ArrayUtils;
+import com.passus.config.*;
+import com.passus.config.schema.NodeDefinition;
+import com.passus.config.validation.Errors;
+import com.passus.filter.config.PredicateNodeTransformer;
+import com.passus.lookup.filter.LookupKeyExistsLeftOperatorTransformer;
+import com.passus.lookup.filter.LookupValueExtractorTransformer;
+
+import java.util.Set;
+import java.util.function.Predicate;
+
+import static com.passus.config.validation.ValidationDefaultMessages.GENERAL_CONVERSION_ERROR;
+
+/**
+ * @author Mirosław Hawrot
+ */
+public class MessagePredicateNodeDefinition extends NodeDefinition<MessagePredicateNodeDefinition> {
+
+    private static final PredicateNodeTransformer TRANSFORMER = Transformers.PREDICATE;
+
+    private final static Set<Class<? extends CNode>> SUPPORTED = ArrayUtils.asUnmodifiableSet(CListNode.class, CMapNode.class);
+
+    @Override
+    public Set<Class<? extends CNode>> getSupportedNode() {
+        return SUPPORTED;
+    }
+
+    @Override
+    protected void doValidate(CNode node, Errors errors, ConfigurationContext context) {
+        try {
+            // TODO: refactor
+            LookupKeyExistsLeftOperatorTransformer lookupKeyExistsTransformer = Transformers.lookupKeyExistsTransformer(context);
+            LookupValueExtractorTransformer lookupValueTransformer = Transformers.lookupValueTransformer(context);
+            TRANSFORMER.addLeftOperatorTransformer(lookupKeyExistsTransformer);
+            TRANSFORMER.addValueExtractorTransformer(lookupValueTransformer);
+            TRANSFORMER.transform(node);
+            TRANSFORMER.removeLeftOperatorTransformer(lookupKeyExistsTransformer);
+            TRANSFORMER.removeValueExtractorTransformer(lookupValueTransformer);
+        } catch (NodeConversionException ex) {
+            errors.reject(node, ex.getMessage());
+        } catch (ConversionException ex) {
+            errors.reject(node, GENERAL_CONVERSION_ERROR);
+        }
+    }
+
+    @Override
+    protected CNode doTransform(CNode node, Errors errors, ConfigurationContext context, boolean reverse) {
+        if (reverse) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        } else {
+            try {
+                // TODO: refactor
+                LookupKeyExistsLeftOperatorTransformer lookupKeyExistsTransformer = Transformers.lookupKeyExistsTransformer(context);
+                LookupValueExtractorTransformer lookupValueTransformer = Transformers.lookupValueTransformer(context);
+                TRANSFORMER.addLeftOperatorTransformer(lookupKeyExistsTransformer);
+                TRANSFORMER.addValueExtractorTransformer(lookupValueTransformer);
+                Predicate predicate = TRANSFORMER.transform(node);
+                TRANSFORMER.removeLeftOperatorTransformer(lookupKeyExistsTransformer);
+                TRANSFORMER.removeValueExtractorTransformer(lookupValueTransformer);
+                return new CValueNode(new MessagePredicate(predicate));
+            } catch (NodeConversionException ex) {
+                errors.reject(node, ex.getMessage());
+            } catch (ConversionException ex) {
+                errors.reject(node, GENERAL_CONVERSION_ERROR);
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    protected boolean doEquals(CNode node1, CNode node2) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/MessageWrapper.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,27 @@
+package com.passus.st.filter;
+
+import com.passus.st.client.FlowContext;
+
+public class MessageWrapper<R, S> {
+
+    protected final R req;
+
+    protected final S resp;
+
+    protected final FlowContext context;
+
+    public MessageWrapper(R req, S resp, FlowContext context) {
+        this.req = req;
+        this.resp = resp;
+        this.context = context;
+    }
+
+    public R getReq() {
+        return req;
+    }
+
+    public S getResp() {
+        return resp;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/MvelFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,162 @@
+package com.passus.st.filter;
+
+import com.passus.commons.Assert;
+import com.passus.commons.annotations.Plugin;
+import com.passus.config.Configuration;
+import com.passus.config.ConfigurationContext;
+import com.passus.config.annotations.NodeDefinitionCreate;
+import com.passus.config.schema.NodeDefinition;
+import com.passus.config.schema.NodeDefinitionCreator;
+import com.passus.net.http.HttpRequest;
+import com.passus.st.client.FlowContext;
+import com.passus.st.client.http.HttpScopes;
+import com.passus.st.config.StringSourceNodeDefinition;
+import com.passus.st.config.StringToExecutableStatementValueTransformer;
+import com.passus.st.plugin.PluginConstants;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.mvel2.compiler.ExecutableStatement;
+import org.mvel2.integration.VariableResolverFactory;
+import org.mvel2.integration.impl.CachingMapVariableResolverFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
+import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
+
+/**
+ * @author Mirosław Hawrot
+ */
+@NodeDefinitionCreate(MvelFilter.MvelFilterNodeDefCreator.class)
+@Plugin(name = MvelFilter.TYPE, category = PluginConstants.CATEGORY_FLOW_FILTER)
+public class MvelFilter implements FlowFilter {
+
+    public static final String TYPE = "mvel";
+
+    private static final Logger LOGGER = LogManager.getLogger(MvelFilter.class);
+
+    private VariableResolverFactory globalFactory;
+
+    private ExecutableStatement statement;
+
+    private FilterDirection direction = FilterDirection.OUT;
+
+    public MvelFilter() {
+    }
+
+    public MvelFilter(ExecutableStatement statement, FilterDirection direction) {
+        Assert.notNull(statement, "statement");
+        Assert.notNull(direction, "direction");
+        this.statement = statement;
+        this.direction = direction;
+    }
+
+    public VariableResolverFactory getGlobalFactory() {
+        return globalFactory;
+    }
+
+    public void setGlobalFactory(VariableResolverFactory globalFactory) {
+        this.globalFactory = globalFactory;
+    }
+
+    public FilterDirection getDirection() {
+        return direction;
+    }
+
+    public void setDirection(FilterDirection direction) {
+        Assert.notNull(direction, "direction");
+        this.direction = direction;
+    }
+
+    public ExecutableStatement getStatement() {
+        return statement;
+    }
+
+    public void setStatement(ExecutableStatement statement) {
+        this.statement = statement;
+    }
+
+    @Override
+    public void configure(Configuration config, ConfigurationContext context) {
+        direction = config.get("dir", FilterDirection.OUT);
+        statement = (ExecutableStatement) config.get("script");
+    }
+
+    private int filter(Object req, Object resp, FlowContext context) {
+        if (statement == null) {
+            return DUNNO;
+        }
+
+        Map<String, Object> vars = new HashMap<>(3);
+        vars.put("$req", req);
+        vars.put("$resp", resp);
+        vars.put("$context", context);
+        if (context != null && req instanceof HttpRequest) {
+            HttpRequest httpReq = (HttpRequest) req;
+            HttpScopes scopes = extractHttpContext(context).scopes();
+            vars.put("$extractHttpContext", scopes);
+            vars.put("$httpSession", scopes.getSession(httpReq));
+        }
+
+        CachingMapVariableResolverFactory factory = new CachingMapVariableResolverFactory(vars);
+        if (globalFactory != null) {
+            factory.setNextFactory(globalFactory);
+        }
+
+        try {
+            Object out = statement.getValue(null, factory);
+            if (out instanceof Integer) {
+                int value = (Integer) out;
+                if (value < 0) {
+                    return DENY;
+                } else if (value > 0) {
+                    return ACCEPT;
+                }
+
+                return DUNNO;
+            }
+        } catch (Throwable th) {
+            LOGGER.trace(th);
+        }
+
+        return DUNNO;
+    }
+
+    @Override
+    public int filterOutbound(Object req, Object resp, FlowContext context) {
+        if (direction == FilterDirection.BOTH || direction == FilterDirection.OUT) {
+            return filter(req, resp, context);
+        }
+
+        return DUNNO;
+    }
+
+    @Override
+    public int filterInbound(Object req, Object resp, FlowContext context) {
+        if (direction == FilterDirection.BOTH || direction == FilterDirection.IN) {
+            return filter(req, resp, context);
+        }
+
+        return DUNNO;
+    }
+
+    @Override
+    public FlowFilter instanceForWorker(int index) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public static final class MvelFilterNodeDefCreator implements NodeDefinitionCreator {
+
+        @Override
+        public NodeDefinition create() {
+            return mapDef(
+                    tupleDef("dir", enumDef(FilterDirection.class)).setRequired(false),
+                    tupleDef("script", new StringSourceNodeDefinition()
+                            .setTransformer(new StringToExecutableStatementValueTransformer())
+                    )
+            );
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/SequenceFilter.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,481 @@
+package com.passus.st.filter;
+
+import com.passus.commons.Assert;
+import com.passus.commons.annotations.Plugin;
+import com.passus.commons.metric.MapMetric;
+import com.passus.commons.service.Registry;
+import com.passus.commons.time.SystemTimeGenerator;
+import com.passus.commons.time.TimeAware;
+import com.passus.commons.time.TimeGenerator;
+import com.passus.config.*;
+import com.passus.config.annotations.NodeDefinitionCreate;
+import com.passus.config.schema.*;
+import com.passus.config.validation.Errors;
+import com.passus.filter.ValueExtractor;
+import com.passus.filter.ValueExtractorParser;
+import com.passus.filter.config.PredicateNodeTransformer;
+import com.passus.st.client.FlowContext;
+import com.passus.st.client.http.ReporterDestination;
+import com.passus.st.plugin.PluginConstants;
+
+import java.io.Serializable;
+import java.text.ParseException;
+import java.util.*;
+import java.util.function.Predicate;
+
+import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
+
+/**
+ * @author mikolaj.podbielski
+ */
+@NodeDefinitionCreate(SequenceFilter.NodeDefCreator.class)
+@Plugin(name = SequenceFilter.TYPE, category = PluginConstants.CATEGORY_FLOW_FILTER)
+public class SequenceFilter implements FlowFilter, TimeAware {
+
+    public static class SequenceItem {
+
+        private final Predicate predicate;
+        private boolean mustOccur = true;
+        private long time = 10_000;
+        private int num;
+        private String alias;
+        private String[] aliases;
+
+        public SequenceItem(Predicate predicate, int num) {
+            this(predicate);
+            this.num = num;
+        }
+
+        public SequenceItem(Predicate predicate) {
+            if (predicate == null) {
+                throw new NullPointerException();
+            }
+            this.predicate = predicate;
+        }
+
+        public Predicate getPredicate() {
+            return predicate;
+        }
+
+        public int getNum() {
+            return num;
+        }
+
+        public void setNum(int num) {
+            this.num = num;
+        }
+
+        public long getTime() {
+            return time;
+        }
+
+        public void setTime(long time) {
+            if (time <= 0) {
+                throw new IllegalArgumentException("Time must be greater than zero.");
+            }
+            this.time = time;
+        }
+
+        public String getAlias() {
+            return alias;
+        }
+
+        public void setAlias(String alias) {
+            this.alias = alias;
+            aliases = null;
+        }
+
+        public boolean isMustOccur() {
+            return mustOccur;
+        }
+
+        public void setMustOccur(boolean mustOccur) {
+            this.mustOccur = mustOccur;
+        }
+
+        private boolean match(Map<String, Object> value) {
+            return predicate.test(value);
+        }
+
+        private String[] getAliases() {
+            if (aliases == null) {
+                if (alias != null) {
+                    aliases = new String[2];
+                    aliases[1] = alias;
+                } else {
+                    aliases = new String[1];
+                }
+
+                aliases[0] = "_i" + num;
+            }
+
+            return aliases;
+        }
+    }
+
+    private static class SeqChainItem {
+
+        private final SequenceItem seq;
+        private SeqChainItem next;
+        private final long startTime;
+        private final long endTime;
+        private boolean active = true;
+
+        public SeqChainItem(SequenceItem seq, long startTime) {
+            this.seq = seq;
+            this.startTime = startTime;
+            this.endTime = startTime + seq.time;
+        }
+
+        public void setNext(SeqChainItem next) {
+            this.next = next;
+        }
+
+        public long getStartTime() {
+            return startTime;
+        }
+
+        public long getEndTime() {
+            return endTime;
+        }
+
+        public boolean match(Map<String, Object> value) {
+            return seq.match(value);
+        }
+
+        public SeqChainItem getNext() {
+            return next;
+        }
+
+        public boolean inTimeRange(long time) {
+            return (startTime <= time && time < endTime);
+        }
+    }
+
+    private static class SeqChain {
+
+        private SeqChainItem seqItem;
+
+        private final Map<String, Object> valueMap;
+//        private Filterable value = new FilterableMap();
+//        private FlowMarker flowMarker;
+
+        public SeqChain(SequenceItem[] seqItems, long startTime, Map<String, Object> valueMap) {
+            this.valueMap = valueMap;
+
+            SeqChainItem curItem = new SeqChainItem(seqItems[1], startTime);
+            seqItem = curItem;
+
+            for (int i = 2; i < seqItems.length; i++) {
+                SeqChainItem nexItem = new SeqChainItem(seqItems[i], curItem.getStartTime());
+                curItem.setNext(nexItem);
+                curItem = nexItem;
+            }
+        }
+
+        public void updateValue(MessageWrapper wrapper) {
+            valueMap.put("req", wrapper.getReq());
+            valueMap.put("resp", wrapper.getResp());
+        }
+
+        public void persistsValue(MessageWrapper wrapper, String[] aliases) {
+            for (String alias : aliases) {
+                valueMap.put(alias, wrapper);
+            }
+        }
+
+        public boolean hasNext() {
+            return (seqItem != null && seqItem.getNext() != null);
+        }
+
+        public boolean next() {
+            if (seqItem == null) {
+                return false;
+            }
+
+            seqItem = seqItem.getNext();
+            return true;
+        }
+
+        public boolean rewind(long time) {
+            if (seqItem == null) {
+                return false;
+            }
+
+            while (hasNext()) {
+                if (seqItem.inTimeRange(time)) {
+                    return true;
+                } else if (seqItem.seq.mustOccur) {
+                    return false;
+                }
+
+                next();
+            }
+
+            return false;
+        }
+    }
+
+    public static final String TYPE = "sequence";
+
+    private final List<SeqChain> chains = new ArrayList<>();
+    private SequenceListener listener = (e) -> {
+    };
+    private SequenceItem[] seqItems;
+    private Map<String, ValueExtractor> values = Collections.EMPTY_MAP;
+
+    private final FlowFilterFactory filterFactory;
+
+    private TimeGenerator timeGenerator = new SystemTimeGenerator();
+
+    public SequenceFilter() {
+        this(FlowFilterFactory.DEFAULT_FACTORY);
+    }
+
+    public SequenceFilter(FlowFilterFactory filterFactory) {
+        Assert.notNull(filterFactory, "filterFactory");
+        this.filterFactory = filterFactory;
+    }
+
+    public SequenceListener getListener() {
+        return listener;
+    }
+
+    public void setListener(SequenceListener listener) {
+        this.listener = listener;
+    }
+
+    public SequenceItem[] getSeqItems() {
+        return seqItems;
+    }
+
+    public void setSeqItems(SequenceItem[] seqItems) {
+        this.seqItems = seqItems;
+    }
+
+    public Map<String, ValueExtractor> getValues() {
+        return values;
+    }
+
+    public void setValues(Map<String, ValueExtractor> values) {
+        this.values = values;
+    }
+
+    @Override
+    public TimeGenerator getTimeGenerator() {
+        return timeGenerator;
+    }
+
+    @Override
+    public void setTimeGenerator(TimeGenerator timeGenerator) {
+        Assert.notNull(timeGenerator, "timeGenerator");
+        this.timeGenerator = timeGenerator;
+    }
+
+    private void processValue(long now, MessageWrapper value) {
+        Iterator<SeqChain> iterator = chains.iterator();
+        while (iterator.hasNext()) {
+            SeqChain chain = iterator.next();
+
+            if (chain.seqItem.startTime <= now) {
+                if (!chain.seqItem.inTimeRange(now) && !chain.rewind(now)) {
+                    chain.persistsValue(value, chain.seqItem.seq.getAliases());
+                    fireEvent(chain);
+                    iterator.remove();
+                } else if (chain.seqItem.active) {
+                    chain.updateValue(value);
+
+                    if (chain.seqItem.match(chain.valueMap)) {
+                        if (!chain.seqItem.seq.mustOccur) {
+                            iterator.remove();
+                        } else {
+                            chain.persistsValue(value, chain.seqItem.seq.getAliases());
+                            if (!chain.hasNext()) {
+                                fireEvent(chain);
+                                iterator.remove();
+                            } else if (!chain.next()) {
+                                fireEvent(chain);
+                                iterator.remove();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        final Map<String, Object> valueMap = new HashMap<>();
+        valueMap.put("req", value.getReq());
+        valueMap.put("resp", value.getResp());
+
+        if (seqItems[0].match(valueMap)) {
+            SeqChain chain = new SeqChain(seqItems, now, valueMap);
+            chain.persistsValue(value, seqItems[0].getAliases());
+            chains.add(chain);
+        }
+    }
+
+    private void fireEvent(SeqChain chain) {
+        Map<String, Serializable> result;
+
+        if (values.isEmpty()) {
+            result = Collections.emptyMap();
+        } else {
+            result = new HashMap<>();
+            Map<String, Object> persisted = chain.valueMap;
+            for (Map.Entry<String, ValueExtractor> e : values.entrySet()) {
+                Object value = e.getValue().extract(persisted);
+                if (value instanceof Serializable) {
+                    result.put(e.getKey(), (Serializable) value);
+                }
+            }
+        }
+
+        listener.sequenceDetected(new MapMetric("sequence", result));
+    }
+
+    @Override
+    public int filterInbound(Object req, Object resp, FlowContext context) {
+        MessageWrapper wrapper = filterFactory.createWrapper(req, resp, context);
+        processValue(timeGenerator.currentTimeMillis(), wrapper);
+        return DUNNO;
+    }
+
+    @Override
+    public void reset() {
+        chains.clear();
+    }
+
+    @Override
+    public SequenceFilter instanceForWorker(int index) {
+        SequenceFilter filter = new SequenceFilter();
+        filter.listener = listener;
+        filter.seqItems = seqItems;
+        filter.values = values;
+        return filter;
+    }
+
+    @Override
+    public void configure(Configuration config, ConfigurationContext context) {
+        List<SequenceItem> itemsList = (List<SequenceItem>) config.get("sequence");
+        seqItems = itemsList.toArray(new SequenceItem[itemsList.size()]);
+        for (int i = 0; i < seqItems.length; ++i) {
+            seqItems[i].setNum(i);
+        }
+
+        values = (Map<String, ValueExtractor>) config.get("values", Collections.EMPTY_MAP);
+        ReporterDestination reporterDestination = Registry.getInstance().get(
+                ReporterDestination.SERVICE_NAME, ReporterDestination.class);
+        if (reporterDestination != null) {
+            listener = reporterDestination;
+        }
+    }
+
+    public static class NodeDefCreator implements NodeDefinitionCreator {
+
+        @Override
+        public NodeDefinition create() {
+            MapNodeDefinition elementDef = mapDef(
+                    tupleDef("match", new MessagePredicateNodeDefinition()),
+                    tupleDef("mustOccur", valueDef(Boolean.class)).setRequired(false),
+                    tupleDef("time", valueDef(Long.class)).setRequired(false),
+                    tupleDef("alias", valueDef()).setRequired(false)
+            );
+            elementDef.setTransformer(new SequencesNodeTransformer());
+            // TODO: mustOccur and time required in all steps but first
+
+            return mapDef(
+                    tupleDef("sequence", new ListNodeDefinition(elementDef).setMinNumberOfValues(2)),
+                    tupleDef("values", new MappingNodeDefinition().setTransformer(new ValuesNodeTransformer())).setRequired(false)
+            );
+        }
+
+    }
+
+    private static class SequencesNodeTransformer implements NodeTransformer<CNode> {
+
+        private static final PredicateNodeTransformer TRANSFORMER = Transformers.PREDICATE;
+
+        @Override
+        public CNode transform(CNode node, Errors errors, ConfigurationContext context) {
+            try {
+                CMapNode mapNode = (CMapNode) node;
+                List<CTupleNode> tupleNodes = mapNode.getChildren();
+
+                Predicate predicate = null;
+                boolean mustOccur = true;
+                long time = 0;
+                String alias = null;
+                for (CTupleNode tupleNode : tupleNodes) {
+                    String name = tupleNode.getName();
+                    CNode valueNode = tupleNode.getNode();
+                    switch (name) {
+                        case "match":
+                            predicate = TRANSFORMER.transform(valueNode);
+                            break;
+                        case "mustOccur":
+                            mustOccur = (Boolean) getValue(valueNode);
+                            break;
+                        case "time":
+                            time = (Long) getValue(valueNode);
+                            break;
+                        case "alias":
+                            alias = (String) getValue(valueNode);
+                            break;
+                    }
+                }
+                SequenceItem item = new SequenceItem(predicate);
+                if (time > 0) {
+                    item.setTime(time);
+                }
+                item.setMustOccur(mustOccur);
+                item.setAlias(alias);
+                return new CValueNode(item);
+            } catch (Exception ex) {
+                return node;
+            }
+        }
+
+        @Override
+        public CNode reverseTransform(CNode node, Errors errors, ConfigurationContext context) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+    }
+
+    private static class ValuesNodeTransformer implements NodeTransformer<CNode> {
+
+        @Override
+        public CNode transform(CNode node, Errors errors, ConfigurationContext context) {
+            Map<String, ValueExtractor> result = new HashMap<>();
+
+            CMapNode mapNode = (CMapNode) node;
+            List<CTupleNode> tupleNodes = mapNode.getChildren();
+            for (CTupleNode tupleNode : tupleNodes) {
+                String name = tupleNode.getName();
+                CValueNode valueNode = (CValueNode) tupleNode.getNode();
+                String value = (String) valueNode.getValue();
+                try {
+                    errors.pushNestedPath(name);
+                    ValueExtractor extractor = ValueExtractorParser.DEFAULT.parse(value);
+                    result.put(name, extractor);
+                } catch (ParseException ex) {
+                    errors.reject(tupleNode, "Invalid expression: \"%s\"", value);
+                } finally {
+                    errors.popNestedPath();
+                }
+            }
+            return new CValueNode(result);
+        }
+
+        @Override
+        public CNode reverseTransform(CNode node, Errors errors, ConfigurationContext context) {
+            throw new UnsupportedOperationException("Impossible."); // ValueExtractor
+        }
+
+    }
+
+    private static Object getValue(CNode node) {
+        CValueNode valueNode = (CValueNode) node;
+        return valueNode.getValue();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/filter/SequenceListener.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,12 @@
+package com.passus.st.filter;
+
+import com.passus.commons.metric.MapMetric;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+public interface SequenceListener {
+
+    void sequenceDetected(MapMetric sequence);
+}
--- a/stress-tester/src/main/java/com/passus/st/job/TestJob.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/main/java/com/passus/st/job/TestJob.java	Thu Mar 26 13:27:52 2020 +0100
@@ -4,7 +4,7 @@
 import com.passus.config.ConfigurationContext;
 import com.passus.filter.ValueExtractor;
 import com.passus.st.client.*;
-import com.passus.st.client.filter.FlowFilter;
+import com.passus.st.filter.FlowFilter;
 import com.passus.st.client.http.ReporterDestination;
 import com.passus.st.client.http.filter.HttpVarsFilter;
 import com.passus.st.emitter.Emitter;
--- a/stress-tester/src/test/java/com/passus/st/ConverterHttpClientTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/ConverterHttpClientTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -4,13 +4,13 @@
 import com.passus.st.client.SessionPayloadEvent;
 import com.passus.st.client.credentials.Credentials;
 import com.passus.st.client.credentials.CredentialsProvider;
-import com.passus.st.client.filter.*;
 import com.passus.st.client.http.filter.*;
 import com.passus.st.client.http.filter.HttpMessageModificationOperations.PostDataSetParamOperation;
 import com.passus.st.client.http.filter.HttpMessageModificationOperations.SetCookieOperation;
 import com.passus.st.client.http.filter.HttpMessageModificationOperations.SetHeaderOperation;
 import com.passus.st.client.http.filter.HttpMessageModificationOperations.SetQueryParameterOperation;
 import com.passus.st.emitter.SessionInfo;
+import com.passus.st.filter.*;
 import com.passus.st.source.ArrayListEventDestination;
 import com.passus.st.source.NullEventDestination;
 import com.passus.st.utils.TestHttpUtils;
--- a/stress-tester/src/test/java/com/passus/st/FilterScanner.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/FilterScanner.java	Thu Mar 26 13:27:52 2020 +0100
@@ -1,6 +1,6 @@
 package com.passus.st;
 
-import com.passus.st.client.filter.FlowFilterPluginFactory;
+import com.passus.st.filter.FlowFilterPluginFactory;
 
 /**
  *
--- a/stress-tester/src/test/java/com/passus/st/client/FlowFilterChainTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/FlowFilterChainTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -1,7 +1,7 @@
 package com.passus.st.client;
 
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFilterChain;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFilterChain;
 import org.testng.annotations.Test;
 
 import static com.passus.st.client.http.filter.HttpFilter.*;
--- a/stress-tester/src/test/java/com/passus/st/client/dns/filter/DnsRecordTypeFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/dns/filter/DnsRecordTypeFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -3,8 +3,8 @@
 import com.passus.config.NodeException;
 import com.passus.config.validation.Errors;
 import com.passus.net.dns.DnsRecordType;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import org.testng.annotations.Test;
 
 import java.io.IOException;
--- a/stress-tester/src/test/java/com/passus/st/client/filter/CounterFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.config.validation.Errors;
-import com.passus.filter.AndPredicate;
-import com.passus.filter.ComparisonOperator;
-import com.passus.filter.ComparisonPredicate;
-import com.passus.net.http.*;
-import org.testng.annotations.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertTrue;
-
-public class CounterFilterTest {
-
-    @Test
-    public void testFilterInbound() {
-        HttpRequest req = HttpRequestBuilder.get("http://example.com").build();
-        HttpResponse resp = HttpResponseBuilder.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
-
-        TestListener listener = new TestListener();
-
-        MessagePredicate predicate = FilterTestUtils.createPredicate("{'@resp.status.code': 500}");
-        CounterFilter filter = new CounterFilter();
-        filter.setLimit(2);
-        filter.setName("filter status code 500");
-        filter.setPredicate(predicate);
-        filter.setListener(listener);
-
-        filter.filterInbound(req, resp, null);
-        assertEquals(0, listener.events.size());
-        filter.filterInbound(req, resp, null);
-        assertEquals(1, listener.events.size());
-    }
-
-    @Test
-    public void testConfigure() throws Exception {
-        String filterConfig = "filters:\n"
-                + "    - type: counter\n"
-                + "      name: 'filter x'\n"
-                + "      limit: 3\n"
-                + "      applyIf: {'@resp.status.code': 404}\n";
-
-        Errors errors = new Errors();
-        List<FlowFilter> filters = FlowFiltersConfigurator.getFilters(filterConfig, errors, null);
-        FilterTestUtils.printErrors(errors);
-
-        assertEquals(0, errors.getErrorCount());
-        assertEquals(1, filters.size());
-        assertTrue(filters.get(0) instanceof CounterFilter);
-
-        CounterFilter filter = (CounterFilter) filters.get(0);
-        assertEquals(3, filter.getLimit());
-        assertEquals("filter x", filter.getName());
-
-        AndPredicate<? extends Object> andPredicate = (AndPredicate) filter.getPredicate().getPredicate();
-        assertEquals(1, andPredicate.getSubPredicates().size());
-        ComparisonPredicate predicate = (ComparisonPredicate) andPredicate.getSubPredicates().get(0);
-        assertEquals("resp.status.code", predicate.getFieldName());
-        assertEquals(ComparisonOperator.EQUAL, predicate.getOperator());
-    }
-
-    private static class TestListener implements CounterListener {
-
-        private final ArrayList<Event> events = new ArrayList<>();
-
-        @Override
-        public void limitReached(Event event) {
-            events.add(event);
-        }
-
-    }
-
-}
\ No newline at end of file
--- a/stress-tester/src/test/java/com/passus/st/client/filter/FilterTestUtils.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.utils.ReflectionUtils;
-import com.passus.config.validation.Errors;
-import com.passus.config.validation.ObjectError;
-import com.passus.filter.config.PredicateNodeTransformer;
-import com.passus.net.http.HttpMessage;
-import com.passus.st.ParametersBag;
-import static com.passus.st.client.http.HttpConsts.TAG_SESSION_ID;
-import static com.passus.st.client.http.HttpConsts.ZONE_DEFAULT;
-
-import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.MessagePredicate;
-import com.passus.st.client.http.HttpFlowConst;
-import com.passus.st.client.http.HttpFlowContext;
-import com.passus.st.client.http.HttpScopes;
-import com.passus.st.filter.Transformers;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- *
- * @author mikolaj.podbielski
- */
-public class FilterTestUtils {
-
-    private static final boolean DEFAULT_USE_ACCELERATED_EXTRACTORS = true;
-
-    public static FlowContext createMockContext() {
-        return createMockContext(new HttpScopes());
-    }
-
-    public static FlowContext createMockContext(HttpScopes scopes) {
-        FlowContext mockContext = mock(FlowContext.class);
-        HttpFlowContext mockHttpContext = mock(HttpFlowContext.class);
-        when(mockContext.getParamValue(HttpFlowConst.PARAM_HTTP_CONTEXT)).thenReturn(mockHttpContext);
-        when(mockHttpContext.scopes()).thenReturn(scopes);
-        return mockContext;
-    }
-
-    public static MessagePredicate createPredicate(String config) {
-        return createPredicate(config, DEFAULT_USE_ACCELERATED_EXTRACTORS);
-    }
-
-    public static MessagePredicate createPredicate(String config, boolean useAcceleratedExtractor) {
-        try {
-            PredicateNodeTransformer transformer = useAcceleratedExtractor
-                    ? Transformers.PREDICATE : PredicateNodeTransformer.DEFAULT;
-            Predicate predicate = transformer.transform(config);
-            return new MessagePredicate(predicate);
-        } catch (Exception e) {
-            throw new RuntimeException(e.getMessage(), e);
-        }
-    }
-
-    public static void tagMessages(HttpMessage... messages) {
-        for (HttpMessage message : messages) {
-            message.setTag(TAG_SESSION_ID, "sid1");
-        }
-    }
-
-    public static void printErrors(Errors errors) {
-        List<ObjectError> allErrors = errors.getAllErrors();
-        if (allErrors.size() > 0) {
-            System.out.println("ERRORS:");
-            for (ObjectError error : allErrors) {
-                System.out.println(error);
-            }
-        }
-    }
-
-    public static Map<String, ParametersBag> getSessions(HttpScopes scopes) {
-        Map<String, Map<String, ParametersBag>> zonesSessions = ReflectionUtils.<Map>getField(scopes, "zonesSessions");
-        Map<String, ParametersBag> defaultZone = zonesSessions.get(ZONE_DEFAULT);
-        System.out.println(defaultZone);
-        return defaultZone;
-    }
-
-}
--- a/stress-tester/src/test/java/com/passus/st/client/filter/MatchFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.config.validation.Errors;
-import com.passus.net.dns.DnsARecord;
-import com.passus.net.dns.DnsRecord;
-import com.passus.net.http.*;
-import com.passus.st.AppUtils;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.passus.st.utils.Assert.assertNoError;
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertTrue;
-
-/**
- * @author Mirosław Hawrot
- */
-public class MatchFilterTest {
-
-    private <T> List<T> filter(List<T> messages, String config) {
-        MessagePredicate predicate = FilterTestUtils.createPredicate(config);
-        return filter(messages, predicate);
-    }
-
-    private <T> List<T> filter(List<T> messages, MessagePredicate predicate) {
-        List<T> out = new ArrayList<>(messages.size());
-        for (T message : messages) {
-            if (predicate.test(message)) {
-                out.add(message);
-            }
-        }
-
-        return out;
-    }
-
-    private List<HttpRequest> httpRequests() {
-        return Arrays.asList(
-                HttpRequestBuilder.get("http://test.com/test").header("Header", "TheValue").build(),
-                HttpRequestBuilder.get("http://test2.com/test2").build()
-        );
-    }
-
-    private List<HttpResponse> httpResponses() {
-        return Arrays.asList(
-                HttpResponseBuilder.ok().build(),
-                HttpResponseBuilder.status(HttpStatus.CREATED).build(),
-                HttpResponseBuilder.status(HttpStatus.ACCEPTED).build()
-        );
-    }
-
-    private List<DnsRecord> dnsRecords() {
-        DnsRecord dnsRecord1 = new DnsARecord();
-        dnsRecord1.setName("test1.com");
-
-        DnsRecord dnsRecord2 = new DnsARecord();
-        dnsRecord2.setName("test2.com");
-
-        return Arrays.asList(dnsRecord1, dnsRecord2);
-    }
-
-    @BeforeClass
-    public static void beforeClass() {
-        AppUtils.registerAll();
-    }
-
-    @AfterClass
-    public static void afterClass() {
-        AppUtils.unregisterAll();
-    }
-
-    @Test
-    public void testHttpFilterOutbound() {
-        List<HttpRequest> httpRequests = httpRequests();
-        List<HttpResponse> httpResponses = httpResponses();
-
-        List<HttpRequest> reqs;
-        reqs = filter(httpRequests, "{'@req.url': {$contains: \"test2\"}}");
-        assertEquals(1, reqs.size());
-        assertEquals("/test2", reqs.get(0).getUri().toString());
-
-        reqs = filter(httpRequests, "{'@req.uri': {$contains: \"test2\"}}");
-        assertEquals(1, reqs.size());
-        assertEquals("/test2", reqs.get(0).getUri().toString());
-
-        reqs = filter(httpRequests, "{\"@req.getHeader('Header')\": {$contains: \"Val\"}}");
-        assertEquals(1, reqs.size());
-        assertEquals("/test", reqs.get(0).getUri().toString());
-
-        List<HttpResponse> resps;
-        resps = filter(httpResponses, "{'@resp.status.code': 200}");
-        assertEquals(1, resps.size());
-
-        resps = filter(httpResponses, "{'@resp.status.code': '200'}");
-        assertEquals(1, resps.size());
-
-        resps = filter(httpResponses, "{'@resp.status.code': {$gt: 200}}");
-        assertEquals(2, resps.size());
-
-        MessagePredicate predicate = FilterTestUtils.createPredicate(
-                "{'@resp.status.code': {$gt: 200}}", false);
-        resps = filter(httpResponses, predicate);
-        assertEquals(2, resps.size());
-    }
-
-    @Test
-    public void testHttpConfigureAndFilterOutbound() throws Exception {
-        List<HttpRequest> httpRequests = httpRequests();
-
-        String filterConfig = "filters:\n"
-                + "    - type: matcher\n"
-                + "      matches:\n"
-                + "          '@req.uri': {$contains: 'test'}\n"
-                + "          '@resp.status.code': 200\n";
-
-        Errors errors = new Errors();
-        List<FlowFilter> filters = FlowFiltersConfigurator.getFilters(filterConfig, errors, null);
-
-        assertNoError(errors);
-        assertEquals(1, filters.size());
-        assertTrue(filters.get(0) instanceof MatchFilter);
-
-        MatchFilter filter = (MatchFilter) filters.get(0);
-        MessagePredicate predicate = filter.getPredicate();
-
-        List<HttpRequest> res = filter(httpRequests, "{'@req.url': {$contains: \"test2\"}}");
-        assertEquals(1, res.size());
-        assertEquals("/test2", res.get(0).getUri().toString());
-
-        res = filter(httpRequests, "{'@req.uri': {$contains: \"test2\"}}");
-        assertEquals(1, res.size());
-        assertEquals("/test2", res.get(0).getUri().toString());
-    }
-
-    @Test
-    public void testDnsFilterOutbound() {
-        List<DnsRecord> dnsRecords = dnsRecords();
-        List<DnsRecord> filtered = filter(dnsRecords, "{'@req.name': {$contains: \"test1\"}}");
-        assertEquals(1, filtered.size());
-        assertEquals("test1.com", filtered.get(0).getName());
-    }
-
-}
--- a/stress-tester/src/test/java/com/passus/st/client/filter/MvelFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.utils.ResourceUtils;
-import com.passus.config.validation.Errors;
-import com.passus.net.http.HttpMethod;
-import com.passus.net.http.HttpRequest;
-import com.passus.net.http.HttpRequestBuilder;
-import java.io.File;
-import java.util.List;
-
-import org.mvel2.MVEL;
-import org.mvel2.compiler.ExecutableStatement;
-import static org.testng.AssertJUnit.*;
-import org.testng.annotations.Test;
-
-/**
- *
- * @author mikolaj.podbielski
- */
-public class MvelFilterTest {
-
-    @Test
-    public void testFilterOutbound() {
-        HttpRequest req = HttpRequestBuilder.get("http://example.com/index.html").build();
-
-        String expression = "$req.setMethod(com.passus.net.http.HttpMethod.HEAD);"
-                + "$req.getUri().toString().equals(\"/index.html\") ? 1 : 0";
-
-        MvelFilter filter = new MvelFilter();
-        filter.setStatement(es(expression));
-
-        int result = filter.filterOutbound(req, null, null);
-
-        assertEquals(1, result);
-        assertEquals(HttpMethod.HEAD, req.getMethod());
-    }
-
-    @Test
-    public void testConfigure() throws Exception {
-        File file = ResourceUtils.getFile("mvel/return1.mvel");
-
-        String filterConfig = "filters:\n"
-                + "  - type: mvel\n"
-                + "    dir: out\n"
-                + "    script: return -1\n";
-
-        Errors errors = new Errors();
-        List<FlowFilter> filters = FlowFiltersConfigurator.getFilters(filterConfig, errors, null);
-        FilterTestUtils.printErrors(errors);
-
-        assertEquals(0, errors.getErrorCount());
-        assertEquals(1, filters.size());
-        assertTrue(filters.get(0) instanceof MvelFilter);
-
-        MvelFilter filter = (MvelFilter) filters.get(0);
-        assertEquals(-1, filter.filterOutbound(null, null, null));
-        assertEquals(0, filter.filterInbound(null, null, null));
-    }
-
-    public static ExecutableStatement es(String expression) {
-        return (ExecutableStatement) MVEL.compileExpression(expression);
-    }
-}
--- a/stress-tester/src/test/java/com/passus/st/client/filter/SequenceFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-package com.passus.st.client.filter;
-
-import com.passus.commons.metric.MapMetric;
-import com.passus.commons.utils.ResourceUtils;
-import com.passus.config.validation.Errors;
-import com.passus.filter.AndPredicate;
-import com.passus.filter.BeanValueExtractor;
-import com.passus.filter.ComparisonOperator;
-import com.passus.filter.ComparisonPredicate;
-import com.passus.filter.UnmutableValueExtractor;
-import com.passus.filter.ValueExtractor;
-import com.passus.filter.ValueExtractorParser;
-import com.passus.filter.config.PredicateNodeTransformer;
-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.AppUtils;
-import com.passus.st.client.filter.SequenceFilter.SequenceItem;
-import com.passus.st.filter.Transformers;
-import java.io.File;
-import java.io.Serializable;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
-import static org.testng.AssertJUnit.*;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-/**
- *
- * @author mikolaj.podbielski
- */
-public class SequenceFilterTest {
-
-    private static final PredicateNodeTransformer TRANSFORMER = Transformers.PREDICATE;
-
-    @BeforeClass
-    public static void beforeClass() {
-        AppUtils.registerAll();
-    }
-
-    @AfterClass
-    public static void afterClass() {
-        AppUtils.unregisterAll();
-    }
-
-    @Test
-    public void testFilterInbound() throws Exception {
-        HttpRequest req0 = HttpRequestBuilder.get("http://example.com/res1").build();
-        HttpResponse resp0 = HttpResponseBuilder.ok().cookie("id", "123").build();
-
-        HttpRequest req1 = HttpRequestBuilder.get("http://example.com/res2")
-                .cookie("id", "123").header("Xyz", "abc").build();
-        HttpResponse resp1 = HttpResponseBuilder.ok().build();
-
-        TestSequenceListener listener = new TestSequenceListener();
-
-        SequenceItem[] seqItems = {
-            item("{\"@req.uri\": \"/res1\", \"@resp.getCookie('id')\": {$neq: \"0\"}}", 0),
-            item("{\"@req.getHeader('xyz')\": \"abc\", \"@req.getCookie('id')\": \"@_i0.resp.getCookie('id')\"}", 1, "last"),};
-
-        Map<String, ValueExtractor> values = new HashMap<>();
-        values.put("code", value("@_i0.resp.status.code"));
-        values.put("xhost", value("@_i0.req.url.host"));
-        values.put("header", value("@_i1.req.getHeader('xyz')"));
-        values.put("cookie", value("@last.req.getCookie('id')"));
-        values.put("seqName", value("example sequence"));
-
-        SequenceFilter filter = new SequenceFilter().instanceForWorker(0);
-        filter.setListener(listener);
-        filter.setSeqItems(seqItems);
-        filter.setValues(values);
-
-        filter.filterInbound(req0, resp0, null);
-        filter.filterInbound(req1, resp1, null);
-
-        assertEquals(1, listener.events.size());
-        Map<String, Serializable> extracted = listener.events.get(0).getAttributesValue();
-        assertEquals(200, extracted.get("code"));
-        assertEquals("example.com", extracted.get("xhost").toString());
-        assertEquals("abc", extracted.get("header").toString());
-        assertEquals("123", extracted.get("cookie").toString());
-        assertEquals("example sequence", extracted.get("seqName").toString());
-    }
-
-    private static SequenceItem item(String predicateString, int num) throws Exception {
-        Predicate predicate = TRANSFORMER.transform(predicateString);
-        return new SequenceItem(predicate, num);
-    }
-
-    private static SequenceItem item(String predicateString, int num, String alias) throws Exception {
-        Predicate predicate = TRANSFORMER.transform(predicateString);
-        SequenceItem si = new SequenceItem(predicate, num);
-        si.setAlias(alias);
-        return si;
-    }
-
-    private static ValueExtractor value(String s) throws ParseException {
-        return ValueExtractorParser.DEFAULT.parse(s);
-    }
-
-    @Test
-    public void testConfigure() throws Exception {
-        File file = ResourceUtils.getFile("com/passus/st/client/http/filter/sequence.yml");
-        String filterConfig = new String(Files.readAllBytes(Paths.get(file.toURI())));
-
-        Errors errors = new Errors();
-        List<FlowFilter> filters = FlowFiltersConfigurator.getFilters(filterConfig, errors, null);
-        FilterTestUtils.printErrors(errors);
-
-        assertEquals(0, errors.getErrorCount());
-        assertEquals(1, filters.size());
-        assertTrue(filters.get(0) instanceof SequenceFilter);
-        SequenceFilter filter = (SequenceFilter) filters.get(0);
-
-        SequenceItem[] seqItems = filter.getSeqItems();
-        assertEquals(2, seqItems.length);
-        assertSeqItemValue(seqItems[0], 0, 10000, null);
-        assertSeqItemValue(seqItems[1], 1, 20000, "last");
-
-        AndPredicate p0 = (AndPredicate) seqItems[0].getPredicate();
-        assertEquals(2, p0.getSubPredicates().size());
-        ComparisonPredicate sp1 = (ComparisonPredicate) p0.getSubPredicates().get(1);
-        assertEquals(ComparisonOperator.NOT_EQUAL, sp1.getOperator());
-
-        assertEquals("resp.getCookie('id')", sp1.getFieldName());
-
-        UnmutableValueExtractor uve = (UnmutableValueExtractor) sp1.getPattern();
-        assertEquals(123, uve.extract(null));
-
-        Map<String, ValueExtractor> values = filter.getValues();
-        assertEquals(4, values.size());
-        ValueExtractor extractor = values.get("header");
-        assertTrue(extractor instanceof BeanValueExtractor);
-        BeanValueExtractor bve = (BeanValueExtractor) extractor;
-        assertEquals("last.req.getHeader('xyz')", bve.getFieldName());
-    }
-
-    private static void assertSeqItemValue(SequenceItem item, int num, long time, String alias) {
-        assertEquals("num", num, item.getNum());
-        assertEquals("time", time, item.getTime());
-        assertEquals("alias", alias, item.getAlias());
-    }
-
-    public static class TestSequenceListener implements SequenceListener {
-
-        ArrayList<MapMetric> events = new ArrayList<>();
-
-        @Override
-        public void sequenceDetected(MapMetric sequence) {
-            events.add(sequence);
-        }
-
-        public void reset() {
-            events.clear();
-        }
-    }
-}
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpBasicAuthLoginFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpBasicAuthLoginFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -11,9 +11,9 @@
 import com.passus.net.http.HttpResponseBuilder;
 import com.passus.st.AppUtils;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FilterTestUtils;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FilterTestUtils;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import com.passus.st.client.credentials.Credentials;
 import com.passus.st.client.credentials.CredentialsProvider;
 import com.passus.st.client.credentials.CsvUsernamePasswordCredentialsProvider;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpCsrfFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpCsrfFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -8,9 +8,9 @@
 import com.passus.net.http.HttpResponseBuilder;
 import com.passus.st.ParametersBag;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FilterTestUtils;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FilterTestUtils;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import com.passus.st.client.http.HttpScopes;
 import com.passus.st.client.http.filter.HttpCsrfFilter.*;
 import com.passus.st.emitter.SessionInfo;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpCsrfFormFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpCsrfFormFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -13,15 +13,15 @@
 import java.io.IOException;
 import java.util.List;
 
-import static com.passus.st.client.filter.FilterTestUtils.createMockContext;
+import static com.passus.st.filter.FilterTestUtils.createMockContext;
 import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.testng.AssertJUnit.*;
 
-import com.passus.st.client.filter.FilterTestUtils;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FilterTestUtils;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import org.testng.annotations.Test;
 
 /**
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpDigestAuthLoginFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpDigestAuthLoginFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -15,7 +15,7 @@
 
 import static com.passus.st.client.http.HttpConsts.PARAM_USERNAME;
 import static com.passus.st.client.http.HttpConsts.TAG_SESSION_ID;
-import static com.passus.st.client.filter.FilterTestUtils.createMockContext;
+import static com.passus.st.filter.FilterTestUtils.createMockContext;
 import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertEquals;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpFiltersConfiguratorTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpFiltersConfiguratorTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -3,8 +3,8 @@
 import com.passus.commons.utils.ResourceUtils;
 import com.passus.config.ConfigurationContext;
 import com.passus.config.validation.Errors;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import com.passus.st.client.credentials.MultiCredentialsProvider;
 import com.passus.st.client.credentials.UsernamePasswordCredentialsProvider;
 import java.io.File;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpFiltersNodeDefinitionCreatorTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpFiltersNodeDefinitionCreatorTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -10,7 +10,7 @@
 import com.passus.config.YamlConfigurationReader;
 import com.passus.config.schema.NodeDefinition;
 import com.passus.config.validation.Errors;
-import com.passus.st.client.filter.FlowFilterNodeDefinitionCreator;
+import com.passus.st.filter.FlowFilterNodeDefinitionCreator;
 import com.passus.st.client.http.filter.HttpMessageModificationOperations.*;
 import java.io.File;
 import java.util.List;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpFormLoginFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpFormLoginFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -12,13 +12,13 @@
 import static com.passus.st.client.http.HttpConsts.PARAM_USERNAME;
 import static com.passus.st.client.http.HttpConsts.TAG_SESSION_ID;
 
-import com.passus.st.client.filter.MessagePredicate;
+import com.passus.st.filter.MessagePredicate;
 import com.passus.st.client.http.HttpScopes;
 import com.passus.st.utils.TestHttpUtils;
 import java.io.IOException;
 import java.util.function.Predicate;
 
-import static com.passus.st.client.filter.FilterTestUtils.createMockContext;
+import static com.passus.st.filter.FilterTestUtils.createMockContext;
 import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpHostRewriterFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpHostRewriterFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -8,8 +8,8 @@
 import static com.passus.net.http.HttpHeaders.*;
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpRequestBuilder;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import com.passus.st.client.http.filter.HttpHostRewriterFilter.HostPort;
 import java.util.List;
 import java.util.Map;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpLogoutFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpLogoutFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -7,10 +7,10 @@
 import com.passus.net.http.HttpResponseBuilder;
 import com.passus.st.AppUtils;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FilterTestUtils;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
-import com.passus.st.client.filter.MessagePredicate;
+import com.passus.st.filter.FilterTestUtils;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.MessagePredicate;
 import com.passus.st.client.http.HttpScopes;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -20,7 +20,7 @@
 
 import static com.passus.st.client.http.HttpConsts.PARAM_USERNAME;
 import static com.passus.st.client.http.HttpConsts.TAG_SESSION_ID;
-import static com.passus.st.client.filter.FilterTestUtils.createMockContext;
+import static com.passus.st.filter.FilterTestUtils.createMockContext;
 import static org.testng.AssertJUnit.*;
 
 /**
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMarkFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMarkFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -6,9 +6,9 @@
 import com.passus.st.AppUtils;
 import static com.passus.st.client.http.HttpConsts.TAG_MARKER;
 
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
-import com.passus.st.client.filter.MessagePredicate;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.MessagePredicate;
 import com.passus.st.client.http.filter.HttpMarkFilter.Marker;
 import com.passus.st.client.http.filter.HttpMarkFilter.MarkerRule;
 import java.util.List;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -7,10 +7,10 @@
 import com.passus.net.http.*;
 import com.passus.st.AppUtils;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FilterTestUtils;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
-import com.passus.st.client.filter.FilterDirection;
+import com.passus.st.filter.FilterTestUtils;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FilterDirection;
 import com.passus.st.extractor.ContentExtractorUtils;
 import com.passus.st.client.http.filter.HttpMessageModificationOperations.*;
 import com.passus.st.utils.ConfigurationContextConsts;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformerTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformerTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -8,7 +8,7 @@
 import com.passus.lookup.Lookup;
 import com.passus.lookup.LookupList;
 import com.passus.lookup.filter.LookupValueExtractor;
-import com.passus.st.client.filter.FilterTestUtils;
+import com.passus.st.filter.FilterTestUtils;
 import com.passus.st.client.http.filter.HttpMessageModificationOperations.*;
 import com.passus.st.generator.GeneratorValueExtractor;
 import com.passus.st.utils.ConfigurationContextConsts;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageWrapperTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageWrapperTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -1,6 +1,5 @@
 package com.passus.st.client.http.filter;
 
-import com.passus.data.ByteString;
 import com.passus.filter.BeanValueExtractor;
 import com.passus.filter.MvelValueExtractor;
 import com.passus.filter.UnmutableValueExtractor;
@@ -12,8 +11,8 @@
 import com.passus.st.AppUtils;
 import com.passus.st.ParametersBag;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilterFactory;
-import com.passus.st.client.filter.MessageWrapper;
+import com.passus.st.filter.FlowFilterFactory;
+import com.passus.st.filter.MessageWrapper;
 import com.passus.st.client.http.HttpFlowConst;
 import com.passus.st.client.http.HttpFlowContext;
 import com.passus.st.client.http.HttpScopes;
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpScopeModificationFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpScopeModificationFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -8,9 +8,9 @@
 import com.passus.net.http.HttpRequestBuilder;
 import com.passus.st.AppUtils;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
-import com.passus.st.client.filter.FilterDirection;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FilterDirection;
 import com.passus.st.client.http.HttpConsts;
 import com.passus.st.client.http.HttpScopes;
 import com.passus.st.client.http.filter.HttpScopeModificationFilter.*;
@@ -21,7 +21,7 @@
 import java.util.Arrays;
 import java.util.List;
 
-import static com.passus.st.client.filter.FilterTestUtils.createMockContext;
+import static com.passus.st.filter.FilterTestUtils.createMockContext;
 import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
 import static org.testng.AssertJUnit.*;
 
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpSessionBlockerFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpSessionBlockerFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -6,7 +6,7 @@
 
 import static com.passus.net.http.HttpRequestBuilder.get;
 import static com.passus.st.client.http.HttpConsts.TAG_SESSION_ID;
-import static com.passus.st.client.filter.FilterTestUtils.createMockContext;
+import static com.passus.st.filter.FilterTestUtils.createMockContext;
 import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
 import static org.testng.AssertJUnit.assertEquals;
 
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpSessionCookieFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpSessionCookieFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -11,7 +11,7 @@
 
 import com.passus.st.client.FlowContext;
 
-import static com.passus.st.client.filter.FilterTestUtils.createMockContext;
+import static com.passus.st.filter.FilterTestUtils.createMockContext;
 import static com.passus.st.client.http.filter.HttpFlowUtils.extractHttpContext;
 import static com.passus.st.utils.TestHttpUtils.*;
 import java.util.Arrays;
@@ -20,9 +20,9 @@
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.*;
 
-import com.passus.st.client.filter.FilterTestUtils;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FilterTestUtils;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import org.testng.annotations.Test;
 
 /**
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpZoneFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpZoneFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -8,9 +8,9 @@
 import com.passus.st.AppUtils;
 import static com.passus.st.client.http.HttpConsts.TAG_ZONE;
 
-import com.passus.st.client.filter.FilterTestUtils;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FilterTestUtils;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import com.passus.st.client.http.filter.HttpZoneFilter.Rule;
 import java.util.List;
 import static org.testng.AssertJUnit.assertEquals;
--- a/stress-tester/src/test/java/com/passus/st/client/netflow/filter/NetflowVersionFilterTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/netflow/filter/NetflowVersionFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -2,8 +2,8 @@
 
 import com.passus.config.NodeException;
 import com.passus.config.validation.Errors;
-import com.passus.st.client.filter.FlowFilter;
-import com.passus.st.client.filter.FlowFiltersConfigurator;
+import com.passus.st.filter.FlowFilter;
+import com.passus.st.filter.FlowFiltersConfigurator;
 import org.testng.annotations.Test;
 
 import java.io.IOException;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/test/java/com/passus/st/filter/CounterFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,76 @@
+package com.passus.st.filter;
+
+import com.passus.config.validation.Errors;
+import com.passus.filter.AndPredicate;
+import com.passus.filter.ComparisonOperator;
+import com.passus.filter.ComparisonPredicate;
+import com.passus.net.http.*;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+public class CounterFilterTest {
+
+    @Test
+    public void testFilterInbound() {
+        HttpRequest req = HttpRequestBuilder.get("http://example.com").build();
+        HttpResponse resp = HttpResponseBuilder.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
+
+        TestListener listener = new TestListener();
+
+        MessagePredicate predicate = FilterTestUtils.createPredicate("{'@resp.status.code': 500}");
+        CounterFilter filter = new CounterFilter();
+        filter.setLimit(2);
+        filter.setName("filter status code 500");
+        filter.setPredicate(predicate);
+        filter.setListener(listener);
+
+        filter.filterInbound(req, resp, null);
+        assertEquals(0, listener.events.size());
+        filter.filterInbound(req, resp, null);
+        assertEquals(1, listener.events.size());
+    }
+
+    @Test
+    public void testConfigure() throws Exception {
+        String filterConfig = "filters:\n"
+                + "    - type: counter\n"
+                + "      name: 'filter x'\n"
+                + "      limit: 3\n"
+                + "      applyIf: {'@resp.status.code': 404}\n";
+
+        Errors errors = new Errors();
+        List<FlowFilter> filters = FlowFiltersConfigurator.getFilters(filterConfig, errors, null);
+        FilterTestUtils.printErrors(errors);
+
+        assertEquals(0, errors.getErrorCount());
+        assertEquals(1, filters.size());
+        assertTrue(filters.get(0) instanceof CounterFilter);
+
+        CounterFilter filter = (CounterFilter) filters.get(0);
+        assertEquals(3, filter.getLimit());
+        assertEquals("filter x", filter.getName());
+
+        AndPredicate<? extends Object> andPredicate = (AndPredicate) filter.getPredicate().getPredicate();
+        assertEquals(1, andPredicate.getSubPredicates().size());
+        ComparisonPredicate predicate = (ComparisonPredicate) andPredicate.getSubPredicates().get(0);
+        assertEquals("resp.status.code", predicate.getFieldName());
+        assertEquals(ComparisonOperator.EQUAL, predicate.getOperator());
+    }
+
+    private static class TestListener implements CounterListener {
+
+        private final ArrayList<Event> events = new ArrayList<>();
+
+        @Override
+        public void limitReached(Event event) {
+            events.add(event);
+        }
+
+    }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/test/java/com/passus/st/filter/FilterTestUtils.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,82 @@
+package com.passus.st.filter;
+
+import com.passus.commons.utils.ReflectionUtils;
+import com.passus.config.validation.Errors;
+import com.passus.config.validation.ObjectError;
+import com.passus.filter.config.PredicateNodeTransformer;
+import com.passus.net.http.HttpMessage;
+import com.passus.st.ParametersBag;
+import static com.passus.st.client.http.HttpConsts.TAG_SESSION_ID;
+import static com.passus.st.client.http.HttpConsts.ZONE_DEFAULT;
+
+import com.passus.st.client.FlowContext;
+import com.passus.st.filter.MessagePredicate;
+import com.passus.st.client.http.HttpFlowConst;
+import com.passus.st.client.http.HttpFlowContext;
+import com.passus.st.client.http.HttpScopes;
+import com.passus.st.filter.Transformers;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+public class FilterTestUtils {
+
+    private static final boolean DEFAULT_USE_ACCELERATED_EXTRACTORS = true;
+
+    public static FlowContext createMockContext() {
+        return createMockContext(new HttpScopes());
+    }
+
+    public static FlowContext createMockContext(HttpScopes scopes) {
+        FlowContext mockContext = mock(FlowContext.class);
+        HttpFlowContext mockHttpContext = mock(HttpFlowContext.class);
+        when(mockContext.getParamValue(HttpFlowConst.PARAM_HTTP_CONTEXT)).thenReturn(mockHttpContext);
+        when(mockHttpContext.scopes()).thenReturn(scopes);
+        return mockContext;
+    }
+
+    public static MessagePredicate createPredicate(String config) {
+        return createPredicate(config, DEFAULT_USE_ACCELERATED_EXTRACTORS);
+    }
+
+    public static MessagePredicate createPredicate(String config, boolean useAcceleratedExtractor) {
+        try {
+            PredicateNodeTransformer transformer = useAcceleratedExtractor
+                    ? Transformers.PREDICATE : PredicateNodeTransformer.DEFAULT;
+            Predicate predicate = transformer.transform(config);
+            return new MessagePredicate(predicate);
+        } catch (Exception e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    public static void tagMessages(HttpMessage... messages) {
+        for (HttpMessage message : messages) {
+            message.setTag(TAG_SESSION_ID, "sid1");
+        }
+    }
+
+    public static void printErrors(Errors errors) {
+        List<ObjectError> allErrors = errors.getAllErrors();
+        if (allErrors.size() > 0) {
+            System.out.println("ERRORS:");
+            for (ObjectError error : allErrors) {
+                System.out.println(error);
+            }
+        }
+    }
+
+    public static Map<String, ParametersBag> getSessions(HttpScopes scopes) {
+        Map<String, Map<String, ParametersBag>> zonesSessions = ReflectionUtils.<Map>getField(scopes, "zonesSessions");
+        Map<String, ParametersBag> defaultZone = zonesSessions.get(ZONE_DEFAULT);
+        System.out.println(defaultZone);
+        return defaultZone;
+    }
+
+}
--- a/stress-tester/src/test/java/com/passus/st/filter/HttpMessageValueExtractorTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/filter/HttpMessageValueExtractorTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -4,12 +4,9 @@
 import com.passus.net.http.*;
 import com.passus.st.ParametersBag;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilterFactory;
-import com.passus.st.client.filter.MessageWrapper;
 import com.passus.st.client.http.HttpFlowConst;
 import com.passus.st.client.http.HttpFlowContext;
 import com.passus.st.client.http.HttpScopes;
-import com.passus.st.client.http.filter.HttpMessageWrapper;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
--- a/stress-tester/src/test/java/com/passus/st/filter/HttpMessageWrapperDynamicExtractorTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/filter/HttpMessageWrapperDynamicExtractorTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -8,9 +8,6 @@
 import com.passus.net.http.HttpStatus;
 import static com.passus.st.client.http.HttpConsts.TAG_SESSION_ID;
 
-import com.passus.st.client.filter.FlowFilterFactory;
-import com.passus.st.client.filter.MessageWrapper;
-import com.passus.st.client.http.filter.HttpMessageWrapper;
 import static com.passus.st.utils.Assert.*;
 import org.testng.annotations.Test;
 
--- a/stress-tester/src/test/java/com/passus/st/filter/HttpMessageWrapperStaticExtractorTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/filter/HttpMessageWrapperStaticExtractorTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -5,7 +5,6 @@
 import com.passus.net.http.HttpResponse;
 import com.passus.net.http.HttpResponseBuilder;
 import com.passus.st.client.FlowContext;
-import com.passus.st.client.filter.FlowFilterFactory;
 import com.passus.st.client.http.HttpConsts;
 import com.passus.st.client.http.HttpFlowConst;
 import com.passus.st.client.http.HttpFlowContext;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/test/java/com/passus/st/filter/MatchFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,147 @@
+package com.passus.st.filter;
+
+import com.passus.config.validation.Errors;
+import com.passus.net.dns.DnsARecord;
+import com.passus.net.dns.DnsRecord;
+import com.passus.net.http.*;
+import com.passus.st.AppUtils;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.passus.st.utils.Assert.assertNoError;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+/**
+ * @author Mirosław Hawrot
+ */
+public class MatchFilterTest {
+
+    private <T> List<T> filter(List<T> messages, String config) {
+        MessagePredicate predicate = FilterTestUtils.createPredicate(config);
+        return filter(messages, predicate);
+    }
+
+    private <T> List<T> filter(List<T> messages, MessagePredicate predicate) {
+        List<T> out = new ArrayList<>(messages.size());
+        for (T message : messages) {
+            if (predicate.test(message)) {
+                out.add(message);
+            }
+        }
+
+        return out;
+    }
+
+    private List<HttpRequest> httpRequests() {
+        return Arrays.asList(
+                HttpRequestBuilder.get("http://test.com/test").header("Header", "TheValue").build(),
+                HttpRequestBuilder.get("http://test2.com/test2").build()
+        );
+    }
+
+    private List<HttpResponse> httpResponses() {
+        return Arrays.asList(
+                HttpResponseBuilder.ok().build(),
+                HttpResponseBuilder.status(HttpStatus.CREATED).build(),
+                HttpResponseBuilder.status(HttpStatus.ACCEPTED).build()
+        );
+    }
+
+    private List<DnsRecord> dnsRecords() {
+        DnsRecord dnsRecord1 = new DnsARecord();
+        dnsRecord1.setName("test1.com");
+
+        DnsRecord dnsRecord2 = new DnsARecord();
+        dnsRecord2.setName("test2.com");
+
+        return Arrays.asList(dnsRecord1, dnsRecord2);
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        AppUtils.registerAll();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        AppUtils.unregisterAll();
+    }
+
+    @Test
+    public void testHttpFilterOutbound() {
+        List<HttpRequest> httpRequests = httpRequests();
+        List<HttpResponse> httpResponses = httpResponses();
+
+        List<HttpRequest> reqs;
+        reqs = filter(httpRequests, "{'@req.url': {$contains: \"test2\"}}");
+        assertEquals(1, reqs.size());
+        assertEquals("/test2", reqs.get(0).getUri().toString());
+
+        reqs = filter(httpRequests, "{'@req.uri': {$contains: \"test2\"}}");
+        assertEquals(1, reqs.size());
+        assertEquals("/test2", reqs.get(0).getUri().toString());
+
+        reqs = filter(httpRequests, "{\"@req.getHeader('Header')\": {$contains: \"Val\"}}");
+        assertEquals(1, reqs.size());
+        assertEquals("/test", reqs.get(0).getUri().toString());
+
+        List<HttpResponse> resps;
+        resps = filter(httpResponses, "{'@resp.status.code': 200}");
+        assertEquals(1, resps.size());
+
+        resps = filter(httpResponses, "{'@resp.status.code': '200'}");
+        assertEquals(1, resps.size());
+
+        resps = filter(httpResponses, "{'@resp.status.code': {$gt: 200}}");
+        assertEquals(2, resps.size());
+
+        MessagePredicate predicate = FilterTestUtils.createPredicate(
+                "{'@resp.status.code': {$gt: 200}}", false);
+        resps = filter(httpResponses, predicate);
+        assertEquals(2, resps.size());
+    }
+
+    @Test
+    public void testHttpConfigureAndFilterOutbound() throws Exception {
+        List<HttpRequest> httpRequests = httpRequests();
+
+        String filterConfig = "filters:\n"
+                + "    - type: matcher\n"
+                + "      matches:\n"
+                + "          '@req.uri': {$contains: 'test'}\n"
+                + "          '@resp.status.code': 200\n";
+
+        Errors errors = new Errors();
+        List<FlowFilter> filters = FlowFiltersConfigurator.getFilters(filterConfig, errors, null);
+
+        assertNoError(errors);
+        assertEquals(1, filters.size());
+        assertTrue(filters.get(0) instanceof MatchFilter);
+
+        MatchFilter filter = (MatchFilter) filters.get(0);
+        MessagePredicate predicate = filter.getPredicate();
+
+        List<HttpRequest> res = filter(httpRequests, "{'@req.url': {$contains: \"test2\"}}");
+        assertEquals(1, res.size());
+        assertEquals("/test2", res.get(0).getUri().toString());
+
+        res = filter(httpRequests, "{'@req.uri': {$contains: \"test2\"}}");
+        assertEquals(1, res.size());
+        assertEquals("/test2", res.get(0).getUri().toString());
+    }
+
+    @Test
+    public void testDnsFilterOutbound() {
+        List<DnsRecord> dnsRecords = dnsRecords();
+        List<DnsRecord> filtered = filter(dnsRecords, "{'@req.name': {$contains: \"test1\"}}");
+        assertEquals(1, filtered.size());
+        assertEquals("test1.com", filtered.get(0).getName());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/test/java/com/passus/st/filter/MvelFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,63 @@
+package com.passus.st.filter;
+
+import com.passus.commons.utils.ResourceUtils;
+import com.passus.config.validation.Errors;
+import com.passus.net.http.HttpMethod;
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpRequestBuilder;
+import java.io.File;
+import java.util.List;
+
+import org.mvel2.MVEL;
+import org.mvel2.compiler.ExecutableStatement;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+public class MvelFilterTest {
+
+    @Test
+    public void testFilterOutbound() {
+        HttpRequest req = HttpRequestBuilder.get("http://example.com/index.html").build();
+
+        String expression = "$req.setMethod(com.passus.net.http.HttpMethod.HEAD);"
+                + "$req.getUri().toString().equals(\"/index.html\") ? 1 : 0";
+
+        MvelFilter filter = new MvelFilter();
+        filter.setStatement(es(expression));
+
+        int result = filter.filterOutbound(req, null, null);
+
+        assertEquals(1, result);
+        assertEquals(HttpMethod.HEAD, req.getMethod());
+    }
+
+    @Test
+    public void testConfigure() throws Exception {
+        File file = ResourceUtils.getFile("mvel/return1.mvel");
+
+        String filterConfig = "filters:\n"
+                + "  - type: mvel\n"
+                + "    dir: out\n"
+                + "    script: return -1\n";
+
+        Errors errors = new Errors();
+        List<FlowFilter> filters = FlowFiltersConfigurator.getFilters(filterConfig, errors, null);
+        FilterTestUtils.printErrors(errors);
+
+        assertEquals(0, errors.getErrorCount());
+        assertEquals(1, filters.size());
+        assertTrue(filters.get(0) instanceof MvelFilter);
+
+        MvelFilter filter = (MvelFilter) filters.get(0);
+        assertEquals(-1, filter.filterOutbound(null, null, null));
+        assertEquals(0, filter.filterInbound(null, null, null));
+    }
+
+    public static ExecutableStatement es(String expression) {
+        return (ExecutableStatement) MVEL.compileExpression(expression);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/test/java/com/passus/st/filter/SequenceFilterTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -0,0 +1,165 @@
+package com.passus.st.filter;
+
+import com.passus.commons.metric.MapMetric;
+import com.passus.commons.utils.ResourceUtils;
+import com.passus.config.validation.Errors;
+import com.passus.filter.AndPredicate;
+import com.passus.filter.BeanValueExtractor;
+import com.passus.filter.ComparisonOperator;
+import com.passus.filter.ComparisonPredicate;
+import com.passus.filter.UnmutableValueExtractor;
+import com.passus.filter.ValueExtractor;
+import com.passus.filter.ValueExtractorParser;
+import com.passus.filter.config.PredicateNodeTransformer;
+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.AppUtils;
+import com.passus.st.filter.SequenceFilter.SequenceItem;
+
+import java.io.File;
+import java.io.Serializable;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+public class SequenceFilterTest {
+
+    private static final PredicateNodeTransformer TRANSFORMER = Transformers.PREDICATE;
+
+    @BeforeClass
+    public static void beforeClass() {
+        AppUtils.registerAll();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        AppUtils.unregisterAll();
+    }
+
+    @Test
+    public void testFilterInbound() throws Exception {
+        HttpRequest req0 = HttpRequestBuilder.get("http://example.com/res1").build();
+        HttpResponse resp0 = HttpResponseBuilder.ok().cookie("id", "123").build();
+
+        HttpRequest req1 = HttpRequestBuilder.get("http://example.com/res2")
+                .cookie("id", "123").header("Xyz", "abc").build();
+        HttpResponse resp1 = HttpResponseBuilder.ok().build();
+
+        TestSequenceListener listener = new TestSequenceListener();
+
+        SequenceItem[] seqItems = {
+            item("{\"@req.uri\": \"/res1\", \"@resp.getCookie('id')\": {$neq: \"0\"}}", 0),
+            item("{\"@req.getHeader('xyz')\": \"abc\", \"@req.getCookie('id')\": \"@_i0.resp.getCookie('id')\"}", 1, "last"),};
+
+        Map<String, ValueExtractor> values = new HashMap<>();
+        values.put("code", value("@_i0.resp.status.code"));
+        values.put("xhost", value("@_i0.req.url.host"));
+        values.put("header", value("@_i1.req.getHeader('xyz')"));
+        values.put("cookie", value("@last.req.getCookie('id')"));
+        values.put("seqName", value("example sequence"));
+
+        SequenceFilter filter = new SequenceFilter().instanceForWorker(0);
+        filter.setListener(listener);
+        filter.setSeqItems(seqItems);
+        filter.setValues(values);
+
+        filter.filterInbound(req0, resp0, null);
+        filter.filterInbound(req1, resp1, null);
+
+        assertEquals(1, listener.events.size());
+        Map<String, Serializable> extracted = listener.events.get(0).getAttributesValue();
+        assertEquals(200, extracted.get("code"));
+        assertEquals("example.com", extracted.get("xhost").toString());
+        assertEquals("abc", extracted.get("header").toString());
+        assertEquals("123", extracted.get("cookie").toString());
+        assertEquals("example sequence", extracted.get("seqName").toString());
+    }
+
+    private static SequenceItem item(String predicateString, int num) throws Exception {
+        Predicate predicate = TRANSFORMER.transform(predicateString);
+        return new SequenceItem(predicate, num);
+    }
+
+    private static SequenceItem item(String predicateString, int num, String alias) throws Exception {
+        Predicate predicate = TRANSFORMER.transform(predicateString);
+        SequenceItem si = new SequenceItem(predicate, num);
+        si.setAlias(alias);
+        return si;
+    }
+
+    private static ValueExtractor value(String s) throws ParseException {
+        return ValueExtractorParser.DEFAULT.parse(s);
+    }
+
+    @Test
+    public void testConfigure() throws Exception {
+        File file = ResourceUtils.getFile("com/passus/st/client/http/filter/sequence.yml");
+        String filterConfig = new String(Files.readAllBytes(Paths.get(file.toURI())));
+
+        Errors errors = new Errors();
+        List<FlowFilter> filters = FlowFiltersConfigurator.getFilters(filterConfig, errors, null);
+        FilterTestUtils.printErrors(errors);
+
+        assertEquals(0, errors.getErrorCount());
+        assertEquals(1, filters.size());
+        assertTrue(filters.get(0) instanceof SequenceFilter);
+        SequenceFilter filter = (SequenceFilter) filters.get(0);
+
+        SequenceItem[] seqItems = filter.getSeqItems();
+        assertEquals(2, seqItems.length);
+        assertSeqItemValue(seqItems[0], 0, 10000, null);
+        assertSeqItemValue(seqItems[1], 1, 20000, "last");
+
+        AndPredicate p0 = (AndPredicate) seqItems[0].getPredicate();
+        assertEquals(2, p0.getSubPredicates().size());
+        ComparisonPredicate sp1 = (ComparisonPredicate) p0.getSubPredicates().get(1);
+        assertEquals(ComparisonOperator.NOT_EQUAL, sp1.getOperator());
+
+        assertEquals("resp.getCookie('id')", sp1.getFieldName());
+
+        UnmutableValueExtractor uve = (UnmutableValueExtractor) sp1.getPattern();
+        assertEquals(123, uve.extract(null));
+
+        Map<String, ValueExtractor> values = filter.getValues();
+        assertEquals(4, values.size());
+        ValueExtractor extractor = values.get("header");
+        assertTrue(extractor instanceof BeanValueExtractor);
+        BeanValueExtractor bve = (BeanValueExtractor) extractor;
+        assertEquals("last.req.getHeader('xyz')", bve.getFieldName());
+    }
+
+    private static void assertSeqItemValue(SequenceItem item, int num, long time, String alias) {
+        assertEquals("num", num, item.getNum());
+        assertEquals("time", time, item.getTime());
+        assertEquals("alias", alias, item.getAlias());
+    }
+
+    public static class TestSequenceListener implements SequenceListener {
+
+        ArrayList<MapMetric> events = new ArrayList<>();
+
+        @Override
+        public void sequenceDetected(MapMetric sequence) {
+            events.add(sequence);
+        }
+
+        public void reset() {
+            events.clear();
+        }
+    }
+}
--- a/stress-tester/src/test/java/com/passus/st/vars/VarsConfiguratorTest.java	Wed Mar 25 14:01:15 2020 +0100
+++ b/stress-tester/src/test/java/com/passus/st/vars/VarsConfiguratorTest.java	Thu Mar 26 13:27:52 2020 +0100
@@ -7,7 +7,7 @@
 import com.passus.filter.ValueExtractor;
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpRequestBuilder;
-import com.passus.st.client.filter.FlowFilterFactory;
+import com.passus.st.filter.FlowFilterFactory;
 import com.passus.st.client.http.filter.HttpMessageWrapper;
 import com.passus.st.filter.HttpMessageWrapperDynamicExtractor;
 import org.testng.annotations.Test;