FELIX-4940 : Use servlet 3.0 API to register all required web components

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1687617 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/proxy/pom.xml b/http/proxy/pom.xml
index 7479681..482e9dd 100644
--- a/http/proxy/pom.xml
+++ b/http/proxy/pom.xml
@@ -62,6 +62,7 @@
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.core</artifactId>
+            <version>6.0.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/http/proxy/src/main/java/org/apache/felix/http/proxy/DispatcherTracker.java b/http/proxy/src/main/java/org/apache/felix/http/proxy/DispatcherTracker.java
index 9901575..82ddba3 100644
--- a/http/proxy/src/main/java/org/apache/felix/http/proxy/DispatcherTracker.java
+++ b/http/proxy/src/main/java/org/apache/felix/http/proxy/DispatcherTracker.java
@@ -16,14 +16,19 @@
  */
 package org.apache.felix.http.proxy;
 
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.Filter;
-import org.osgi.framework.Constants;
-import javax.servlet.http.HttpServlet;
 import javax.servlet.ServletConfig;
+import javax.servlet.http.HttpServlet;
 
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * @deprecated
+ */
+@Deprecated
 public final class DispatcherTracker
     extends ServiceTracker
 {
diff --git a/http/proxy/src/main/java/org/apache/felix/http/proxy/ProxyListener.java b/http/proxy/src/main/java/org/apache/felix/http/proxy/ProxyListener.java
index 175b5f2..1a0923b 100644
--- a/http/proxy/src/main/java/org/apache/felix/http/proxy/ProxyListener.java
+++ b/http/proxy/src/main/java/org/apache/felix/http/proxy/ProxyListener.java
@@ -18,8 +18,6 @@
  */
 package org.apache.felix.http.proxy;
 
-import java.util.EventListener;
-
 import javax.servlet.ServletContext;
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
@@ -29,12 +27,10 @@
 import javax.servlet.http.HttpSessionIdListener;
 import javax.servlet.http.HttpSessionListener;
 
+import org.apache.felix.http.proxy.impl.EventDispatcherTracker;
+import org.apache.felix.http.proxy.impl.ProxyServletContextListener;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
 
 /**
  * The <code>ProxyListener</code> implements a Servlet API listener for HTTP
@@ -42,7 +38,9 @@
  * and forwarded to the event dispatcher.
  *
  * @since 2.1.0
+ * @deprecated Use the {@link ProxyServletContextListener} instead.
  */
+@Deprecated
 public class ProxyListener
     implements HttpSessionAttributeListener,
                HttpSessionListener,
@@ -50,15 +48,9 @@
                ServletContextListener
 {
 
-    private ServletContext servletContext;
+    private volatile ServletContext servletContext;
 
-    private ServiceTracker eventDispatcherTracker;
-
-    private HttpSessionListener sessionDispatcher;
-
-    private HttpSessionIdListener sessionIdDispatcher;
-
-    private HttpSessionAttributeListener attributeDispatcher;
+    private volatile EventDispatcherTracker eventDispatcherTracker;
 
     // ---------- ServletContextListener
 
@@ -162,18 +154,7 @@
             try
             {
                 BundleContext bundleContext = (BundleContext) bundleContextAttr;
-                Filter filter = createFilter(bundleContext, null);
-                this.eventDispatcherTracker = new ServiceTracker(bundleContext, filter, null)
-                {
-                    @Override
-                    public void removedService(ServiceReference reference, Object service)
-                    {
-                        ProxyListener.this.sessionDispatcher = null;
-                        ProxyListener.this.attributeDispatcher = null;
-                        ProxyListener.this.sessionIdDispatcher = null;
-                        super.removedService(reference, service);
-                    }
-                };
+                this.eventDispatcherTracker = new EventDispatcherTracker(bundleContext);
                 this.eventDispatcherTracker.open();
             }
             catch (InvalidSyntaxException e)
@@ -187,49 +168,28 @@
 
     private HttpSessionListener getSessionDispatcher()
     {
-        if (this.sessionDispatcher == null)
+        if (this.eventDispatcherTracker != null)
         {
-            final Object dispatcher = getDispatcher();
-            if (dispatcher instanceof HttpSessionListener)
-            {
-                this.sessionDispatcher = (HttpSessionListener) dispatcher;
-            }
+            return this.eventDispatcherTracker.getHttpSessionListener();
         }
-        return this.sessionDispatcher;
+        return null;
     }
 
     private HttpSessionIdListener getSessionIdDispatcher()
     {
-        if (this.sessionIdDispatcher == null)
+        if (this.eventDispatcherTracker != null)
         {
-            final Object dispatcher = getDispatcher();
-            if (dispatcher instanceof HttpSessionIdListener)
-            {
-                this.sessionIdDispatcher = (HttpSessionIdListener) dispatcher;
-            }
+            return this.eventDispatcherTracker.getHttpSessionIdListener();
         }
-        return this.sessionIdDispatcher;
+        return null;
     }
 
     private HttpSessionAttributeListener getAttributeDispatcher()
     {
-        if (this.attributeDispatcher == null)
+        if (this.eventDispatcherTracker != null)
         {
-            final Object dispatcher = getDispatcher();
-            if (dispatcher instanceof HttpSessionAttributeListener)
-            {
-                this.attributeDispatcher = (HttpSessionAttributeListener) dispatcher;
-            }
+            return this.eventDispatcherTracker.getHttpSessionAttributeListener();
         }
-        return this.attributeDispatcher;
-    }
-
-    private static Filter createFilter(BundleContext context, String filter) throws InvalidSyntaxException
-    {
-        StringBuffer str = new StringBuffer();
-        str.append("(&(").append(Constants.OBJECTCLASS).append("=");
-        str.append(EventListener.class.getName()).append(")");
-        str.append(filter != null ? filter : DispatcherTracker.DEFAULT_FILTER).append(")");
-        return context.createFilter(str.toString());
+        return null;
     }
 }
diff --git a/http/proxy/src/main/java/org/apache/felix/http/proxy/impl/BridgeServiceTracker.java b/http/proxy/src/main/java/org/apache/felix/http/proxy/impl/BridgeServiceTracker.java
new file mode 100644
index 0000000..36679ff
--- /dev/null
+++ b/http/proxy/src/main/java/org/apache/felix/http/proxy/impl/BridgeServiceTracker.java
@@ -0,0 +1,81 @@
+/*
+ * 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.felix.http.proxy.impl;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * @since 3.0.0
+ */
+public abstract class BridgeServiceTracker<T>
+    extends ServiceTracker<T, T>
+{
+    private final static String DEFAULT_FILTER = "(http.felix.dispatcher=*)";
+
+    private volatile T usedService;
+
+    public BridgeServiceTracker(final BundleContext context, final Class<?> objectClass)
+        throws InvalidSyntaxException
+    {
+        super(context, createFilter(context, objectClass), null);
+    }
+
+
+    protected abstract void setService(final T service);
+
+    protected abstract void unsetService();
+
+
+    @Override
+    public T addingService(final ServiceReference<T> ref)
+    {
+        final T service = super.addingService(ref);
+        if ( usedService == null )
+        {
+            this.usedService = service;
+            this.setService(service);
+        }
+
+        return service;
+    }
+
+    @Override
+    public void removedService(final ServiceReference<T> ref, final T service)
+    {
+        if ( service == usedService )
+        {
+            this.unsetService();
+        }
+
+        super.removedService(ref, service);
+    }
+
+    private static Filter createFilter(final BundleContext context, final Class<?> objectClass)
+        throws InvalidSyntaxException
+    {
+        StringBuffer str = new StringBuffer();
+        str.append("(&(").append(Constants.OBJECTCLASS).append("=");
+        str.append(objectClass.getName()).append(")");
+        str.append(DEFAULT_FILTER).append(")");
+        return context.createFilter(str.toString());
+    }
+}
diff --git a/http/proxy/src/main/java/org/apache/felix/http/proxy/impl/EventDispatcherTracker.java b/http/proxy/src/main/java/org/apache/felix/http/proxy/impl/EventDispatcherTracker.java
new file mode 100644
index 0000000..9c11700
--- /dev/null
+++ b/http/proxy/src/main/java/org/apache/felix/http/proxy/impl/EventDispatcherTracker.java
@@ -0,0 +1,85 @@
+/*
+ * 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.felix.http.proxy.impl;
+
+import java.util.EventListener;
+
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionIdListener;
+import javax.servlet.http.HttpSessionListener;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * @since 3.0.0
+ */
+public final class EventDispatcherTracker
+    extends BridgeServiceTracker<EventListener>
+{
+    private HttpSessionListener sessionListener;
+
+    private HttpSessionIdListener sessionIdListener;
+
+    private HttpSessionAttributeListener sessionAttributeListener;
+
+    public EventDispatcherTracker(final BundleContext context)
+        throws InvalidSyntaxException
+    {
+        super(context, EventListener.class);
+    }
+
+    @Override
+    protected void setService(final EventListener service)
+    {
+        if ( service instanceof HttpSessionListener )
+        {
+            this.sessionListener = (HttpSessionListener)service;
+        }
+        if ( service instanceof HttpSessionIdListener )
+        {
+            this.sessionIdListener = (HttpSessionIdListener)service;
+        }
+        if ( service instanceof HttpSessionAttributeListener )
+        {
+            this.sessionAttributeListener = (HttpSessionAttributeListener)service;
+        }
+    }
+
+    public HttpSessionListener getHttpSessionListener()
+    {
+        return this.sessionListener;
+    }
+
+    public HttpSessionIdListener getHttpSessionIdListener()
+    {
+        return this.sessionIdListener;
+    }
+
+    public HttpSessionAttributeListener getHttpSessionAttributeListener()
+    {
+        return this.sessionAttributeListener;
+    }
+
+    @Override
+    protected void unsetService()
+    {
+        sessionListener = null;
+        sessionIdListener = null;
+        sessionAttributeListener = null;
+    }
+}
diff --git a/http/proxy/src/main/java/org/apache/felix/http/proxy/impl/ProxyServletContextListener.java b/http/proxy/src/main/java/org/apache/felix/http/proxy/impl/ProxyServletContextListener.java
new file mode 100644
index 0000000..e31656c
--- /dev/null
+++ b/http/proxy/src/main/java/org/apache/felix/http/proxy/impl/ProxyServletContextListener.java
@@ -0,0 +1,219 @@
+/*
+ * 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.felix.http.proxy.impl;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionIdListener;
+import javax.servlet.http.HttpSessionListener;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * The ProxyServletContextListener is a servlet context listener which will setup
+ * all required listeners for the http service implementation.
+ *
+ * @since 3.0.0
+ */
+@WebListener
+public class ProxyServletContextListener
+    implements ServletContextListener
+{
+
+    private volatile ServletContext servletContext;
+
+    private volatile EventDispatcherTracker eventDispatcherTracker;
+
+    // ---------- ServletContextListener
+
+    @Override
+    public void contextInitialized(final ServletContextEvent sce)
+    {
+        this.servletContext = sce.getServletContext();
+
+        // add all required listeners
+        this.servletContext.addListener(new HttpSessionListener()
+        {
+            private HttpSessionListener getHttpSessionListener()
+            {
+                final EventDispatcherTracker tracker = eventDispatcherTracker;
+                if ( tracker != null )
+                {
+                    return tracker.getHttpSessionListener();
+                }
+                return null;
+            }
+
+            @Override
+            public void sessionCreated(final HttpSessionEvent se)
+            {
+                final HttpSessionListener sessionDispatcher = getHttpSessionListener();
+                if (sessionDispatcher != null)
+                {
+                    sessionDispatcher.sessionCreated(se);
+                }
+            }
+
+            @Override
+            public void sessionDestroyed(final HttpSessionEvent se)
+            {
+                final HttpSessionListener sessionDispatcher = getHttpSessionListener();
+                if (sessionDispatcher != null)
+                {
+                    sessionDispatcher.sessionDestroyed(se);
+                }
+            }
+        });
+        this.servletContext.addListener(new HttpSessionIdListener()
+        {
+            private HttpSessionIdListener getHttpSessionIdListener()
+            {
+                final EventDispatcherTracker tracker = eventDispatcherTracker;
+                if ( tracker != null )
+                {
+                    return tracker.getHttpSessionIdListener();
+                }
+                return null;
+            }
+
+            @Override
+            public void sessionIdChanged(final HttpSessionEvent event, final String oldSessionId)
+            {
+                final HttpSessionIdListener sessionIdDispatcher = getHttpSessionIdListener();
+                if (sessionIdDispatcher != null)
+                {
+                    sessionIdDispatcher.sessionIdChanged(event, oldSessionId);
+                }
+            }
+        });
+        this.servletContext.addListener(new HttpSessionAttributeListener()
+        {
+            private HttpSessionAttributeListener getHttpSessionAttributeListener()
+            {
+                final EventDispatcherTracker tracker = eventDispatcherTracker;
+                if ( tracker != null )
+                {
+                    return tracker.getHttpSessionAttributeListener();
+                }
+                return null;
+            }
+
+            @Override
+            public void attributeAdded(final HttpSessionBindingEvent se)
+            {
+                final HttpSessionAttributeListener attributeDispatcher = getHttpSessionAttributeListener();
+                if (attributeDispatcher != null)
+                {
+                    attributeDispatcher.attributeAdded(se);
+                }
+            }
+
+            @Override
+            public void attributeRemoved(final HttpSessionBindingEvent se)
+            {
+                final HttpSessionAttributeListener attributeDispatcher = getHttpSessionAttributeListener();
+                if (attributeDispatcher != null)
+                {
+                    attributeDispatcher.attributeRemoved(se);
+                }
+            }
+
+            @Override
+            public void attributeReplaced(final HttpSessionBindingEvent se)
+            {
+                final HttpSessionAttributeListener attributeDispatcher = getHttpSessionAttributeListener();
+                if (attributeDispatcher != null)
+                {
+                    attributeDispatcher.attributeReplaced(se);
+                }
+            }
+        });
+        this.servletContext.addListener(new ServletContextAttributeListener()
+        {
+            @Override
+            public void attributeAdded(final ServletContextAttributeEvent event)
+            {
+                if ( event.getName().equals(BundleContext.class.getName()) )
+                {
+                    startTracker(event.getValue());
+                }
+            }
+
+            @Override
+            public void attributeRemoved(final ServletContextAttributeEvent event)
+            {
+                if ( event.getName().equals(BundleContext.class.getName()) )
+                {
+                    stopTracker();
+                }
+            }
+
+            @Override
+            public void attributeReplaced(final ServletContextAttributeEvent event)
+            {
+                if ( event.getName().equals(BundleContext.class.getName()) )
+                {
+                    stopTracker();
+                    startTracker(event.getServletContext().getAttribute(event.getName()));
+                }
+            }
+        });
+    }
+
+    private void startTracker(final Object bundleContextAttr)
+    {
+        if (bundleContextAttr instanceof BundleContext)
+        {
+            try
+            {
+                final BundleContext bundleContext = (BundleContext) bundleContextAttr;
+                this.eventDispatcherTracker = new EventDispatcherTracker(bundleContext);
+                this.eventDispatcherTracker.open();
+            }
+            catch (final InvalidSyntaxException e)
+            {
+                // not expected for our simple filter
+            }
+        }
+    }
+
+    private void stopTracker()
+    {
+        if (this.eventDispatcherTracker != null)
+        {
+            this.eventDispatcherTracker.close();
+            this.eventDispatcherTracker = null;
+        }
+    }
+
+    @Override
+    public void contextDestroyed(final ServletContextEvent sce)
+    {
+        this.stopTracker();
+        this.servletContext = null;
+    }
+}