This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-dynamic-include.git

commit f536ba972b02d67791dd2953e24eaca6a04d45c2
Author: Bertrand Delacretaz <[email protected]>
AuthorDate: Mon Apr 4 11:35:14 2016 +0000

    SLING-5594 - Sling Dynamic Include module, donated by Cognifide Ltd, many 
thanks!
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1737658 
13f79535-47bb-0310-9956-ffa450edef68
---
 .gitignore                                         |   5 +
 README.md                                          | 151 +++++++++++++++
 pom.xml                                            | 141 ++++++++++++++
 src/main/doc/plain-include.png                     | Bin 0 -> 58783 bytes
 .../sling/dynamicinclude/CacheControlFilter.java   |  72 ++++++++
 .../apache/sling/dynamicinclude/Configuration.java | 188 +++++++++++++++++++
 .../dynamicinclude/ConfigurationWhiteboard.java    |  61 +++++++
 .../sling/dynamicinclude/IncludeTagFilter.java     | 202 +++++++++++++++++++++
 .../dynamicinclude/SyntheticResourceFilter.java    |  86 +++++++++
 .../dynamicinclude/generator/IncludeGenerator.java |  32 ++++
 .../generator/IncludeGeneratorWhiteboard.java      |  61 +++++++
 .../generator/types/EsiGenerator.java              |  51 ++++++
 .../generator/types/JsiGenerator.java              | 115 ++++++++++++
 .../generator/types/SsiGenerator.java              |  61 +++++++
 src/main/resources/generators/jquery.html          |  26 +++
 15 files changed, 1252 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b757e23
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.class
+.classpath
+.settings
+.project
+/target
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1373fda
--- /dev/null
+++ b/README.md
@@ -0,0 +1,151 @@
+# Sling Dynamic Include
+
+## Purpose
+
+The purpose of the module presented here is to replace dynamic generated 
components (eg. current time or foreign exchange rates) with server-side 
include tag (eg. [SSI](http://httpd.apache.org/docs/current/howto/ssi.html) or 
[ESI](http://www.w3.org/TR/esi-lang)). Therefore the dispatcher is able to 
cache the whole page but dynamic components are generated and included with 
every request. Components to include are chosen in filter configuration using 
`resourceType` attribute.
+
+When the filter intercepts request for a component with given `resourceType`, 
it'll return a server-side include tag (eg. `<!--#include 
virtual="/path/to/resource" -->` for Apache server). However the path is 
extended by new selector (`nocache` by default). This is required because 
filter has to know when to return actual content.
+
+Components don't have to be modified in order to use this module (or even 
aware of its existence). It's servlet filter, installed as an OSGi bundle and 
it can be enabled, disabled or reconfigured without touching CQ installation.
+
+## Prerequisites
+
+* CQ / Apache Sling 2
+* Maven 2.x, 3.x
+
+## Installation
+
+Add following dependency to your project:
+
+    <dependency>
+           <groupId>org.apache.sling</groupId>
+           <artifactId>dynamic-include</artifactId>
+           <version>2.2.0</version>
+    </dependency>
+
+## Configuration
+
+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
+* **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
+* **Filter selector** - selector used to get actual content
+* **Component TTL** - time to live in seconds, set for rendered component 
(require Dispatcher 4.1.11+)
+* **Required header** - SDI will be enabled only if the configured header is 
present in the request. By default it's `Server-Agent=Communique-Dispatcher` 
header, added by the AEM dispatcher. You may enter just the header name only or 
the name and the value split with `=`.
+* **Ignore URL params** - SDI normally skips all requests containing any GET 
parameters. This option allows to set a list of parameters that should be 
ignored in the test. See the [Ignoring URL 
parameters](https://docs.adobe.com/docs/en/dispatcher/disp-config.html#Ignoring%20URL%20Parameters)
 section in the dispatcher documentation.
+* **Include path rewriting** -- enable rewriting link (according to sling 
mappings) that is used for dynamic content including.
+
+## Compatibility with components
+
+Filter is incompatible with following types of component:
+
+* components which handles POST requests or GET parameters,
+* synthetic components which uses suffixes (because suffix is used to pass 
`requestType` of the synthetic resource).
+
+If component do not generate HTML but eg. JS or binary data then remember to 
turn off *Comment* option in configuration.
+
+## Enabling SSI in Apache & dispatcher
+
+In order to enable SSI in Apache with dispatcher first enable `Include` mod 
(on Debian: `a2enmod include`). Then add `Includes` option to the `Options` 
directive in your virtual configuration host. After that find following lines 
in `dispatcher.conf` file:
+
+        <IfModule dispatcher_module>
+            SetHandler dispatcher-handler
+        </IfModule>
+
+and modify it:
+
+        <IfModule dispatcher_module>
+            SetHandler dispatcher-handler
+        </IfModule>
+        SetOutputFilter INCLUDES
+
+After setting output filter open virtualhost configuration and add `Includes` 
option to `Options` directive:
+
+        <Directory />
+            Options FollowSymLinks Includes
+            AllowOverride None
+        </Directory>
+        <Directory /var/www/>
+            Options Indexes FollowSymLinks MultiViews Includes
+            AllowOverride None
+            Order allow,deny
+            allow from all
+        </Directory>
+
+It's also a good idea to disable the caching for `.nocache.html` files in 
`dispatcher.any` config file. Just add:
+
+        /disable-nocache
+        {
+            /glob "*.nocache.html*"
+            /type "deny"
+        }
+
+at the end of the `/rules` section.
+
+## Enabling TTL in dispatcher 4.1.11+
+In order to enable TTL on Apache with dispatcher just add:
+
+       /enableTTL "1"
+
+to your dispatcher configuration.
+
+
+## Enabling ESI in Varnish
+
+Just add following lines at the beginning of the `vcl_fetch` section in 
`/etc/varnish/default.vcl` file:
+
+        if(req.url ~ "\.nocache.html") {
+            set beresp.ttl = 0s;
+        } else if (req.url ~ "\.html") {
+            set beresp.do_esi = true;
+        }
+
+It'll enable ESI includes in `.html` files and disable caching of the 
`.nocache.html` files.
+
+## JavaScript Include
+
+Dynamic Include Filter can also replace dynamic components with AJAX tags, so 
they are loaded by the browser. It's called JSI. In the current version jQuery 
framework is used. More attention is required if included component has some 
Javascript code. Eg. Geometrixx Carousel component won't work because it's 
initialization is done in page `<head>` section while the component itself is 
still not loaded.
+
+## Plain and synthetic resources
+
+There are two cases: the first involves including a component which is 
available at some URL, eg.
+
+    /content/geometrixx/en/jcr:content/carousel.html
+
+In this case, component is replaced with include tag, and `nocache` selector 
is added
+
+    <!--#include 
virtual="/content/geometrixx/en/jcr:content/carousel.nocache.html" -->
+    
+If the filter gets request with selector it'll pass it (using `doChain`) 
further without taking any action.
+
+![Plain 
include](https://raw.github.com/Cognifide/Sling-Dynamic-Include/master/src/main/doc/plain-include.png)
+
+There are also components which are created from so-called synthetic 
resources. Synthetic resource have some resourceType and path, but they don't 
have any node is JCR repository. An example is
+
+    /content/geometrixx/en/jcr:content/userinfo
+
+component with `foundation/components/userinfo` resource type. These 
components return 404 error if you try to make a HTTP request. SDI recognizes 
these components and forms a different include URL for them in which resource 
type is added as a suffix, eg.:
+
+    
/content/geometrixx/en/jcr:content/userinfo.nocache.html/foundation/components/userinfo
+
+If filter got such request, it'll try to emulate `<sling:include>` JSP tag and 
includes resource with given type and `nocache` selector:
+
+    /content/geometrixx/en/jcr:content/userinfo.nocache.html
+
+Selector is necessary, because otherwise filter would again replace component 
with a SSI tag.
+
+# External resources
+
+* [SDI 
presentation](http://www.pro-vision.de/content/medialib/pro-vision/production/adaptto/2012/adaptto2012-sling-dynamic-include-tomasz-rekaweki-pdf/_jcr_content/renditions/rendition.file/adaptto2012-sling-dynamic-include-tomasz-rekaweki.pdf)
 on [adaptTo() 2012](http://www.pro-vision.de/de/adaptto/adaptto-2012.html)
+* [SDI blog](http://www.cognifide.com/blogs/cq/sling-dynamic-include/) post on 
the Cognifide website
+* See the [Apache Sling website](http://sling.apache.org/) for the Sling 
reference documentation. Apache Sling, Apache and Sling are trademarks of the 
[Apache Software Foundation](http://apache.org).
+
+# Release notes
+
+## 2.2.0
+
+\#17 Support for time-based (TTL) caching, Dispatcher 4.1.11+ required
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d7e4f6b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+
+    <modelVersion>4.0.0</modelVersion>
+    
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>26</version>
+        <relativePath/>
+    </parent>    
+    
+    <artifactId>org.apache.sling.dynamic-include</artifactId>
+    <version>2.2.1-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    
+    <name>Apache Sling Dynamic Include</name>
+    <description>Dynamic Include filter for Apache Sling</description>
+
+    <scm>
+        
<connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/dynamic-include</connection>
+        
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/dynamic-include</developerConnection>
+        
<url>http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/dynamic-include</url>
+    </scm>    
+
+    <developers>
+        <developer>
+            <id>pacoolsky</id>
+            <name>Przemyslaw Pakulski</name>
+            <email>[email protected]</email>
+        </developer>
+        <developer>
+            <id>trekawek</id>
+            <name>Tomasz Rekawek</name>
+            <email>[email protected]</email>
+        </developer>
+    </developers>
+
+    <build>
+        <plugins>
+           <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-scr-scrdescriptor</id>
+                        <goals>
+                            <goal>scr</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>        
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <!-- osgi -->
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- javax -->
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- sling -->
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.engine</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- commons -->
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.4</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/src/main/doc/plain-include.png b/src/main/doc/plain-include.png
new file mode 100644
index 0000000..1c5b29e
Binary files /dev/null and b/src/main/doc/plain-include.png differ
diff --git 
a/src/main/java/org/apache/sling/dynamicinclude/CacheControlFilter.java 
b/src/main/java/org/apache/sling/dynamicinclude/CacheControlFilter.java
new file mode 100644
index 0000000..a4883ca
--- /dev/null
+++ b/src/main/java/org/apache/sling/dynamicinclude/CacheControlFilter.java
@@ -0,0 +1,72 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.sling.SlingFilter;
+import org.apache.felix.scr.annotations.sling.SlingFilterScope;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SlingFilter(scope = SlingFilterScope.REQUEST, order = 0)
+public class CacheControlFilter implements Filter {
+
+    private static final String HEADER_CACHE_CONTROL = "Cache-Control";
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(CacheControlFilter.class);
+
+    @Reference
+    private ConfigurationWhiteboard configurationWhiteboard;
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException,
+            ServletException {
+        final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) 
request;
+        final String resourceType = 
slingRequest.getResource().getResourceType();
+        final Configuration config = 
configurationWhiteboard.getConfiguration(slingRequest, resourceType);
+
+        if (config != null && config.hasTtlSet()) {
+            SlingHttpServletResponse slingResponse = 
(SlingHttpServletResponse) response;
+            slingResponse.setHeader(HEADER_CACHE_CONTROL, "max-age=" + 
config.getTtl());
+            LOG.debug("set \"{}: max-age={}\" to {}", HEADER_CACHE_CONTROL, 
config.getTtl(), resourceType);
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
diff --git a/src/main/java/org/apache/sling/dynamicinclude/Configuration.java 
b/src/main/java/org/apache/sling/dynamicinclude/Configuration.java
new file mode 100755
index 0000000..adaa0ff
--- /dev/null
+++ b/src/main/java/org/apache/sling/dynamicinclude/Configuration.java
@@ -0,0 +1,188 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.PropertyOption;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+
+/**
+ * Include filter configuration.
+ * 
+ * @author tomasz.rekawek
+ * 
+ */
+@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") })
+public class Configuration {
+
+    static final String PROPERTY_FILTER_PATH = "include-filter.config.path";
+
+    static final String DEFAULT_FILTER_PATH = "/content";
+
+    static final String PROPERTY_FILTER_ENABLED = 
"include-filter.config.enabled";
+
+    static final boolean DEFAULT_FILTER_ENABLED = false;
+
+    static final String PROPERTY_FILTER_RESOURCE_TYPES = 
"include-filter.config.resource-types";
+
+    static final String PROPERTY_FILTER_SELECTOR = 
"include-filter.config.selector";
+
+    static final String DEFAULT_FILTER_SELECTOR = "nocache";
+
+    static final String PROPERTY_COMPONENT_TTL = "include-filter.config.ttl";
+
+    static final String PROPERTY_INCLUDE_TYPE = 
"include-filter.config.include-type";
+
+    static final String DEFAULT_INCLUDE_TYPE = "SSI";
+
+    static final String PROPERTY_ADD_COMMENT = 
"include-filter.config.add_comment";
+
+    static final boolean DEFAULT_ADD_COMMENT = false;
+
+    static final String PROPERTY_REQUIRED_HEADER = 
"include-filter.config.required_header";
+
+    static final String DEFAULT_REQUIRED_HEADER = 
"Server-Agent=Communique-Dispatcher";
+
+    static final String PROPERTY_IGNORE_URL_PARAMS = 
"include-filter.config.ignoreUrlParams";
+
+    static final String PROPERTY_REWRITE_PATH = 
"include-filter.config.rewrite";
+
+    static final boolean DEFAULT_REWRITE_DISABLED = false;
+
+    private boolean isEnabled;
+
+    private String path;
+
+    private String includeSelector;
+
+    private int ttl;
+
+    private List<String> resourceTypes;
+
+    private boolean addComment;
+
+    private String includeTypeName;
+
+    private String requiredHeader;
+
+    private List<String> ignoreUrlParams;
+
+    private boolean rewritePath;
+
+    @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);
+
+        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 String getBasePath() {
+        return path;
+    }
+
+    public boolean hasIncludeSelector(SlingHttpServletRequest request) {
+        return 
ArrayUtils.contains(request.getRequestPathInfo().getSelectors(), 
includeSelector);
+    }
+
+    public String getIncludeSelector() {
+        return includeSelector;
+    }
+
+    public boolean hasTtlSet() {
+        return ttl >= 0;
+    }
+
+    public int getTtl() {
+        return ttl;
+    }
+
+    public boolean isSupportedResourceType(String resourceType) {
+        return StringUtils.isNotBlank(resourceType) && 
resourceTypes.contains(resourceType);
+    }
+
+    public boolean getAddComment() {
+        return addComment;
+    }
+
+    public String getIncludeTypeName() {
+        return includeTypeName;
+    }
+
+    public boolean isEnabled() {
+        return isEnabled;
+    }
+
+    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
new file mode 100644
index 0000000..d7a4186
--- /dev/null
+++ b/src/main/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboard.java
@@ -0,0 +1,61 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingHttpServletRequest;
+
+@Component
+@Service(ConfigurationWhiteboard.class)
+public class ConfigurationWhiteboard {
+
+    @Reference(referenceInterface = Configuration.class, cardinality = 
ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
+    private Set<Configuration> configs = new 
CopyOnWriteArraySet<Configuration>();
+
+    public Configuration getConfiguration(SlingHttpServletRequest request, 
String resourceType) {
+        for (Configuration c : configs) {
+            if (isEnabled(c, request) && 
c.isSupportedResourceType(resourceType)) {
+                return c;
+            }
+        }
+        return null;
+    }
+
+    private boolean isEnabled(Configuration config, SlingHttpServletRequest 
request) {
+        final String requestPath = 
request.getRequestPathInfo().getResourcePath();
+        return config.isEnabled() && StringUtils.startsWith(requestPath, 
config.getBasePath());
+    }
+
+    protected void bindConfigs(final Configuration config) {
+        configs.add(config);
+    }
+
+    protected void unbindConfigs(final Configuration config) {
+        configs.remove(config);
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/dynamicinclude/IncludeTagFilter.java 
b/src/main/java/org/apache/sling/dynamicinclude/IncludeTagFilter.java
new file mode 100644
index 0000000..c8e33ca
--- /dev/null
+++ b/src/main/java/org/apache/sling/dynamicinclude/IncludeTagFilter.java
@@ -0,0 +1,202 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.sling.SlingFilter;
+import org.apache.felix.scr.annotations.sling.SlingFilterScope;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.dynamicinclude.generator.IncludeGenerator;
+import org.apache.sling.dynamicinclude.generator.IncludeGeneratorWhiteboard;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SlingFilter(scope = SlingFilterScope.INCLUDE, order = -500)
+public class IncludeTagFilter implements Filter {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(IncludeTagFilter.class);
+
+    private static final String COMMENT = "<!-- SDI include (path: %s, 
resourceType: %s) -->\n";
+
+    @Reference
+    private ConfigurationWhiteboard configurationWhiteboard;
+
+    @Reference
+    private IncludeGeneratorWhiteboard generatorWhiteboard;
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException,
+            ServletException {
+        final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) 
request;
+        final String resourceType = 
slingRequest.getResource().getResourceType();
+
+        final Configuration config = 
configurationWhiteboard.getConfiguration(slingRequest, resourceType);
+        if (config == null) {
+            chain.doFilter(request, response);
+            return;
+        }
+
+        final IncludeGenerator generator = 
generatorWhiteboard.getGenerator(config.getIncludeTypeName());
+        if (generator == null) {
+            LOG.error("Invalid generator: " + config.getIncludeTypeName());
+            chain.doFilter(request, response);
+            return;
+        }
+
+        final PrintWriter writer = response.getWriter();
+        final String url = getUrl(config, slingRequest);
+        if (url == null) {
+            chain.doFilter(request, response);
+            return;
+        }
+
+        if (config.getAddComment()) {
+            writer.append(String.format(COMMENT, 
StringEscapeUtils.escapeHtml(url), resourceType));
+        }
+
+        // Only write the includes markup if the required, configurable request
+        // header is present
+        if (shouldWriteIncludes(config, slingRequest)) {
+            String include = generator.getInclude(url);
+            LOG.debug(include);
+            writer.append(include);
+        } else {
+            chain.doFilter(request, response);
+        }
+    }
+
+    private boolean shouldWriteIncludes(Configuration config, 
SlingHttpServletRequest request) {
+        if (requestHasParameters(config.getIgnoreUrlParams(), request)) {
+            return false;
+        }
+        final String requiredHeader = config.getRequiredHeader();
+        return StringUtils.isBlank(requiredHeader) || 
containsHeader(requiredHeader, request);
+    }
+
+    private boolean requestHasParameters(List<String> ignoreUrlParams, 
SlingHttpServletRequest request) {
+        final Enumeration<?> paramNames = request.getParameterNames();
+        while (paramNames.hasMoreElements()) {
+            final String paramName = (String) paramNames.nextElement();
+            if (!ignoreUrlParams.contains(paramName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean containsHeader(String requiredHeader, 
SlingHttpServletRequest request) {
+        final String name, expectedValue;
+        if (StringUtils.contains(requiredHeader, '=')) {
+            final String split[] = StringUtils.split(requiredHeader, '=');
+            name = split[0];
+            expectedValue = split[1];
+        } else {
+            name = requiredHeader;
+            expectedValue = null;
+        }
+
+        final String actualValue = request.getHeader(name);
+        if (actualValue == null) {
+            return false;
+        } else if (expectedValue == null) {
+            return true;
+        } else {
+            return actualValue.equalsIgnoreCase(expectedValue);
+        }
+    }
+
+    private String getUrl(Configuration config, SlingHttpServletRequest 
request) {
+        String url = buildUrl(config, request);
+        if (config.isRewritePath()) {
+            url = removeQuestionMark(url);
+            url = request.getResourceResolver().map(request, url);
+        } else {
+            url = encodeJcrContentPart(url);
+            try {
+                url = new URI(null, null, url, null).toASCIIString();
+            } catch (URISyntaxException e) {
+                LOG.error("Include url is in the wrong format", e);
+                return null;
+            }
+        }
+
+        return url;
+    }
+
+    private String buildUrl(Configuration config, SlingHttpServletRequest 
request) {
+        final boolean synthetic = 
ResourceUtil.isSyntheticResource(request.getResource());
+        final Resource resource = request.getResource();
+        final StringBuilder builder = new StringBuilder();
+        final RequestPathInfo pathInfo = request.getRequestPathInfo();
+
+        final String resourcePath = pathInfo.getResourcePath();
+        builder.append(resourcePath);
+        if (pathInfo.getSelectorString() != null) {
+            builder.append('.').append(sanitize(pathInfo.getSelectorString()));
+        }
+        builder.append('.').append(config.getIncludeSelector());
+        builder.append('.').append(pathInfo.getExtension());
+        if (synthetic) {
+            builder.append('/').append(resource.getResourceType());
+        } else {
+            builder.append(sanitize(pathInfo.getSuffix()));
+        }
+        return builder.toString();
+    }
+
+    private static String sanitize(String path) {
+        return StringUtils.defaultString(path);
+    }
+
+    private static String encodeJcrContentPart(String url) {
+        return url.replace("jcr:content", "_jcr_content");
+    }
+
+    private static String removeQuestionMark(String url) {
+        return url.replaceAll("[?]", "");
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/dynamicinclude/SyntheticResourceFilter.java 
b/src/main/java/org/apache/sling/dynamicinclude/SyntheticResourceFilter.java
new file mode 100644
index 0000000..d2e2725
--- /dev/null
+++ b/src/main/java/org/apache/sling/dynamicinclude/SyntheticResourceFilter.java
@@ -0,0 +1,86 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.request.RequestDispatcherOptions;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.engine.EngineConstants;
+import org.osgi.framework.Constants;
+
+@Component(metatype = true, label = "Apache Sling Dynamic Include - Synthetic 
Resource Filter")
+@Service
+@Properties({
+        @Property(name = Constants.SERVICE_VENDOR, value = "The Apache 
Software Foundation"),
+        @Property(name = EngineConstants.SLING_FILTER_SCOPE, value = 
EngineConstants.FILTER_SCOPE_REQUEST, propertyPrivate = true),
+        @Property(name = Constants.SERVICE_RANKING, intValue = 
Integer.MIN_VALUE, propertyPrivate = false), })
+public class SyntheticResourceFilter implements Filter {
+
+    @Reference
+    private ConfigurationWhiteboard configurationWhiteboard;
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException,
+            ServletException {
+        final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) 
request;
+        final String resourceType = getResourceTypeFromSuffix(slingRequest);
+        final Configuration config = 
configurationWhiteboard.getConfiguration(slingRequest, resourceType);
+
+        if (config == null || !config.hasIncludeSelector(slingRequest)
+                || 
!ResourceUtil.isSyntheticResource(slingRequest.getResource())) {
+            chain.doFilter(request, response);
+            return;
+        }
+
+        final RequestDispatcherOptions options = new 
RequestDispatcherOptions();
+        options.setForceResourceType(resourceType);
+        final RequestDispatcher dispatcher = 
slingRequest.getRequestDispatcher(slingRequest.getResource(), options);
+        dispatcher.forward(request, response);
+    }
+
+    private static String getResourceTypeFromSuffix(SlingHttpServletRequest 
request) {
+        final String suffix = request.getRequestPathInfo().getSuffix();
+        return StringUtils.removeStart(suffix, "/");
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/dynamicinclude/generator/IncludeGenerator.java 
b/src/main/java/org/apache/sling/dynamicinclude/generator/IncludeGenerator.java
new file mode 100755
index 0000000..bd3020b
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/dynamicinclude/generator/IncludeGenerator.java
@@ -0,0 +1,32 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude.generator;
+
+/**
+ * Include generator interface
+ * 
+ * @author tomasz.rekawek
+ * 
+ */
+public interface IncludeGenerator {
+    String getType();
+
+    String getInclude(String url);
+}
diff --git 
a/src/main/java/org/apache/sling/dynamicinclude/generator/IncludeGeneratorWhiteboard.java
 
b/src/main/java/org/apache/sling/dynamicinclude/generator/IncludeGeneratorWhiteboard.java
new file mode 100755
index 0000000..2263277
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/dynamicinclude/generator/IncludeGeneratorWhiteboard.java
@@ -0,0 +1,61 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude.generator;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+
+/**
+ * Service that provides include generator of given type.
+ * 
+ * @author tomasz.rekawek
+ */
+
+@Component
+@Service(IncludeGeneratorWhiteboard.class)
+public class IncludeGeneratorWhiteboard {
+
+    @Reference(referenceInterface = IncludeGenerator.class, cardinality = 
ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
+    private Set<IncludeGenerator> generators = new 
CopyOnWriteArraySet<IncludeGenerator>();
+
+    public IncludeGenerator getGenerator(String type) {
+        for (IncludeGenerator generator : generators) {
+            if (type.equals(generator.getType())) {
+                return generator;
+            }
+        }
+        return null;
+    }
+
+    void bindGenerators(IncludeGenerator generator) {
+        generators.add(generator);
+    }
+
+    void unbindGenerators(IncludeGenerator generator) {
+        generators.remove(generator);
+    }
+
+}
diff --git 
a/src/main/java/org/apache/sling/dynamicinclude/generator/types/EsiGenerator.java
 
b/src/main/java/org/apache/sling/dynamicinclude/generator/types/EsiGenerator.java
new file mode 100755
index 0000000..4107585
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/dynamicinclude/generator/types/EsiGenerator.java
@@ -0,0 +1,51 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude.generator.types;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.dynamicinclude.generator.IncludeGenerator;
+
+/**
+ * ESI include generator
+ * 
+ * @author tomasz.rekawek
+ * 
+ */
+@Component
+@Service
+public class EsiGenerator implements IncludeGenerator {
+    private static final String GENERATOR_NAME = "ESI";
+
+    @Override
+    public String getType() {
+        return GENERATOR_NAME;
+    }
+
+    @Override
+    public String getInclude(String url) {
+        StringBuffer buf = new StringBuffer();
+        buf.append("<esi:include src=\"");
+        buf.append(StringEscapeUtils.escapeHtml(url));
+        buf.append("\"/>");
+        return buf.toString();
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/dynamicinclude/generator/types/JsiGenerator.java
 
b/src/main/java/org/apache/sling/dynamicinclude/generator/types/JsiGenerator.java
new file mode 100755
index 0000000..673c725
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/dynamicinclude/generator/types/JsiGenerator.java
@@ -0,0 +1,115 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude.generator.types;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.dynamicinclude.generator.IncludeGenerator;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Client side include generator - using Ajax/JQuery.
+ * 
+ * @author tomasz.rekawek
+ */
+@Component
+@Service
+public class JsiGenerator implements IncludeGenerator {
+    private static final String TEMPLATE_FILENAME = "generators/jquery.html";
+
+    private static final String UUID_FIELD = "${uniqueId}";
+
+    private static final String URL_FIELD = "${url}";
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(JsiGenerator.class);
+
+    private static final String GENERATOR_NAME = "JSI";
+
+    private volatile int divId = 1000;
+
+    private String template;
+
+    @Activate
+    public void activate(ComponentContext ctx) {
+        URL url = 
ctx.getBundleContext().getBundle().getResource(TEMPLATE_FILENAME);
+        if (url == null) {
+            LOG.error("File " + TEMPLATE_FILENAME + " not found in bundle.");
+            return;
+        }
+        readTemplateFromUrl(url);
+    }
+
+    @Override
+    public String getType() {
+        return GENERATOR_NAME;
+    }
+
+    @Override
+    public String getInclude(String url) {
+        if (template == null) {
+            throw new IllegalStateException("JSI generator hasn't be 
initialized");
+        }
+
+        String divName;
+        synchronized (this) {
+            divName = "dynamic_include_filter_div_" + divId++;
+        }
+
+        return template.replace(UUID_FIELD, divName).replace(URL_FIELD, 
StringEscapeUtils.escapeJavaScript(url));
+    }
+
+    private void readTemplateFromUrl(URL url) {
+        BufferedReader br = null;
+        try {
+            InputStream in = url.openStream();
+            br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+            StringBuilder builder = new StringBuilder();
+            String line;
+            while ((line = br.readLine()) != null) {
+                builder.append(line).append('\n');
+            }
+            template = builder.toString();
+        } catch (UnsupportedEncodingException e) {
+            LOG.error("Error while reading template", e);
+        } catch (IOException e) {
+            LOG.error("Error while reading template", e);
+        } finally {
+            try {
+                if (br != null) {
+                    br.close();
+                }
+            } catch (Exception e) {
+                LOG.error("Error while closing reader", e);
+            }
+        }
+    }
+
+}
diff --git 
a/src/main/java/org/apache/sling/dynamicinclude/generator/types/SsiGenerator.java
 
b/src/main/java/org/apache/sling/dynamicinclude/generator/types/SsiGenerator.java
new file mode 100755
index 0000000..c2f8b41
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/dynamicinclude/generator/types/SsiGenerator.java
@@ -0,0 +1,61 @@
+/*-
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sling.dynamicinclude.generator.types;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.dynamicinclude.generator.IncludeGenerator;
+
+/**
+ * Apache SSI include generator
+ * 
+ * @author tomasz.rekawek
+ * 
+ */
+@Component
+@Service
+public class SsiGenerator implements IncludeGenerator {
+    private static final String GENERATOR_NAME = "SSI";
+
+    @Override
+    public String getType() {
+        return GENERATOR_NAME;
+    }
+
+    @Override
+    public String getInclude(String url) {
+        StringBuffer buf = new StringBuffer();
+        buf.append("<!--#include virtual=\"");
+        buf.append(escapeForApache(url));
+        buf.append("\" -->");
+        return buf.toString();
+    }
+
+    /**
+     * Escapes $ to \$
+     * 
+     * @param url
+     *            url to escape
+     * @return escaped url
+     */
+    private Object escapeForApache(String url) {
+        return url.replace("$", "\\$");
+    }
+}
diff --git a/src/main/resources/generators/jquery.html 
b/src/main/resources/generators/jquery.html
new file mode 100755
index 0000000..dd064b2
--- /dev/null
+++ b/src/main/resources/generators/jquery.html
@@ -0,0 +1,26 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<div id="${uniqueId}"></div>
+<script type="text/javascript">
+jQuery.get("${url}", 
+       function(data) {
+               jQuery("#${uniqueId}").replaceWith(data);
+       });
+</script>
+<noscript>Your browser does not support JavaScript. Some components may not be 
visible.</noscript>
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"[email protected]" <[email protected]>.

Reply via email to