You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by pa...@apache.org on 2020/04/28 22:19:13 UTC

[sling-org-apache-sling-servlets-resolver] branch issues/SLING-9365 updated (0b87457 -> 4085489)

This is an automated email from the ASF dual-hosted git repository.

pauls pushed a change to branch issues/SLING-9365
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlets-resolver.git.


 discard 0b87457  SLING-9365: optionally use internal resource provider for servlets.
 discard e2c3b19  Remove servlet extensions from main scripts and adaptto servlets directly if they are provided by us.
 discard 5b54a8b  Optimize unregister of servlets.
 discard b21713f  Fix decorated resource resource resolver
 discard f2c2aca  Use resource decorator to decorate scripts into the tree
 discard 32b0424  Implement INTERNAL resolve.
 discard 243c7c2  SLING-9365: add merging resource provider for servlets.
     new 4085489  SLING-9365: add merging resource provider for servlets.

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (0b87457)
            \
             N -- N -- N   refs/heads/issues/SLING-9365 (4085489)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:


[sling-org-apache-sling-servlets-resolver] 01/01: SLING-9365: add merging resource provider for servlets.

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pauls pushed a commit to branch issues/SLING-9365
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlets-resolver.git

commit 4085489b9b861abb7d5defa1c6c45f6eace8a940
Author: Karl Pauls <ka...@gmail.com>
AuthorDate: Mon Apr 13 23:17:25 2020 +0200

    SLING-9365: add merging resource provider for servlets.
---
 .../servlets/resolver/internal/ResolverConfig.java |   5 +
 .../servlets/resolver/internal/ScriptResource.java |  14 +-
 .../resolver/internal/ScriptResourceDecorator.java |  98 ++++++++++++
 .../resolver/internal/ScriptResourceResolver.java  | 174 +++++++++++++++++++++
 .../resolver/internal/SlingServletResolver.java    |  37 +++--
 .../internal/helper/AbstractResourceCollector.java |   3 +-
 .../resource/MergingServletResourceProvider.java   | 166 ++++++++++++++++++++
 .../resolver/internal/resource/ServletMounter.java |  71 +++++++--
 .../internal/resource/ServletResource.java         |  17 +-
 .../resource/ServletResourceProviderFactory.java   |   8 +-
 .../internal/SlingServletResolverTestBase.java     |   2 +-
 11 files changed, 558 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..7d5b9d5 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 Internal", description = "Should servlets be mounted internally and not as a resource provider. If true,"
+        + " an internal resource provider will be used for servlet resolving and the servlets be decorated back into the resource tree instead of " +
+        "serveral resource provider per servlet. ")
+    boolean servletresolver_mountInternal() default false;
 }
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..fb4c9bb
--- /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, null, null);
+    }
+}
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..84be911
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlets/resolver/internal/ScriptResourceResolver.java
@@ -0,0 +1,174 @@
+/*
+ * 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.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<ResourceProvider> provider;
+
+    public ScriptResourceResolver(ResourceResolver resolver, Supplier<ResourceProvider> provider) {
+        super(resolver);
+        this.resolver = resolver;
+        this.provider = provider;
+    }
+
+    public static ScriptResourceResolver wrap(ResourceResolver scriptResourceResolver, Supplier<ResourceProvider> provider) {
+        return new ScriptResourceResolver(scriptResourceResolver, provider);
+    }
+
+    @Override
+    public Iterable<Resource> getChildren(Resource parent) {
+        return () -> listChildren(parent);
+    }
+
+    public Resource getResource(String scriptPath) {
+        ResourceProvider 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, null, null));
+        }
+    }
+
+    @Override
+    public Iterator<Resource> listChildren(Resource parent) {
+        ResourceProvider 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..82f6249
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlets/resolver/internal/resource/MergingServletResourceProvider.java
@@ -0,0 +1,166 @@
+/*
+ * 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.ResourceContext;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public class MergingServletResourceProvider extends ResourceProvider<Void> {
+    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);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public Resource getResource(ResolveContext resolveContext, String path, ResourceContext resourceContext, Resource parent) {
+        Resource wrapped = null;
+        final ResourceProvider parentProvider = resolveContext.getParentResourceProvider();
+        if (parentProvider != null) {
+            wrapped = parentProvider.getResource(resolveContext.getParentResolveContext(), path, resourceContext, parent);
+        }
+        Resource result;
+        Pair<ServletResourceProvider, ServiceReference<?>> provider = providers.get(path);
+
+        if (provider != null) {
+            result = provider.getLeft().getResource(resolveContext, path, resourceContext, parent);
+            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;
+    }
+
+    @Override
+    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..71606ef 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_mountInternal()) {
+            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);
 
     }