SLING-9233 - Skip registering the Dispatcher servlet if the resource types are not versioned
diff --git a/src/main/java/org/apache/sling/scripting/bundle/tracker/ResourceType.java b/src/main/java/org/apache/sling/scripting/bundle/tracker/ResourceType.java
index 263879b..b1399f1 100644
--- a/src/main/java/org/apache/sling/scripting/bundle/tracker/ResourceType.java
+++ b/src/main/java/org/apache/sling/scripting/bundle/tracker/ResourceType.java
@@ -19,6 +19,7 @@
package org.apache.sling.scripting.bundle.tracker;
import java.util.Objects;
+import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
@@ -39,9 +40,12 @@
*/
public final class ResourceType {
+ private static final Pattern versionPattern = Pattern.compile("[\\d\\.]+(-.*)*$");
+
private final String type;
private final String version;
private final String resourceLabel;
+ private final String toString;
private ResourceType(@NotNull String type, @Nullable String version) {
this.type = type;
@@ -53,6 +57,7 @@
} else {
resourceLabel = type;
}
+ toString = type + (version == null ? "" : "/" + version);
}
/**
@@ -90,7 +95,7 @@
@Override
public String toString() {
- return type + (version == null ? "" : "/" + version);
+ return toString;
}
/**
@@ -115,10 +120,15 @@
if (StringUtils.isNotEmpty(resourceTypeString)) {
int lastSlash = resourceTypeString.lastIndexOf('/');
if (lastSlash != -1 && !resourceTypeString.endsWith("/")) {
- try {
- version = Version.parseVersion(resourceTypeString.substring(lastSlash + 1)).toString();
- type = resourceTypeString.substring(0, lastSlash);
- } catch (IllegalArgumentException e) {
+ String versionString = resourceTypeString.substring(lastSlash + 1);
+ if (versionPattern.matcher(versionString).matches()) {
+ try {
+ version = Version.parseVersion(versionString).toString();
+ type = resourceTypeString.substring(0, lastSlash);
+ } catch (IllegalArgumentException e) {
+ type = resourceTypeString;
+ }
+ } else {
type = resourceTypeString;
}
} else {
diff --git a/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/BundledScriptServlet.java b/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/BundledScriptServlet.java
index 2cf4be3..0516ece 100644
--- a/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/BundledScriptServlet.java
+++ b/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/BundledScriptServlet.java
@@ -19,13 +19,12 @@
package org.apache.sling.scripting.bundle.tracker.internal;
import java.io.IOException;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collection;
import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
import javax.script.Bindings;
import javax.script.ScriptContext;
@@ -42,7 +41,6 @@
import org.apache.sling.api.request.RequestPathInfo;
import org.apache.sling.api.scripting.ScriptEvaluationException;
import org.apache.sling.api.scripting.SlingBindings;
-import org.apache.sling.scripting.bundle.tracker.ResourceType;
import org.apache.sling.scripting.core.ScriptHelper;
class BundledScriptServlet extends GenericServlet {
@@ -53,8 +51,7 @@
private final LinkedHashSet<TypeProvider> m_wiredTypeProviders;
private final boolean m_precompiledScripts;
- private Map<String, Executable> scriptsMap = new HashMap<>();
- private ReadWriteLock lock = new ReentrantReadWriteLock();
+ private ConcurrentMap<String, Optional<Executable>> scriptsMap = new ConcurrentHashMap<>();
@@ -83,55 +80,27 @@
}
String scriptsMapKey = getScriptsMapKey(request);
- Executable executable;
- lock.readLock().lock();
+ Executable executable = scriptsMap.computeIfAbsent(scriptsMapKey,
+ key -> Optional.ofNullable(m_bundledScriptFinder.getScript(request, m_wiredTypeProviders, m_precompiledScripts))
+ ).orElseThrow(() -> new ServletException("Unable to locate a " + (m_precompiledScripts ? "class" : "script") + " for rendering."));
+
+ RequestWrapper requestWrapper = new RequestWrapper(request,
+ m_wiredTypeProviders.stream().map(TypeProvider::getResourceTypes).flatMap(Collection::stream).collect(Collectors.toSet()));
+ ScriptContext scriptContext = m_scriptContextProvider.prepareScriptContext(requestWrapper, response, executable);
try {
- executable = scriptsMap.get(scriptsMapKey);
- if (executable == null) {
- lock.readLock().unlock();
- lock.writeLock().lock();
- try {
- executable = scriptsMap.get(scriptsMapKey);
- if (executable == null) {
- executable = m_bundledScriptFinder.getScript(request, m_wiredTypeProviders, m_precompiledScripts);
- if (executable != null) {
- scriptsMap.put(scriptsMapKey, executable);
- }
- }
- } finally {
- lock.readLock().lock();
- lock.writeLock().unlock();
- }
- }
+ executable.eval(scriptContext);
+ } catch (ScriptException se) {
+ Throwable cause = (se.getCause() == null) ? se : se.getCause();
+ throw new ScriptEvaluationException(executable.getName(), se.getMessage(), cause);
} finally {
- lock.readLock().unlock();
- }
- if (executable != null) {
- Set<String> wiredResourceTypes = new HashSet<>();
- for (TypeProvider typeProvider : m_wiredTypeProviders) {
- for (ResourceType resourceType : typeProvider.getResourceTypes()) {
- wiredResourceTypes.add(resourceType.toString());
+ Bindings engineBindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
+ if (engineBindings != null && engineBindings.containsKey(SlingBindings.SLING)) {
+ Object scriptHelper = engineBindings.get(SlingBindings.SLING);
+ if (scriptHelper instanceof ScriptHelper) {
+ ((ScriptHelper) scriptHelper).cleanup();
}
}
- RequestWrapper requestWrapper = new RequestWrapper(request, wiredResourceTypes);
- ScriptContext scriptContext = m_scriptContextProvider.prepareScriptContext(requestWrapper, response, executable);
- try {
- executable.eval(scriptContext);
- } catch (ScriptException se) {
- Throwable cause = (se.getCause() == null) ? se : se.getCause();
- throw new ScriptEvaluationException(executable.getName(), se.getMessage(), cause);
- } finally {
- Bindings engineBindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
- if (engineBindings != null && engineBindings.containsKey(SlingBindings.SLING)) {
- Object scriptHelper = engineBindings.get(SlingBindings.SLING);
- if (scriptHelper instanceof ScriptHelper) {
- ((ScriptHelper) scriptHelper).cleanup();
- }
- }
- executable.releaseDependencies();
- }
- } else {
- throw new ServletException("Unable to locate a " + (m_precompiledScripts ? "class" : "script") + " for rendering.");
+ executable.releaseDependencies();
}
} else {
throw new ServletException("Not a Sling HTTP request/response");
diff --git a/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/BundledScriptTracker.java b/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/BundledScriptTracker.java
index bbe4d15..6c8bd63 100644
--- a/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/BundledScriptTracker.java
+++ b/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/BundledScriptTracker.java
@@ -99,7 +99,7 @@
private volatile BundleContext m_context;
private volatile BundleTracker<List<ServiceRegistration<Servlet>>> m_tracker;
- private volatile Map<String, ServiceRegistration<Servlet>> m_dispatchers = new HashMap<>();
+ private volatile Map<Set<String>, ServiceRegistration<Servlet>> m_dispatchers = new HashMap<>();
@Activate
protected void activate(BundleContext context) {
@@ -126,7 +126,7 @@
{
Hashtable<String, Object> properties = new Hashtable<>();
properties.put(ServletResolverConstants.SLING_SERVLET_NAME, BundledScriptServlet.class.getName());
- properties.put(Constants.SERVICE_DESCRIPTION, cap.toString());
+ properties.put(Constants.SERVICE_DESCRIPTION, BundledScriptServlet.class.getName() + cap.getAttributes());
ResourceTypeCapability resourceTypeCapability = ResourceTypeCapability.fromBundleCapability(cap);
String[] resourceTypesRegistrationValue = new String[resourceTypeCapability.getResourceTypes().size()];
int rtIndex = 0;
@@ -220,12 +220,14 @@
}
private void refreshDispatcher(List<ServiceRegistration<Servlet>> regs) {
- Map<String, ServiceRegistration<Servlet>> dispatchers = new HashMap<>();
- Stream.concat(m_tracker.getTracked().values().stream(), Stream.of(regs)).flatMap(List::stream).map(this::toProperties).collect(
- Collectors.groupingBy(this::getResourceType)).forEach((rt, propList) -> {
+ Map<Set<String>, ServiceRegistration<Servlet>> dispatchers = new HashMap<>();
+ Stream.concat(m_tracker.getTracked().values().stream(), Stream.of(regs)).flatMap(List::stream)
+ .filter(ref -> getResourceTypeVersion(ref.getReference()) != null)
+ .map(this::toProperties)
+ .collect(Collectors.groupingBy(BundledScriptTracker::getResourceTypes)).forEach((rt, propList) -> {
Hashtable<String, Object> properties = new Hashtable<>();
properties.put(ServletResolverConstants.SLING_SERVLET_NAME, DispatcherServlet.class.getName());
- properties.put(ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES, rt);
+ properties.put(ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES, rt.toArray());
Set<String> methods = propList.stream()
.map(props -> props.getOrDefault(ServletResolverConstants.SLING_SERVLET_METHODS, new String[]{"GET", "HEAD"}))
.map(PropertiesUtil::toStringArray).map(Arrays::asList).flatMap(List::stream).collect(Collectors.toSet());
@@ -245,9 +247,11 @@
}
return null;
}).findFirst();
- properties.put(Constants.SERVICE_DESCRIPTION, ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES + "=" + rt + "; " +
+ properties.put(Constants.SERVICE_DESCRIPTION,
+ DispatcherServlet.class.getName() + "{" + ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES +
+ "=" + rt + "; " +
ServletResolverConstants.SLING_SERVLET_EXTENSIONS + "=" + extensions + "; " +
- ServletResolverConstants.SLING_SERVLET_METHODS + "=" + methods);
+ ServletResolverConstants.SLING_SERVLET_METHODS + "=" + methods + "}");
reg = registeringBundle.orElse(m_context).registerService(Servlet.class, new DispatcherServlet(rt), properties);
} else {
if (!new HashSet<>(Arrays.asList(PropertiesUtil
@@ -275,13 +279,6 @@
return result;
}
- private String getResourceType(Hashtable<String, Object> props) {
- String[] values = PropertiesUtil.toStringArray(props.get(ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES));
- String resourceTypeValue = values[0];
- ResourceType resourceType = ResourceType.parseResourceType(resourceTypeValue);
- return resourceType.getType();
- }
-
private void set(String key, ServiceReference<?> ref, Hashtable<String, Object> props) {
Object value = ref.getProperty(key);
if (value != null) {
@@ -302,9 +299,9 @@
}
private class DispatcherServlet extends GenericServlet {
- private final String m_rt;
+ private final Set<String> m_rt;
- DispatcherServlet(String rt) {
+ DispatcherServlet(Set<String> rt) {
m_rt = rt;
}
@@ -322,7 +319,7 @@
.filter(reg ->
{
Hashtable<String, Object> props = toProperties(reg);
- return getResourceType(props).equals(m_rt) &&
+ return getResourceTypes(props).equals(m_rt) &&
Arrays.asList(PropertiesUtil
.toStringArray(props.get(ServletResolverConstants.SLING_SERVLET_METHODS),
new String[]{"GET", "HEAD"}))
@@ -331,8 +328,7 @@
.toStringArray(props.get(ServletResolverConstants.SLING_SERVLET_EXTENSIONS), new String[]{"html"}))
.contains(slingRequest.getRequestPathInfo().getExtension() == null ? "html" :
slingRequest.getRequestPathInfo().getExtension());
- })
- .sorted((left, right) ->
+ }).min((left, right) ->
{
boolean la = Arrays.asList(PropertiesUtil
.toStringArray(toProperties(left).get(ServletResolverConstants.SLING_SERVLET_SELECTORS), new String[0]))
@@ -349,8 +345,7 @@
return 1;
}
- })
- .findFirst();
+ });
if (target.isPresent()) {
String[] targetRT =
@@ -382,12 +377,21 @@
((SlingHttpServletResponse) res).sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
+ }
- private String getResourceTypeVersion(ServiceReference<?> ref) {
- String[] values = PropertiesUtil.toStringArray(ref.getProperty(ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES));
- String resourceTypeValue = values[0];
- ResourceType resourceType = ResourceType.parseResourceType(resourceTypeValue);
- return resourceType.getVersion();
+ private static String getResourceTypeVersion(ServiceReference<?> ref) {
+ String[] values = PropertiesUtil.toStringArray(ref.getProperty(ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES));
+ String resourceTypeValue = values[0];
+ ResourceType resourceType = ResourceType.parseResourceType(resourceTypeValue);
+ return resourceType.getVersion();
+ }
+
+ private static Set<String> getResourceTypes(Hashtable<String, Object> props) {
+ Set<String> resourceTypes = new HashSet<>();
+ String[] values = PropertiesUtil.toStringArray(props.get(ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES));
+ for (String resourceTypeValue : values) {
+ resourceTypes.add(ResourceType.parseResourceType(resourceTypeValue).getType());
}
+ return resourceTypes;
}
}
diff --git a/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/RequestWrapper.java b/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/RequestWrapper.java
index 7a9da1f..57ebfea 100644
--- a/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/RequestWrapper.java
+++ b/src/main/java/org/apache/sling/scripting/bundle/tracker/internal/RequestWrapper.java
@@ -27,12 +27,13 @@
import org.apache.sling.api.request.RequestDispatcherOptions;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
+import org.apache.sling.scripting.bundle.tracker.ResourceType;
class RequestWrapper extends SlingHttpServletRequestWrapper {
- private final Set<String> wiredResourceTypes;
+ private final Set<ResourceType> wiredResourceTypes;
- RequestWrapper(SlingHttpServletRequest wrappedRequest, Set<String> wiredResourceTypes) {
+ RequestWrapper(SlingHttpServletRequest wrappedRequest, Set<ResourceType> wiredResourceTypes) {
super(wrappedRequest);
this.wiredResourceTypes = wiredResourceTypes;
}
@@ -67,9 +68,10 @@
requestDispatcherOptions.setReplaceSuffix(options.getReplaceSuffix());
String forcedResourceType = options.getForceResourceType();
if (StringUtils.isNotEmpty(forcedResourceType)) {
- for (String wiredResourceType : wiredResourceTypes) {
- if (wiredResourceType.startsWith(forcedResourceType + "/")) {
- requestDispatcherOptions.setForceResourceType(wiredResourceType);
+ for (ResourceType wiredResourceType : wiredResourceTypes) {
+ String type = wiredResourceType.getType();
+ if (type.startsWith(forcedResourceType + "/")) {
+ requestDispatcherOptions.setForceResourceType(type);
break;
}
}