Merge pull request #3 from mkrokosinski/master

SLING-7621 - regex path matching in Sling Dynamic Include
diff --git a/README.md b/README.md
index 39a3415..1752828 100644
--- a/README.md
+++ b/README.md
@@ -30,8 +30,7 @@
 Filter is delivered as a standard OSGi bundle. SDI is configured via the configuration factory called *SDI Configuration*. Following properties are available:
 
 * **Enabled** - enable SDI
-* **Base path** - given SDI configuration will be enabled only for this
-  path
+* **Base path** - This SDI configuration will work only for paths matching this value. If value starts with "^" sign, regex matching will be performed. Otherwise it will check for path prefix.'
 * **Resource types** - which components should be replaced with tags
 * **Include type** - type of include tag (Apache SSI, ESI or Javascript)
 * **Add comment** - adds debug comment: `<!-- SDI include (path: %s, resourceType: %s) -->` to every replaced component
diff --git a/pom.xml b/pom.xml
index c132b9b..6743856 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
     </parent>    
     
     <artifactId>org.apache.sling.dynamic-include</artifactId>
-    <version>3.0.1-SNAPSHOT</version>
+    <version>3.1.0-SNAPSHOT</version>
     <packaging>bundle</packaging>
     
     <name>Apache Sling Dynamic Include</name>
@@ -136,6 +136,19 @@
             <version>2.4</version>
             <scope>provided</scope>
         </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>2.18.3</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/src/main/java/org/apache/sling/dynamicinclude/Configuration.java b/src/main/java/org/apache/sling/dynamicinclude/Configuration.java
index 4d3a2c7..2aedebe 100755
--- a/src/main/java/org/apache/sling/dynamicinclude/Configuration.java
+++ b/src/main/java/org/apache/sling/dynamicinclude/Configuration.java
@@ -22,7 +22,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-
+import java.util.regex.Pattern;
 import org.apache.commons.lang.ArrayUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.felix.scr.annotations.Activate;
@@ -34,8 +34,13 @@
 import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.dynamicinclude.pathmatcher.PathMatcher;
+import org.apache.sling.dynamicinclude.pathmatcher.PrefixPathMatcher;
+import org.apache.sling.dynamicinclude.pathmatcher.RegexPathMatcher;
 import org.osgi.framework.Constants;
 import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Include filter configuration.
@@ -43,143 +48,158 @@
 @Component(metatype = true, configurationFactory = true, label = "Apache Sling Dynamic Include - Configuration", immediate = true, policy = ConfigurationPolicy.REQUIRE)
 @Service(Configuration.class)
 @Properties({
-        @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation"),
-        @Property(name = Configuration.PROPERTY_FILTER_ENABLED, boolValue = Configuration.DEFAULT_FILTER_ENABLED, label = "Enabled", description = "Check to enable the filter"),
-        @Property(name = Configuration.PROPERTY_FILTER_PATH, value = Configuration.DEFAULT_FILTER_PATH, label = "Base path", description = "This SDI configuration will work only for this path"),
-        @Property(name = Configuration.PROPERTY_FILTER_RESOURCE_TYPES, cardinality = Integer.MAX_VALUE, label = "Resource types", description = "Filter will replace components with selected resource types"),
-        @Property(name = Configuration.PROPERTY_INCLUDE_TYPE, value = Configuration.DEFAULT_INCLUDE_TYPE, label = "Include type", description = "Type of generated include tags", options = {
-                @PropertyOption(name = "SSI", value = "Apache SSI"), @PropertyOption(name = "ESI", value = "ESI"),
-                @PropertyOption(name = "JSI", value = "Javascript") }),
-        @Property(name = Configuration.PROPERTY_ADD_COMMENT, boolValue = Configuration.DEFAULT_ADD_COMMENT, label = "Add comment", description = "Add comment to included components"),
-        @Property(name = Configuration.PROPERTY_FILTER_SELECTOR, value = Configuration.DEFAULT_FILTER_SELECTOR, label = "Filter selector", description = "Selector used to mark included resources"),
-        @Property(name = Configuration.PROPERTY_COMPONENT_TTL, label = "Component TTL", description = "\"Time to live\" cache header for rendered component (in seconds)"),
-        @Property(name = Configuration.PROPERTY_REQUIRED_HEADER, value = Configuration.DEFAULT_REQUIRED_HEADER, label = "Required header", description = "SDI will work only for requests with given header"),
-        @Property(name = Configuration.PROPERTY_IGNORE_URL_PARAMS, cardinality = Integer.MAX_VALUE, label = "Ignore URL params", description = "SDI will process the request even if it contains configured GET parameters"),
-        @Property(name = Configuration.PROPERTY_REWRITE_PATH, boolValue = Configuration.DEFAULT_REWRITE_DISABLED, label = "Include path rewriting", description = "Check to enable include path rewriting") })
+    @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation"),
+    @Property(name = Configuration.PROPERTY_FILTER_ENABLED, boolValue = Configuration.DEFAULT_FILTER_ENABLED, label = "Enabled", description = "Check to enable the filter"),
+    @Property(name = Configuration.PROPERTY_FILTER_PATH, value = Configuration.DEFAULT_FILTER_PATH, label = "Base path regular expression", description = "This SDI configuration will work only for paths matching this value. If value starts with \"^\" sign, regex matching will be performed. Otherwise it will check for path prefix."),
+    @Property(name = Configuration.PROPERTY_FILTER_RESOURCE_TYPES, cardinality = Integer.MAX_VALUE, label = "Resource types", description = "Filter will replace components with selected resource types"),
+    @Property(name = Configuration.PROPERTY_INCLUDE_TYPE, value = Configuration.DEFAULT_INCLUDE_TYPE, label = "Include type", description = "Type of generated include tags", options = {
+        @PropertyOption(name = "SSI", value = "Apache SSI"), @PropertyOption(name = "ESI", value = "ESI"),
+        @PropertyOption(name = "JSI", value = "Javascript")}),
+    @Property(name = Configuration.PROPERTY_ADD_COMMENT, boolValue = Configuration.DEFAULT_ADD_COMMENT, label = "Add comment", description = "Add comment to included components"),
+    @Property(name = Configuration.PROPERTY_FILTER_SELECTOR, value = Configuration.DEFAULT_FILTER_SELECTOR, label = "Filter selector", description = "Selector used to mark included resources"),
+    @Property(name = Configuration.PROPERTY_COMPONENT_TTL, label = "Component TTL", description = "\"Time to live\" cache header for rendered component (in seconds)"),
+    @Property(name = Configuration.PROPERTY_REQUIRED_HEADER, value = Configuration.DEFAULT_REQUIRED_HEADER, label = "Required header", description = "SDI will work only for requests with given header"),
+    @Property(name = Configuration.PROPERTY_IGNORE_URL_PARAMS, cardinality = Integer.MAX_VALUE, label = "Ignore URL params", description = "SDI will process the request even if it contains configured GET parameters"),
+    @Property(name = Configuration.PROPERTY_REWRITE_PATH, boolValue = Configuration.DEFAULT_REWRITE_DISABLED, label = "Include path rewriting", description = "Check to enable include path rewriting")})
 public class Configuration {
 
-    static final String PROPERTY_FILTER_PATH = "include-filter.config.path";
+  private static final Logger LOG = LoggerFactory.getLogger(Configuration.class);
 
-    static final String DEFAULT_FILTER_PATH = "/content";
+  static final String PROPERTY_FILTER_PATH = "include-filter.config.path";
 
-    static final String PROPERTY_FILTER_ENABLED = "include-filter.config.enabled";
+  static final String DEFAULT_FILTER_PATH = "/content";
 
-    static final boolean DEFAULT_FILTER_ENABLED = false;
+  static final String PROPERTY_FILTER_ENABLED = "include-filter.config.enabled";
 
-    static final String PROPERTY_FILTER_RESOURCE_TYPES = "include-filter.config.resource-types";
+  static final boolean DEFAULT_FILTER_ENABLED = false;
 
-    static final String PROPERTY_FILTER_SELECTOR = "include-filter.config.selector";
+  static final String PROPERTY_FILTER_RESOURCE_TYPES = "include-filter.config.resource-types";
 
-    static final String DEFAULT_FILTER_SELECTOR = "nocache";
+  static final String PROPERTY_FILTER_SELECTOR = "include-filter.config.selector";
 
-    static final String PROPERTY_COMPONENT_TTL = "include-filter.config.ttl";
+  static final String DEFAULT_FILTER_SELECTOR = "nocache";
 
-    static final String PROPERTY_INCLUDE_TYPE = "include-filter.config.include-type";
+  static final String PROPERTY_COMPONENT_TTL = "include-filter.config.ttl";
 
-    static final String DEFAULT_INCLUDE_TYPE = "SSI";
+  static final String PROPERTY_INCLUDE_TYPE = "include-filter.config.include-type";
 
-    static final String PROPERTY_ADD_COMMENT = "include-filter.config.add_comment";
+  static final String DEFAULT_INCLUDE_TYPE = "SSI";
 
-    static final boolean DEFAULT_ADD_COMMENT = false;
+  static final String PROPERTY_ADD_COMMENT = "include-filter.config.add_comment";
 
-    static final String PROPERTY_REQUIRED_HEADER = "include-filter.config.required_header";
+  static final boolean DEFAULT_ADD_COMMENT = false;
 
-    static final String DEFAULT_REQUIRED_HEADER = "Server-Agent=Communique-Dispatcher";
+  static final String PROPERTY_REQUIRED_HEADER = "include-filter.config.required_header";
 
-    static final String PROPERTY_IGNORE_URL_PARAMS = "include-filter.config.ignoreUrlParams";
+  static final String DEFAULT_REQUIRED_HEADER = "Server-Agent=Communique-Dispatcher";
 
-    static final String PROPERTY_REWRITE_PATH = "include-filter.config.rewrite";
+  static final String PROPERTY_IGNORE_URL_PARAMS = "include-filter.config.ignoreUrlParams";
 
-    static final boolean DEFAULT_REWRITE_DISABLED = false;
+  static final String PROPERTY_REWRITE_PATH = "include-filter.config.rewrite";
 
-    private boolean isEnabled;
+  static final boolean DEFAULT_REWRITE_DISABLED = false;
 
-    private String path;
+  private PathMatcher pathMatcher;
 
-    private String includeSelector;
+  private boolean isEnabled;
 
-    private int ttl;
+  private String includeSelector;
 
-    private List<String> resourceTypes;
+  private int ttl;
 
-    private boolean addComment;
+  private List<String> resourceTypes;
 
-    private String includeTypeName;
+  private boolean addComment;
 
-    private String requiredHeader;
+  private String includeTypeName;
 
-    private List<String> ignoreUrlParams;
+  private String requiredHeader;
 
-    private boolean rewritePath;
+  private List<String> ignoreUrlParams;
 
-    @Activate
-    public void activate(ComponentContext context, Map<String, ?> properties) {
-        isEnabled = PropertiesUtil.toBoolean(properties.get(PROPERTY_FILTER_ENABLED), DEFAULT_FILTER_ENABLED);
-        path = PropertiesUtil.toString(properties.get(PROPERTY_FILTER_PATH), DEFAULT_FILTER_PATH);
-        String[] resourceTypeList;
-        resourceTypeList = PropertiesUtil.toStringArray(properties.get(PROPERTY_FILTER_RESOURCE_TYPES), new String[0]);
-        for (int i = 0; i < resourceTypeList.length; i++) {
-            String[] s = resourceTypeList[i].split(";");
-            String name = s[0].trim();
-            resourceTypeList[i] = name;
-        }
-        this.resourceTypes = Arrays.asList(resourceTypeList);
+  private boolean rewritePath;
 
-        includeSelector = PropertiesUtil.toString(properties.get(PROPERTY_FILTER_SELECTOR), DEFAULT_FILTER_SELECTOR);
-        ttl = PropertiesUtil.toInteger(properties.get(PROPERTY_COMPONENT_TTL), -1);
-        addComment = PropertiesUtil.toBoolean(properties.get(PROPERTY_ADD_COMMENT), DEFAULT_ADD_COMMENT);
-        includeTypeName = PropertiesUtil.toString(properties.get(PROPERTY_INCLUDE_TYPE), DEFAULT_INCLUDE_TYPE);
-        requiredHeader = PropertiesUtil.toString(properties.get(PROPERTY_REQUIRED_HEADER), DEFAULT_REQUIRED_HEADER);
-        ignoreUrlParams = Arrays.asList(PropertiesUtil.toStringArray(properties.get(PROPERTY_IGNORE_URL_PARAMS),
-                new String[0]));
-        rewritePath = PropertiesUtil.toBoolean(properties.get(PROPERTY_REWRITE_PATH), DEFAULT_REWRITE_DISABLED);
+  @Activate
+  public void activate(ComponentContext context, Map<String, ?> properties) {
+    isEnabled = PropertiesUtil.toBoolean(properties.get(PROPERTY_FILTER_ENABLED), DEFAULT_FILTER_ENABLED);
+    String pathPattern = PropertiesUtil.toString(properties.get(PROPERTY_FILTER_PATH), DEFAULT_FILTER_PATH);
+    pathMatcher = choosePathMatcher(pathPattern);
+    String[] resourceTypeList;
+    resourceTypeList = PropertiesUtil.toStringArray(properties.get(PROPERTY_FILTER_RESOURCE_TYPES), new String[0]);
+    for (int i = 0; i < resourceTypeList.length; i++) {
+      String[] s = resourceTypeList[i].split(";");
+      String name = s[0].trim();
+      resourceTypeList[i] = name;
     }
+    this.resourceTypes = Arrays.asList(resourceTypeList);
 
-    public String getBasePath() {
-        return path;
-    }
+    includeSelector = PropertiesUtil.toString(properties.get(PROPERTY_FILTER_SELECTOR), DEFAULT_FILTER_SELECTOR);
+    ttl = PropertiesUtil.toInteger(properties.get(PROPERTY_COMPONENT_TTL), -1);
+    addComment = PropertiesUtil.toBoolean(properties.get(PROPERTY_ADD_COMMENT), DEFAULT_ADD_COMMENT);
+    includeTypeName = PropertiesUtil.toString(properties.get(PROPERTY_INCLUDE_TYPE), DEFAULT_INCLUDE_TYPE);
+    requiredHeader = PropertiesUtil.toString(properties.get(PROPERTY_REQUIRED_HEADER), DEFAULT_REQUIRED_HEADER);
+    ignoreUrlParams = Arrays.asList(PropertiesUtil.toStringArray(properties.get(PROPERTY_IGNORE_URL_PARAMS),
+        new String[0]));
+    rewritePath = PropertiesUtil.toBoolean(properties.get(PROPERTY_REWRITE_PATH), DEFAULT_REWRITE_DISABLED);
+  }
 
-    public boolean hasIncludeSelector(SlingHttpServletRequest request) {
-        return ArrayUtils.contains(request.getRequestPathInfo().getSelectors(), includeSelector);
+  private PathMatcher choosePathMatcher(String pathPattern) {
+    PathMatcher result;
+    if (pathPattern.startsWith("^")) {
+      LOG.debug("Configured path value: {} is a regex experession. Picking RegexPathMatcher.", pathPattern);
+      result = new RegexPathMatcher(pathPattern);
+    } else {
+      LOG.debug("Configured path value: {} is not a regex experession. Picking PrefixPathMatcher.", pathPattern);
+      result = new PrefixPathMatcher(pathPattern);
     }
+    return result;
+  }
 
-    public String getIncludeSelector() {
-        return includeSelector;
-    }
+  public PathMatcher getPathMatcher() {
+    return pathMatcher;
+  }
 
-    public boolean hasTtlSet() {
-        return ttl >= 0;
-    }
+  public boolean hasIncludeSelector(SlingHttpServletRequest request) {
+    return ArrayUtils.contains(request.getRequestPathInfo().getSelectors(), includeSelector);
+  }
 
-    public int getTtl() {
-        return ttl;
-    }
+  public String getIncludeSelector() {
+    return includeSelector;
+  }
 
-    public boolean isSupportedResourceType(String resourceType) {
-        return StringUtils.isNotBlank(resourceType) && resourceTypes.contains(resourceType);
-    }
+  public boolean hasTtlSet() {
+    return ttl >= 0;
+  }
 
-    public boolean getAddComment() {
-        return addComment;
-    }
+  public int getTtl() {
+    return ttl;
+  }
 
-    public String getIncludeTypeName() {
-        return includeTypeName;
-    }
+  public boolean isSupportedResourceType(String resourceType) {
+    return StringUtils.isNotBlank(resourceType) && resourceTypes.contains(resourceType);
+  }
 
-    public boolean isEnabled() {
-        return isEnabled;
-    }
+  public boolean getAddComment() {
+    return addComment;
+  }
 
-    public String getRequiredHeader() {
-        return requiredHeader;
-    }
+  public String getIncludeTypeName() {
+    return includeTypeName;
+  }
 
-    public List<String> getIgnoreUrlParams() {
-        return ignoreUrlParams;
-    }
+  public boolean isEnabled() {
+    return isEnabled;
+  }
 
-    public boolean isRewritePath() {
-        return rewritePath;
-    }
+  public String getRequiredHeader() {
+    return requiredHeader;
+  }
+
+  public List<String> getIgnoreUrlParams() {
+    return ignoreUrlParams;
+  }
+
+  public boolean isRewritePath() {
+    return rewritePath;
+  }
 }
diff --git a/src/main/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboard.java b/src/main/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboard.java
index d7a4186..377fbe7 100644
--- a/src/main/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboard.java
+++ b/src/main/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboard.java
@@ -22,7 +22,7 @@
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
 
-import org.apache.commons.lang.StringUtils;
+import java.util.regex.Matcher;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -48,7 +48,7 @@
 
     private boolean isEnabled(Configuration config, SlingHttpServletRequest request) {
         final String requestPath = request.getRequestPathInfo().getResourcePath();
-        return config.isEnabled() && StringUtils.startsWith(requestPath, config.getBasePath());
+        return config.isEnabled() && config.getPathMatcher().match(requestPath);
     }
 
     protected void bindConfigs(final Configuration config) {
diff --git a/src/main/java/org/apache/sling/dynamicinclude/pathmatcher/PathMatcher.java b/src/main/java/org/apache/sling/dynamicinclude/pathmatcher/PathMatcher.java
new file mode 100644
index 0000000..9688e03
--- /dev/null
+++ b/src/main/java/org/apache/sling/dynamicinclude/pathmatcher/PathMatcher.java
@@ -0,0 +1,11 @@
+package org.apache.sling.dynamicinclude.pathmatcher;
+
+public interface PathMatcher {
+
+  /**
+   * Matches given path with the configured path parameter
+   * @param path path to match
+   * @return true if path matches, false otherwise
+   */
+  boolean match(String path);
+}
diff --git a/src/main/java/org/apache/sling/dynamicinclude/pathmatcher/PrefixPathMatcher.java b/src/main/java/org/apache/sling/dynamicinclude/pathmatcher/PrefixPathMatcher.java
new file mode 100644
index 0000000..315d101
--- /dev/null
+++ b/src/main/java/org/apache/sling/dynamicinclude/pathmatcher/PrefixPathMatcher.java
@@ -0,0 +1,17 @@
+package org.apache.sling.dynamicinclude.pathmatcher;
+
+import org.apache.commons.lang.StringUtils;
+
+public class PrefixPathMatcher implements PathMatcher {
+
+  private final String configurationValue;
+
+  public PrefixPathMatcher(String configurationValue) {
+    this.configurationValue = configurationValue;
+  }
+
+  @Override
+  public boolean match(String path) {
+    return StringUtils.isNotBlank(path) && path.startsWith(configurationValue);
+  }
+}
diff --git a/src/main/java/org/apache/sling/dynamicinclude/pathmatcher/RegexPathMatcher.java b/src/main/java/org/apache/sling/dynamicinclude/pathmatcher/RegexPathMatcher.java
new file mode 100644
index 0000000..f82a4b7
--- /dev/null
+++ b/src/main/java/org/apache/sling/dynamicinclude/pathmatcher/RegexPathMatcher.java
@@ -0,0 +1,18 @@
+package org.apache.sling.dynamicinclude.pathmatcher;
+
+import java.util.regex.Pattern;
+import org.apache.commons.lang.StringUtils;
+
+public class RegexPathMatcher implements PathMatcher {
+
+  private final Pattern configurationPattern;
+
+  public RegexPathMatcher(String configurationRegex) {
+    this.configurationPattern = Pattern.compile(configurationRegex);
+  }
+
+  @Override
+  public boolean match(String path) {
+    return StringUtils.isNotBlank(path) && configurationPattern.matcher(path).matches();
+  }
+}
diff --git a/src/test/java/org/apache/sling/dynamicinclude/ConfigurationTest.java b/src/test/java/org/apache/sling/dynamicinclude/ConfigurationTest.java
new file mode 100644
index 0000000..9bdfda3
--- /dev/null
+++ b/src/test/java/org/apache/sling/dynamicinclude/ConfigurationTest.java
@@ -0,0 +1,77 @@
+package org.apache.sling.dynamicinclude;
+
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.PatternSyntaxException;
+import org.apache.sling.dynamicinclude.pathmatcher.PrefixPathMatcher;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ConfigurationTest {
+
+  private Configuration tested;
+
+  @Before
+  public void setUp() {
+    tested = new Configuration();
+  }
+
+  @Test(expected = PatternSyntaxException.class)
+  public void shouldThrowExceptionWhenRegexisInvalid() throws Exception {
+    Map<String, Object> properties = new HashMap<String, Object>();
+    properties.put(Configuration.PROPERTY_FILTER_PATH, "^(");
+
+    tested.activate(null, properties);
+  }
+
+  @Test
+  public void shouldSetDefaultValuesWhenPropertiesAreEmpty() throws Exception {
+    Map<String, Object> properties = new HashMap<String, Object>();
+
+    tested.activate(null, properties);
+
+    assertThat(tested.getPathMatcher().getClass().isAssignableFrom(PrefixPathMatcher.class), is(true));
+    assertThat(tested.getAddComment(), is(false));
+    assertThat(tested.getIgnoreUrlParams().size(), is(0));
+    assertThat(tested.getIncludeSelector(), is(Configuration.DEFAULT_FILTER_SELECTOR));
+    assertThat(tested.getIncludeTypeName(), is(Configuration.DEFAULT_INCLUDE_TYPE));
+    assertThat(tested.getRequiredHeader(), is(Configuration.DEFAULT_REQUIRED_HEADER));
+    assertThat(tested.getTtl(), is(-1));
+    assertThat(tested.isEnabled(), is(false));
+    assertThat(tested.hasTtlSet(), is(false));
+    assertThat(tested.isRewritePath(), is(false));
+  }
+
+  @Test
+  public void shouldSetConfigurationValues() throws Exception {
+    Map<String, Object> properties = new HashMap<String, Object>();
+    properties.put(Configuration.PROPERTY_FILTER_PATH, "/content/test/path");
+    properties.put(Configuration.PROPERTY_INCLUDE_TYPE, "ESI");
+    properties.put(Configuration.PROPERTY_ADD_COMMENT, true);
+    properties.put(Configuration.PROPERTY_COMPONENT_TTL, 60);
+    properties.put(Configuration.PROPERTY_FILTER_ENABLED, true);
+    properties.put(Configuration.PROPERTY_FILTER_RESOURCE_TYPES, new String[]{"test/resource/type"});
+    properties.put(Configuration.PROPERTY_REQUIRED_HEADER, "CustomHeader: value");
+    properties.put(Configuration.PROPERTY_FILTER_SELECTOR, "cache");
+    properties.put(Configuration.PROPERTY_REWRITE_PATH, true);
+    properties.put(Configuration.PROPERTY_IGNORE_URL_PARAMS, new String[] {"query"});
+
+    tested.activate(null, properties);
+
+    assertThat(tested.getPathMatcher().getClass().isAssignableFrom(PrefixPathMatcher.class), is(true));
+    assertThat(tested.getAddComment(), is(true));
+    assertThat(tested.getIgnoreUrlParams().size(), is(1));
+    assertThat(tested.getIncludeSelector(), is("cache"));
+    assertThat(tested.getIncludeTypeName(), is("ESI"));
+    assertThat(tested.getRequiredHeader(), is("CustomHeader: value"));
+    assertThat(tested.getTtl(), is(60));
+    assertThat(tested.isEnabled(), is(true));
+    assertThat(tested.hasTtlSet(), is(true));
+    assertThat(tested.isRewritePath(), is(true));
+    assertThat(tested.isSupportedResourceType("test/resource/type"), is(true));
+  }
+}
diff --git a/src/test/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboardTest.java b/src/test/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboardTest.java
new file mode 100644
index 0000000..3208456
--- /dev/null
+++ b/src/test/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboardTest.java
@@ -0,0 +1,90 @@
+package org.apache.sling.dynamicinclude;
+
+import static org.apache.sling.dynamicinclude.Configuration.PROPERTY_FILTER_ENABLED;
+import static org.apache.sling.dynamicinclude.Configuration.PROPERTY_FILTER_PATH;
+import static org.apache.sling.dynamicinclude.Configuration.PROPERTY_FILTER_RESOURCE_TYPES;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.PatternSyntaxException;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.request.RequestPathInfo;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ConfigurationWhiteboardTest {
+
+  private static final String TEST_RESOURCE_PATH = "/content/test/engl/home/pageresource";
+  public static final String TEST_RESOURCE_TYPE = "test/component/resourceType";
+
+  private ConfigurationWhiteboard tested;
+
+  @Mock
+  private SlingHttpServletRequest request;
+
+  @Mock
+  private RequestPathInfo requestPathInfo;
+
+  @Before
+  public void setUp() throws Exception {
+    tested = new ConfigurationWhiteboard();
+    when(request.getRequestPathInfo()).thenReturn(requestPathInfo);
+    when(requestPathInfo.getResourcePath()).thenReturn(TEST_RESOURCE_PATH);
+  }
+
+  private Configuration buildConfiguration(boolean enabled, String pathRegex, String[] resourceTypes) {
+    Configuration configuration = new Configuration();
+    Map<String, Object> properties = new HashMap<String, Object>();
+    properties.put(PROPERTY_FILTER_ENABLED, enabled);
+    properties.put(PROPERTY_FILTER_PATH, pathRegex);
+    properties.put(PROPERTY_FILTER_RESOURCE_TYPES, resourceTypes);
+    configuration.activate(null, properties);
+    return configuration;
+  }
+
+  @Test
+  public void shouldNotReturnConfigsIfNotConfigsHaveBeenBound() throws Exception {
+    assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(nullValue()));
+  }
+
+  @Test
+  public void shouldNotReturnConfigurationIfResourceTypeDoesNotMatch() throws Exception {
+    Configuration testConfiguration = buildConfiguration(true, "^/content.*$", new String[]{"invalid/resourceType"});
+    tested.bindConfigs(testConfiguration);
+
+    assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(nullValue()));
+  }
+
+  @Test
+  public void shouldNotReturnConfigurationIfConfigurationIsDisabled() throws Exception {
+    Configuration testConfiguration = buildConfiguration(false, "^/content.*$", new String[]{TEST_RESOURCE_TYPE});
+    tested.bindConfigs(testConfiguration);
+
+    assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(nullValue()));
+  }
+
+  @Test
+  public void shouldNotReturnConfigurationIfPathDoesNotMatchRegex() throws Exception {
+    Configuration testConfiguration = buildConfiguration(true, "^/content/notMatched/.*$",
+        new String[]{TEST_RESOURCE_TYPE});
+    tested.bindConfigs(testConfiguration);
+
+    assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(nullValue()));
+  }
+
+  @Test
+  public void shouldReturnValidConfiguration() throws Exception {
+    Configuration testConfiguration = buildConfiguration(true, "^/content.*$", new String[]{TEST_RESOURCE_TYPE});
+    tested.bindConfigs(testConfiguration);
+
+    assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(testConfiguration));
+  }
+}