SLING-7785: made virtual resources cacheable
diff --git a/src/main/java/org/apache/sling/dynamicinclude/CacheControlFilter.java b/src/main/java/org/apache/sling/dynamicinclude/CacheControlFilter.java
index a4883ca..984d0c8 100644
--- a/src/main/java/org/apache/sling/dynamicinclude/CacheControlFilter.java
+++ b/src/main/java/org/apache/sling/dynamicinclude/CacheControlFilter.java
@@ -39,6 +39,8 @@
@SlingFilter(scope = SlingFilterScope.REQUEST, order = 0)
public class CacheControlFilter implements Filter {
+ private static final String HEADER_DATE = "Date";
+
private static final String HEADER_CACHE_CONTROL = "Cache-Control";
private static final Logger LOG = LoggerFactory.getLogger(CacheControlFilter.class);
@@ -57,6 +59,9 @@
SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) response;
slingResponse.setHeader(HEADER_CACHE_CONTROL, "max-age=" + config.getTtl());
LOG.debug("set \"{}: max-age={}\" to {}", HEADER_CACHE_CONTROL, config.getTtl(), resourceType);
+ if (!slingResponse.containsHeader(HEADER_DATE)) {
+ slingResponse.setDateHeader(HEADER_DATE, System.currentTimeMillis());
+ }
}
chain.doFilter(request, response);
diff --git a/src/main/java/org/apache/sling/dynamicinclude/Configuration.java b/src/main/java/org/apache/sling/dynamicinclude/Configuration.java
index 9cb6695..027dae6 100755
--- a/src/main/java/org/apache/sling/dynamicinclude/Configuration.java
+++ b/src/main/java/org/apache/sling/dynamicinclude/Configuration.java
@@ -57,6 +57,7 @@
@PropertyOption(name = "JSI", value = "Javascript")}),
@Property(name = Configuration.PROPERTY_ADD_COMMENT, boolValue = Configuration.DEFAULT_ADD_COMMENT, label = "Add comment", description = "Add comment to included components"),
@Property(name = Configuration.PROPERTY_FILTER_SELECTOR, value = Configuration.DEFAULT_FILTER_SELECTOR, label = "Filter selector", description = "Selector used to mark included resources"),
+ @Property(name = Configuration.PROPERTY_EXTENSION, value = Configuration.DEFAULT_EXTENSION, label = "Extension", description = "Extension to append to virtual resources to make caching possible"),
@Property(name = Configuration.PROPERTY_COMPONENT_TTL, label = "Component TTL", description = "\"Time to live\" cache header for rendered component (in seconds)"),
@Property(name = Configuration.PROPERTY_REQUIRED_HEADER, value = Configuration.DEFAULT_REQUIRED_HEADER, label = "Required header", description = "SDI will work only for requests with given header"),
@Property(name = Configuration.PROPERTY_IGNORE_URL_PARAMS, cardinality = Integer.MAX_VALUE, label = "Ignore URL params", description = "SDI will process the request even if it contains configured GET parameters"),
@@ -80,6 +81,10 @@
static final String DEFAULT_FILTER_SELECTOR = "nocache";
+ static final String PROPERTY_EXTENSION = "include-filter.config.extension";
+
+ static final String DEFAULT_EXTENSION = "";
+
static final String PROPERTY_COMPONENT_TTL = "include-filter.config.ttl";
static final String PROPERTY_INCLUDE_TYPE = "include-filter.config.include-type";
@@ -111,6 +116,8 @@
private String includeSelector;
+ private String extension;
+
private int ttl;
private List<String> resourceTypes;
@@ -140,6 +147,7 @@
this.resourceTypes = Arrays.asList(resourceTypeList);
includeSelector = PropertiesUtil.toString(properties.get(PROPERTY_FILTER_SELECTOR), DEFAULT_FILTER_SELECTOR);
+ extension = PropertiesUtil.toString(properties.get(PROPERTY_EXTENSION), DEFAULT_EXTENSION);
ttl = PropertiesUtil.toInteger(properties.get(PROPERTY_COMPONENT_TTL), -1);
addComment = PropertiesUtil.toBoolean(properties.get(PROPERTY_ADD_COMMENT), DEFAULT_ADD_COMMENT);
includeTypeName = PropertiesUtil.toString(properties.get(PROPERTY_INCLUDE_TYPE), DEFAULT_INCLUDE_TYPE);
@@ -173,6 +181,19 @@
return includeSelector;
}
+ public boolean hasExtension(final SlingHttpServletRequest request) {
+ final String suffix = request.getRequestPathInfo().getSuffix();
+ return suffix.endsWith("." + this.extension);
+ }
+
+ public boolean hasExtensionSet() {
+ return StringUtils.isNotBlank(this.extension);
+ }
+
+ public String getExtension() {
+ return this.extension;
+ }
+
public boolean hasTtlSet() {
return ttl >= 0;
}
diff --git a/src/main/java/org/apache/sling/dynamicinclude/IncludeTagFilter.java b/src/main/java/org/apache/sling/dynamicinclude/IncludeTagFilter.java
index 62b64c3..ccbf2bd 100644
--- a/src/main/java/org/apache/sling/dynamicinclude/IncludeTagFilter.java
+++ b/src/main/java/org/apache/sling/dynamicinclude/IncludeTagFilter.java
@@ -161,8 +161,9 @@
private String buildUrl(Configuration config, SlingHttpServletRequest request) {
final Resource resource = request.getResource();
+
final boolean synthetic = ResourceUtil.isSyntheticResource(request.getResource());
- return UrlBuilder.buildUrl(config.getIncludeSelector(), resource.getResourceType(), synthetic, request.getRequestPathInfo());
+ return UrlBuilder.buildUrl(config.getIncludeSelector(), resource.getResourceType(), synthetic, config, request.getRequestPathInfo());
}
private static String sanitize(String path) {
diff --git a/src/main/java/org/apache/sling/dynamicinclude/SyntheticResourceFilter.java b/src/main/java/org/apache/sling/dynamicinclude/SyntheticResourceFilter.java
index d2e2725..0f2ac1b 100644
--- a/src/main/java/org/apache/sling/dynamicinclude/SyntheticResourceFilter.java
+++ b/src/main/java/org/apache/sling/dynamicinclude/SyntheticResourceFilter.java
@@ -37,6 +37,7 @@
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.request.RequestDispatcherOptions;
+import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.engine.EngineConstants;
import org.osgi.framework.Constants;
@@ -60,19 +61,23 @@
final Configuration config = configurationWhiteboard.getConfiguration(slingRequest, resourceType);
if (config == null || !config.hasIncludeSelector(slingRequest)
- || !ResourceUtil.isSyntheticResource(slingRequest.getResource())) {
+ || !ResourceUtil.isSyntheticResource(slingRequest.getResource())
+ || (config.hasExtensionSet() && !config.hasExtension(slingRequest))) {
chain.doFilter(request, response);
return;
}
final RequestDispatcherOptions options = new RequestDispatcherOptions();
options.setForceResourceType(resourceType);
- final RequestDispatcher dispatcher = slingRequest.getRequestDispatcher(slingRequest.getResource(), options);
+ String resourcePath = StringUtils.substringBefore(slingRequest.getRequestPathInfo().getResourcePath(), ".");
+ Resource resource = slingRequest.getResourceResolver().resolve(resourcePath);
+ final RequestDispatcher dispatcher = slingRequest.getRequestDispatcher(resource, options);
dispatcher.forward(request, response);
}
- private static String getResourceTypeFromSuffix(SlingHttpServletRequest request) {
- final String suffix = request.getRequestPathInfo().getSuffix();
+ private static String getResourceTypeFromSuffix(final SlingHttpServletRequest request) {
+ String suffix = request.getRequestPathInfo().getSuffix();
+ suffix = StringUtils.substringBeforeLast(suffix, ".");
return StringUtils.removeStart(suffix, "/");
}
diff --git a/src/main/java/org/apache/sling/dynamicinclude/impl/UrlBuilder.java b/src/main/java/org/apache/sling/dynamicinclude/impl/UrlBuilder.java
index 79338b9..9077cae 100644
--- a/src/main/java/org/apache/sling/dynamicinclude/impl/UrlBuilder.java
+++ b/src/main/java/org/apache/sling/dynamicinclude/impl/UrlBuilder.java
@@ -19,15 +19,16 @@
package org.apache.sling.dynamicinclude.impl;
+import java.util.Arrays;
+
import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.request.RequestPathInfo;
-
-import java.util.Arrays;
+import org.apache.sling.dynamicinclude.Configuration;
public final class UrlBuilder {
- public static String buildUrl(String includeSelector, String resourceType, boolean synthetic, RequestPathInfo pathInfo) {
+ public static String buildUrl(String includeSelector, String resourceType, boolean synthetic, Configuration config, RequestPathInfo pathInfo) {
final StringBuilder builder = new StringBuilder();
final String resourcePath = pathInfo.getResourcePath();
@@ -42,6 +43,9 @@
builder.append('.').append(pathInfo.getExtension());
if (synthetic) {
builder.append('/').append(resourceType);
+ if (config.hasExtensionSet()) {
+ builder.append('.').append(config.getExtension());
+ }
} else {
builder.append(StringUtils.defaultString(pathInfo.getSuffix()));
}
diff --git a/src/main/java/org/apache/sling/dynamicinclude/package-info.java b/src/main/java/org/apache/sling/dynamicinclude/package-info.java
index 6eed06e..78fc7ba 100644
--- a/src/main/java/org/apache/sling/dynamicinclude/package-info.java
+++ b/src/main/java/org/apache/sling/dynamicinclude/package-info.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-@Version("4.0.0")
+@Version("4.1.0")
package org.apache.sling.dynamicinclude;
import aQute.bnd.annotation.Version;
diff --git a/src/test/java/org/apache/sling/dynamicinclude/impl/UrlBuilderTest.java b/src/test/java/org/apache/sling/dynamicinclude/impl/UrlBuilderTest.java
index 3184bb7..ccf6e67 100644
--- a/src/test/java/org/apache/sling/dynamicinclude/impl/UrlBuilderTest.java
+++ b/src/test/java/org/apache/sling/dynamicinclude/impl/UrlBuilderTest.java
@@ -21,6 +21,8 @@
import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.dynamicinclude.Configuration;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -29,6 +31,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.reset;
@RunWith(MockitoJUnitRunner.class)
public class UrlBuilderTest {
@@ -36,13 +39,16 @@
@Mock
private RequestPathInfo requestPathInfo;
+ @Mock
+ private Configuration config;
+
@Test
public void shouldAppendTheIncludeSelectorToUrlWithNoSelectors() {
givenAnHtmlRequestForResource("/resource/path");
withSelectorString(null);
boolean isSyntheticResource = false;
- String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, requestPathInfo);
+ String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo);
assertThat(actualResult, is("/resource/path.include.html"));
}
@@ -53,7 +59,7 @@
withSelectorString("foo.bar.baz");
boolean isSyntheticResource = false;
- String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, requestPathInfo);
+ String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo);
assertThat(actualResult, is("/resource/path.foo.bar.baz.include.html"));
}
@@ -64,7 +70,7 @@
withSelectorString("foo.2.31");
boolean isSyntheticResource = false;
- String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, requestPathInfo);
+ String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo);
assertThat(actualResult, is("/resource/path.foo.2.31.include.html"));
}
@@ -75,7 +81,7 @@
withSelectorString("foo.include");
boolean isSyntheticResource = false;
- String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, requestPathInfo);
+ String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo);
assertThat(actualResult, is("/resource/path.foo.include.html"));
}
@@ -86,7 +92,7 @@
withSelectorString("longerSelectorThatHappensToContainTheIncludeSelector");
boolean isSyntheticResource = false;
- String actualResult = UrlBuilder.buildUrl("IncludeSelector", "apps/example/resource/type", isSyntheticResource, requestPathInfo);
+ String actualResult = UrlBuilder.buildUrl("IncludeSelector", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo);
assertThat(actualResult, is("/resource/path.longerSelectorThatHappensToContainTheIncludeSelector.IncludeSelector.html"));
}
@@ -97,10 +103,25 @@
withSelectorString("foo.include");
boolean isSyntheticResource = true;
- String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, requestPathInfo);
+ String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo);
assertThat(actualResult, is("/resource/path.foo.include.html/apps/example/resource/type"));
}
+
+ @Test
+ public void shouldAppendExtensionForSyntheticResources() {
+ givenAnHtmlRequestForResource("/resource/path");
+ withSelectorString("foo.include");
+
+ when(config.hasExtensionSet()).thenReturn(true);
+ when(config.getExtension()).thenReturn("sdi");
+
+ boolean isSyntheticResource = true;
+
+ String actualResult = UrlBuilder.buildUrl("include", "apps/example/resource/type", isSyntheticResource, config, requestPathInfo);
+
+ assertThat(actualResult, is("/resource/path.foo.include.html/apps/example/resource/type.sdi"));
+ }
private void givenAnHtmlRequestForResource(String resourcePath) {
when(requestPathInfo.getExtension()).thenReturn("html");