├── .asf.yaml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Jenkinsfile ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── doc │ ├── plain_include.png │ └── plain_include.svg ├── java │ └── org │ │ └── apache │ │ └── sling │ │ └── dynamicinclude │ │ ├── CacheControlFilter.java │ │ ├── Configuration.java │ │ ├── ConfigurationWhiteboard.java │ │ ├── IncludeTagFilter.java │ │ ├── SyntheticResourceFilter.java │ │ ├── api │ │ ├── IncludeGenerator.java │ │ └── package-info.java │ │ ├── generator │ │ ├── IncludeGeneratorWhiteboard.java │ │ └── types │ │ │ ├── EsiGenerator.java │ │ │ ├── JsiGenerator.java │ │ │ └── SsiGenerator.java │ │ ├── impl │ │ └── UrlBuilder.java │ │ ├── pathmatcher │ │ ├── PathMatcher.java │ │ ├── PrefixPathMatcher.java │ │ └── RegexPathMatcher.java │ │ └── util │ │ └── RequestHelperUtil.java └── resources │ └── generators │ └── javascript.html └── test └── java └── org └── apache └── sling └── dynamicinclude ├── ConfigurationTest.java ├── ConfigurationWhiteboardTest.java ├── impl └── UrlBuilderTest.java ├── pathmatcher ├── PrefixPathMatcherTest.java └── RegexPathMatcherTest.java └── util └── RequestHelperUtilTest.java /.asf.yaml: -------------------------------------------------------------------------------- 1 | github: 2 | autolink_jira: 3 | - "SLING" 4 | - "OAK" 5 | - "JCR" 6 | - "JCRVLT" 7 | - "INFRA" 8 | - "FELIX" 9 | - "MNG" 10 | notifications: 11 | jira_options: "link" 12 | pullrequests_bot_sonarcloud: "commits@sling.apache.org" 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | .classpath 4 | .metadata 5 | .project 6 | .settings 7 | .externalToolBuilders 8 | maven-eclipse.xml 9 | *.swp 10 | *.iml 11 | *.ipr 12 | *.iws 13 | *.bak 14 | .vlt 15 | .DS_Store 16 | jcr.log 17 | atlassian-ide-plugin.xml 18 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 19 | Apache Software Foundation Code of Conduct 20 | ==== 21 | 22 | Being an Apache project, Apache Sling adheres to the Apache Software Foundation's [Code of Conduct](https://www.apache.org/foundation/policies/conduct.html). 23 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 19 | Contributing 20 | ==== 21 | 22 | Thanks for choosing to contribute! 23 | 24 | You will find all the necessary details about how you can do this at https://sling.apache.org/contributing.html. 25 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | slingOsgiBundleBuild() 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Apache Sling](https://sling.apache.org/res/logos/sling.png)](https://sling.apache.org) 2 | 3 | [![Build Status](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-dynamic-include/job/master/badge/icon)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-dynamic-include/job/master/) [![Test Status](https://img.shields.io/jenkins/tests.svg?jobUrl=https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-dynamic-include/job/master/)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-dynamic-include/job/master/test/?width=800&height=600) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=apache_sling-org-apache-sling-dynamic-include&metric=coverage)](https://sonarcloud.io/dashboard?id=apache_sling-org-apache-sling-dynamic-include) [![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=apache_sling-org-apache-sling-dynamic-include&metric=alert_status)](https://sonarcloud.io/dashboard?id=apache_sling-org-apache-sling-dynamic-include) [![JavaDoc](https://www.javadoc.io/badge/org.apache.sling/org.apache.sling.dynamic-include.svg)](https://www.javadoc.io/doc/org.apache.sling/org.apache.sling.dynamic-include) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.sling/org.apache.sling.dynamic-include/badge.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.apache.sling%22%20a%3A%22org.apache.sling.dynamic-include%22) [![Contrib](https://sling.apache.org/badges/status-contrib.svg)](https://github.com/apache/sling-aggregator/blob/master/docs/status/contrib.md) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) 4 | 5 | # Apache Sling Dynamic Include 6 | 7 | This module is part of the [Apache Sling](https://sling.apache.org) project. 8 | 9 | ## Purpose 10 | 11 | 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. 12 | 13 | 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. 14 | 15 | 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. 16 | 17 | ## Prerequisites 18 | 19 | * CQ / Apache Sling 2 20 | * Maven 2.x, 3.x 21 | 22 | ## Installation 23 | 24 | Add following dependency to your project: 25 | 26 | 27 | org.apache.sling 28 | org.apache.sling.dynamic-include 29 | 3.1.2 30 | 31 | 32 | ## Configuration 33 | 34 | Filter is delivered as a standard OSGi bundle. SDI is configured via the configuration factory called *SDI Configuration*. Following properties are available: 35 | 36 | * **Enabled** - enable SDI 37 | * **Base path** - This SDI configuration will work only for paths matching this value. If value starts with "^" sign, regex matching will be performed. Otherwise it will check for path prefix. (Available since 3.1.0) 38 | * **Resource types** - which components should be replaced with tags 39 | * **Include type** - type of include tag (Apache SSI, ESI or Javascript) 40 | * **Add comment** - adds debug comment: `` to every replaced component 41 | * **Filter selector** - selector used to get actual content 42 | * **Component TTL** - time to live in seconds, set for rendered component (require Dispatcher 4.1.11+) 43 | * **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 `=`. 44 | * **Disable Ignore URL params check** - SDI will process all requests and discard ignore URL params check including requests with GET params. 45 | * **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. (Supports Java Regex Pattern e.g. "**tracking-(.*)**" or "**param-[0-9]**") See the [Ignoring URL parameters](https://docs.adobe.com/docs/en/dispatcher/disp-config.html#Ignoring%20URL%20Parameters) section in the dispatcher documentation. 46 | * **Include path rewriting** -- enable rewriting link (according to sling mappings) that is used for dynamic content including. 47 | * **Append suffix** -- ensures that the suffix of the parent request is included with the dynamic include. 48 | 49 | ## Compatibility with components 50 | 51 | Filter is incompatible with following types of component: 52 | 53 | * components which handles POST requests or GET parameters, 54 | * synthetic components which uses suffixes (because suffix is used to pass `requestType` of the synthetic resource). 55 | 56 | If component do not generate HTML but eg. JS or binary data then remember to turn off *Comment* option in configuration. 57 | 58 | ## Enabling SSI in Apache & dispatcher 59 | 60 | 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: 61 | 62 | 63 | SetHandler dispatcher-handler 64 | 65 | 66 | and modify it: 67 | 68 | 69 | SetHandler dispatcher-handler 70 | 71 | SetOutputFilter INCLUDES 72 | 73 | After setting output filter open virtualhost configuration and add `Includes` option to `Options` directive: 74 | 75 | 76 | Options FollowSymLinks Includes 77 | AllowOverride None 78 | 79 | 80 | Options Indexes FollowSymLinks Includes 81 | AllowOverride None 82 | Order allow,deny 83 | allow from all 84 | 85 | 86 | It's also a good idea to disable the caching for `.nocache.html` files in `dispatcher.any` config file. Just add: 87 | 88 | /disable-nocache 89 | { 90 | /glob "*.nocache.html*" 91 | /type "deny" 92 | } 93 | 94 | at the end of the `/rules` section. 95 | 96 | ## Enabling TTL in dispatcher 4.1.11+ 97 | In order to enable TTL on Apache with dispatcher just add: 98 | 99 | /enableTTL "1" 100 | 101 | to your dispatcher configuration. 102 | 103 | 104 | ## Enabling ESI in Varnish 105 | 106 | Just add following lines at the beginning of the `vcl_fetch` section in `/etc/varnish/default.vcl` file: 107 | 108 | if(req.url ~ "\.nocache.html") { 109 | set beresp.ttl = 0s; 110 | } else if (req.url ~ "\.html") { 111 | set beresp.do_esi = true; 112 | } 113 | 114 | It'll enable ESI includes in `.html` files and disable caching of the `.nocache.html` files. 115 | 116 | ## JavaScript Include 117 | 118 | 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. 119 | 120 | ## Plain and synthetic resources 121 | 122 | There are two cases: the first involves including a component which is available at some URL, eg. 123 | 124 | /content/geometrixx/en/jcr:content/carousel.html 125 | 126 | In this case, component is replaced with include tag, and `nocache` selector is added 127 | 128 | 129 | 130 | If the filter gets request with selector it'll pass it (using `doChain`) further without taking any action. 131 | 132 | ![Plain include](src/main/doc/plain_include.png) 133 | 134 | 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 135 | 136 | /content/geometrixx/en/jcr:content/userinfo 137 | 138 | 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.: 139 | 140 | /content/geometrixx/en/jcr:content/userinfo.nocache.html/foundation/components/userinfo 141 | 142 | If filter got such request, it'll try to emulate `` JSP tag and includes resource with given type and `nocache` selector: 143 | 144 | /content/geometrixx/en/jcr:content/userinfo.nocache.html 145 | 146 | Selector is necessary, because otherwise filter would again replace component with a SSI tag. 147 | 148 | # External resources 149 | 150 | * [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) 151 | * [SDI blog](http://www.cognifide.com/blogs/cq/sling-dynamic-include/) post on the Cognifide website 152 | * 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). 153 | 154 | # Release notes 155 | 156 | ## 3.1.2 157 | 158 | - Introduced readable names for SDI configuration entries in the OSGi console ([SLING-7695](https://issues.apache.org/jira/browse/SLING-7695)) 159 | - Fixed a bug where the selector configured for use with SDI was added multiple times to the same selector string ([SLING-7742](https://issues.apache.org/jira/browse/SLING-7742)) 160 | - Introduced a mechanism that allows synthetic resources included via SDI to be cached by the AEM Dispatcher ([SLING-7785](https://issues.apache.org/jira/browse/SLING-7785)) 161 | 162 | ## 3.1.0 163 | 164 | - Regular expression matching can be used when configuring resource paths ([SLING-7621](https://issues.apache.org/jira/browse/SLING-7621)) 165 | 166 | ## 3.0.0 167 | 168 | Sling Dynamic Include donated to the Apache Sling project ([SLING-5594](https://issues.apache.org/jira/browse/SLING-5594)), repackaged and released ([SLING-6301](https://issues.apache.org/jira/browse/SLING-6301)) 169 | 170 | ## 2.2.0 171 | 172 | \#17 Support for time-based (TTL) caching, Dispatcher 4.1.11+ required 173 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 4.0.0 23 | 24 | 25 | org.apache.sling 26 | sling-bundle-parent 27 | 52 28 | 29 | 30 | 31 | org.apache.sling.dynamic-include 32 | 3.3.3-SNAPSHOT 33 | 34 | Apache Sling Dynamic Include 35 | Dynamic Include filter for Apache Sling 36 | 37 | 38 | scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-dynamic-include.git 39 | scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-dynamic-include.git 40 | https://github.com/apache/sling-org-apache-sling-dynamic-include.git 41 | HEAD 42 | 43 | 44 | 45 | 46 | pacoolsky 47 | Przemyslaw Pakulski 48 | ppakulski@gmail.com 49 | 50 | 51 | trekawek 52 | Tomasz Rekawek 53 | rekawek@adobe.com 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.osgi 61 | org.osgi.framework 62 | provided 63 | 64 | 65 | org.osgi 66 | org.osgi.service.component 67 | provided 68 | 69 | 70 | org.osgi 71 | org.osgi.service.metatype.annotations 72 | provided 73 | 74 | 75 | org.osgi 76 | org.osgi.service.component.annotations 77 | provided 78 | 79 | 80 | org.osgi 81 | org.osgi.annotation.versioning 82 | provided 83 | 84 | 85 | org.apache.sling 86 | org.apache.sling.servlets.annotations 87 | provided 88 | 89 | 90 | 91 | 92 | javax.servlet 93 | javax.servlet-api 94 | provided 95 | 96 | 97 | 98 | 99 | org.apache.sling 100 | org.apache.sling.api 101 | 2.25.4 102 | provided 103 | 104 | 105 | org.apache.sling 106 | org.apache.sling.engine 107 | 2.14.0 108 | provided 109 | 110 | 111 | org.apache.sling 112 | org.apache.sling.commons.osgi 113 | 2.2.0 114 | provided 115 | 116 | 117 | 118 | 119 | org.slf4j 120 | slf4j-api 121 | provided 122 | 123 | 124 | 125 | 126 | org.apache.commons 127 | commons-lang3 128 | 3.0 129 | provided 130 | 131 | 132 | 133 | 134 | junit 135 | junit 136 | test 137 | 138 | 139 | org.mockito 140 | mockito-core 141 | 2.18.3 142 | test 143 | 144 | 145 | org.apache.sling 146 | org.apache.sling.testing.osgi-mock.junit4 147 | 2.4.10 148 | test 149 | 150 | 151 | org.osgi 152 | org.osgi.service.event 153 | test 154 | 155 | 156 | org.osgi 157 | org.osgi.service.log 158 | test 159 | 160 | 161 | org.osgi 162 | org.osgi.service.cm 163 | test 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /src/main/doc/plain_include.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apache/sling-org-apache-sling-dynamic-include/ff77046e4fee55893f2b4df652f0d38fb0dd3a47/src/main/doc/plain_include.png -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/CacheControlFilter.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude; 21 | 22 | import java.io.IOException; 23 | 24 | import javax.servlet.Filter; 25 | import javax.servlet.FilterChain; 26 | import javax.servlet.FilterConfig; 27 | import javax.servlet.ServletException; 28 | import javax.servlet.ServletRequest; 29 | import javax.servlet.ServletResponse; 30 | 31 | import org.apache.sling.api.SlingHttpServletRequest; 32 | import org.apache.sling.api.SlingHttpServletResponse; 33 | import org.apache.sling.servlets.annotations.SlingServletFilter; 34 | import org.apache.sling.servlets.annotations.SlingServletFilterScope; 35 | import org.osgi.framework.Constants; 36 | import org.osgi.service.component.annotations.Component; 37 | import org.osgi.service.component.annotations.Reference; 38 | import org.slf4j.Logger; 39 | import org.slf4j.LoggerFactory; 40 | 41 | 42 | @SlingServletFilter(scope = { SlingServletFilterScope.REQUEST, SlingServletFilterScope.FORWARD } ) 43 | @Component(property = { 44 | Constants.SERVICE_RANKING + ":Integer=0" 45 | }) 46 | public class CacheControlFilter implements Filter { 47 | 48 | private static final String HEADER_DATE = "Date"; 49 | 50 | private static final String HEADER_CACHE_CONTROL = "Cache-Control"; 51 | 52 | private static final Logger LOG = LoggerFactory.getLogger(CacheControlFilter.class); 53 | 54 | @Reference 55 | private ConfigurationWhiteboard configurationWhiteboard; 56 | 57 | @Override 58 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, 59 | ServletException { 60 | final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request; 61 | final String resourceType = slingRequest.getResource().getResourceType(); 62 | final Configuration config = configurationWhiteboard.getConfiguration(slingRequest, resourceType); 63 | 64 | if (config != null && config.hasTtlSet()) { 65 | SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) response; 66 | slingResponse.setHeader(HEADER_CACHE_CONTROL, "max-age=" + config.getTtl()); 67 | LOG.debug("set \"{}: max-age={}\" to {}", HEADER_CACHE_CONTROL, config.getTtl(), resourceType); 68 | if (!slingResponse.containsHeader(HEADER_DATE)) { 69 | slingResponse.setDateHeader(HEADER_DATE, System.currentTimeMillis()); 70 | } 71 | } 72 | 73 | chain.doFilter(request, response); 74 | } 75 | 76 | @Override 77 | public void init(FilterConfig filterConfig) throws ServletException { 78 | } 79 | 80 | @Override 81 | public void destroy() { 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/Configuration.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude; 21 | 22 | import java.util.Arrays; 23 | import java.util.Collection; 24 | import java.util.HashSet; 25 | import java.util.List; 26 | 27 | import org.apache.commons.lang3.ArrayUtils; 28 | import org.apache.commons.lang3.StringUtils; 29 | import org.apache.sling.api.SlingHttpServletRequest; 30 | import org.apache.sling.commons.osgi.PropertiesUtil; 31 | import org.apache.sling.dynamicinclude.pathmatcher.PathMatcher; 32 | import org.apache.sling.dynamicinclude.pathmatcher.PrefixPathMatcher; 33 | import org.apache.sling.dynamicinclude.pathmatcher.RegexPathMatcher; 34 | import org.osgi.framework.Constants; 35 | import org.osgi.service.component.annotations.Activate; 36 | import org.osgi.service.component.annotations.Component; 37 | import org.osgi.service.component.annotations.ConfigurationPolicy; 38 | import org.osgi.service.metatype.annotations.AttributeDefinition; 39 | import org.osgi.service.metatype.annotations.AttributeType; 40 | import org.osgi.service.metatype.annotations.Designate; 41 | import org.osgi.service.metatype.annotations.ObjectClassDefinition; 42 | import org.slf4j.Logger; 43 | import org.slf4j.LoggerFactory; 44 | 45 | /** 46 | * Include filter configuration. 47 | */ 48 | @Component(service = Configuration.class, 49 | immediate = true, 50 | configurationPolicy = ConfigurationPolicy.REQUIRE, 51 | property = { 52 | Constants.SERVICE_VENDOR + "=The Apache Software Foundation", 53 | "webconsole.configurationFactory.nameHint={include-filter.config.include-type} for [{include-filter.config.resource-types}] at path: {include-filter.config.path}" 54 | }) 55 | @Designate(ocd = Configuration.Config.class, factory = true) 56 | public class Configuration { 57 | 58 | @ObjectClassDefinition(name = "Apache Sling Dynamic Include - Configuration") 59 | public @interface Config { 60 | @AttributeDefinition(name="Enabled", description="Check to enable the filter") 61 | boolean include$_$filter_config_enabled() default false; 62 | 63 | @AttributeDefinition(name="Base path regular expression", description="This SDI configuration will work only for paths matching this value. If value starts with \\\"^\\\" sign, regex matching will be performed. Otherwise it will check for path prefix.") 64 | String include$_$filter_config_path() default "/content"; 65 | 66 | @AttributeDefinition(name = "Resource types", 67 | description = "Filter will replace components with selected resource types", 68 | type = AttributeType.STRING) 69 | String[] include$_$filter_config_resource$_$types() default {}; 70 | 71 | @AttributeDefinition(name = "Include type", description = "Type of generated include tags. The Built-in option are 'SSI','ESI' and 'JSI'. Must match one of the IncludeGenerator service's type") 72 | String include$_$filter_config_include$_$type() default "SSI"; 73 | 74 | @AttributeDefinition(name="Add comment", description = "Add comment to included components") 75 | boolean include$_$filter_config_add__comment() default false; 76 | 77 | @AttributeDefinition(name = "Filter selector", description = "Selector used to mark included resources") 78 | String include$_$filter_config_selector() default "nocache"; 79 | 80 | @AttributeDefinition(name = "Extension", description = "Extension to append to virtual resources to make caching possible") 81 | String include$_$filter_config_extension() default ""; 82 | 83 | @AttributeDefinition(name = "Component TTL", description = "\"Time to live\" cache header for rendered component (in seconds)") 84 | String include$_$filter_config_ttl() default ""; 85 | 86 | @AttributeDefinition(name = "Required header", description = "SDI will work only for requests with given header") 87 | String include$_$filter_config_required__header() default "Server-Agent=Communique-Dispatcher"; 88 | 89 | @AttributeDefinition(name = "Ignore URL params", 90 | description = "SDI will process the request even if it contains configured GET parameters", 91 | type = AttributeType.STRING) 92 | String[] include$_$filter_config_ignoreUrlParams() default {}; 93 | 94 | @AttributeDefinition(name = "Include path rewriting", description = "Check to enable include path rewriting") 95 | boolean include$_$filter_config_rewrite() default false; 96 | 97 | @AttributeDefinition(name = "Append suffix to dynamic includes", description = "Check to append the suffix of the parent request to the dynamic include.") 98 | boolean include$_$filter_config_appendSuffix() default true; 99 | 100 | @AttributeDefinition(name = "Disable ignore URL params check", description = "Disable the check in the Ignore URL Params setting.") 101 | boolean include$_$filter_config_disableIgnoreUrlParams() default false; 102 | } 103 | 104 | private static final Logger LOG = LoggerFactory.getLogger(Configuration.class); 105 | 106 | private PathMatcher pathMatcher; 107 | 108 | private boolean isEnabled; 109 | 110 | private String includeSelector; 111 | 112 | private String extension; 113 | 114 | private int ttl; 115 | 116 | private List resourceTypes; 117 | 118 | private boolean addComment; 119 | 120 | private String includeTypeName; 121 | 122 | private String requiredHeader; 123 | 124 | private boolean disableIgnoreUrlParams; 125 | 126 | private Collection ignoreUrlParams; 127 | 128 | private boolean rewritePath; 129 | 130 | private boolean appendSuffix; 131 | 132 | @Activate 133 | public void activate(Config cfg) { 134 | isEnabled = cfg.include$_$filter_config_enabled(); 135 | String pathPattern = cfg.include$_$filter_config_path(); 136 | pathMatcher = choosePathMatcher(pathPattern); 137 | String[] resourceTypeList; 138 | resourceTypeList = PropertiesUtil.toStringArray(cfg.include$_$filter_config_resource$_$types(), new String[0]); 139 | for (int i = 0; i < resourceTypeList.length; i++) { 140 | String[] s = resourceTypeList[i].split(";"); 141 | String name = s[0].trim(); 142 | resourceTypeList[i] = name; 143 | } 144 | this.resourceTypes = Arrays.asList(resourceTypeList); 145 | 146 | includeSelector = cfg.include$_$filter_config_selector(); 147 | extension = cfg.include$_$filter_config_extension(); 148 | ttl = PropertiesUtil.toInteger(cfg.include$_$filter_config_ttl(), -1); 149 | addComment = cfg.include$_$filter_config_add__comment(); 150 | includeTypeName = cfg.include$_$filter_config_include$_$type(); 151 | requiredHeader = cfg.include$_$filter_config_required__header(); 152 | ignoreUrlParams = new HashSet<>( 153 | Arrays.asList(PropertiesUtil.toStringArray(cfg.include$_$filter_config_ignoreUrlParams(), new String[0])) 154 | ); 155 | rewritePath = cfg.include$_$filter_config_rewrite(); 156 | appendSuffix = cfg.include$_$filter_config_appendSuffix(); 157 | disableIgnoreUrlParams = cfg.include$_$filter_config_disableIgnoreUrlParams(); 158 | } 159 | 160 | private PathMatcher choosePathMatcher(String pathPattern) { 161 | PathMatcher result; 162 | if (pathPattern.startsWith("^")) { 163 | LOG.debug("Configured path value: {} is a regexp - will use a RegexPathMatcher.", pathPattern); 164 | result = new RegexPathMatcher(pathPattern); 165 | } else { 166 | LOG.debug("Configured path value: {} is NOT a regexp - will use a PrefixPathMatcher.", pathPattern); 167 | result = new PrefixPathMatcher(pathPattern); 168 | } 169 | return result; 170 | } 171 | 172 | public PathMatcher getPathMatcher() { 173 | return pathMatcher; 174 | } 175 | 176 | public boolean hasIncludeSelector(SlingHttpServletRequest request) { 177 | return ArrayUtils.contains(request.getRequestPathInfo().getSelectors(), includeSelector); 178 | } 179 | 180 | public String getIncludeSelector() { 181 | return includeSelector; 182 | } 183 | 184 | public boolean hasExtension(final SlingHttpServletRequest request) { 185 | final String suffix = request.getRequestPathInfo().getSuffix(); 186 | return suffix.endsWith("." + this.extension); 187 | } 188 | 189 | public boolean hasExtensionSet() { 190 | return StringUtils.isNotBlank(this.extension); 191 | } 192 | 193 | public String getExtension() { 194 | return this.extension; 195 | } 196 | 197 | public boolean hasTtlSet() { 198 | return ttl >= 0; 199 | } 200 | 201 | public int getTtl() { 202 | return ttl; 203 | } 204 | 205 | public boolean isSupportedResourceType(String resourceType) { 206 | return StringUtils.isNotBlank(resourceType) && resourceTypes.contains(resourceType); 207 | } 208 | 209 | public boolean getAddComment() { 210 | return addComment; 211 | } 212 | 213 | public String getIncludeTypeName() { 214 | return includeTypeName; 215 | } 216 | 217 | public boolean isEnabled() { 218 | return isEnabled; 219 | } 220 | 221 | public String getRequiredHeader() { 222 | return requiredHeader; 223 | } 224 | 225 | public Collection getIgnoreUrlParams() { 226 | return ignoreUrlParams; 227 | } 228 | 229 | public boolean isDisableIgnoreUrlParams() { 230 | return disableIgnoreUrlParams; 231 | } 232 | 233 | public boolean isRewritePath() { 234 | return rewritePath; 235 | } 236 | 237 | public boolean isAppendSuffix() { 238 | return appendSuffix; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboard.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude; 21 | 22 | import static org.osgi.service.component.annotations.FieldOption.UPDATE; 23 | import static org.osgi.service.component.annotations.ReferenceCardinality.MULTIPLE; 24 | import static org.osgi.service.component.annotations.ReferencePolicy.DYNAMIC; 25 | 26 | import java.util.Collection; 27 | import java.util.concurrent.CopyOnWriteArraySet; 28 | 29 | import org.apache.sling.api.SlingHttpServletRequest; 30 | import org.osgi.service.component.annotations.Component; 31 | import org.osgi.service.component.annotations.Reference; 32 | 33 | @Component(service = ConfigurationWhiteboard.class) 34 | public class ConfigurationWhiteboard { 35 | 36 | @Reference(service = Configuration.class, cardinality = MULTIPLE, policy = DYNAMIC, fieldOption = UPDATE) 37 | // declared Collection due to SLING-8986 38 | private volatile Collection configs = new CopyOnWriteArraySet(); 39 | 40 | public Configuration getConfiguration(SlingHttpServletRequest request, String resourceType) { 41 | for (Configuration c : configs) { 42 | if (isEnabled(c, request) && c.isSupportedResourceType(resourceType)) { 43 | return c; 44 | } 45 | } 46 | return null; 47 | } 48 | 49 | private boolean isEnabled(Configuration config, SlingHttpServletRequest request) { 50 | final String requestPath = request.getRequestPathInfo().getResourcePath(); 51 | return config.isEnabled() && config.getPathMatcher().match(requestPath); 52 | } 53 | 54 | // visible for testing 55 | void bindConfigs(final Configuration config) { 56 | configs.add(config); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/IncludeTagFilter.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude; 21 | 22 | import java.io.IOException; 23 | import java.io.PrintWriter; 24 | import java.net.URI; 25 | import java.net.URISyntaxException; 26 | 27 | import javax.servlet.Filter; 28 | import javax.servlet.FilterChain; 29 | import javax.servlet.FilterConfig; 30 | import javax.servlet.ServletException; 31 | import javax.servlet.ServletRequest; 32 | import javax.servlet.ServletResponse; 33 | 34 | import org.apache.commons.lang3.StringEscapeUtils; 35 | import org.apache.commons.lang3.StringUtils; 36 | import org.apache.sling.api.SlingHttpServletRequest; 37 | import org.apache.sling.api.resource.Resource; 38 | import org.apache.sling.api.resource.ResourceUtil; 39 | import org.apache.sling.dynamicinclude.api.IncludeGenerator; 40 | import org.apache.sling.dynamicinclude.generator.IncludeGeneratorWhiteboard; 41 | import org.apache.sling.dynamicinclude.impl.UrlBuilder; 42 | import org.apache.sling.dynamicinclude.util.RequestHelperUtil; 43 | import org.apache.sling.servlets.annotations.SlingServletFilter; 44 | import org.apache.sling.servlets.annotations.SlingServletFilterScope; 45 | import org.osgi.framework.Constants; 46 | import org.osgi.service.component.annotations.Component; 47 | import org.osgi.service.component.annotations.Reference; 48 | import org.slf4j.Logger; 49 | import org.slf4j.LoggerFactory; 50 | 51 | @SlingServletFilter(scope = SlingServletFilterScope.INCLUDE) 52 | @Component(property = { Constants.SERVICE_RANKING + ":Integer=-500"} ) 53 | public class IncludeTagFilter implements Filter { 54 | 55 | private static final Logger LOG = LoggerFactory.getLogger(IncludeTagFilter.class); 56 | 57 | private static final String COMMENT = "\n"; 58 | 59 | @Reference 60 | private ConfigurationWhiteboard configurationWhiteboard; 61 | 62 | @Reference 63 | private IncludeGeneratorWhiteboard generatorWhiteboard; 64 | 65 | @Override 66 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, 67 | ServletException { 68 | final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request; 69 | final String resourceType = slingRequest.getResource().getResourceType(); 70 | 71 | final Configuration config = configurationWhiteboard.getConfiguration(slingRequest, resourceType); 72 | if (config == null) { 73 | chain.doFilter(request, response); 74 | return; 75 | } 76 | 77 | final IncludeGenerator generator = generatorWhiteboard.getGenerator(config.getIncludeTypeName()); 78 | if (generator == null) { 79 | LOG.error("Invalid generator: " + config.getIncludeTypeName()); 80 | chain.doFilter(request, response); 81 | return; 82 | } 83 | 84 | final PrintWriter writer = response.getWriter(); 85 | final String url = getUrl(config, slingRequest); 86 | if (url == null) { 87 | chain.doFilter(request, response); 88 | return; 89 | } 90 | 91 | if (config.getAddComment()) { 92 | writer.append(String.format(COMMENT, StringEscapeUtils.escapeHtml4(url), resourceType)); 93 | } 94 | 95 | // Only write the includes markup if the required, configurable request 96 | // header is present 97 | if (shouldWriteIncludes(config, slingRequest)) { 98 | String include = generator.getInclude(slingRequest,url); 99 | LOG.debug(include); 100 | writer.append(include); 101 | } else { 102 | chain.doFilter(request, response); 103 | } 104 | } 105 | 106 | private boolean shouldWriteIncludes(Configuration config, SlingHttpServletRequest request) { 107 | // Do not skip GET requests when DisableIgnoreUrlParams set to true. 108 | if (!config.isDisableIgnoreUrlParams() && RequestHelperUtil.requestHasNonIgnoredParameters(config.getIgnoreUrlParams(), request)) { 109 | return false; 110 | } 111 | final String requiredHeader = config.getRequiredHeader(); 112 | return StringUtils.isBlank(requiredHeader) || containsHeader(requiredHeader, request); 113 | } 114 | 115 | private boolean containsHeader(String requiredHeader, SlingHttpServletRequest request) { 116 | final String name, expectedValue; 117 | if (StringUtils.contains(requiredHeader, '=')) { 118 | final String split[] = StringUtils.split(requiredHeader, '='); 119 | name = split[0]; 120 | expectedValue = split[1]; 121 | } else { 122 | name = requiredHeader; 123 | expectedValue = null; 124 | } 125 | 126 | final String actualValue = request.getHeader(name); 127 | if (actualValue == null) { 128 | return false; 129 | } else if (expectedValue == null) { 130 | return true; 131 | } else { 132 | return actualValue.equalsIgnoreCase(expectedValue); 133 | } 134 | } 135 | 136 | private String getUrl(Configuration config, SlingHttpServletRequest request) { 137 | String url = buildUrl(config, request); 138 | if (config.isRewritePath()) { 139 | url = removeQuestionMark(url); 140 | url = request.getResourceResolver().map(request, url); 141 | } else { 142 | url = encodeJcrContentPart(url); 143 | try { 144 | url = new URI(null, null, url, null).toASCIIString(); 145 | } catch (URISyntaxException e) { 146 | LOG.error("Include url is in the wrong format", e); 147 | return null; 148 | } 149 | } 150 | 151 | return url; 152 | } 153 | 154 | private String buildUrl(Configuration config, SlingHttpServletRequest request) { 155 | final Resource resource = request.getResource(); 156 | 157 | final boolean synthetic = ResourceUtil.isSyntheticResource(request.getResource()); 158 | return UrlBuilder.buildUrl(config.getIncludeSelector(), resource.getResourceType(), synthetic, config, request.getRequestPathInfo()); 159 | } 160 | 161 | private static String sanitize(String path) { 162 | return StringUtils.defaultString(path); 163 | } 164 | 165 | private static String encodeJcrContentPart(String url) { 166 | return url.replace("jcr:content", "_jcr_content"); 167 | } 168 | 169 | private static String removeQuestionMark(String url) { 170 | return url.replaceAll("[?]", ""); 171 | } 172 | 173 | @Override 174 | public void init(FilterConfig filterConfig) throws ServletException { 175 | } 176 | 177 | @Override 178 | public void destroy() { 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/SyntheticResourceFilter.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude; 21 | 22 | import java.io.IOException; 23 | 24 | import javax.servlet.Filter; 25 | import javax.servlet.FilterChain; 26 | import javax.servlet.FilterConfig; 27 | import javax.servlet.RequestDispatcher; 28 | import javax.servlet.ServletException; 29 | import javax.servlet.ServletRequest; 30 | import javax.servlet.ServletResponse; 31 | 32 | import org.apache.commons.lang3.StringUtils; 33 | import org.apache.sling.api.SlingHttpServletRequest; 34 | import org.apache.sling.api.request.RequestDispatcherOptions; 35 | import org.apache.sling.api.resource.Resource; 36 | import org.apache.sling.api.resource.ResourceUtil; 37 | import org.apache.sling.servlets.annotations.SlingServletFilter; 38 | import org.apache.sling.servlets.annotations.SlingServletFilterScope; 39 | import org.osgi.framework.Constants; 40 | import org.osgi.service.component.annotations.Component; 41 | import org.osgi.service.component.annotations.Reference; 42 | 43 | @SlingServletFilter(scope = SlingServletFilterScope.REQUEST) 44 | @Component(property = { 45 | Constants.SERVICE_RANKING + ":Integer=" + Integer.MIN_VALUE, 46 | Constants.SERVICE_VENDOR +"=The Apache Software Foundation" 47 | }) 48 | public class SyntheticResourceFilter implements Filter { 49 | 50 | @Reference 51 | private ConfigurationWhiteboard configurationWhiteboard; 52 | 53 | @Override 54 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, 55 | ServletException { 56 | final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request; 57 | final String resourceType = getResourceTypeFromSuffix(slingRequest); 58 | final Configuration config = configurationWhiteboard.getConfiguration(slingRequest, resourceType); 59 | 60 | if (config == null || !config.hasIncludeSelector(slingRequest) 61 | || !ResourceUtil.isSyntheticResource(slingRequest.getResource()) 62 | || (config.hasExtensionSet() && !config.hasExtension(slingRequest))) { 63 | chain.doFilter(request, response); 64 | return; 65 | } 66 | 67 | final RequestDispatcherOptions options = new RequestDispatcherOptions(); 68 | options.setForceResourceType(resourceType); 69 | String resourcePath = StringUtils.substringBefore(slingRequest.getRequestPathInfo().getResourcePath(), "."); 70 | Resource resource = slingRequest.getResourceResolver().resolve(resourcePath); 71 | final RequestDispatcher dispatcher = slingRequest.getRequestDispatcher(resource, options); 72 | dispatcher.forward(request, response); 73 | } 74 | 75 | private static String getResourceTypeFromSuffix(final SlingHttpServletRequest request) { 76 | String suffix = request.getRequestPathInfo().getSuffix(); 77 | suffix = StringUtils.substringBeforeLast(suffix, "."); 78 | return StringUtils.removeStart(suffix, "/"); 79 | } 80 | 81 | @Override 82 | public void init(FilterConfig filterConfig) throws ServletException { 83 | } 84 | 85 | @Override 86 | public void destroy() { 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/api/IncludeGenerator.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.api; 21 | 22 | import org.apache.sling.api.SlingHttpServletRequest; 23 | 24 | /** 25 | * Include generator interface 26 | */ 27 | public interface IncludeGenerator { 28 | String getType(); 29 | 30 | /** 31 | * Returns the string used to include the resource. 32 | * For example, this might be a Javascript code that retrieves a snippet of html, 33 | * an Apache SSI Tag, or an Edge Side Include Tag. 34 | *

35 | * This method receives the sling request and an Url that has already be normalized as followed: 36 | *

    37 | *
  • The query string has been removed
  • 38 | *
  • The Url has been mapped using the ResourceResolver
  • 39 | *
  • The jcr:content paths have been encoded to _jcr_content.
  • 40 | *
41 | * 42 | * @param request the Sling request object 43 | * @param normalizedUrl the requested url, normalized 44 | * @return a String used to include the resource 45 | **/ 46 | String getInclude(SlingHttpServletRequest request, String normalizedUrl); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/api/package-info.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | @Version("1.0.0") 21 | package org.apache.sling.dynamicinclude.api; 22 | 23 | import org.osgi.annotation.versioning.Version; -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/generator/IncludeGeneratorWhiteboard.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.generator; 21 | 22 | import static org.osgi.service.component.annotations.FieldOption.UPDATE; 23 | import static org.osgi.service.component.annotations.ReferenceCardinality.MULTIPLE; 24 | import static org.osgi.service.component.annotations.ReferencePolicy.DYNAMIC; 25 | 26 | import java.util.Set; 27 | import java.util.concurrent.CopyOnWriteArraySet; 28 | 29 | import org.apache.sling.dynamicinclude.api.IncludeGenerator; 30 | import org.osgi.service.component.annotations.Component; 31 | import org.osgi.service.component.annotations.Reference; 32 | 33 | /** 34 | * Service that provides include generator of given type. 35 | */ 36 | 37 | @Component(service = IncludeGeneratorWhiteboard.class) 38 | public class IncludeGeneratorWhiteboard { 39 | 40 | @Reference(service = IncludeGenerator.class, cardinality = MULTIPLE, policy = DYNAMIC, fieldOption = UPDATE) 41 | private Set generators = new CopyOnWriteArraySet(); 42 | 43 | public IncludeGenerator getGenerator(String type) { 44 | for (IncludeGenerator generator : generators) { 45 | if (type.equals(generator.getType())) { 46 | return generator; 47 | } 48 | } 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/generator/types/EsiGenerator.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.generator.types; 21 | 22 | import org.apache.commons.lang3.StringEscapeUtils; 23 | import org.apache.sling.api.SlingHttpServletRequest; 24 | import org.apache.sling.dynamicinclude.api.IncludeGenerator; 25 | import org.osgi.service.component.annotations.Component; 26 | 27 | /** 28 | * ESI include generator 29 | */ 30 | @Component 31 | public class EsiGenerator implements IncludeGenerator { 32 | private static final String GENERATOR_NAME = "ESI"; 33 | 34 | @Override 35 | public String getType() { 36 | return GENERATOR_NAME; 37 | } 38 | 39 | @Override 40 | public String getInclude(SlingHttpServletRequest request, String url) { 41 | StringBuffer buf = new StringBuffer(); 42 | buf.append(""); 45 | return buf.toString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/generator/types/JsiGenerator.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.generator.types; 21 | 22 | import java.io.BufferedReader; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.io.InputStreamReader; 26 | import java.io.UnsupportedEncodingException; 27 | import java.net.URL; 28 | 29 | import org.apache.commons.lang3.StringEscapeUtils; 30 | import org.apache.sling.api.SlingHttpServletRequest; 31 | import org.apache.sling.dynamicinclude.api.IncludeGenerator; 32 | import org.osgi.service.component.ComponentContext; 33 | import org.osgi.service.component.annotations.Activate; 34 | import org.osgi.service.component.annotations.Component; 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | 38 | /** 39 | * Client side include generator - using Ajax/JQuery. 40 | */ 41 | @Component 42 | public class JsiGenerator implements IncludeGenerator { 43 | private static final String TEMPLATE_FILENAME = "generators/javascript.html"; 44 | 45 | private static final String UUID_FIELD = "${uniqueId}"; 46 | 47 | private static final String URL_FIELD = "${url}"; 48 | 49 | private static final Logger LOG = LoggerFactory.getLogger(JsiGenerator.class); 50 | 51 | private static final String GENERATOR_NAME = "JSI"; 52 | 53 | private volatile int divId = 1000; 54 | 55 | private String template; 56 | 57 | @Activate 58 | public void activate(ComponentContext ctx) { 59 | URL url = ctx.getBundleContext().getBundle().getResource(TEMPLATE_FILENAME); 60 | if (url == null) { 61 | LOG.error("File " + TEMPLATE_FILENAME + " not found in bundle."); 62 | return; 63 | } 64 | readTemplateFromUrl(url); 65 | } 66 | 67 | @Override 68 | public String getType() { 69 | return GENERATOR_NAME; 70 | } 71 | 72 | @Override 73 | public String getInclude(SlingHttpServletRequest request, String url) { 74 | if (template == null) { 75 | throw new IllegalStateException("JSI generator hasn't be initialized"); 76 | } 77 | 78 | String divName; 79 | synchronized (this) { 80 | divName = "dynamic_include_filter_div_" + divId++; 81 | } 82 | 83 | return template.replace(UUID_FIELD, divName).replace(URL_FIELD, StringEscapeUtils.escapeEcmaScript(url)); 84 | } 85 | 86 | private void readTemplateFromUrl(URL url) { 87 | BufferedReader br = null; 88 | try { 89 | InputStream in = url.openStream(); 90 | br = new BufferedReader(new InputStreamReader(in, "UTF-8")); 91 | StringBuilder builder = new StringBuilder(); 92 | String line; 93 | while ((line = br.readLine()) != null) { 94 | builder.append(line).append('\n'); 95 | } 96 | template = builder.toString(); 97 | } catch (UnsupportedEncodingException e) { 98 | LOG.error("Error while reading template", e); 99 | } catch (IOException e) { 100 | LOG.error("Error while reading template", e); 101 | } finally { 102 | try { 103 | if (br != null) { 104 | br.close(); 105 | } 106 | } catch (Exception e) { 107 | LOG.error("Error while closing reader", e); 108 | } 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/generator/types/SsiGenerator.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.generator.types; 21 | 22 | import org.apache.sling.api.SlingHttpServletRequest; 23 | import org.apache.sling.dynamicinclude.api.IncludeGenerator; 24 | import org.osgi.service.component.annotations.Component; 25 | 26 | /** 27 | * Apache SSI include generator 28 | */ 29 | @Component 30 | public class SsiGenerator implements IncludeGenerator { 31 | private static final String GENERATOR_NAME = "SSI"; 32 | 33 | @Override 34 | public String getType() { 35 | return GENERATOR_NAME; 36 | } 37 | 38 | @Override 39 | public String getInclude(SlingHttpServletRequest request, String url) { 40 | StringBuffer buf = new StringBuffer(); 41 | buf.append(""); 44 | return buf.toString(); 45 | } 46 | 47 | /** 48 | * Escapes $ to \$ 49 | * 50 | * @param url 51 | * url to escape 52 | * @return escaped url 53 | */ 54 | private Object escapeForApache(String url) { 55 | return url.replace("$", "\\$"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/impl/UrlBuilder.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.impl; 21 | 22 | import java.util.Arrays; 23 | 24 | import org.apache.commons.lang3.StringUtils; 25 | import org.apache.sling.api.request.RequestPathInfo; 26 | import org.apache.sling.dynamicinclude.Configuration; 27 | 28 | public final class UrlBuilder { 29 | 30 | 31 | public static String buildUrl(String includeSelector, String resourceType, boolean synthetic, Configuration config, RequestPathInfo pathInfo) { 32 | final StringBuilder builder = new StringBuilder(); 33 | 34 | final String resourcePath = pathInfo.getResourcePath(); 35 | builder.append(resourcePath); 36 | String currentSelectorString = StringUtils.defaultString(pathInfo.getSelectorString()); 37 | if (pathInfo.getSelectorString() != null) { 38 | builder.append('.').append(currentSelectorString); 39 | } 40 | if (includeSelectorNotAlreadyPresent(pathInfo.getSelectors(), includeSelector)) { 41 | builder.append('.').append(includeSelector); 42 | } 43 | builder.append('.').append(pathInfo.getExtension()); 44 | if (synthetic) { 45 | builder.append('/').append(resourceType); 46 | if (config.hasExtensionSet()) { 47 | builder.append('.').append(config.getExtension()); 48 | } 49 | } else { 50 | if (config.isAppendSuffix()) { 51 | builder.append(StringUtils.defaultString(pathInfo.getSuffix())); 52 | } 53 | } 54 | return builder.toString(); 55 | } 56 | 57 | private static boolean includeSelectorNotAlreadyPresent(String[] currentSelectors, String includeSelector) { 58 | if (includeSelector.isEmpty()) { 59 | return false; 60 | } 61 | return !Arrays.asList(currentSelectors).contains(includeSelector); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/pathmatcher/PathMatcher.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sling.dynamicinclude.pathmatcher; 20 | 21 | public interface PathMatcher { 22 | 23 | /** 24 | * Matches given path with the configured path parameter 25 | * @param path path to match 26 | * @return true if path matches, false otherwise 27 | */ 28 | boolean match(String path); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/pathmatcher/PrefixPathMatcher.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sling.dynamicinclude.pathmatcher; 20 | 21 | import org.apache.commons.lang3.StringUtils; 22 | 23 | public class PrefixPathMatcher implements PathMatcher { 24 | 25 | private final String configurationValue; 26 | 27 | public PrefixPathMatcher(String configurationValue) { 28 | this.configurationValue = configurationValue; 29 | } 30 | 31 | @Override 32 | public boolean match(String path) { 33 | return StringUtils.isNotBlank(path) && path.startsWith(configurationValue); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/pathmatcher/RegexPathMatcher.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sling.dynamicinclude.pathmatcher; 20 | 21 | import java.util.regex.Pattern; 22 | import org.apache.commons.lang3.StringUtils; 23 | 24 | public class RegexPathMatcher implements PathMatcher { 25 | 26 | private final Pattern configurationPattern; 27 | 28 | public RegexPathMatcher(String configurationRegex) { 29 | this.configurationPattern = Pattern.compile(configurationRegex); 30 | } 31 | 32 | @Override 33 | public boolean match(String path) { 34 | return StringUtils.isNotBlank(path) && configurationPattern.matcher(path).matches(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/apache/sling/dynamicinclude/util/RequestHelperUtil.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.util; 21 | 22 | import org.apache.sling.api.SlingHttpServletRequest; 23 | 24 | import java.util.Collection; 25 | 26 | public class RequestHelperUtil { 27 | 28 | private RequestHelperUtil() { 29 | // private constructor to prevent instance creation of util classes 30 | } 31 | 32 | /** 33 | * Checks if a request contains any parameters that are not defined in the ignoreUrlParams. 34 | * Wildcards as they are possible to configure on Apache-Dispatcher, are also properly checked. 35 | * 36 | * @param ignoreUrlParams The list of configured ignoreUrlParams (regular expressions) 37 | * @param request The slingRequest whose parameters we want to check 38 | * @return true if there was any parameter that is not defined on the ignoreUrlsParams-Collection, otherwise false 39 | */ 40 | public static boolean requestHasNonIgnoredParameters(Collection ignoreUrlParams, SlingHttpServletRequest request) { 41 | return request.getParameterMap().keySet().stream() 42 | .anyMatch(urlParameter -> !matchesRegularExpressionIgnoreUrlParameter(ignoreUrlParams, urlParameter)); 43 | } 44 | 45 | private static boolean matchesRegularExpressionIgnoreUrlParameter(Collection ignoreUrlParameters, String requestParameter) { 46 | for (String ignoreUrlParameter : ignoreUrlParameters) { 47 | if (requestParameter.matches(ignoreUrlParameter)) { 48 | return true; 49 | } 50 | } 51 | 52 | return false; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/resources/generators/javascript.html: -------------------------------------------------------------------------------- 1 | 19 |
20 | 37 | 38 |
39 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sling/dynamicinclude/ConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude; 21 | 22 | import static org.hamcrest.CoreMatchers.is; 23 | import static org.hamcrest.MatcherAssert.assertThat; 24 | 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | import org.apache.sling.dynamicinclude.pathmatcher.PrefixPathMatcher; 28 | import org.apache.sling.testing.mock.osgi.junit.OsgiContext; 29 | import org.junit.Before; 30 | import org.junit.Rule; 31 | import org.junit.Test; 32 | 33 | public class ConfigurationTest { 34 | 35 | private Configuration tested; 36 | 37 | @Rule 38 | public final OsgiContext context = new OsgiContext(); 39 | 40 | @Before 41 | public void setUp() { 42 | tested = new Configuration(); 43 | } 44 | 45 | @Test(expected = RuntimeException.class) 46 | public void shouldThrowExceptionWhenRegexisInvalid() throws Exception { 47 | 48 | context.registerInjectActivateService(tested, "include-filter.config.path", "^("); 49 | } 50 | 51 | @Test 52 | public void shouldSetDefaultValuesWhenPropertiesAreEmpty() throws Exception { 53 | Map properties = new HashMap(); 54 | 55 | context.registerInjectActivateService(tested, properties); 56 | 57 | assertThat(tested.getPathMatcher().getClass().isAssignableFrom(PrefixPathMatcher.class), is(true)); 58 | assertThat(tested.getAddComment(), is(false)); 59 | assertThat(tested.getIgnoreUrlParams().size(), is(0)); 60 | assertThat(tested.getIncludeSelector(), is("nocache")); 61 | assertThat(tested.getIncludeTypeName(), is("SSI")); 62 | assertThat(tested.getRequiredHeader(), is("Server-Agent=Communique-Dispatcher")); 63 | assertThat(tested.getTtl(), is(-1)); 64 | assertThat(tested.isEnabled(), is(false)); 65 | assertThat(tested.hasTtlSet(), is(false)); 66 | assertThat(tested.isRewritePath(), is(false)); 67 | } 68 | 69 | @Test 70 | public void shouldSetConfigurationValues() throws Exception { 71 | Map properties = new HashMap(); 72 | properties.put("include-filter.config.path", "/content/test/path"); 73 | properties.put("include-filter.config.include-type", "ESI"); 74 | properties.put("include-filter.config.add_comment", true); 75 | properties.put("include-filter.config.ttl", 60); 76 | properties.put("include-filter.config.enabled", true); 77 | properties.put("include-filter.config.resource-types", 78 | new String[] { "test/resource/type", "test/resource/type2" }); 79 | properties.put("include-filter.config.required_header", "CustomHeader: value"); 80 | properties.put("include-filter.config.selector", "cache"); 81 | properties.put("include-filter.config.rewrite", true); 82 | properties.put("include-filter.config.ignoreUrlParams", new String[] { "query", "query2" }); 83 | 84 | context.registerInjectActivateService(tested, properties); 85 | 86 | assertThat(tested.getPathMatcher().getClass().isAssignableFrom(PrefixPathMatcher.class), is(true)); 87 | assertThat(tested.getAddComment(), is(true)); 88 | assertThat(tested.getIgnoreUrlParams().size(), is(2)); 89 | assertThat(tested.getIncludeSelector(), is("cache")); 90 | assertThat(tested.getIncludeTypeName(), is("ESI")); 91 | assertThat(tested.getRequiredHeader(), is("CustomHeader: value")); 92 | assertThat(tested.getTtl(), is(60)); 93 | assertThat(tested.isEnabled(), is(true)); 94 | assertThat(tested.hasTtlSet(), is(true)); 95 | assertThat(tested.isRewritePath(), is(true)); 96 | assertThat(tested.isSupportedResourceType("test/resource/type"), is(true)); 97 | assertThat(tested.isSupportedResourceType("test/resource/type2"), is(true)); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sling/dynamicinclude/ConfigurationWhiteboardTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.apache.sling.dynamicinclude; 20 | 21 | import static org.hamcrest.CoreMatchers.is; 22 | import static org.hamcrest.CoreMatchers.nullValue; 23 | import static org.hamcrest.MatcherAssert.assertThat; 24 | import static org.mockito.Mockito.when; 25 | 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | import org.apache.sling.api.SlingHttpServletRequest; 30 | import org.apache.sling.api.request.RequestPathInfo; 31 | import org.apache.sling.testing.mock.osgi.junit.OsgiContext; 32 | import org.junit.Before; 33 | import org.junit.Rule; 34 | import org.junit.Test; 35 | import org.junit.runner.RunWith; 36 | import org.mockito.Mock; 37 | import org.mockito.junit.MockitoJUnitRunner; 38 | 39 | @RunWith(MockitoJUnitRunner.class) 40 | public class ConfigurationWhiteboardTest { 41 | 42 | private static final String TEST_RESOURCE_PATH = "/content/test/engl/home/pageresource"; 43 | public static final String TEST_RESOURCE_TYPE = "test/component/resourceType"; 44 | 45 | @Rule 46 | public final OsgiContext context = new OsgiContext(); 47 | 48 | private ConfigurationWhiteboard tested; 49 | 50 | @Mock 51 | private SlingHttpServletRequest request; 52 | 53 | @Mock 54 | private RequestPathInfo requestPathInfo; 55 | 56 | @Before 57 | public void setUp() throws Exception { 58 | tested = new ConfigurationWhiteboard(); 59 | when(request.getRequestPathInfo()).thenReturn(requestPathInfo); 60 | when(requestPathInfo.getResourcePath()).thenReturn(TEST_RESOURCE_PATH); 61 | } 62 | 63 | private Configuration buildConfiguration(boolean enabled, String pathRegex, String[] resourceTypes) { 64 | Configuration configuration = new Configuration(); 65 | Map properties = new HashMap(); 66 | properties.put("include-filter.config.enabled", enabled); 67 | properties.put("include-filter.config.path", pathRegex); 68 | properties.put("include-filter.config.resource-types", resourceTypes); 69 | 70 | return context.registerInjectActivateService(configuration, properties); 71 | } 72 | 73 | @Test 74 | public void shouldNotReturnConfigsIfNotConfigsHaveBeenBound() throws Exception { 75 | assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(nullValue())); 76 | } 77 | 78 | @Test 79 | public void shouldNotReturnConfigurationIfResourceTypeDoesNotMatch() throws Exception { 80 | buildConfiguration(true, "^/content.*$", new String[]{"invalid/resourceType"}); 81 | context.registerInjectActivateService(tested); 82 | 83 | assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(nullValue())); 84 | } 85 | 86 | @Test 87 | public void shouldNotReturnConfigurationIfConfigurationIsDisabled() throws Exception { 88 | buildConfiguration(false, "^/content.*$", new String[]{TEST_RESOURCE_TYPE}); 89 | context.registerInjectActivateService(tested); 90 | 91 | assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(nullValue())); 92 | } 93 | 94 | @Test 95 | public void shouldNotReturnConfigurationIfPathDoesNotMatchRegex() throws Exception { 96 | buildConfiguration(true, "^/content/notMatched/.*$", 97 | new String[]{TEST_RESOURCE_TYPE}); 98 | context.registerInjectActivateService(tested); 99 | 100 | assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(nullValue())); 101 | } 102 | 103 | @Test 104 | public void shouldReturnValidConfiguration() throws Exception { 105 | Configuration testConfiguration = buildConfiguration(true, "^/content.*$", new String[]{TEST_RESOURCE_TYPE}); 106 | context.registerInjectActivateService(tested); 107 | 108 | assertThat(tested.getConfiguration(request, TEST_RESOURCE_TYPE), is(testConfiguration)); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sling/dynamicinclude/impl/UrlBuilderTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.impl; 21 | 22 | import org.apache.commons.lang3.StringUtils; 23 | import org.apache.sling.api.request.RequestPathInfo; 24 | import org.apache.sling.dynamicinclude.Configuration; 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | import org.junit.runner.RunWith; 28 | import org.mockito.Mock; 29 | import org.mockito.junit.MockitoJUnitRunner; 30 | 31 | import static org.hamcrest.CoreMatchers.is; 32 | import static org.hamcrest.MatcherAssert.assertThat; 33 | import static org.mockito.Mockito.*; 34 | 35 | @RunWith(MockitoJUnitRunner.class) 36 | public class UrlBuilderTest { 37 | 38 | @Mock 39 | private RequestPathInfo requestPathInfo; 40 | 41 | @Mock 42 | private Configuration config; 43 | 44 | @Test 45 | public void shouldAppendTheIncludeSelectorToUrlWithNoSelectors() { 46 | givenAnHtmlRequestForResource("/resource/path"); 47 | withSelectorString(null); 48 | boolean isSyntheticResource = false; 49 | 50 | String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 51 | 52 | assertThat(actualResult, is("/resource/path.include.html")); 53 | } 54 | 55 | @Test 56 | public void shouldNotAppendTheIncludeSelectorToUrlWhenNotSetAndAppendRequestPathInfoSelectorWhenNotSet() { 57 | givenAnHtmlRequestForResource("/resource/path"); 58 | withSelectorString(null); 59 | boolean isSyntheticResource = false; 60 | 61 | String actualResult = UrlBuilder.buildUrl("", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 62 | 63 | assertThat(actualResult, is("/resource/path.html")); 64 | } 65 | 66 | @Test 67 | public void shouldNotAppendTheIncludeSelectorToUrlWhenNotSetAndAppendRequestPathInfoSelectorWhenSet() { 68 | givenAnHtmlRequestForResource("/resource/path"); 69 | withSelectorString("foo.bar.baz"); 70 | boolean isSyntheticResource = false; 71 | 72 | String actualResult = UrlBuilder.buildUrl("", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 73 | 74 | assertThat(actualResult, is("/resource/path.foo.bar.baz.html")); 75 | } 76 | 77 | @Test 78 | public void shouldAppendTheIncludeSelectorToUrlWhenSetAndNotAppendRequestPathInfoSelectorWhenNotSet() { 79 | givenAnHtmlRequestForResource("/resource/path"); 80 | withSelectorString(null); 81 | boolean isSyntheticResource = false; 82 | 83 | String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 84 | 85 | assertThat(actualResult, is("/resource/path.include.html")); 86 | } 87 | 88 | @Test 89 | public void shouldAppendTheIncludeSelectorToUrlThatAlreadyContainsOtherSelectors() { 90 | givenAnHtmlRequestForResource("/resource/path"); 91 | withSelectorString("foo.bar.baz"); 92 | boolean isSyntheticResource = false; 93 | 94 | String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 95 | 96 | assertThat(actualResult, is("/resource/path.foo.bar.baz.include.html")); 97 | } 98 | 99 | @Test 100 | public void shouldAppendTheIncludeSelectorToUrlContainingMixedAlphanumericSelectors() { 101 | givenAnHtmlRequestForResource("/resource/path"); 102 | withSelectorString("foo.2.31"); 103 | boolean isSyntheticResource = false; 104 | 105 | String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 106 | 107 | assertThat(actualResult, is("/resource/path.foo.2.31.include.html")); 108 | } 109 | 110 | @Test 111 | public void shouldNotDuplicateTheIncludeSelectorIfAlreadyPresent() { 112 | givenAnHtmlRequestForResource("/resource/path"); 113 | withSelectorString("foo.include"); 114 | boolean isSyntheticResource = false; 115 | 116 | String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 117 | 118 | assertThat(actualResult, is("/resource/path.foo.include.html")); 119 | } 120 | 121 | @Test 122 | public void shouldAppendTheIncludeSelectorWhenThereIsAnotherSelectorThatAccidentallyContainsTheIncludeOne() { 123 | givenAnHtmlRequestForResource("/resource/path"); 124 | withSelectorString("longerSelectorThatHappensToContainTheIncludeSelector"); 125 | boolean isSyntheticResource = false; 126 | 127 | String actualResult = UrlBuilder.buildUrl("IncludeSelector", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 128 | 129 | assertThat(actualResult, is("/resource/path.longerSelectorThatHappensToContainTheIncludeSelector.IncludeSelector.html")); 130 | } 131 | 132 | @Test 133 | public void shouldAppendSuffixForSyntheticResources() { 134 | givenAnHtmlRequestForResource("/resource/path"); 135 | withSelectorString("foo.include"); 136 | boolean isSyntheticResource = true; 137 | 138 | String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 139 | 140 | assertThat(actualResult, is("/resource/path.foo.include.html/apps/example/resource/type")); 141 | } 142 | 143 | @Test 144 | public void shouldAppendSuffixWhenRequestedByDefault() { 145 | givenAnHtmlRequestForResource("/resource/path"); 146 | withSelectorString("foo.include"); 147 | withSuffixString("/suffix/to/some/other/information"); 148 | boolean isSyntheticResource = false; 149 | 150 | when(config.isAppendSuffix()).thenReturn(true); 151 | 152 | String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 153 | 154 | assertThat(actualResult, is("/resource/path.foo.include.html/suffix/to/some/other/information")); 155 | } 156 | 157 | @Test 158 | public void shouldNotAppendSuffixWhenConfigured() { 159 | givenAnHtmlRequestForResource("/resource/path"); 160 | withSelectorString("foo.include"); 161 | withSuffixString("/suffix/to/some/other/information"); 162 | boolean isSyntheticResource = false; 163 | 164 | when(config.isAppendSuffix()).thenReturn(false); 165 | 166 | String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 167 | 168 | verify(requestPathInfo,times(0)).getSuffix(); 169 | assertThat(actualResult, is("/resource/path.foo.include.html")); 170 | } 171 | 172 | @Test 173 | public void shouldAppendExtensionForSyntheticResources() { 174 | givenAnHtmlRequestForResource("/resource/path"); 175 | withSelectorString("foo.include"); 176 | 177 | when(config.hasExtensionSet()).thenReturn(true); 178 | when(config.getExtension()).thenReturn("sdi"); 179 | 180 | boolean isSyntheticResource = true; 181 | 182 | String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo); 183 | 184 | assertThat(actualResult, is("/resource/path.foo.include.html/apps/example/resource/type.sdi")); 185 | } 186 | 187 | private void givenAnHtmlRequestForResource(String resourcePath) { 188 | when(requestPathInfo.getExtension()).thenReturn("html"); 189 | when(requestPathInfo.getResourcePath()).thenReturn(resourcePath); 190 | } 191 | 192 | private void withSelectorString(String selectorString) { 193 | when(requestPathInfo.getSelectorString()).thenReturn(selectorString); 194 | when(requestPathInfo.getSelectors()).thenReturn(StringUtils.defaultString(selectorString).split("\\.")); 195 | } 196 | 197 | private void withSuffixString(String suffixString) { 198 | when(requestPathInfo.getSuffix()).thenReturn(suffixString); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sling/dynamicinclude/pathmatcher/PrefixPathMatcherTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.pathmatcher; 21 | 22 | import static org.junit.Assert.assertFalse; 23 | import static org.junit.Assert.assertTrue; 24 | import org.junit.Test; 25 | 26 | public class PrefixPathMatcherTest { 27 | private final PrefixPathMatcher ppm = new PrefixPathMatcher("/foo"); 28 | 29 | @Test 30 | public void testMatch() { 31 | assertTrue(ppm.match("/foo")); 32 | assertTrue(ppm.match("/foobar")); 33 | } 34 | 35 | @Test 36 | public void testNoMatch() { 37 | assertFalse(ppm.match("/barbar")); 38 | assertFalse(ppm.match("/bar/foo")); 39 | assertFalse(ppm.match("")); 40 | assertFalse(ppm.match(null)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sling/dynamicinclude/pathmatcher/RegexPathMatcherTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.pathmatcher; 21 | 22 | import static org.junit.Assert.assertFalse; 23 | import static org.junit.Assert.assertTrue; 24 | import org.junit.Test; 25 | 26 | public class RegexPathMatcherTest { 27 | private final RegexPathMatcher rpm = new RegexPathMatcher("^/[fg]oo"); 28 | 29 | @Test 30 | public void testMatch() { 31 | assertTrue(rpm.match("/foo")); 32 | assertTrue(rpm.match("/goo")); 33 | } 34 | 35 | @Test 36 | public void testNoMatch() { 37 | assertFalse(rpm.match("/foobar")); 38 | assertFalse(rpm.match("")); 39 | assertFalse(rpm.match(null)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/apache/sling/dynamicinclude/util/RequestHelperUtilTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package org.apache.sling.dynamicinclude.util; 21 | 22 | import org.apache.sling.api.SlingHttpServletRequest; 23 | import org.junit.Assert; 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.mockito.Mockito; 28 | import org.mockito.junit.MockitoJUnitRunner; 29 | 30 | import java.util.ArrayList; 31 | import java.util.Collection; 32 | import java.util.HashMap; 33 | import java.util.Map; 34 | 35 | @RunWith(MockitoJUnitRunner.class) 36 | public class RequestHelperUtilTest { 37 | 38 | private static final String TEST_PARAM_NAME = "test-param"; 39 | private static final String IGNORE_PARAM_REGEX_STAR_WILDCARD = "test-(.*)"; 40 | 41 | private SlingHttpServletRequest slingHttpServletRequest; 42 | private Collection ignoreUrlParams; 43 | private Map parameterMap; 44 | 45 | @Before 46 | public void prepareTestData() { 47 | slingHttpServletRequest = Mockito.mock(SlingHttpServletRequest.class); 48 | 49 | ignoreUrlParams = new ArrayList<>(); 50 | parameterMap = new HashMap<>(); 51 | } 52 | 53 | @Test 54 | public void requestHasParameters_noParametersAtAll() { 55 | Mockito.when(slingHttpServletRequest.getParameterMap()).thenReturn(parameterMap); 56 | 57 | Assert.assertFalse(RequestHelperUtil.requestHasNonIgnoredParameters(ignoreUrlParams, slingHttpServletRequest)); 58 | } 59 | 60 | @Test 61 | public void requestHasParameters_onlyIgnoredParameters_withoutWildcards() { 62 | ignoreUrlParams.add(TEST_PARAM_NAME); 63 | 64 | parameterMap.put(TEST_PARAM_NAME, new String[] {}); 65 | 66 | Mockito.when(slingHttpServletRequest.getParameterMap()).thenReturn(parameterMap); 67 | 68 | Assert.assertFalse(RequestHelperUtil.requestHasNonIgnoredParameters(ignoreUrlParams, slingHttpServletRequest)); 69 | } 70 | 71 | @Test 72 | public void requestHasParameters_onlyIgnoredParameters_withWildcards() { 73 | ignoreUrlParams.add(IGNORE_PARAM_REGEX_STAR_WILDCARD); 74 | ignoreUrlParams.add("hello"); 75 | 76 | parameterMap.put(TEST_PARAM_NAME, new String[] {}); 77 | parameterMap.put("hello", new String[] {}); 78 | 79 | Mockito.when(slingHttpServletRequest.getParameterMap()).thenReturn(parameterMap); 80 | 81 | Assert.assertFalse(RequestHelperUtil.requestHasNonIgnoredParameters(ignoreUrlParams, slingHttpServletRequest)); 82 | } 83 | 84 | @Test 85 | public void requestHasParameters_hasParametersThatAreNotIgnored_withoutWildcards() { 86 | ignoreUrlParams.add(TEST_PARAM_NAME); 87 | 88 | parameterMap.put(TEST_PARAM_NAME, new String[] {}); 89 | parameterMap.put("some-other-param", new String[] {}); 90 | 91 | Mockito.when(slingHttpServletRequest.getParameterMap()).thenReturn(parameterMap); 92 | 93 | Assert.assertTrue(RequestHelperUtil.requestHasNonIgnoredParameters(ignoreUrlParams, slingHttpServletRequest)); 94 | } 95 | 96 | @Test 97 | public void requestHasParameters_hasParametersThatAreNotIgnored_withWildcards() { 98 | ignoreUrlParams.add(IGNORE_PARAM_REGEX_STAR_WILDCARD); 99 | 100 | parameterMap.put(TEST_PARAM_NAME, new String[] {}); 101 | parameterMap.put("some-other-param", new String[] {}); 102 | 103 | Mockito.when(slingHttpServletRequest.getParameterMap()).thenReturn(parameterMap); 104 | 105 | Assert.assertTrue(RequestHelperUtil.requestHasNonIgnoredParameters(ignoreUrlParams, slingHttpServletRequest)); 106 | } 107 | 108 | @Test 109 | public void requestHasParameters_specificRegex_hasIgnoredParameters() { 110 | ignoreUrlParams.add("hello-[0-9]-world"); 111 | 112 | parameterMap.put(TEST_PARAM_NAME, new String[] {}); 113 | parameterMap.put("hello-1-world", new String[] {}); 114 | parameterMap.put("hello-2-world", new String[] {}); 115 | 116 | Mockito.when(slingHttpServletRequest.getParameterMap()).thenReturn(parameterMap); 117 | 118 | Assert.assertTrue(RequestHelperUtil.requestHasNonIgnoredParameters(ignoreUrlParams, slingHttpServletRequest)); 119 | } 120 | 121 | @Test 122 | public void requestHasParameters_specificRegex_hasNoIgnoredParameters() { 123 | ignoreUrlParams.add("hello-[0-9]-world"); 124 | 125 | parameterMap.put("hello-1-world", new String[] {}); 126 | parameterMap.put("hello-2-world", new String[] {}); 127 | parameterMap.put("hello-3-world", new String[] {}); 128 | 129 | Mockito.when(slingHttpServletRequest.getParameterMap()).thenReturn(parameterMap); 130 | 131 | Assert.assertFalse(RequestHelperUtil.requestHasNonIgnoredParameters(ignoreUrlParams, slingHttpServletRequest)); 132 | } 133 | 134 | } 135 | --------------------------------------------------------------------------------