This is an automated email from the ASF dual-hosted git repository.
pauls pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlets-resolver.git
The following commit(s) were added to refs/heads/master by this push:
new 3636ce5 SLING-9365: Optionally don't create resource providers for
servlets
3636ce5 is described below
commit 3636ce54b218cef6f017e0cf26e047c60f88a921
Author: Karl Pauls <[email protected]>
AuthorDate: Wed Apr 29 10:49:39 2020 +0200
SLING-9365: Optionally don't create resource providers for servlets
---
.../servlets/resolver/internal/ResolverConfig.java | 5 +
.../servlets/resolver/internal/ScriptResource.java | 14 +-
.../resolver/internal/ScriptResourceDecorator.java | 98 ++++++++++++
.../resolver/internal/ScriptResourceResolver.java | 175 +++++++++++++++++++++
.../resolver/internal/SlingServletResolver.java | 37 +++--
.../internal/helper/AbstractResourceCollector.java | 3 +-
.../resource/MergingServletResourceProvider.java | 163 +++++++++++++++++++
.../resolver/internal/resource/ServletMounter.java | 71 +++++++--
.../internal/resource/ServletResource.java | 17 +-
.../resource/ServletResourceProviderFactory.java | 8 +-
.../internal/SlingServletResolverTestBase.java | 2 +-
11 files changed, 556 insertions(+), 37 deletions(-)
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/ResolverConfig.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/ResolverConfig.java
index 9159970..21941d8 100644
---
a/src/main/java/org/apache/sling/servlets/resolver/internal/ResolverConfig.java
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/ResolverConfig.java
@@ -58,4 +58,9 @@ public @interface ResolverConfig {
@AttributeDefinition(name = "Default Extensions", description = "The list
of extensions for which the default behavior "
+ "will be used. This means that the last path segment of the
resource type can be used as the script name.")
String[] servletresolver_defaultExtensions() default "html";
+
+ @AttributeDefinition(name = "Mount Providers", description = "Should
servlets be mounted as resource providers?" +
+ " If true (the default), servlets will be represented in the content
tree using resource provider -" +
+ " otherwise, servlets will be decorated back into the content tree
using a decorator.")
+ boolean servletresolver_mountProviders() default true;
}
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResource.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResource.java
index 36842f2..ff995bb 100644
---
a/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResource.java
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResource.java
@@ -19,6 +19,7 @@
package org.apache.sling.servlets.resolver.internal;
import java.util.Iterator;
+import java.util.function.Supplier;
import javax.servlet.Servlet;
@@ -28,6 +29,7 @@ import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.scripting.SlingScript;
+import org.apache.sling.servlets.resolver.internal.resource.ServletResource;
/**
* ScriptResource is a resource wrapper of a resource fetched by a
@@ -45,12 +47,12 @@ public class ScriptResource extends AbstractResource {
private final ResourceResolver sharedResourceResolver;
- private final ThreadLocal<ResourceResolver> perThreadResourceResolver;
+ private final Supplier<ResourceResolver> perThreadResourceResolver;
private final String path;
public ScriptResource(final Resource resource,
- final ThreadLocal<ResourceResolver> perThreadScriptResolver,
+ final Supplier<ResourceResolver> perThreadScriptResolver,
final ResourceResolver sharedResourceResolver) {
this.path = resource.getPath();
this.sharedResourceResolver = sharedResourceResolver;
@@ -99,9 +101,11 @@ public class ScriptResource extends AbstractResource {
@Override
public <AdapterType> AdapterType adaptTo(final Class<AdapterType> type) {
if ( type == Servlet.class ) {
- final Servlet s = (Servlet)super.adaptTo(type);
- if ( s != null ) {
- return (AdapterType)s;
+ if (! (this.getActiveResource() instanceof ServletResource)) {
+ final Servlet s = (Servlet) super.adaptTo(type);
+ if ( s != null ) {
+ return (AdapterType)s;
+ }
}
} else if ( type == SlingScript.class ) {
final SlingScript s = (SlingScript)super.adaptTo(type);
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResourceDecorator.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResourceDecorator.java
new file mode 100644
index 0000000..5034cdc
--- /dev/null
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResourceDecorator.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlets.resolver.internal;
+
+import java.util.Iterator;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceDecorator;
+import org.apache.sling.api.resource.ResourceResolver;
+import
org.apache.sling.servlets.resolver.internal.resource.MergingServletResourceProvider;
+import org.apache.sling.spi.resource.provider.ResolveContext;
+import org.apache.sling.spi.resource.provider.ResourceContext;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component
+public class ScriptResourceDecorator implements ResourceDecorator {
+ private final MergingServletResourceProvider provider;
+
+ @Activate
+ public ScriptResourceDecorator(@Reference MergingServletResourceProvider
provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public Resource decorate(Resource resource) {
+ String path = resource.getPath();
+ Resource script = getResource(resource, path);
+ if (script == resource &&
Resource.RESOURCE_TYPE_NON_EXISTING.equals(resource.getResourceType())) {
+ int idx = path.indexOf('.');
+ if (idx != -1) {
+ path = path.substring(0, idx);
+ script = getResource(resource, path);
+ }
+ }
+ script.getResourceMetadata().putAll(resource.getResourceMetadata());
+
+ return script;
+ }
+
+ @Override
+ public Resource decorate(Resource resource, HttpServletRequest request) {
+ return decorate(resource);
+ }
+
+ private Resource getResource(Resource resource, String path) {
+ return provider.getResource(new ResolveContext<Void>() {
+ @Override
+ public ResourceResolver getResourceResolver() {
+ return new
ScriptResourceResolver(resource.getResourceResolver(), () -> provider);
+ }
+
+ @Override
+ public Void getProviderState() {
+ return null;
+ }
+
+ @Override
+ public ResolveContext<?> getParentResolveContext() {
+ return null;
+ }
+
+ @Override
+ public ResourceProvider<?> getParentResourceProvider() {
+ return new ResourceProvider<Object>() {
+ @Override
+ public Resource getResource(ResolveContext<Object> ctx,
String path, ResourceContext resourceContext, Resource parent) {
+ return resource;
+ }
+
+ @Override
+ public Iterator<Resource>
listChildren(ResolveContext<Object> ctx, Resource parent) {
+ return null;
+ }
+ };
+ }
+ }, path);
+ }
+}
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResourceResolver.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResourceResolver.java
new file mode 100644
index 0000000..c6ff904
--- /dev/null
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResourceResolver.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlets.resolver.internal;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceWrapper;
+import org.apache.sling.api.wrappers.IteratorWrapper;
+import org.apache.sling.api.wrappers.ResourceResolverWrapper;
+import
org.apache.sling.servlets.resolver.internal.resource.MergingServletResourceProvider;
+import org.apache.sling.spi.resource.provider.ResolveContext;
+import org.apache.sling.spi.resource.provider.ResourceContext;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+
+public class ScriptResourceResolver extends ResourceResolverWrapper {
+ private final ResourceResolver resolver;
+ private final Supplier<MergingServletResourceProvider> provider;
+
+ public ScriptResourceResolver(ResourceResolver resolver,
Supplier<MergingServletResourceProvider> provider) {
+ super(resolver);
+ this.resolver = resolver;
+ this.provider = provider;
+ }
+
+ public static ScriptResourceResolver wrap(ResourceResolver
scriptResourceResolver, Supplier<MergingServletResourceProvider> provider) {
+ return new ScriptResourceResolver(scriptResourceResolver, provider);
+ }
+
+ @Override
+ public Iterable<Resource> getChildren(Resource parent) {
+ return () -> listChildren(parent);
+ }
+
+ public Resource getResource(String scriptPath) {
+ MergingServletResourceProvider provider = this.provider.get();
+
+ if (provider == null) {
+ return super.getResource(scriptPath);
+ }
+ else {
+ return wrap(provider.getResource(new ResolveContext() {
+ @Override
+ public ResourceResolver getResourceResolver() {
+ return ScriptResourceResolver.this;
+ }
+
+ @Override
+ public Object getProviderState() {
+ return null;
+ }
+
+ @Override
+ public ResolveContext<?> getParentResolveContext() {
+ return null;
+ }
+
+ @Override
+ public ResourceProvider<?> getParentResourceProvider() {
+ return new ResourceProvider() {
+ @Override
+ public Resource getResource(ResolveContext ctx, String
path, ResourceContext resourceContext, Resource parent) {
+ return resolver.getResource(path);
+ }
+
+ @Override
+ public Iterator<Resource> listChildren(ResolveContext
ctx, Resource parent) {
+ return resolver.listChildren(parent);
+ }
+ };
+ }
+ }, scriptPath));
+ }
+ }
+
+ @Override
+ public Iterator<Resource> listChildren(Resource parent) {
+ MergingServletResourceProvider provider = this.provider.get();
+ if (provider == null) {
+ return super.listChildren(parent);
+ }
+ else {
+ return wrap(provider.listChildren(new ResolveContext() {
+ @Override
+ public ResourceResolver getResourceResolver() {
+ return ScriptResourceResolver.this;
+ }
+
+ @Override
+ public Object getProviderState() {
+ return null;
+ }
+
+ public ResolveContext<?> getParentResolveContext() {
+ return null;
+ }
+
+ public ResourceProvider<?> getParentResourceProvider() {
+ return new ResourceProvider() {
+ @Override
+ public Resource getResource(ResolveContext ctx, String
path, ResourceContext resourceContext, Resource parent) {
+ return resolver.getResource(path);
+ }
+
+ @Override
+ public Iterator<Resource> listChildren(ResolveContext
ctx, Resource parent) {
+ return resolver.listChildren(parent);
+ }
+ };
+ }
+ }, unwrap(parent)));
+ }
+ }
+
+ private Resource wrap(Resource resource) {
+ if (resource != null && !(resource.getResourceResolver() instanceof
ScriptResourceResolver)) {
+ resource = new ScriptResourceResolverResourceWrapper(resource);
+ }
+ return resource;
+ }
+
+ private Iterator<Resource> wrap(Iterator<Resource> iter) {
+ if (iter != null) {
+ iter = new IteratorWrapper<Resource>(iter){
+ @Override
+ public Resource next() {
+ return wrap(super.next());
+ }
+ };
+ }
+ return iter;
+ }
+
+ private Resource unwrap(Resource resource) {
+ if (resource instanceof ScriptResourceResolverResourceWrapper) {
+ resource = ((ResourceWrapper) resource).getResource();
+ }
+ return resource;
+ }
+
+ public ScriptResourceResolver clone(Map o) throws LoginException {
+ return ScriptResourceResolver.wrap(resolver.clone(o), provider);
+ }
+
+ private class ScriptResourceResolverResourceWrapper extends
ResourceWrapper {
+ public ScriptResourceResolverResourceWrapper(Resource resource) {
+ super(resource);
+ }
+
+ @Override
+ public ResourceResolver getResourceResolver() {
+ return ScriptResourceResolver.this;
+ }
+ }
+}
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/SlingServletResolver.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/SlingServletResolver.java
index 97c41ee..170bc32 100644
---
a/src/main/java/org/apache/sling/servlets/resolver/internal/SlingServletResolver.java
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/SlingServletResolver.java
@@ -27,6 +27,7 @@ import static
org.apache.sling.api.servlets.ServletResolverConstants.DEFAULT_ERR
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
+import java.util.function.Supplier;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
@@ -59,7 +60,9 @@ import
org.apache.sling.servlets.resolver.internal.helper.AbstractResourceCollec
import
org.apache.sling.servlets.resolver.internal.helper.NamedScriptResourceCollector;
import org.apache.sling.servlets.resolver.internal.helper.ResourceCollector;
import org.apache.sling.servlets.resolver.internal.resolution.ResolutionCache;
+import
org.apache.sling.servlets.resolver.internal.resource.MergingServletResourceProvider;
import org.apache.sling.servlets.resolver.internal.resource.SlingServletConfig;
+import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
@@ -67,6 +70,7 @@ import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -117,6 +121,8 @@ public class SlingServletResolver
private volatile ResourceResolver sharedScriptResolver;
+ private final ThreadLocal<ResourceResolver> perThreadScriptResolver = new
ThreadLocal<>();
+
/**
* The allowed execution paths.
*/
@@ -141,7 +147,7 @@ public class SlingServletResolver
// ---------- ServletResolver interface -----------------------------------
/**
- * @see
org.apache.sling.api.servlets.ServletResolver#resolveServlet(org.apache.sling.api.SlingHttpServletRequest)
+ * @see ServletResolver#resolveServlet(SlingHttpServletRequest)
*/
@Override
public Servlet resolveServlet(final SlingHttpServletRequest request) {
@@ -192,7 +198,7 @@ public class SlingServletResolver
}
/**
- * @see
org.apache.sling.api.servlets.ServletResolver#resolveServlet(org.apache.sling.api.resource.Resource,
java.lang.String)
+ * @see ServletResolver#resolveServlet(Resource, java.lang.String)
*/
@Override
public Servlet resolveServlet(final Resource resource, final String
scriptName) {
@@ -219,7 +225,7 @@ public class SlingServletResolver
}
/**
- * @see
org.apache.sling.api.servlets.ServletResolver#resolveServlet(org.apache.sling.api.resource.ResourceResolver,
java.lang.String)
+ * @see ServletResolver#resolveServlet(ResourceResolver, java.lang.String)
*/
@Override
public Servlet resolveServlet(final ResourceResolver resolver, final
String scriptName) {
@@ -255,14 +261,14 @@ public class SlingServletResolver
}
// if resource is fetched using shared resource resolver
// or resource is a servlet resource, just adapt to servlet
- if ( scriptResource.getResourceResolver() == this.sharedScriptResolver
+ if (scriptResource.getResourceResolver() == this.sharedScriptResolver
||
"sling/bundle/resource".equals(scriptResource.getResourceSuperType()) ) {
return scriptResource.adaptTo(Servlet.class);
}
// return a resource wrapper to make sure the implementation
// switches from the per thread resource resolver to the shared once
// the per thread resource resolver is closed
- return new ScriptResource(scriptResource, perThreadScriptResolver,
this.sharedScriptResolver).adaptTo(Servlet.class);
+ return new ScriptResource(scriptResource,
perThreadScriptResolver::get, this.sharedScriptResolver).adaptTo(Servlet.class);
}
// ---------- ErrorHandler interface --------------------------------------
@@ -326,7 +332,7 @@ public class SlingServletResolver
}
/**
- * @see
org.apache.sling.engine.servlets.ErrorHandler#handleError(java.lang.Throwable,
org.apache.sling.api.SlingHttpServletRequest,
org.apache.sling.api.SlingHttpServletResponse)
+ * @see
org.apache.sling.engine.servlets.ErrorHandler#handleError(java.lang.Throwable,
SlingHttpServletRequest, SlingHttpServletResponse)
*/
@Override
public void handleError(final Throwable throwable, final
SlingHttpServletRequest request, final SlingHttpServletResponse response)
@@ -392,10 +398,8 @@ public class SlingServletResolver
return scriptResolver;
}
- private final ThreadLocal<ResourceResolver> perThreadScriptResolver = new
ThreadLocal<>();
-
/**
- * @see
org.apache.sling.api.request.SlingRequestListener#onEvent(org.apache.sling.api.request.SlingRequestEvent)
+ * @see SlingRequestListener#onEvent(SlingRequestEvent)
*/
@Override
public void onEvent(final SlingRequestEvent event) {
@@ -419,7 +423,7 @@ public class SlingServletResolver
* error handling. If the resource has not yet been set in the request
* because the error occurred before the resource could be set (e.g. during
* resource resolution) a synthetic resource is returned whose type is
- * {@link ServletResolverConstants#ERROR_HANDLER_PATH}.
+ * {@link ServletResolverConstants#DEFAULT_ERROR_HANDLER_RESOURCE_TYPE}.
*
* @param request The request whose resource is to be returned.
*/
@@ -653,13 +657,17 @@ public class SlingServletResolver
// ---------- SCR Integration
----------------------------------------------
+ private volatile ServiceTracker tracker;
/**
* Activate this component.
*/
@Activate
- protected void activate(final ResolverConfig config) throws LoginException
{
+ protected void activate(final BundleContext context, final ResolverConfig
config) throws LoginException {
+ this.tracker = new ServiceTracker(context,
MergingServletResourceProvider.class, null);
+ this.tracker.open();
this.sharedScriptResolver =
-
resourceResolverFactory.getServiceResourceResolver(Collections.singletonMap(ResourceResolverFactory.SUBSERVICE,
(Object)SERVICE_USER));
+
ScriptResourceResolver.wrap(resourceResolverFactory.getServiceResourceResolver(Collections.singletonMap(ResourceResolverFactory.SUBSERVICE,
(Object)SERVICE_USER)),
+ (Supplier) this.tracker::getService);
this.executionPaths =
getExecutionPaths(config.servletresolver_paths());
this.defaultExtensions = config.servletresolver_defaultExtensions();
@@ -669,9 +677,9 @@ public class SlingServletResolver
}
@Modified
- protected void modified(final ResolverConfig config) throws LoginException
{
+ protected void modified(final BundleContext context, final ResolverConfig
config) throws LoginException {
this.deactivate();
- this.activate(config);
+ this.activate(context, config);
}
/**
@@ -679,6 +687,7 @@ public class SlingServletResolver
*/
@Deactivate
protected void deactivate() {
+ this.tracker.close();
this.resolutionCache.flushCache();
// destroy the fallback error handler servlet
if (fallbackErrorServlet != null) {
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/helper/AbstractResourceCollector.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/helper/AbstractResourceCollector.java
index 9c72f35..9384957 100644
---
a/src/main/java/org/apache/sling/servlets/resolver/internal/helper/AbstractResourceCollector.java
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/helper/AbstractResourceCollector.java
@@ -35,7 +35,7 @@ import org.apache.sling.api.resource.SyntheticResource;
/**
* The <code>ResourceCollector</code> class provides a single public method -
- * {@link #getServlets(ResourceResolver)} - which is used to find an ordered
collection
+ * {@link #getServlets(ResourceResolver,List<String>)} - which is used to find
an ordered collection
* of <code>Resource</code> instances which may be used to find a servlet or
* script to handle a request to the given resource.
*/
@@ -138,7 +138,6 @@ public abstract class AbstractResourceCollector {
* the name of the resource.
* @param methodPrefixWeight The method/prefix weight assigned to the
* resource according to the resource name.
- * @param scriptExtensionPriority The priority of the script engine used
to run this script
*/
protected final void addWeightedResource(final Set<WeightedResource>
resources,
final Resource resource,
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/resource/MergingServletResourceProvider.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/resource/MergingServletResourceProvider.java
new file mode 100644
index 0000000..230bef0
--- /dev/null
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/resource/MergingServletResourceProvider.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlets.resolver.internal.resource;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.spi.resource.provider.ResolveContext;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public class MergingServletResourceProvider {
+ private final List<Pair<ServletResourceProvider, ServiceReference<?>>>
registrations = new ArrayList<>();
+
+ private final ConcurrentHashMap<String, Set<String>> tree = new
ConcurrentHashMap<>();
+ private final ConcurrentHashMap<String, Pair<ServletResourceProvider,
ServiceReference<?>>> providers = new ConcurrentHashMap<>();
+
+ synchronized void add(ServletResourceProvider provider,
ServiceReference<?> reference) {
+ registrations.add(Pair.of(provider, reference));
+ index(Arrays.asList(registrations.get(registrations.size() - 1)));
+ }
+
+ synchronized void remove(ServletResourceProvider provider,
ServiceReference<?> reference) {
+ for (Iterator<Pair<ServletResourceProvider, ServiceReference<?>>>
regIter = registrations.iterator(); regIter.hasNext(); ) {
+ Pair<ServletResourceProvider, ServiceReference<?>> reg =
regIter.next();
+ if (reg.getLeft() == provider) {
+ regIter.remove();
+ }
+ else {
+ Bundle bundle = reg.getRight().getBundle();
+ if (bundle == null || bundle.getState() == Bundle.STOPPING) {
+ regIter.remove();
+ }
+ }
+ }
+ tree.clear();
+ providers.clear();
+ index(registrations);
+ }
+
+ synchronized void clear() {
+ registrations.clear();
+ tree.clear();
+ providers.clear();
+ }
+
+ private void index(List<Pair<ServletResourceProvider,
ServiceReference<?>>> registrations) {
+ for (Pair<ServletResourceProvider, ServiceReference<?>> reference :
registrations) {
+ for (String path : reference.getLeft().getServletPaths()) {
+ String current = "";
+ for (String part : path.split("/")) {
+ Set<String> childs = tree.get(current);
+ if (childs == null) {
+ childs = Collections.synchronizedSet(new
LinkedHashSet<>());
+ tree.put(current, childs);
+ }
+ current += "/" + part;
+ current = current.trim().replace("//", "/");
+
+ childs.add(current);
+ }
+
+ Pair<ServletResourceProvider, ServiceReference<?>> old =
providers.put(path, reference);
+ if (old != null) {
+ if (reference.getRight().compareTo(old.getRight()) < 0) {
+ providers.put(path, old);
+ }
+ }
+ }
+ }
+ }
+
+ public Resource getResource(ResolveContext resolveContext, String path) {
+ Resource wrapped = null;
+ final ResourceProvider parentProvider =
resolveContext.getParentResourceProvider();
+ if (parentProvider != null) {
+ wrapped =
parentProvider.getResource(resolveContext.getParentResolveContext(), path,
null, null);
+ }
+ Resource result;
+ Pair<ServletResourceProvider, ServiceReference<?>> provider =
providers.get(path);
+
+ if (provider != null) {
+ result = provider.getLeft().getResource(resolveContext, path,
null, null);
+ if (result instanceof ServletResource) {
+ ((ServletResource) result).setWrappedResource(wrapped);
+ }
+ }
+ else {
+ if (wrapped != null) {
+ result = wrapped;
+ }
+ else {
+ result = null;
+ }
+ if (result == null && tree.containsKey(path)) {
+ result = new
SyntheticResource(resolveContext.getResourceResolver(), path,
ResourceProvider.RESOURCE_TYPE_SYNTHETIC);
+ }
+ }
+
+ return result;
+ }
+
+ public Iterator<Resource> listChildren(final ResolveContext ctx, final
Resource parent) {
+ Map<String, Resource> result = new LinkedHashMap<>();
+
+ final ResourceProvider parentProvider =
ctx.getParentResourceProvider();
+ if (parentProvider != null) {
+ for (Iterator<Resource> iter =
parentProvider.listChildren(ctx.getParentResolveContext(), parent); iter !=
null && iter.hasNext(); ) {
+ Resource resource = iter.next();
+ result.put(resource.getPath(), resource);
+ }
+ }
+ Set<String> paths = tree.get(parent.getPath());
+
+ if (paths != null) {
+ for (String path : paths) {
+ Pair<ServletResourceProvider, ServiceReference<?>> provider =
providers.get(path);
+
+ if (provider != null) {
+ Resource resource = provider.getLeft().getResource(ctx,
path, null, parent);
+ if (resource != null) {
+ Resource wrapped = result.put(path, resource);
+ if (resource instanceof ServletResource) {
+ ((ServletResource)
resource).setWrappedResource(wrapped);
+ }
+ }
+ }
+ else if (!result.containsKey(path)) {
+ result.put(path, new
SyntheticResource(ctx.getResourceResolver(), path,
ResourceProvider.RESOURCE_TYPE_SYNTHETIC));
+ }
+ }
+ }
+ return result.values().iterator();
+ }
+}
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletMounter.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletMounter.java
index 6c7d48a..1eb5866 100644
---
a/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletMounter.java
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletMounter.java
@@ -30,6 +30,7 @@ import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
@@ -40,6 +41,7 @@ import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.servlets.ServletResolver;
import org.apache.sling.api.servlets.ServletResolverConstants;
import org.apache.sling.servlets.resolver.internal.ResolverConfig;
+import org.apache.sling.servlets.resolver.internal.resolution.ResolutionCache;
import org.apache.sling.spi.resource.provider.ResourceProvider;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@@ -71,6 +73,8 @@ public class ServletMounter {
private static final String REF_SERVLET = "Servlet";
+ private static final String REF_CACHE = "Cache";
+
private final ServletContext servletContext;
private final Map<ServiceReference<Servlet>, ServletReg>
servletsByReference = new HashMap<>();
@@ -79,6 +83,12 @@ public class ServletMounter {
private final ServletResourceProviderFactory
servletResourceProviderFactory;
+ private final MergingServletResourceProvider provider;
+
+ private final ServiceRegistration<MergingServletResourceProvider>
providerReg;
+
+ private final ConcurrentHashMap<ResolutionCache, ResolutionCache>
resolutionCaches = new ConcurrentHashMap<>();
+
/**
* Activate this component.
*/
@@ -89,6 +99,15 @@ public class ServletMounter {
this.servletContext = servletContext;
servletResourceProviderFactory = new
ServletResourceProviderFactory(config.servletresolver_servletRoot(),
resourceResolverFactory.getSearchPath());
+
+ if (!config.servletresolver_mountProviders()) {
+ provider = new MergingServletResourceProvider();
+ providerReg =
context.registerService(MergingServletResourceProvider.class, provider, null);
+ }
+ else {
+ provider = null;
+ providerReg = null;
+ }
}
/**
@@ -103,9 +122,16 @@ public class ServletMounter {
synchronized (this.servletsByReference) {
refs = new ArrayList<>(servletsByReference.keySet());
}
+ if (provider != null) {
+ provider.clear();
+ }
// destroy all servlets
destroyAllServlets(refs);
+ if (providerReg != null) {
+ providerReg.unregister();
+ }
+
// sanity check: clear array (it should be empty now anyway)
synchronized ( this.servletsByReference ) {
this.servletsByReference.clear();
@@ -128,6 +154,21 @@ public class ServletMounter {
destroyServlet(reference);
}
+ @Reference(
+ name = REF_CACHE,
+ service = ResolutionCache.class,
+ cardinality = ReferenceCardinality.MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC
+ )
+ protected void bindResolutionCache(ResolutionCache cache) {
+ cache.flushCache();
+ resolutionCaches.put(cache, cache);
+ }
+
+ protected void unbindResolutionCache(ResolutionCache cache) {
+ resolutionCaches.remove(cache);
+ }
+
private boolean createServlet(final Servlet servlet, final
ServiceReference<Servlet> reference) {
// check for a name, this is required
final String name = getName(reference);
@@ -158,13 +199,18 @@ public class ServletMounter {
if ( bundleContext != null ) {
final List<ServiceRegistration<ResourceProvider<Object>>> regs
= new ArrayList<>();
try {
- for(final String root : provider.getServletPaths()) {
- @SuppressWarnings("unchecked")
- final ServiceRegistration<ResourceProvider<Object>>
reg = (ServiceRegistration<ResourceProvider<Object>>)
bundleContext.registerService(
- ResourceProvider.class.getName(),
- provider,
- createServiceProperties(reference, root));
- regs.add(reg);
+ if (this.provider != null) {
+ this.provider.add(provider, reference);
+
resolutionCaches.values().forEach(ResolutionCache::flushCache);
+ }
+ else {
+ for (final String root : provider.getServletPaths()) {
+ @SuppressWarnings("unchecked") final
ServiceRegistration<ResourceProvider<Object>> reg =
(ServiceRegistration<ResourceProvider<Object>>) bundleContext.registerService(
+ ResourceProvider.class.getName(),
+ provider,
+ createServiceProperties(reference, root));
+ regs.add(reg);
+ }
}
registered = true;
} catch ( final IllegalStateException ise ) {
@@ -175,7 +221,7 @@ public class ServletMounter {
logger.debug("Registered {}", provider);
}
synchronized (this.servletsByReference) {
- servletsByReference.put(reference, new
ServletReg(servlet, regs));
+ servletsByReference.put(reference, new
ServletReg(servlet, regs, provider));
}
}
}
@@ -224,6 +270,9 @@ public class ServletMounter {
// this might happen on shutdown
}
}
+ if (registration.provider != null && provider != null) {
+ provider.remove(registration.provider, reference);
+ }
final String name =
RequestUtil.getServletName(registration.servlet);
logger.debug("unbindServlet: Servlet {} removed", name);
@@ -257,13 +306,15 @@ public class ServletMounter {
return servletName;
}
- private static final class ServletReg {
+ static final class ServletReg {
public final Servlet servlet;
public final List<ServiceRegistration<ResourceProvider<Object>>>
registrations;
+ private final ServletResourceProvider provider;
- public ServletReg(final Servlet s, final
List<ServiceRegistration<ResourceProvider<Object>>> srs) {
+ public ServletReg(final Servlet s, final
List<ServiceRegistration<ResourceProvider<Object>>> srs, final
ServletResourceProvider provider) {
this.servlet = s;
this.registrations = srs;
+ this.provider = provider;
}
}
}
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletResource.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletResource.java
index 3f3f7c0..90a97e0 100644
---
a/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletResource.java
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletResource.java
@@ -25,12 +25,13 @@ import javax.servlet.Servlet;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.AbstractResource;
+import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ValueMapDecorator;
-class ServletResource extends AbstractResource {
+public class ServletResource extends AbstractResource {
private final ResourceResolver resourceResolver;
@@ -43,6 +44,8 @@ class ServletResource extends AbstractResource {
private final ResourceMetadata metadata;
+ private volatile Resource wrapped;
+
public ServletResource(ResourceResolver resourceResolver, Servlet servlet,
String path) {
this(resourceResolver, servlet, path, null);
}
@@ -59,6 +62,10 @@ class ServletResource extends AbstractResource {
this.metadata = new ResourceMetadata();
}
+ void setWrappedResource(Resource wrapped) {
+ this.wrapped = wrapped;
+ }
+
@Override
public ResourceMetadata getResourceMetadata() {
return metadata;
@@ -105,9 +112,14 @@ class ServletResource extends AbstractResource {
@Override
@SuppressWarnings("unchecked")
public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ Resource wrapped = this.wrapped;
if (type == Servlet.class && servlet != null) {
return (AdapterType) servlet; // unchecked cast
- } else if ( type == ValueMap.class ) {
+ }
+ else if (wrapped != null) {
+ return wrapped.adaptTo(type);
+ }
+ else if ( type == ValueMap.class ) {
final Map<String, Object> props = new HashMap<>();
props.put("sling:resourceType", this.getResourceType());
props.put("sling:resourceSuperType", this.getResourceSuperType());
@@ -127,5 +139,4 @@ class ServletResource extends AbstractResource {
return getClass().getSimpleName() + ", servlet=" +
this.getServletName()
+ ", path=" + getPath();
}
-
}
diff --git
a/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletResourceProviderFactory.java
b/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletResourceProviderFactory.java
index e4f6995..7e61e31 100644
---
a/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletResourceProviderFactory.java
+++
b/src/main/java/org/apache/sling/servlets/resolver/internal/resource/ServletResourceProviderFactory.java
@@ -34,6 +34,7 @@ import java.util.Set;
import javax.servlet.Servlet;
+import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.servlets.HttpConstants;
@@ -220,8 +221,11 @@ public class ServletResourceProviderFactory {
// add the unmodified path
pathSet.add(path);
- // ensure we have another entry which has the .servlet ext.
- pathSet.add(ensureServletNameExtension(path));
+ if (StringUtils.isEmpty(FilenameUtils.getExtension(path))) {
+ // ensure we have another entry which has the .servlet
ext. if there wasn't one to begin with
+ // Radu says: this will make sure that scripts are equal
to servlets in the resolution process
+ pathSet.add(ensureServletNameExtension(path));
+ }
}
}
}
diff --git
a/src/test/java/org/apache/sling/servlets/resolver/internal/SlingServletResolverTestBase.java
b/src/test/java/org/apache/sling/servlets/resolver/internal/SlingServletResolverTestBase.java
index 8e150fe..5ec3674 100644
---
a/src/test/java/org/apache/sling/servlets/resolver/internal/SlingServletResolverTestBase.java
+++
b/src/test/java/org/apache/sling/servlets/resolver/internal/SlingServletResolverTestBase.java
@@ -125,7 +125,7 @@ public abstract class SlingServletResolverTestBase {
Mockito.when(bundle.getBundleContext()).thenReturn(bundleContext);
defineTestServlets(bundle);
- servletResolver.activate(config);
+ servletResolver.activate(bundleContext, config);
}