Optimize dispatcher servlet registration to use less registrations.
diff --git a/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptTracker.java b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptTracker.java
index e70ca8a..8f6a9bc 100644
--- a/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptTracker.java
+++ b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptTracker.java
@@ -23,13 +23,17 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -38,6 +42,7 @@
 import javax.servlet.RequestDispatcher;
 import javax.servlet.Servlet;
 import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletResponse;
@@ -96,6 +101,7 @@
 
     private volatile BundleContext m_context;
     private volatile BundleTracker<List<ServiceRegistration<Servlet>>> m_tracker;
+    private volatile Map<String, ServiceRegistration<Servlet>> m_dispatchers = new HashMap<>();
 
     @Activate
     private void activate(BundleContext context) {
@@ -119,7 +125,7 @@
 
             if (!capabilities.isEmpty()) {
 
-                return capabilities.stream().flatMap(cap ->
+                List<ServiceRegistration<Servlet>> serviceRegistrations = capabilities.stream().flatMap(cap ->
                 {
                     Hashtable<String, Object> properties = new Hashtable<>();
 
@@ -128,7 +134,8 @@
 
                     Version version = (Version) attributes.get(AT_VERSION);
 
-                    if (version != null) {
+                    if (version != null)
+                    {
                         resourceType += "/" + version;
                     }
 
@@ -136,36 +143,39 @@
 
                     Object selectors = attributes.get(AT_SLING_SELECTORS);
                     Set<String> extensions = new HashSet<>(
-                            Arrays.asList(PropertiesUtil.toStringArray(attributes.get(AT_SLING_EXTENSIONS), new String[0]))
+                        Arrays.asList(PropertiesUtil.toStringArray(attributes.get(AT_SLING_EXTENSIONS), new String[0]))
                     );
                     extensions.add("html");
                     properties.put(ServletResolverConstants.SLING_SERVLET_EXTENSIONS, extensions);
 
-                    if (selectors != null) {
+                    if (selectors != null)
+                    {
                         properties.put(ServletResolverConstants.SLING_SERVLET_SELECTORS, selectors);
                     }
 
                     Set<String> methods = new HashSet<>(Arrays.asList(PropertiesUtil.toStringArray(attributes.get(ServletResolverConstants.SLING_SERVLET_METHODS), new String[0])));
                     if (!methods.isEmpty())
                     {
-                        properties.put(ServletResolverConstants.SLING_SERVLET_METHODS, String.join(",",methods));
+                        properties.put(ServletResolverConstants.SLING_SERVLET_METHODS, String.join(",", methods));
                     }
 
                     String extendsRT = (String) attributes.get(AT_EXTENDS);
                     Optional<BundleWire> optionalWire = Optional.empty();
 
-                    if (StringUtils.isNotEmpty(extendsRT)) {
+                    if (StringUtils.isNotEmpty(extendsRT))
+                    {
 
                         LOGGER.debug("Bundle {} extends resource type {} through {}.", bundle.getSymbolicName(), extendsRT, resourceType);
                         optionalWire = bundleWiring.getRequiredWires(NS_SLING_RESOURCE_TYPE).stream().filter(
-                                bundleWire -> extendsRT.equals(bundleWire.getCapability().getAttributes().get(NS_SLING_RESOURCE_TYPE)) &&
-                                    !bundleWire.getCapability().getAttributes().containsKey(AT_SLING_SELECTORS)
+                            bundleWire -> extendsRT.equals(bundleWire.getCapability().getAttributes().get(NS_SLING_RESOURCE_TYPE)) &&
+                                !bundleWire.getCapability().getAttributes().containsKey(AT_SLING_SELECTORS)
                         ).findFirst();
                     }
 
                     List<ServiceRegistration<Servlet>> regs = new ArrayList<>();
 
-                    if (optionalWire.isPresent()) {
+                    if (optionalWire.isPresent())
+                    {
                         BundleWire extendsWire = optionalWire.get();
                         Map<String, Object> wireCapabilityAttributes = extendsWire.getCapability().getAttributes();
                         String wireResourceType = (String) wireCapabilityAttributes.get(NS_SLING_RESOURCE_TYPE);
@@ -175,10 +185,10 @@
                             new BundledScriptServlet(bundledScriptFinder, optionalWire.get().getProvider().getBundle(),
                                 scriptContextProvider, wireResourceType + (wireResourceTypeVersion != null ? "/" +
                                 wireResourceTypeVersion.toString() : ""), getWiredResourceTypes(
-                                    new HashSet<>(Arrays.asList((String) attributes.get(NS_SLING_RESOURCE_TYPE), wireResourceType)),
-                                    new HashSet<>(Arrays.asList(resourceType, wireResourceType + (wireResourceTypeVersion != null ? "/" +
-                                        wireResourceTypeVersion.toString() : ""))),
-                                    bundle,optionalWire.get().getProvider().getBundle())),
+                                new HashSet<>(Arrays.asList((String) attributes.get(NS_SLING_RESOURCE_TYPE), wireResourceType)),
+                                new HashSet<>(Arrays.asList(resourceType, wireResourceType + (wireResourceTypeVersion != null ? "/" +
+                                    wireResourceTypeVersion.toString() : ""))),
+                                bundle, optionalWire.get().getProvider().getBundle())),
                             properties
                         ));
                     }
@@ -187,16 +197,13 @@
                         regs.add(bundle.getBundleContext()
                             .registerService(Servlet.class, new BundledScriptServlet(bundledScriptFinder, bundle, scriptContextProvider,
                                     getWiredResourceTypes(new HashSet<>(Arrays.asList((String) attributes.get(NS_SLING_RESOURCE_TYPE))),
-                                        new HashSet<>(Arrays.asList(resourceType)),bundle)),
+                                        new HashSet<>(Arrays.asList(resourceType)), bundle)),
                                 properties));
                     }
-                    if (version != null)
-                    {
-                        properties.put(ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES, attributes.get(NS_SLING_RESOURCE_TYPE));
-                        regs.add(m_context.registerService(Servlet.class, new DispatcherServlet((String) attributes.get(NS_SLING_RESOURCE_TYPE)), properties));
-                    }
                     return regs.stream();
                 }).collect(Collectors.toList());
+                refreshDispatcher(serviceRegistrations);
+                return serviceRegistrations;
             } else {
                 return null;
             }
@@ -243,6 +250,38 @@
         return wiredResourceTypes;
     }
 
+    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) -> {
+                Hashtable<String, Object> properties = new Hashtable<>();
+                properties.put(ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES, rt);
+                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());
+                if (!methods.equals(new HashSet<>(Arrays.asList("GET", "HEAD"))))
+                {
+                    properties.put(ServletResolverConstants.SLING_SERVLET_METHODS, methods.toArray(new String[0]));
+                }
+                ServiceRegistration<Servlet> reg = m_dispatchers.remove(rt);
+                if (reg == null)
+                {
+                    reg = m_context.registerService(Servlet.class, new DispatcherServlet(rt), properties);
+                }
+                else
+                {
+                    if (!new HashSet<>(Arrays.asList(PropertiesUtil.toStringArray(reg.getReference().getProperty(ServletResolverConstants.SLING_SERVLET_METHODS), new String[0])))
+                        .equals(methods))
+                    {
+                        reg.setProperties(properties);
+                    }
+                }
+                dispatchers.put(rt, reg);
+            });
+        m_dispatchers.values().forEach(ServiceRegistration::unregister);
+        m_dispatchers = dispatchers;
+    }
+
     private Hashtable<String, Object> toProperties(ServiceRegistration<?> reg)
     {
         Hashtable<String, Object> result = new Hashtable<>();
@@ -302,6 +341,7 @@
     public void removedBundle(Bundle bundle, BundleEvent event, List<ServiceRegistration<Servlet>> regs) {
         LOGGER.debug("Bundle {} removed", bundle.getSymbolicName());
         regs.forEach(ServiceRegistration::unregister);
+        refreshDispatcher(Collections.EMPTY_LIST);
     }
 
     private class DispatcherServlet extends GenericServlet
@@ -316,6 +356,7 @@
         @Override
         public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
         {
+
             SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) req;
 
             Optional<ServiceRegistration<Servlet>> target = m_tracker.getTracked().values().stream().flatMap(List::stream)
@@ -328,9 +369,7 @@
                     Hashtable<String, Object> props = toProperties(reg);
                     if (getResourceType(props).equals(m_rt))
                     {
-                        if (Arrays.asList(PropertiesUtil.toStringArray(props.get(ServletResolverConstants.SLING_SERVLET_SELECTORS), new String[0]))
-                            .containsAll(Arrays.asList(slingRequest.getRequestPathInfo().getSelectors()))
-                            &&
+                        if (
                             Arrays.asList(PropertiesUtil.toStringArray(props.get(ServletResolverConstants.SLING_SERVLET_METHODS), new String[]{"GET", "HEAD"}))
                                 .contains(slingRequest.getMethod())
                             &&
@@ -342,7 +381,26 @@
                     }
                     return false;
                 })
-                .sorted(Comparator.comparing(reg -> new Version(getResourceTypeVersion(reg.getReference())), Comparator.reverseOrder()))
+                .sorted((left, right) ->
+                {
+                    boolean la = Arrays.asList(PropertiesUtil.toStringArray(toProperties(left).get(ServletResolverConstants.SLING_SERVLET_SELECTORS), new String[0]))
+                        .containsAll(Arrays.asList(slingRequest.getRequestPathInfo().getSelectors()));
+                    boolean ra = Arrays.asList(PropertiesUtil.toStringArray(toProperties(right).get(ServletResolverConstants.SLING_SERVLET_SELECTORS), new String[0]))
+                        .containsAll(Arrays.asList(slingRequest.getRequestPathInfo().getSelectors()));
+                    if ((la && ra) || (!la && !ra))
+                    {
+                        return new Version(getResourceTypeVersion(right.getReference())).compareTo(new Version(getResourceTypeVersion(left.getReference())));
+                    }
+                    else if (la)
+                    {
+                        return -1;
+                    }
+                    else
+                    {
+                        return 1;
+                    }
+
+                })
                 .findFirst();
 
             if (target.isPresent())