FELIX-4060 : Implement HTTP Service Update (RFC-189)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1656336 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextAttributeListenerInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextAttributeListenerInfo.java
new file mode 100644
index 0000000..27fd5da
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextAttributeListenerInfo.java
@@ -0,0 +1,36 @@
+/*
+ * 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.base.internal.runtime;
+
+import javax.servlet.ServletContextAttributeListener;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Info object for registered servlet context attribute listeners
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class ServletContextAttributeListenerInfo extends WhiteboardServiceInfo<ServletContextAttributeListener>
+{
+    public ServletContextAttributeListenerInfo(final ServiceReference<ServletContextAttributeListener> ref)
+    {
+        super(ref);
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
index 68b9366..4ba0741 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
@@ -18,14 +18,18 @@
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.annotation.Nonnull;
 import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
 import org.apache.felix.http.base.internal.runtime.ContextInfo;
+import org.apache.felix.http.base.internal.runtime.ServletContextAttributeListenerInfo;
 import org.apache.felix.http.base.internal.runtime.ServletContextListenerInfo;
 import org.osgi.framework.Bundle;
 import org.osgi.service.http.context.ServletContextHelper;
@@ -41,8 +45,12 @@
     /** The shared part of the servlet context. */
     private final ServletContext sharedContext;
 
+    /** Servlet context listeners. */
     private final Map<Long, ServletContextListener> listeners = new HashMap<Long, ServletContextListener>();
 
+    /** Servlet context attribute listeners. */
+    private final Map<Long, ServletContextAttributeListener> contextAttributeListeners = new ConcurrentHashMap<Long, ServletContextAttributeListener>();
+
     /**
      * Create new handler.
      * @param info
@@ -51,7 +59,35 @@
     public ContextHandler(final ContextInfo info, final ServletContext webContext)
     {
         this.info = info;
-        this.sharedContext = new SharedServletContextImpl(webContext, info.getPrefix(), info.getName());
+        this.sharedContext = new SharedServletContextImpl(webContext,
+                info.getPrefix(),
+                info.getName(),
+                new ServletContextAttributeListener() {
+
+                    @Override
+                    public void attributeReplaced(final ServletContextAttributeEvent event) {
+                        for(final ServletContextAttributeListener l : contextAttributeListeners.values())
+                        {
+                            l.attributeReplaced(event);
+                        }
+                    }
+
+                    @Override
+                    public void attributeRemoved(final ServletContextAttributeEvent event) {
+                        for(final ServletContextAttributeListener l : contextAttributeListeners.values())
+                        {
+                            l.attributeReplaced(event);
+                        }
+                    }
+
+                    @Override
+                    public void attributeAdded(final ServletContextAttributeEvent event) {
+                        for(final ServletContextAttributeListener l : contextAttributeListeners.values())
+                        {
+                            l.attributeReplaced(event);
+                        }
+                    }
+                });
     }
 
     /**
@@ -143,6 +179,24 @@
         }
     }
 
+    public void addListener(@Nonnull final Bundle bundle, @Nonnull final ServletContextAttributeListenerInfo info)
+    {
+        final  ServletContextAttributeListener service = bundle.getBundleContext().getServiceObjects(info.getServiceReference()).getService();
+        if ( service != null )
+        {
+            this.contextAttributeListeners.put(info.getServiceId(), service);
+        }
+    }
+
+    public void removeListener(@Nonnull final Bundle bundle, @Nonnull final ServletContextAttributeListenerInfo info)
+    {
+        final  ServletContextAttributeListener service = this.contextAttributeListeners.remove(info.getServiceId());
+        if ( service != null )
+        {
+            bundle.getBundleContext().getServiceObjects(info.getServiceReference()).ungetService(service);
+        }
+    }
+
     private static final class ContextHolder
     {
         public long counter;
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
index 41c2b0a..935d8e2 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
@@ -30,6 +30,7 @@
 import org.apache.felix.http.base.internal.runtime.ContextInfo;
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.runtime.ResourceInfo;
+import org.apache.felix.http.base.internal.runtime.ServletContextAttributeListenerInfo;
 import org.apache.felix.http.base.internal.runtime.ServletContextListenerInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
 import org.apache.felix.http.base.internal.runtime.WhiteboardServiceInfo;
@@ -316,6 +317,10 @@
         {
             this.httpService.registerResource(handler, (ResourceInfo)info);
         }
+        else if ( info instanceof ServletContextAttributeListenerInfo )
+        {
+            handler.addListener(this.bundle, (ServletContextAttributeListenerInfo)info );
+        }
     }
 
     /**
@@ -337,5 +342,9 @@
         {
             this.httpService.unregisterResource(handler, (ResourceInfo)info);
         }
+        else if ( info instanceof ServletContextAttributeListenerInfo )
+        {
+            handler.removeListener(this.bundle, (ServletContextAttributeListenerInfo)info );
+        }
     }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/SharedServletContextImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/SharedServletContextImpl.java
index d7d1cdd..dd10996 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/SharedServletContextImpl.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/SharedServletContextImpl.java
@@ -31,6 +31,8 @@
 import javax.servlet.RequestDispatcher;
 import javax.servlet.Servlet;
 import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRegistration;
 import javax.servlet.SessionCookieConfig;
@@ -52,10 +54,12 @@
     private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
     private final String contextPath;
     private final String name;
+    private final ServletContextAttributeListener attributeListener;
 
     public SharedServletContextImpl(final ServletContext webContext,
             final String name,
-            final String prefix)
+            final String prefix,
+            final ServletContextAttributeListener servletContextAttributeListener)
     {
         this.context = webContext;
         if ( prefix == null )
@@ -67,6 +71,7 @@
             this.contextPath = webContext.getContextPath() + prefix;
         }
         this.name = name;
+        this.attributeListener = servletContextAttributeListener;
     }
 
     @Override
@@ -384,11 +389,11 @@
     @Override
     public void removeAttribute(final String name)
     {
-        Object oldValue = this.attributes.remove(name);
+        final Object oldValue = this.attributes.remove(name);
 
         if (oldValue != null)
         {
-            //this.attributeListener.attributeRemoved(new ServletContextAttributeEvent(this, name, oldValue));
+            this.attributeListener.attributeRemoved(new ServletContextAttributeEvent(this, name, oldValue));
         }
     }
 
@@ -405,11 +410,11 @@
 
             if (oldValue == null)
             {
-                //this.attributeListener.attributeAdded(new ServletContextAttributeEvent(this, name, value));
+                this.attributeListener.attributeAdded(new ServletContextAttributeEvent(this, name, value));
             }
             else
             {
-                //this.attributeListener.attributeReplaced(new ServletContextAttributeEvent(this, name, oldValue));
+                this.attributeListener.attributeReplaced(new ServletContextAttributeEvent(this, name, oldValue));
             }
         }
     }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextAttributeListenerTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextAttributeListenerTracker.java
new file mode 100644
index 0000000..9df9831
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextAttributeListenerTracker.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.base.internal.whiteboard.tracker;
+
+import javax.servlet.ServletContextAttributeListener;
+
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.ServletContextAttributeListenerInfo;
+import org.apache.felix.http.base.internal.whiteboard.ServletContextHelperManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+public final class ServletContextAttributeListenerTracker extends AbstractReferenceTracker<ServletContextAttributeListener>
+{
+    private final ServletContextHelperManager contextManager;
+
+    /** TODO Currently under discussion. */
+    private static final String OPT_IN_PROP = "osgi.http.whiteboard.listener";
+
+    private static org.osgi.framework.Filter createFilter(final BundleContext btx)
+    {
+        try
+        {
+            return btx.createFilter(String.format("(&(objectClass=%s)(%s=*))",
+                    ServletContextAttributeListener.class.getName(),
+                    OPT_IN_PROP));
+        }
+        catch ( final InvalidSyntaxException ise)
+        {
+            // we can safely ignore it as the above filter is a constant
+        }
+        return null; // we never get here - and if we get an NPE which is fine
+    }
+
+    public ServletContextAttributeListenerTracker(final BundleContext context, final ServletContextHelperManager manager)
+    {
+        super(context, createFilter(context));
+        this.contextManager = manager;
+    }
+
+    @Override
+    protected void added(final ServiceReference<ServletContextAttributeListener> ref)
+    {
+        final ServletContextAttributeListenerInfo info = new ServletContextAttributeListenerInfo(ref);
+
+        if ( info.isValid() )
+        {
+            this.contextManager.addWhiteboardService(info);
+        }
+        else
+        {
+            SystemLogger.debug("Ignoring ServletContextAttributeListenerInfo service " + ref);
+        }
+    }
+
+    @Override
+    protected void removed(final ServiceReference<ServletContextAttributeListener> ref)
+    {
+        final ServletContextAttributeListenerInfo info = new ServletContextAttributeListenerInfo(ref);
+
+        if ( info.isValid() )
+        {
+            this.contextManager.removeWhiteboardService(info);
+        }
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextListenerTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextListenerTracker.java
index e7632b3..37306ef 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextListenerTracker.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextListenerTracker.java
@@ -64,7 +64,7 @@
         }
         else
         {
-            SystemLogger.debug("Ignoring Filter service " + ref);
+            SystemLogger.debug("Ignoring ServletContextListenerInfo service " + ref);
         }
     }