SLING-9006 : Remove contention on sling request listener manager
diff --git a/src/main/java/org/apache/sling/engine/impl/SlingMainServlet.java b/src/main/java/org/apache/sling/engine/impl/SlingMainServlet.java
index 5f94c31..7451c76 100644
--- a/src/main/java/org/apache/sling/engine/impl/SlingMainServlet.java
+++ b/src/main/java/org/apache/sling/engine/impl/SlingMainServlet.java
@@ -61,6 +61,7 @@
 import org.osgi.service.component.annotations.Reference;
 import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
 import org.osgi.service.component.propertytypes.ServiceDescription;
 import org.osgi.service.component.propertytypes.ServiceVendor;
 import org.osgi.service.http.context.ServletContextHelper;
@@ -139,6 +140,9 @@
     @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
     private volatile AdapterManager adapterManager;
 
+    @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
+    private volatile RequestListenerManager requestListenerManager;
+
     private BundleContext bundleContext;
 
     /** default log */
@@ -183,8 +187,6 @@
      */
     private volatile String serverInfo = PRODUCT_NAME;
 
-    private volatile RequestListenerManager requestListenerManager;
-
     private volatile boolean allowTrace;
 
     // new properties
@@ -227,7 +229,10 @@
             // set the thread name according to the request
             String threadName = setThreadName(request);
 
-            requestListenerManager.sendEvent( request, SlingRequestEvent.EventType.EVENT_INIT );
+            final RequestListenerManager localRLM = requestListenerManager;
+            if (localRLM != null) {
+                localRLM.sendEvent(request, SlingRequestEvent.EventType.EVENT_INIT);
+            }
 
             ResourceResolver resolver = null;
             try {
@@ -269,7 +274,9 @@
                     resolver.close();
                 }
 
-                requestListenerManager.sendEvent( request, SlingRequestEvent.EventType.EVENT_DESTROY );
+                if (localRLM != null) {
+                    localRLM.sendEvent(request, SlingRequestEvent.EventType.EVENT_DESTROY);
+                }
 
                 // reset the thread name
                 if (threadName != null) {
@@ -497,9 +504,6 @@
                     filterManager.open();
                     requestProcessor.setFilterManager(filterManager);
 
-                    // initialize requestListenerManager
-                    requestListenerManager = new RequestListenerManager(bundleContext, slingServletContext);
-
                     try {
                         Dictionary<String, String> mbeanProps = new Hashtable<>();
                         mbeanProps.put("jmx.objectname", "org.apache.sling:type=engine,service=RequestProcessor");
@@ -547,13 +551,6 @@
             this.servletRegistration = null;
         }
 
-        // dispose of request listener manager after unregistering the servlet
-        // to prevent a potential NPE in the service method
-        if ( this.requestListenerManager != null ) {
-            this.requestListenerManager.dispose();
-            this.requestListenerManager = null;
-        }
-
         // reset the sling main servlet reference (help GC and be nice)
         RequestData.setSlingMainServlet(null);
 
diff --git a/src/main/java/org/apache/sling/engine/impl/helper/RequestListenerManager.java b/src/main/java/org/apache/sling/engine/impl/helper/RequestListenerManager.java
index 96a9174..4cd3fd0 100644
--- a/src/main/java/org/apache/sling/engine/impl/helper/RequestListenerManager.java
+++ b/src/main/java/org/apache/sling/engine/impl/helper/RequestListenerManager.java
@@ -18,38 +18,55 @@
  */

 package org.apache.sling.engine.impl.helper;

 

+import java.util.List;

+

 import javax.servlet.ServletContext;

 import javax.servlet.http.HttpServletRequest;

 

 import org.apache.sling.api.request.SlingRequestEvent;

 import org.apache.sling.api.request.SlingRequestListener;

-import org.osgi.framework.BundleContext;

-import org.osgi.util.tracker.ServiceTracker;

+import org.apache.sling.engine.impl.SlingMainServlet;

+import org.osgi.service.component.annotations.Activate;

+import org.osgi.service.component.annotations.Component;

+import org.osgi.service.component.annotations.FieldOption;

+import org.osgi.service.component.annotations.Reference;

+import org.osgi.service.component.annotations.ReferenceCardinality;

+import org.osgi.service.component.annotations.ReferencePolicy;

+import org.osgi.service.component.propertytypes.ServiceDescription;

+import org.osgi.service.component.propertytypes.ServiceVendor;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

 

+@Component(service = RequestListenerManager.class)

+@ServiceDescription("Request listener manager")

+@ServiceVendor("The Apache Software Foundation")

 public class RequestListenerManager  {

 

-    private final ServiceTracker serviceTracker;

-

     private final ServletContext servletContext;

 

-	public RequestListenerManager( final BundleContext context, final ServletContext servletContext ) {

-		serviceTracker = new ServiceTracker( context, SlingRequestListener.SERVICE_NAME, null );

-		serviceTracker.open();

+    private final Logger logger = LoggerFactory.getLogger(this.getClass());

+

+    @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, fieldOption = FieldOption.REPLACE)

+    private volatile List<SlingRequestListener> listeners;

+

+    @Activate

+    public RequestListenerManager(@Reference(target = "(name=" + SlingMainServlet.SERVLET_CONTEXT_NAME

+            + ")") final ServletContext servletContext) {

 		this.servletContext = servletContext;

 	}

 

 	public void sendEvent ( final HttpServletRequest request,

 	        final SlingRequestEvent.EventType type) {

-		final Object[] services = serviceTracker.getServices();

-		if ( services != null && services.length > 0 ) {

+        final List<SlingRequestListener> local = listeners;

+        if (local != null && !local.isEmpty()) {

 		    final SlingRequestEvent event = new SlingRequestEvent(this.servletContext, request, type);

-			for ( final Object service : services ) {

-				( (SlingRequestListener) service ).onEvent( event );

+            for (final SlingRequestListener service : local) {

+                try {

+                    service.onEvent(event);

+                } catch (final Throwable t) {

+                    logger.error("Error invoking sling request listener " + service + " : " + t.getMessage(), t);

+                }

 			}

 		}

 	}

-

-	public void dispose() {

-	    this.serviceTracker.close();

-	}

 }