├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
├── doc
└── plain-include.png
├── java
└── com
│ └── cognifide
│ └── cq
│ └── includefilter
│ ├── CacheControlFilter.java
│ ├── Configuration.java
│ ├── ConfigurationWhiteboard.java
│ ├── IncludeTagWritingFilter.java
│ ├── MutableUrl.java
│ ├── SyntheticResourceIncludingFilter.java
│ ├── SyntheticResourceIncludingServlet.java
│ └── generator
│ ├── IncludeGenerator.java
│ ├── IncludeGeneratorWhiteboard.java
│ └── types
│ ├── EsiGenerator.java
│ ├── JsiGenerator.java
│ └── SsiGenerator.java
└── resources
└── generators
└── jquery.html
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | .classpath
3 | .settings
4 | .project
5 | /target
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2012 Wunderman Thompson Technology
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sling Dynamic Include
2 |
3 | ___
4 | **Now part of Apache Sling**
5 |
6 | Sling Dynamic Include (SDI) project has been [contributed to the Apache Sling project](https://github.com/apache/sling-org-apache-sling-dynamic-include).
7 |
8 | **SDI is no longer maintained here** - this Github project has been left as an information hub only. All tickets should be raised in the [Apache Sling jira](https://issues.apache.org/jira/projects/SLING).
9 | ___
10 |
11 |
12 |
13 | ## Purpose
14 |
15 | 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.
16 |
17 | When the filter intercepts request for a component with given `resourceType`, it'll return a server-side include tag (eg. `` 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.
18 |
19 | 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.
20 |
21 | ## Prerequisites
22 |
23 | * CQ / Apache Sling 2
24 | * Maven 2.x, 3.x
25 |
26 | ## Installation
27 |
28 | Add following dependency to your project:
29 |
30 |
31 | com.cognifide.cq
32 | sling-dynamic-include
33 | 2.0.4
34 |
35 |
36 | ## Configuration
37 |
38 | Filter is delivered as a standard OSGi bundle. SDI is configured via the configuration factory called *SDI Configuration*. Following properties are available:
39 |
40 | * **Enabled** - enable SDI
41 | * **Base path** - given SDI configuration will be enabled only for this
42 | path
43 | * **Resource types** - which components should be replaced with tags
44 | * **Include type** - type of include tag (Apache SSI, ESI or Javascript)
45 | * **Add comment** - adds debug comment: `` to every replaced component
46 | * **Filter selector** - selector used to get actual content
47 | * **Component TTL** - time to live in seconds, set for rendered component (require Dispatcher 4.1.11+)
48 | * **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 `=`.
49 | * **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.
50 | * **Include path rewriting** -- enable rewriting link (according to sling mappings) that is used for dynamic content including.
51 |
52 | ## Compatibility with components
53 |
54 | Filter is incompatible with following types of component:
55 |
56 | * components which handles POST requests or GET parameters,
57 | * synthetic components which uses suffixes (because suffix is used to pass `requestType` of the synthetic resource).
58 |
59 | If component do not generate HTML but eg. JS or binary data then remember to turn off *Comment* option in configuration.
60 |
61 | ## Enabling SSI in Apache & dispatcher
62 |
63 | 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:
64 |
65 |
66 | SetHandler dispatcher-handler
67 |
68 |
69 | and modify it:
70 |
71 |
72 | SetHandler dispatcher-handler
73 |
74 | SetOutputFilter INCLUDES
75 |
76 | After setting output filter open virtualhost configuration and add `Includes` option to `Options` directive:
77 |
78 |
79 | Options FollowSymLinks Includes
80 | AllowOverride None
81 |
82 |
83 | Options Indexes FollowSymLinks MultiViews Includes
84 | AllowOverride None
85 | Order allow,deny
86 | allow from all
87 |
88 |
89 | It's also a good idea to disable the caching for `.nocache.html` files in `dispatcher.any` config file. Just add:
90 |
91 | /disable-nocache
92 | {
93 | /glob "*.nocache.html*"
94 | /type "deny"
95 | }
96 |
97 | at the end of the `/rules` section.
98 |
99 | ## Enabling TTL in dispatcher 4.1.11+
100 | In order to enable TTL on Apache with dispatcher just add:
101 |
102 | /enableTTL "1"
103 |
104 | to your dispatcher configuration.
105 |
106 |
107 | ## Enabling ESI in Varnish
108 |
109 | Just add following lines at the beginning of the `vcl_fetch` section in `/etc/varnish/default.vcl` file:
110 |
111 | if(req.url ~ "\.nocache.html") {
112 | set beresp.ttl = 0s;
113 | } else if (req.url ~ "\.html") {
114 | set beresp.do_esi = true;
115 | }
116 |
117 | It'll enable ESI includes in `.html` files and disable caching of the `.nocache.html` files.
118 |
119 | ## JavaScript Include
120 |
121 | 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 `
` section while the component itself is still not loaded.
122 |
123 | ## Plain and synthetic resources
124 |
125 | There are two cases: the first involves including a component which is available at some URL, eg.
126 |
127 | /content/geometrixx/en/jcr:content/carousel.html
128 |
129 | In this case, component is replaced with include tag, and `nocache` selector is added
130 |
131 |
132 |
133 | If the filter gets request with selector it'll pass it (using `doChain`) further without taking any action.
134 |
135 | 
136 |
137 | 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
138 |
139 | /content/geometrixx/en/jcr:content/userinfo
140 |
141 | 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.:
142 |
143 | /content/geometrixx/en/jcr:content/userinfo.nocache.html/foundation/components/userinfo
144 |
145 | If filter got such request, it'll try to emulate `` JSP tag and includes resource with given type and `nocache` selector:
146 |
147 | /content/geometrixx/en/jcr:content/userinfo.nocache.html
148 |
149 | Selector is necessary, because otherwise filter would again replace component with a SSI tag.
150 |
151 | # External resources
152 |
153 | * [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)
154 | * [SDI blog](http://www.cognifide.com/blogs/cq/sling-dynamic-include/) post on the Cognifide website
155 | * 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).
156 |
157 | # Release notes
158 |
159 | ## 2.2.0
160 |
161 | \#17 Support for time-based (TTL) caching, Dispatcher 4.1.11+ required
162 |
163 | # Integrated with Sling
164 |
165 | Sling Dynamic Include (SDI) project has been contributed to the Apache Sling project as [an extension](https://github.com/apache/sling/tree/trunk/contrib/extensions/sling-dynamic-include).
166 | See the official [announcement](https://www.cognifide.com/our-blogs/cq/contributing-sling-dynamic-include-to-apache-sling) on the Cognifide website.
167 |
168 | This Github project has been left as an information hub.
169 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | org.sonatype.oss
5 | oss-parent
6 | 7
7 |
8 |
9 | 4.0.0
10 | com.cognifide.cq
11 | sling-dynamic-include
12 | Sling Dynamic Include
13 | 2.2.1-SNAPSHOT
14 | bundle
15 |
16 | Wunderman Thompson Technology
17 | http://cognifide.com
18 |
19 |
20 | Dynamic include filter for Apache Sling
21 | https://github.com/wttech/Sling-Dynamic-Include
22 |
23 |
24 | The Apache Software License, Version 2.0
25 | http://www.apache.org/licenses/LICENSE-2.0.txt
26 | repo
27 |
28 |
29 |
30 |
31 | UTF-8
32 | http://localhost:4502/system/console
33 | admin
34 | admin
35 |
36 |
37 |
38 | scm:git:git@github.com:wttech/Sling-Dynamic-Include.git
39 | scm:git:git@github.com:wttech/Sling-Dynamic-Include.git
40 | scm:git:git@github.com:wttech/Sling-Dynamic-Include.git
41 |
42 |
43 |
44 |
45 | paco
46 | Przemyslaw Pakulski
47 | przemo.pakulski@cognifide.com
48 |
49 |
50 | trekawek
51 | Tomasz Rekawek
52 | tomasz.rekawek@cognifide.com
53 |
54 |
55 |
56 |
57 |
58 |
59 | org.apache.felix
60 | maven-scr-plugin
61 | 1.15.0
62 |
63 |
64 | generate-scr-scrdescriptor
65 |
66 | scr
67 |
68 |
69 |
70 |
71 |
72 | org.apache.maven.plugins
73 | maven-compiler-plugin
74 | 2.3.2
75 |
76 | true
77 | 1.6
78 | 1.6
79 | 1.6
80 |
81 |
82 |
83 | org.apache.felix
84 | maven-bundle-plugin
85 | 2.4.0
86 | true
87 |
88 |
89 | com.cognifide.cq.includefilter.*
90 | ${project.groupId}.${project.artifactId}
91 | ${project.name}
92 | Wunderman Thompson Technology
93 | false
94 |
95 |
96 |
97 |
98 | org.apache.sling
99 | maven-sling-plugin
100 | 2.1.0
101 |
102 | ${project.build.directory}/${project.build.finalName}.jar
103 | ${sling.username}
104 | ${sling.password}
105 | ${sling.url}
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | org.osgi
115 | org.osgi.core
116 | 4.2.0
117 | provided
118 |
119 |
120 | org.osgi
121 | org.osgi.compendium
122 | 4.2.0
123 | provided
124 |
125 |
126 | org.apache.felix
127 | org.apache.felix.scr.annotations
128 | 1.9.4
129 | provided
130 |
131 |
132 |
133 |
134 | javax.servlet
135 | servlet-api
136 | 2.4
137 | provided
138 |
139 |
140 |
141 |
142 | org.apache.sling
143 | org.apache.sling.api
144 | 2.2.0
145 | provided
146 |
147 |
148 | org.apache.sling
149 | org.apache.sling.engine
150 | 2.2.0
151 | provided
152 |
153 |
154 | org.apache.sling
155 | org.apache.sling.commons.osgi
156 | 2.2.0
157 | provided
158 |
159 |
160 |
161 |
162 | org.slf4j
163 | slf4j-api
164 | 1.7.0
165 | provided
166 |
167 |
168 |
169 |
170 | commons-lang
171 | commons-lang
172 | 2.4
173 | provided
174 |
175 |
176 |
177 |
178 |
179 | release-sign-artifacts
180 |
181 |
182 | performRelease
183 | true
184 |
185 |
186 |
187 |
188 |
189 | org.apache.maven.plugins
190 | maven-gpg-plugin
191 | 1.4
192 |
193 |
194 | sign-artifacts
195 | verify
196 |
197 | sign
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/src/main/doc/plain-include.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wttech/Sling-Dynamic-Include/616f2efe19dc85f6ded2bb2835c3e865b0775baa/src/main/doc/plain-include.png
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/CacheControlFilter.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.servlet.Filter;
6 | import javax.servlet.FilterChain;
7 | import javax.servlet.FilterConfig;
8 | import javax.servlet.ServletException;
9 | import javax.servlet.ServletRequest;
10 | import javax.servlet.ServletResponse;
11 |
12 | import org.apache.felix.scr.annotations.Reference;
13 | import org.apache.felix.scr.annotations.sling.SlingFilter;
14 | import org.apache.felix.scr.annotations.sling.SlingFilterScope;
15 | import org.apache.sling.api.SlingHttpServletRequest;
16 | import org.apache.sling.api.SlingHttpServletResponse;
17 | import org.slf4j.Logger;
18 | import org.slf4j.LoggerFactory;
19 |
20 | @SlingFilter(scope = SlingFilterScope.REQUEST, order = 0)
21 | public class CacheControlFilter implements Filter {
22 |
23 | private static final String HEADER_CACHE_CONTROL = "Cache-Control";
24 |
25 | private static final Logger LOG = LoggerFactory.getLogger(CacheControlFilter.class);
26 |
27 | @Reference
28 | private ConfigurationWhiteboard configurationWhiteboard;
29 |
30 | @Override
31 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
32 | throws IOException, ServletException {
33 | final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
34 | final String resourceType = slingRequest.getResource().getResourceType();
35 | final Configuration config = configurationWhiteboard.getConfiguration(slingRequest, resourceType);
36 |
37 | if (config != null && config.hasTtlSet()) {
38 | SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) response;
39 | slingResponse.setHeader(HEADER_CACHE_CONTROL, "max-age=" + config.getTtl());
40 | LOG.debug("set \"{}: max-age={}\" to {}", HEADER_CACHE_CONTROL, config.getTtl(), resourceType);
41 | }
42 |
43 | chain.doFilter(request, response);
44 | }
45 |
46 | @Override
47 | public void init(FilterConfig filterConfig) throws ServletException {
48 | }
49 |
50 | @Override
51 | public void destroy() {
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/Configuration.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | import org.apache.commons.lang.ArrayUtils;
8 | import org.apache.commons.lang.StringUtils;
9 | import org.apache.felix.scr.annotations.Activate;
10 | import org.apache.felix.scr.annotations.Component;
11 | import org.apache.felix.scr.annotations.ConfigurationPolicy;
12 | import org.apache.felix.scr.annotations.Properties;
13 | import org.apache.felix.scr.annotations.Property;
14 | import org.apache.felix.scr.annotations.PropertyOption;
15 | import org.apache.felix.scr.annotations.Service;
16 | import org.apache.sling.api.SlingHttpServletRequest;
17 | import org.apache.sling.commons.osgi.PropertiesUtil;
18 | import org.osgi.framework.Constants;
19 | import org.osgi.service.component.ComponentContext;
20 |
21 | /**
22 | * Include filter configuration.
23 | *
24 | * @author tomasz.rekawek
25 | *
26 | */
27 | @Component(metatype = true, configurationFactory = true,
28 | label = "Cognifide : SDI : Configuration", immediate = true, policy = ConfigurationPolicy.REQUIRE)
29 | @Service(Configuration.class)
30 | @Properties({
31 | @Property(name = Constants.SERVICE_VENDOR, value = "Cognifide"),
32 | @Property(name = Configuration.PROPERTY_FILTER_ENABLED, boolValue = Configuration.DEFAULT_FILTER_ENABLED, label = "Enabled", description = "Check to enable the filter"),
33 | @Property(name = Configuration.PROPERTY_FILTER_PATH, value = Configuration.DEFAULT_FILTER_PATH, label = "Base path", description = "This SDI configuration will work only for this path"),
34 | @Property(name = Configuration.PROPERTY_FILTER_RESOURCE_TYPES, cardinality = Integer.MAX_VALUE, label = "Resource types", description = "Filter will replace components with selected resource types"),
35 | @Property(name = Configuration.PROPERTY_INCLUDE_TYPE, value = Configuration.DEFAULT_INCLUDE_TYPE, label = "Include type", description = "Type of generated include tags", options = {
36 | @PropertyOption(name = "SSI", value = "Apache SSI"),
37 | @PropertyOption(name = "ESI", value = "ESI"),
38 | @PropertyOption(name = "JSI", value = "Javascript") }),
39 | @Property(name = Configuration.PROPERTY_ADD_COMMENT, boolValue = Configuration.DEFAULT_ADD_COMMENT, label = "Add comment", description = "Add comment to included components"),
40 | @Property(name = Configuration.PROPERTY_FILTER_SELECTOR, value = Configuration.DEFAULT_FILTER_SELECTOR, label = "Filter selector", description = "Selector used to mark included resources"),
41 | @Property(name = Configuration.PROPERTY_COMPONENT_TTL, label = "Component TTL", description = "\"Time to live\" cache header for rendered component (in seconds)"),
42 | @Property(name = Configuration.PROPERTY_REQUIRED_HEADER, value = Configuration.DEFAULT_REQUIRED_HEADER, label = "Required header", description = "SDI will work only for requests with given header"),
43 | @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"),
44 | @Property(name = Configuration.PROPERTY_REWRITE_PATH, boolValue = Configuration.DEFAULT_REWRITE_DISABLED, label = "Include path rewriting", description = "Check to enable include path rewriting")
45 | })
46 | public class Configuration {
47 |
48 | static final String PROPERTY_FILTER_PATH = "include-filter.config.path";
49 |
50 | static final String DEFAULT_FILTER_PATH = "/content";
51 |
52 | static final String PROPERTY_FILTER_ENABLED = "include-filter.config.enabled";
53 |
54 | static final boolean DEFAULT_FILTER_ENABLED = false;
55 |
56 | static final String PROPERTY_FILTER_RESOURCE_TYPES = "include-filter.config.resource-types";
57 |
58 | static final String PROPERTY_FILTER_SELECTOR = "include-filter.config.selector";
59 |
60 | static final String DEFAULT_FILTER_SELECTOR = "nocache";
61 |
62 | static final String PROPERTY_COMPONENT_TTL = "include-filter.config.ttl";
63 |
64 | static final String PROPERTY_INCLUDE_TYPE = "include-filter.config.include-type";
65 |
66 | static final String DEFAULT_INCLUDE_TYPE = "SSI";
67 |
68 | static final String PROPERTY_ADD_COMMENT = "include-filter.config.add_comment";
69 |
70 | static final boolean DEFAULT_ADD_COMMENT = false;
71 |
72 | static final String PROPERTY_REQUIRED_HEADER = "include-filter.config.required_header";
73 |
74 | static final String DEFAULT_REQUIRED_HEADER = "Server-Agent=Communique-Dispatcher";
75 |
76 | static final String PROPERTY_IGNORE_URL_PARAMS = "include-filter.config.ignoreUrlParams";
77 |
78 | static final String PROPERTY_REWRITE_PATH = "include-filter.config.rewrite";
79 |
80 | static final boolean DEFAULT_REWRITE_DISABLED = false;
81 |
82 | private boolean isEnabled;
83 |
84 | private String path;
85 |
86 | private String includeSelector;
87 |
88 | private int ttl;
89 |
90 | private List resourceTypes;
91 |
92 | private boolean addComment;
93 |
94 | private String includeTypeName;
95 |
96 | private String requiredHeader;
97 |
98 | private List ignoreUrlParams;
99 |
100 | private boolean rewritePath;
101 |
102 | @Activate
103 | public void activate(ComponentContext context, Map properties) {
104 | isEnabled = PropertiesUtil.toBoolean(properties.get(PROPERTY_FILTER_ENABLED), DEFAULT_FILTER_ENABLED);
105 | path = PropertiesUtil.toString(properties.get(PROPERTY_FILTER_PATH), DEFAULT_FILTER_PATH);
106 | String[] resourceTypeList;
107 | resourceTypeList = PropertiesUtil.toStringArray(properties.get(PROPERTY_FILTER_RESOURCE_TYPES), new String[0]);
108 | for (int i = 0; i < resourceTypeList.length; i++) {
109 | String[] s = resourceTypeList[i].split(";");
110 | String name = s[0].trim();
111 | resourceTypeList[i] = name;
112 | }
113 | this.resourceTypes = Arrays.asList(resourceTypeList);
114 |
115 | includeSelector = PropertiesUtil.toString(properties.get(PROPERTY_FILTER_SELECTOR),
116 | DEFAULT_FILTER_SELECTOR);
117 | ttl = PropertiesUtil.toInteger(properties.get(PROPERTY_COMPONENT_TTL), -1);
118 | addComment = PropertiesUtil.toBoolean(properties.get(PROPERTY_ADD_COMMENT), DEFAULT_ADD_COMMENT);
119 | includeTypeName = PropertiesUtil
120 | .toString(properties.get(PROPERTY_INCLUDE_TYPE), DEFAULT_INCLUDE_TYPE);
121 | requiredHeader = PropertiesUtil.toString(properties.get(PROPERTY_REQUIRED_HEADER),
122 | DEFAULT_REQUIRED_HEADER);
123 | ignoreUrlParams = Arrays.asList(PropertiesUtil.toStringArray(properties.get(PROPERTY_IGNORE_URL_PARAMS), new String[0]));
124 | rewritePath = PropertiesUtil.toBoolean(properties.get(PROPERTY_REWRITE_PATH), DEFAULT_REWRITE_DISABLED);
125 | }
126 |
127 | public String getBasePath() {
128 | return path;
129 | }
130 |
131 | public boolean hasIncludeSelector(SlingHttpServletRequest request) {
132 | return ArrayUtils.contains(request.getRequestPathInfo().getSelectors(), includeSelector);
133 | }
134 |
135 | public String getIncludeSelector() {
136 | return includeSelector;
137 | }
138 |
139 | public boolean hasTtlSet() {
140 | return ttl >= 0;
141 | }
142 |
143 | public int getTtl() {
144 | return ttl;
145 | }
146 |
147 | public boolean isSupportedResourceType(String resourceType) {
148 | return StringUtils.isNotBlank(resourceType) && resourceTypes.contains(resourceType);
149 | }
150 |
151 | public boolean getAddComment() {
152 | return addComment;
153 | }
154 |
155 | public String getIncludeTypeName() {
156 | return includeTypeName;
157 | }
158 |
159 | public boolean isEnabled() {
160 | return isEnabled;
161 | }
162 |
163 | public String getRequiredHeader() {
164 | return requiredHeader;
165 | }
166 |
167 | public List getIgnoreUrlParams() {
168 | return ignoreUrlParams;
169 | }
170 |
171 | public boolean isRewritePath() {
172 | return rewritePath;
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/ConfigurationWhiteboard.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter;
2 |
3 | import java.util.Set;
4 | import java.util.concurrent.CopyOnWriteArraySet;
5 |
6 | import org.apache.commons.lang.StringUtils;
7 | import org.apache.felix.scr.annotations.Component;
8 | import org.apache.felix.scr.annotations.Reference;
9 | import org.apache.felix.scr.annotations.ReferenceCardinality;
10 | import org.apache.felix.scr.annotations.ReferencePolicy;
11 | import org.apache.felix.scr.annotations.Service;
12 | import org.apache.sling.api.SlingHttpServletRequest;
13 |
14 | @Component
15 | @Service(ConfigurationWhiteboard.class)
16 | public class ConfigurationWhiteboard {
17 |
18 | @Reference(referenceInterface = Configuration.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
19 | private Set configs = new CopyOnWriteArraySet();
20 |
21 | public Configuration getConfiguration(SlingHttpServletRequest request, String resourceType) {
22 | for (Configuration c : configs) {
23 | if (isEnabled(c, request) && c.isSupportedResourceType(resourceType)) {
24 | return c;
25 | }
26 | }
27 | return null;
28 | }
29 |
30 | private boolean isEnabled(Configuration config, SlingHttpServletRequest request) {
31 | final String requestPath = request.getRequestPathInfo().getResourcePath();
32 | return config.isEnabled() && StringUtils.startsWith(requestPath, config.getBasePath());
33 | }
34 |
35 | protected void bindConfigs(final Configuration config) {
36 | configs.add(config);
37 | }
38 |
39 | protected void unbindConfigs(final Configuration config) {
40 | configs.remove(config);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/IncludeTagWritingFilter.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter;
2 |
3 | import java.io.IOException;
4 | import java.io.PrintWriter;
5 | import java.net.URI;
6 | import java.net.URISyntaxException;
7 | import java.util.Enumeration;
8 | import java.util.List;
9 |
10 | import javax.servlet.Filter;
11 | import javax.servlet.FilterChain;
12 | import javax.servlet.FilterConfig;
13 | import javax.servlet.ServletException;
14 | import javax.servlet.ServletRequest;
15 | import javax.servlet.ServletResponse;
16 |
17 | import org.apache.commons.lang.StringEscapeUtils;
18 | import org.apache.commons.lang.StringUtils;
19 | import org.apache.felix.scr.annotations.Reference;
20 | import org.apache.felix.scr.annotations.sling.SlingFilter;
21 | import org.apache.felix.scr.annotations.sling.SlingFilterScope;
22 | import org.apache.sling.api.SlingHttpServletRequest;
23 | import org.apache.sling.api.request.RequestPathInfo;
24 | import org.apache.sling.api.resource.Resource;
25 | import org.apache.sling.api.resource.ResourceUtil;
26 | import org.slf4j.Logger;
27 | import org.slf4j.LoggerFactory;
28 |
29 | import com.cognifide.cq.includefilter.generator.IncludeGenerator;
30 | import com.cognifide.cq.includefilter.generator.IncludeGeneratorWhiteboard;
31 |
32 | @SlingFilter(scope = SlingFilterScope.INCLUDE, order = -500)
33 | public class IncludeTagWritingFilter implements Filter {
34 |
35 | private static final Logger LOG = LoggerFactory.getLogger(IncludeTagWritingFilter.class);
36 |
37 | private static final String COMMENT = "\n";
38 |
39 | @Reference
40 | private ConfigurationWhiteboard configurationWhiteboard;
41 |
42 | @Reference
43 | private IncludeGeneratorWhiteboard generatorWhiteboard;
44 |
45 | @Override
46 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
47 | throws IOException, ServletException {
48 | final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
49 | final String resourceType = slingRequest.getResource().getResourceType();
50 |
51 | final Configuration config = configurationWhiteboard.getConfiguration(slingRequest, resourceType);
52 | if (config == null) {
53 | chain.doFilter(request, response);
54 | return;
55 | }
56 |
57 | final IncludeGenerator generator = generatorWhiteboard.getGenerator(config.getIncludeTypeName());
58 | if (generator == null) {
59 | LOG.error("Invalid generator: " + config.getIncludeTypeName());
60 | chain.doFilter(request, response);
61 | return;
62 | }
63 |
64 | final PrintWriter writer = response.getWriter();
65 | final String url = getUrl(config, slingRequest);
66 | if (url == null) {
67 | chain.doFilter(request, response);
68 | return;
69 | }
70 |
71 | if (config.getAddComment()) {
72 | writer.append(String.format(COMMENT, StringEscapeUtils.escapeHtml(url), resourceType));
73 | }
74 |
75 | // Only write the includes markup if the required, configurable request header is present
76 | if (shouldWriteIncludes(config, slingRequest)) {
77 | String include = generator.getInclude(url);
78 | LOG.debug(include);
79 | writer.append(include);
80 | } else {
81 | chain.doFilter(request, response);
82 | }
83 | }
84 |
85 | private boolean shouldWriteIncludes(Configuration config, SlingHttpServletRequest request) {
86 | if (requestHasParameters(config.getIgnoreUrlParams(), request)) {
87 | return false;
88 | }
89 | final String requiredHeader = config.getRequiredHeader();
90 | return StringUtils.isBlank(requiredHeader) || containsHeader(requiredHeader, request);
91 | }
92 |
93 | private boolean requestHasParameters(List ignoreUrlParams, SlingHttpServletRequest request) {
94 | final Enumeration> paramNames = request.getParameterNames();
95 | while (paramNames.hasMoreElements()) {
96 | final String paramName = (String) paramNames.nextElement();
97 | if (!ignoreUrlParams.contains(paramName)) {
98 | return true;
99 | }
100 | }
101 | return false;
102 | }
103 |
104 | private boolean containsHeader(String requiredHeader, SlingHttpServletRequest request) {
105 | final String name, expectedValue;
106 | if (StringUtils.contains(requiredHeader, '=')) {
107 | final String split[] = StringUtils.split(requiredHeader, '=');
108 | name = split[0];
109 | expectedValue = split[1];
110 | } else {
111 | name = requiredHeader;
112 | expectedValue = null;
113 | }
114 |
115 | final String actualValue = request.getHeader(name);
116 | if (actualValue == null) {
117 | return false;
118 | } else if (expectedValue == null) {
119 | return true;
120 | } else {
121 | return actualValue.equalsIgnoreCase(expectedValue);
122 | }
123 | }
124 |
125 | private String getUrl(Configuration config, SlingHttpServletRequest request) {
126 | String url = buildUrl(config, request);
127 | if (config.isRewritePath()) {
128 | url = removeQuestionMark(url);
129 | url = request.getResourceResolver().map(request, url);
130 | }
131 | else {
132 | url = encodeJcrContentPart(url);
133 | try {
134 | url = new URI(null, null, url, null).toASCIIString();
135 | } catch (URISyntaxException e) {
136 | LOG.error("Include url is in the wrong format", e);
137 | return null;
138 | }
139 | }
140 |
141 | return url;
142 | }
143 |
144 | private String buildUrl(Configuration config, SlingHttpServletRequest request) {
145 | final boolean synthetic = ResourceUtil.isSyntheticResource(request.getResource());
146 | final Resource resource = request.getResource();
147 | final StringBuilder builder = new StringBuilder();
148 | final RequestPathInfo pathInfo = request.getRequestPathInfo();
149 |
150 | final String resourcePath = pathInfo.getResourcePath();
151 | builder.append(resourcePath);
152 | if (pathInfo.getSelectorString() != null) {
153 | builder.append('.').append(sanitize(pathInfo.getSelectorString()));
154 | }
155 | builder.append('.').append(config.getIncludeSelector());
156 | builder.append('.').append(pathInfo.getExtension());
157 | if (synthetic) {
158 | builder.append('/').append(resource.getResourceType());
159 | } else {
160 | builder.append(sanitize(pathInfo.getSuffix()));
161 | }
162 | return builder.toString();
163 | }
164 |
165 | private static String sanitize(String path) {
166 | return StringUtils.defaultString(path);
167 | }
168 |
169 | private static String encodeJcrContentPart(String url) {
170 | return url.replace("jcr:content", "_jcr_content");
171 | }
172 |
173 | private static String removeQuestionMark(String url) {
174 | return url.replaceAll("[?]", "");
175 | }
176 |
177 | @Override
178 | public void init(FilterConfig filterConfig) throws ServletException {
179 | }
180 |
181 | @Override
182 | public void destroy() {
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/MutableUrl.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.apache.commons.lang.StringUtils;
7 | import org.apache.sling.api.SlingHttpServletRequest;
8 | import org.apache.sling.api.request.RequestPathInfo;
9 |
10 | /**
11 | * Class providing useful methods for URL manipulation (add/remove selector, set default extension, change
12 | * suffix, etc.)
13 | *
14 | * @author tomasz.rekawek
15 | *
16 | */
17 | public class MutableUrl {
18 | private final RequestPathInfo originalPathInfo;
19 |
20 | private List selectorsToRemove;
21 |
22 | private List selectorsToAdd;
23 |
24 | private String replaceSuffix;
25 |
26 | private String replaceExt;
27 |
28 | private String replacePath;
29 |
30 | private String defaultExt;
31 |
32 | private boolean escapeNamespace;
33 |
34 | public MutableUrl(SlingHttpServletRequest request, boolean escapeNamespace) {
35 | originalPathInfo = request.getRequestPathInfo();
36 | selectorsToAdd = new ArrayList();
37 | selectorsToRemove = new ArrayList();
38 | this.escapeNamespace = escapeNamespace;
39 | }
40 |
41 | public void addSelector(String selector) {
42 | selectorsToAdd.add(selector);
43 | selectorsToRemove.remove(selector);
44 | }
45 |
46 | public void removeSelector(String selector) {
47 | selectorsToRemove.add(selector);
48 | selectorsToAdd.remove(selector);
49 | }
50 |
51 | public void replacePath(String path) {
52 | replacePath = path;
53 | }
54 |
55 | public void replaceSuffix(String suffix) {
56 | replaceSuffix = suffix;
57 | }
58 |
59 | public void setDefaultExtension(String extension) {
60 | defaultExt = extension;
61 | }
62 |
63 | public void replaceExtension(String extension) {
64 | replaceExt = extension;
65 | }
66 |
67 | public String getPath() {
68 | String resPath;
69 |
70 | if (replacePath != null) {
71 | resPath = replacePath;
72 | } else {
73 | resPath = originalPathInfo.getResourcePath();
74 | }
75 |
76 | // According to
77 | // http://sling.apache.org/apidocs/sling5/org/apache/sling/api/request/RequestPathInfo.html
78 | // it shouldn't contain dot or slash. Unfortunately, sometimes contains - especially if the resource
79 | // doesn't exist.
80 | if (resPath.contains(".")) {
81 | resPath = resPath.substring(0, resPath.indexOf('.'));
82 | }
83 | return resPath;
84 | }
85 |
86 | @Override
87 | public String toString() {
88 | StringBuffer buf = new StringBuffer();
89 | buf.append(getPath());
90 | buildSelectors(buf);
91 |
92 | if (replaceExt != null) {
93 | if (!replaceExt.isEmpty()) {
94 | buf.append('.');
95 | buf.append(replaceExt);
96 | }
97 | } else if (originalPathInfo.getExtension() == null && defaultExt != null) {
98 | buf.append('.');
99 | buf.append(defaultExt);
100 | } else if (originalPathInfo.getExtension() != null) {
101 | buf.append('.');
102 | buf.append(originalPathInfo.getExtension());
103 | }
104 |
105 | if (replaceSuffix != null) {
106 | if (!replaceSuffix.isEmpty()) {
107 | buf.append('/');
108 | buf.append(sanitize(replaceSuffix));
109 | }
110 | } else if (originalPathInfo.getSuffix() != null) {
111 | buf.append(sanitize(originalPathInfo.getSuffix()));
112 | }
113 |
114 | String url = buf.toString();
115 | if(escapeNamespace) {
116 | url = url.replaceAll("(\\w+):(\\w+)", "_$1_$2");
117 | }
118 | return url;
119 | }
120 |
121 | private void buildSelectors(StringBuffer buf) {
122 | String[] selectors = originalPathInfo.getSelectors();
123 | for (String sel : selectors) {
124 | if (!selectorsToRemove.contains(sel) && !selectorsToAdd.contains(sel)) {
125 | buf.append('.');
126 | buf.append(sanitize(sel));
127 | }
128 | }
129 | for (String sel : selectorsToAdd) {
130 | buf.append('.');
131 | buf.append(sanitize(sel));
132 | }
133 | }
134 |
135 | private static String sanitize(String dirtyString) {
136 | if (StringUtils.isBlank(dirtyString)) {
137 | return "";
138 | } else {
139 | return dirtyString.replaceAll("[^0-9a-zA-Z:.\\-/_=]", "");
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/SyntheticResourceIncludingFilter.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.servlet.Filter;
6 | import javax.servlet.FilterChain;
7 | import javax.servlet.FilterConfig;
8 | import javax.servlet.RequestDispatcher;
9 | import javax.servlet.ServletException;
10 | import javax.servlet.ServletRequest;
11 | import javax.servlet.ServletResponse;
12 |
13 | import org.apache.commons.lang.StringUtils;
14 | import org.apache.felix.scr.annotations.Component;
15 | import org.apache.felix.scr.annotations.Properties;
16 | import org.apache.felix.scr.annotations.Property;
17 | import org.apache.felix.scr.annotations.Reference;
18 | import org.apache.felix.scr.annotations.Service;
19 | import org.apache.sling.api.SlingHttpServletRequest;
20 | import org.apache.sling.api.request.RequestDispatcherOptions;
21 | import org.apache.sling.api.resource.ResourceUtil;
22 | import org.apache.sling.engine.EngineConstants;
23 | import org.osgi.framework.Constants;
24 |
25 | @Component(metatype = true, label="Cognifide : SDI : Synthetic Resource Include Filter")
26 | @Service
27 | @Properties({
28 | @Property(name = Constants.SERVICE_VENDOR, value = "Cognifide"),
29 | @Property(name = EngineConstants.SLING_FILTER_SCOPE, value = EngineConstants.FILTER_SCOPE_REQUEST, propertyPrivate = true),
30 | @Property(name = Constants.SERVICE_RANKING, intValue = Integer.MIN_VALUE, description = "< -2500 after 6.0 SP2, > -2500 before SP2 ", propertyPrivate = false),
31 | })
32 | public class SyntheticResourceIncludingFilter implements Filter {
33 |
34 | @Reference
35 | private ConfigurationWhiteboard configurationWhiteboard;
36 |
37 | @Override
38 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
39 | throws IOException, ServletException {
40 | final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
41 | final String resourceType = getResourceTypeFromSuffix(slingRequest);
42 | final Configuration config = configurationWhiteboard.getConfiguration(slingRequest, resourceType);
43 |
44 | if (config == null
45 | || !config.hasIncludeSelector(slingRequest)
46 | || !ResourceUtil.isSyntheticResource(slingRequest.getResource())) {
47 | chain.doFilter(request, response);
48 | return;
49 | }
50 |
51 | final RequestDispatcherOptions options = new RequestDispatcherOptions();
52 | options.setForceResourceType(resourceType);
53 | final RequestDispatcher dispatcher = slingRequest.getRequestDispatcher(slingRequest.getResource(),
54 | options);
55 | dispatcher.forward(request, response);
56 | }
57 |
58 | private static String getResourceTypeFromSuffix(SlingHttpServletRequest request) {
59 | final String suffix = request.getRequestPathInfo().getSuffix();
60 | return StringUtils.removeStart(suffix, "/");
61 | }
62 |
63 | @Override
64 | public void init(FilterConfig filterConfig) throws ServletException {
65 | }
66 |
67 | @Override
68 | public void destroy() {
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/SyntheticResourceIncludingServlet.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.servlet.RequestDispatcher;
6 | import javax.servlet.ServletException;
7 |
8 | import org.apache.commons.lang.StringUtils;
9 | import org.apache.felix.scr.annotations.Reference;
10 | import org.apache.felix.scr.annotations.sling.SlingServlet;
11 | import org.apache.sling.api.SlingHttpServletRequest;
12 | import org.apache.sling.api.SlingHttpServletResponse;
13 | import org.apache.sling.api.request.RequestDispatcherOptions;
14 | import org.apache.sling.api.servlets.OptingServlet;
15 | import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
16 |
17 | /**
18 | * Experimental implementation for synthetic resource include
19 | *
20 | * @author miroslaw.stawniak
21 | *
22 | */
23 | //@SlingServlet(resourceTypes = "sling:nonexisting", methods="GET")
24 | public class SyntheticResourceIncludingServlet extends SlingSafeMethodsServlet implements OptingServlet {
25 |
26 | private static final long serialVersionUID = 1L;
27 |
28 | @Reference
29 | private ConfigurationWhiteboard configurationWhiteboard;
30 |
31 | @Override
32 | protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
33 | throws ServletException, IOException {
34 |
35 | final String resourceType = getResourceTypeFromSuffix(request);
36 |
37 | final RequestDispatcherOptions options = new RequestDispatcherOptions();
38 | options.setForceResourceType(resourceType);
39 | final RequestDispatcher dispatcher = request.getRequestDispatcher(request.getResource(),
40 | options);
41 | dispatcher.forward(request, response);
42 | }
43 |
44 | @Override
45 | public boolean accepts(SlingHttpServletRequest request) {
46 | final String resourceType = getResourceTypeFromSuffix(request);
47 | final Configuration config = configurationWhiteboard.getConfiguration(request, resourceType);
48 |
49 | return config != null
50 | && config.hasIncludeSelector(request);
51 | }
52 |
53 | private static String getResourceTypeFromSuffix(SlingHttpServletRequest request) {
54 | final String suffix = request.getRequestPathInfo().getSuffix();
55 | return StringUtils.removeStart(suffix, "/");
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/generator/IncludeGenerator.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter.generator;
2 |
3 | /**
4 | * Include generator interface
5 | *
6 | * @author tomasz.rekawek
7 | *
8 | */
9 | public interface IncludeGenerator {
10 | String getType();
11 |
12 | String getInclude(String url);
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/generator/IncludeGeneratorWhiteboard.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter.generator;
2 |
3 | import java.util.Set;
4 | import java.util.concurrent.CopyOnWriteArraySet;
5 |
6 | import org.apache.felix.scr.annotations.Component;
7 | import org.apache.felix.scr.annotations.Reference;
8 | import org.apache.felix.scr.annotations.ReferenceCardinality;
9 | import org.apache.felix.scr.annotations.ReferencePolicy;
10 | import org.apache.felix.scr.annotations.Service;
11 |
12 | /**
13 | * Service that provides include generator of given type.
14 | *
15 | * @author tomasz.rekawek
16 | */
17 |
18 | @Component
19 | @Service(IncludeGeneratorWhiteboard.class)
20 | public class IncludeGeneratorWhiteboard {
21 |
22 | @Reference(referenceInterface = IncludeGenerator.class, cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
23 | private Set generators = new CopyOnWriteArraySet();
24 |
25 | public IncludeGenerator getGenerator(String type) {
26 | for (IncludeGenerator generator : generators) {
27 | if (type.equals(generator.getType())) {
28 | return generator;
29 | }
30 | }
31 | return null;
32 | }
33 |
34 | void bindGenerators(IncludeGenerator generator) {
35 | generators.add(generator);
36 | }
37 |
38 | void unbindGenerators(IncludeGenerator generator) {
39 | generators.remove(generator);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/generator/types/EsiGenerator.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter.generator.types;
2 |
3 | import org.apache.commons.lang.StringEscapeUtils;
4 | import org.apache.felix.scr.annotations.Component;
5 | import org.apache.felix.scr.annotations.Service;
6 |
7 | import com.cognifide.cq.includefilter.generator.IncludeGenerator;
8 |
9 | /**
10 | * ESI include generator
11 | *
12 | * @author tomasz.rekawek
13 | *
14 | */
15 | @Component
16 | @Service
17 | public class EsiGenerator implements IncludeGenerator {
18 | private static final String GENERATOR_NAME = "ESI";
19 |
20 | @Override
21 | public String getType() {
22 | return GENERATOR_NAME;
23 | }
24 |
25 | @Override
26 | public String getInclude(String url) {
27 | StringBuffer buf = new StringBuffer();
28 | buf.append(" ");
31 | return buf.toString();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/generator/types/JsiGenerator.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter.generator.types;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.InputStreamReader;
7 | import java.io.UnsupportedEncodingException;
8 | import java.net.URL;
9 |
10 | import org.apache.commons.lang.StringEscapeUtils;
11 | import org.apache.felix.scr.annotations.Activate;
12 | import org.apache.felix.scr.annotations.Component;
13 | import org.apache.felix.scr.annotations.Service;
14 | import org.osgi.service.component.ComponentContext;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import com.cognifide.cq.includefilter.generator.IncludeGenerator;
19 |
20 | /**
21 | * Client side include generator - using Ajax/JQuery.
22 | *
23 | * @author tomasz.rekawek
24 | */
25 | @Component
26 | @Service
27 | public class JsiGenerator implements IncludeGenerator {
28 | private static final String TEMPLATE_FILENAME = "generators/jquery.html";
29 |
30 | private static final String UUID_FIELD = "${uniqueId}";
31 |
32 | private static final String URL_FIELD = "${url}";
33 |
34 | private static final Logger LOG = LoggerFactory.getLogger(JsiGenerator.class);
35 |
36 | private static final String GENERATOR_NAME = "JSI";
37 |
38 | private volatile int divId = 1000;
39 |
40 | private String template;
41 |
42 | @Activate
43 | public void activate(ComponentContext ctx) {
44 | URL url = ctx.getBundleContext().getBundle().getResource(TEMPLATE_FILENAME);
45 | if (url == null) {
46 | LOG.error("File " + TEMPLATE_FILENAME + " not found in bundle.");
47 | return;
48 | }
49 | readTemplateFromUrl(url);
50 | }
51 |
52 | @Override
53 | public String getType() {
54 | return GENERATOR_NAME;
55 | }
56 |
57 | @Override
58 | public String getInclude(String url) {
59 | if (template == null) {
60 | throw new IllegalStateException("JSI generator hasn't be initialized");
61 | }
62 |
63 | String divName;
64 | synchronized (this) {
65 | divName = "dynamic_include_filter_div_" + divId++;
66 | }
67 |
68 | return template
69 | .replace(UUID_FIELD, divName)
70 | .replace(URL_FIELD, StringEscapeUtils.escapeJavaScript(url));
71 | }
72 |
73 |
74 | private void readTemplateFromUrl(URL url) {
75 | BufferedReader br = null;
76 | try {
77 | InputStream in = url.openStream();
78 | br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
79 | StringBuilder builder = new StringBuilder();
80 | String line;
81 | while ((line = br.readLine()) != null) {
82 | builder.append(line).append('\n');
83 | }
84 | template = builder.toString();
85 | } catch (UnsupportedEncodingException e) {
86 | LOG.error("Error while reading template", e);
87 | } catch (IOException e) {
88 | LOG.error("Error while reading template", e);
89 | } finally {
90 | try {
91 | if (br != null) {
92 | br.close();
93 | }
94 | } catch (Exception e) {
95 | LOG.error("Error while closing reader", e);
96 | }
97 | }
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/com/cognifide/cq/includefilter/generator/types/SsiGenerator.java:
--------------------------------------------------------------------------------
1 | package com.cognifide.cq.includefilter.generator.types;
2 |
3 | import org.apache.felix.scr.annotations.Component;
4 | import org.apache.felix.scr.annotations.Service;
5 |
6 | import com.cognifide.cq.includefilter.generator.IncludeGenerator;
7 |
8 | /**
9 | * Apache SSI include generator
10 | *
11 | * @author tomasz.rekawek
12 | *
13 | */
14 | @Component
15 | @Service
16 | public class SsiGenerator implements IncludeGenerator {
17 | private static final String GENERATOR_NAME = "SSI";
18 |
19 | @Override
20 | public String getType() {
21 | return GENERATOR_NAME;
22 | }
23 |
24 | @Override
25 | public String getInclude(String url) {
26 | StringBuffer buf = new StringBuffer();
27 | buf.append("");
30 | return buf.toString();
31 | }
32 |
33 | /**
34 | * Escapes $ to \$
35 | *
36 | * @param url url to escape
37 | * @return escaped url
38 | */
39 | private Object escapeForApache(String url) {
40 | return url.replace("$", "\\$");
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/resources/generators/jquery.html:
--------------------------------------------------------------------------------
1 |
2 |
8 | Your browser does not support JavaScript. Some components may not be visible.
--------------------------------------------------------------------------------