FELIX-4545 : Implement Servlet Context Helper

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1655989 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
index 535ad38..75a9532 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
@@ -30,6 +30,7 @@
 import org.apache.felix.http.base.internal.listener.ServletRequestAttributeListenerManager;
 import org.apache.felix.http.base.internal.listener.ServletRequestListenerManager;
 import org.apache.felix.http.base.internal.service.HttpServiceFactory;
+import org.apache.felix.http.base.internal.service.HttpServiceImpl;
 import org.apache.felix.http.base.internal.service.HttpServiceRuntimeImpl;
 import org.apache.felix.http.base.internal.whiteboard.ExtenderManager;
 import org.osgi.framework.BundleContext;
@@ -143,7 +144,7 @@
         HttpServiceFactory factory = new HttpServiceFactory(servletContext, this.registry, this.contextAttributeListener, this.sharedContextAttributes);
 
         this.serviceReg = this.bundleContext.registerService(ifaces, factory, this.serviceProps);
-        this.manager = new ExtenderManager((HttpService)factory.getService(this.bundleContext.getBundle(), this.serviceReg), this.bundleContext);
+        this.manager = new ExtenderManager((HttpServiceImpl)factory.getService(this.bundleContext.getBundle(), this.serviceReg), this.bundleContext);
 
         this.runtimeReg = this.bundleContext.registerService(HttpServiceRuntime.class, new HttpServiceRuntimeImpl(), null);
     }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
index 3a33ef6..dc61cd4 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
@@ -17,7 +17,9 @@
 package org.apache.felix.http.base.internal.handler;
 
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -25,6 +27,7 @@
 import javax.servlet.Servlet;
 import javax.servlet.ServletException;
 
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
 import org.osgi.service.http.NamespaceException;
 
 public final class HandlerRegistry
@@ -50,7 +53,8 @@
         return filters;
     }
 
-    public void addServlet(ServletHandler handler) throws ServletException, NamespaceException
+    public void addServlet(final ServletHandler handler)
+    throws ServletException, NamespaceException
     {
         handler.init();
 
@@ -78,7 +82,37 @@
         updateFilterArray();
     }
 
-    public void removeServlet(Servlet servlet, final boolean destroy)
+    public Set<Servlet> removeServlet(final ServletInfo servletInfo, final boolean destroy)
+    {
+        final Set<Servlet> servletInstances = new HashSet<Servlet>();
+        boolean update = false;
+        for (Iterator<ServletHandler> it = aliasMap.values().iterator(); it.hasNext(); )
+        {
+            final ServletHandler handler = it.next();
+            if (handler.getServletInfo().compareTo(servletInfo) == 0 ) {
+                it.remove();
+                servletInstances.add(handler.getServlet());
+                if (destroy)
+                {
+                    handler.destroy();
+                }
+                update = true;
+            }
+        }
+        if ( update )
+        {
+            updateServletArray();
+        }
+
+        return servletInstances;
+    }
+
+    /**
+     * Support for old Http Service registrations
+     * @param servlet
+     * @param destroy
+     */
+    public void removeServlet(final Servlet servlet, final boolean destroy)
     {
         boolean update = false;
         for (Iterator<ServletHandler> it = aliasMap.values().iterator(); it.hasNext(); )
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
index e88cfc4..65ad762 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
@@ -235,29 +235,29 @@
         }
     }
 
+    private final ServletInfo servletInfo;
+
     private final Servlet servlet;
-    private final Pattern[] patterns;
+
+    private final Pattern pattern;
 
     private final String alias;
 
     public ServletHandler(final ExtServletContext context,
-            final ServletInfo servletInfo)
+                          final ServletInfo servletInfo,
+                          final Servlet servlet,
+                          final String alias)
     {
         super(context, servletInfo.getInitParams(), servletInfo.getName());
-        this.servlet = servletInfo.getServlet();
-        this.patterns = new Pattern[servletInfo.getPatterns().length];
-        for(int i=0; i<servletInfo.getPatterns().length;i++)
-        {
-            this.patterns[i] = Pattern.compile(servletInfo.getPatterns()[i].replace(".", "\\.").replace("*", ".*"));
-        }
-        // TODO - currently we just provide one
-        this.alias = servletInfo.getPatterns()[0];
+        this.servlet = servlet;
+        this.pattern = Pattern.compile(alias.replace(".", "\\.").replace("*", ".*"));
+        this.alias = alias;
+        this.servletInfo = servletInfo;
     }
 
     @Override
     public int compareTo(ServletHandler other)
     {
-        // TODO - currently we just compare based on the first one
         int result = other.alias.length() - this.alias.length();
         if ( result == 0 )
         {
@@ -342,8 +342,12 @@
         {
             return true;
         }
-        // TODO only first pattern is used
-        return this.patterns[0].matcher(uri).matches();
+        return this.pattern.matcher(uri).matches();
+    }
+
+    public ServletInfo getServletInfo()
+    {
+        return this.servletInfo;
     }
 
     final void doHandle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java
index fbb8ddb..b352faf 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java
@@ -76,7 +76,7 @@
 
     private final Servlet servlet;
 
-    public ServletInfo(final ServiceReference<Servlet> ref, final Servlet servlet)
+    public ServletInfo(final ServiceReference<Servlet> ref)
     {
         super(ref);
         this.name = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME);
@@ -85,7 +85,7 @@
         this.asyncSupported = getBooleanProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED);
         this.initParams = getInitParams(ref, SERVLET_INIT_PREFIX);
         this.context = null;
-        this.servlet = servlet;
+        this.servlet = null;
     }
 
     /**
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
index 8c98d51..64cc5a8 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
@@ -21,6 +21,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
 import javax.servlet.Filter;
@@ -207,18 +208,31 @@
             throw new IllegalArgumentException("ServletInfo must at least have one pattern or error page!");
         }
 
-        final ServletHandler handler = new ServletHandler(getServletContext(servletInfo.getContext()), servletInfo);
-        try {
-            this.handlerRegistry.addServlet(handler);
-        } catch (ServletException e) {
-            // TODO create failure DTO
-        } catch (NamespaceException e) {
-            // TODO create failure DTO
+        for(final String alias : servletInfo.getPatterns())
+        {
+            // create a handler for each alias
+            Servlet servlet = servletInfo.getServlet();
+            if ( servlet == null )
+            {
+                servlet = this.bundle.getBundleContext().getServiceObjects(servletInfo.getServiceReference()).getService();
+                // TODO - handle null
+            }
+            final ServletHandler handler = new ServletHandler(getServletContext(servletInfo.getContext()),
+                    servletInfo,
+                    servlet,
+                    alias);
+            try {
+                this.handlerRegistry.addServlet(handler);
+            } catch (ServletException e) {
+                // TODO create failure DTO
+            } catch (NamespaceException e) {
+                // TODO create failure DTO
+            }
+            this.localServlets.add(servlet);
         }
-        this.localServlets.add(servletInfo.getServlet());
     }
 
-    public void unregisterServlet(final Servlet servlet, final ServletInfo servletInfo)
+    public void unregisterServlet(final ServletInfo servletInfo)
     {
         if (servletInfo == null)
         {
@@ -226,8 +240,11 @@
         }
         if ( servletInfo.getPatterns() != null )
         {
-            this.handlerRegistry.removeServlet(servlet, true);
-            this.localServlets.remove(servlet);
+            final Set<Servlet> instances = this.handlerRegistry.removeServlet(servletInfo, true);
+            for(final Servlet servlet : instances)
+            {
+                this.localServlets.remove(servlet);
+            }
         }
     }
 
@@ -245,7 +262,8 @@
      * @see org.osgi.service.http.HttpService#registerServlet(java.lang.String, javax.servlet.Servlet, java.util.Dictionary, org.osgi.service.http.HttpContext)
      */
     @Override
-    public void registerServlet(String alias, Servlet servlet, Dictionary initParams, HttpContext context) throws ServletException, NamespaceException
+    public void registerServlet(String alias, Servlet servlet, Dictionary initParams, HttpContext context)
+    throws ServletException, NamespaceException
     {
         if (servlet == null)
         {
@@ -326,9 +344,18 @@
      * @see org.apache.felix.http.api.ExtHttpService#unregisterServlet(javax.servlet.Servlet)
      */
     @Override
-    public void unregisterServlet(Servlet servlet)
+    public void unregisterServlet(final Servlet servlet)
     {
-        unregisterServlet(servlet, true);
+        this.unregisterServlet(servlet, true);
+    }
+
+    private void unregisterServlet(final Servlet servlet, final boolean destroy)
+    {
+        if ( servlet != null )
+        {
+            this.handlerRegistry.removeServlet(servlet, destroy);
+            this.localServlets.remove(servlet);
+        }
     }
 
     private ExtServletContext getServletContext(HttpContext context)
@@ -365,15 +392,6 @@
         }
     }
 
-    private void unregisterServlet(Servlet servlet, final boolean destroy)
-    {
-        if (servlet != null)
-        {
-            this.handlerRegistry.removeServlet(servlet, destroy);
-            this.localServlets.remove(servlet);
-        }
-    }
-
     private boolean isAliasValid(String alias)
     {
         if (alias == null)
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
index 0cd0dc9..bc9e4c1 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
@@ -23,11 +23,9 @@
 
 import javax.servlet.DispatcherType;
 import javax.servlet.Filter;
-import javax.servlet.Servlet;
 
 import org.apache.felix.http.base.internal.logger.SystemLogger;
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
-import org.apache.felix.http.base.internal.runtime.ServletInfo;
 import org.apache.felix.http.base.internal.service.HttpServiceImpl;
 import org.apache.felix.http.base.internal.whiteboard.tracker.FilterTracker;
 import org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextHelperTracker;
@@ -64,13 +62,13 @@
 
     private final ArrayList<ServiceTracker<?, ?>> trackers = new ArrayList<ServiceTracker<?, ?>>();
 
-    public ExtenderManager(final HttpService httpService, final BundleContext bundleContext)
+    public ExtenderManager(final HttpServiceImpl httpService, final BundleContext bundleContext)
     {
         this.mapping = new HashMap<String, AbstractMapping>();
-        this.contextManager = new ServletContextHelperManager();
+        this.contextManager = new ServletContextHelperManager(httpService);
         this.httpService = httpService;
         addTracker(new FilterTracker(bundleContext, this));
-        addTracker(new ServletTracker(bundleContext, this));
+        addTracker(new ServletTracker(bundleContext, this.contextManager));
         addTracker(new ServletContextHelperTracker(bundleContext, this.contextManager));
     }
 
@@ -272,19 +270,6 @@
         return filterInfo;
     }
 
-    public void addServlet(final Servlet service, final ServiceReference<Servlet> ref)
-    {
-        final ServletInfo servletInfo = new ServletInfo(ref, service);
-        if ( servletInfo.isValid() )
-        {
-            ((HttpServiceImpl)this.httpService).registerServlet(servletInfo);
-        }
-        else
-        {
-            SystemLogger.debug("Ignoring Servlet Service " + ref);
-        }
-    }
-
     public void removeFilter(final Filter service, ServiceReference<Filter> ref)
     {
         final FilterInfo filterInfo = createFilterInfo(ref, false);
@@ -294,15 +279,6 @@
         }
     }
 
-    public void removeServlet(final Servlet service, final ServiceReference<Servlet> ref)
-    {
-        final ServletInfo servletInfo = new ServletInfo(ref, null);
-        if ( servletInfo.isValid() )
-        {
-            ((HttpServiceImpl)this.httpService).unregisterServlet(service, servletInfo);
-        }
-    }
-
     private synchronized void unregisterAll()
     {
         AbstractMapping[] mappings = null;
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 d51fbe9..bc35748 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
@@ -24,17 +24,23 @@
 import java.util.Map;
 
 import org.apache.felix.http.base.internal.runtime.ContextInfo;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.service.HttpServiceImpl;
 
 public final class ServletContextHelperManager
 {
     private final Map<String, List<ContextHolder>> nameMap = new HashMap<String, List<ContextHolder>>();
 
+    private final HttpServiceImpl httpService;
+
     /**
      * Create a new servlet context helper manager
      * and the default context
      */
-    public ServletContextHelperManager()
+    public ServletContextHelperManager(final HttpServiceImpl httpService)
     {
+        this.httpService = httpService;
+
         // create default context
         final ContextInfo info = new ContextInfo();
         this.addContextHelper(info);
@@ -126,6 +132,16 @@
         }
     }
 
+    public void addServlet(final ServletInfo servletInfo)
+    {
+        this.httpService.registerServlet(servletInfo);
+    }
+
+    public void removeServlet(final ServletInfo servletInfo)
+    {
+        this.httpService.unregisterServlet(servletInfo);
+    }
+
     private final static class ContextHolder implements Comparable<ContextHolder>
     {
         private final ContextInfo info;
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java
index de944db..fe89b1e 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java
@@ -18,15 +18,17 @@
 
 import javax.servlet.Servlet;
 
-import org.apache.felix.http.base.internal.whiteboard.ExtenderManager;
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.whiteboard.ServletContextHelperManager;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 
-public final class ServletTracker extends AbstractTracker<Servlet>
+public final class ServletTracker extends AbstractReferenceTracker<Servlet>
 {
-    private final ExtenderManager manager;
+    private final ServletContextHelperManager contextManager;
 
     private static org.osgi.framework.Filter createFilter(final BundleContext btx)
     {
@@ -43,28 +45,35 @@
         return null; // we never get here - and if we get an NPE which is fine
     }
 
-    public ServletTracker(final BundleContext context, final ExtenderManager manager)
+    public ServletTracker(final BundleContext context, final ServletContextHelperManager manager)
     {
         super(context, createFilter(context));
-        this.manager = manager;
+        this.contextManager = manager;
     }
 
     @Override
-    protected void added(Servlet service, ServiceReference ref)
+    protected void added(final ServiceReference<Servlet> ref)
     {
-        this.manager.addServlet(service, ref);
+        final ServletInfo info = new ServletInfo(ref);
+
+        if ( info.isValid() )
+        {
+            this.contextManager.addServlet(info);
+        }
+        else
+        {
+            SystemLogger.debug("Ignoring Servlet service " + ref);
+        }
     }
 
     @Override
-    protected void modified(Servlet service, ServiceReference ref)
+    protected void removed(final ServiceReference<Servlet> ref)
     {
-        removed(service, ref);
-        added(service, ref);
-    }
+        final ServletInfo info = new ServletInfo(ref);
 
-    @Override
-    protected void removed(Servlet service, ServiceReference ref)
-    {
-        this.manager.removeServlet(service, ref);
+        if ( info.isValid() )
+        {
+            this.contextManager.removeServlet(info);
+        }
     }
 }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java
index d1492ba..93bb41f 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java
@@ -44,7 +44,7 @@
 
         Servlet servlet = Mockito.mock(Servlet.class);
         final ServletInfo info = new ServletInfo("foo", "/foo", 0, null, servlet, null);
-        ServletHandler handler = new ServletHandler(null, info);
+        ServletHandler handler = new ServletHandler(null, info, info.getServlet(), info.getPatterns()[0]);
         assertEquals("Precondition", 0, hr.getServlets().length);
         hr.addServlet(handler);
         Mockito.verify(servlet, Mockito.times(1)).init(Mockito.any(ServletConfig.class));
@@ -52,7 +52,7 @@
         assertSame(handler, hr.getServlets()[0]);
 
         final ServletInfo info2 = new ServletInfo("bar", "/bar", 0, null, servlet, null);
-        ServletHandler handler2 = new ServletHandler(null, info2);
+        ServletHandler handler2 = new ServletHandler(null, info2, info2.getServlet(), info2.getPatterns()[0]);
         try
         {
             hr.addServlet(handler2);
@@ -66,7 +66,7 @@
         assertArrayEquals(new ServletHandler[] {handler2, handler}, hr.getServlets());
 
         final ServletInfo info3 = new ServletInfo("zar", "/foo", 0, null, Mockito.mock(Servlet.class), null);
-        ServletHandler handler3 = new ServletHandler(null,info3);
+        ServletHandler handler3 = new ServletHandler(null,info3, info3.getServlet(), info3.getPatterns()[0]);
 
         try
         {
@@ -93,7 +93,7 @@
 
         Servlet servlet = Mockito.mock(Servlet.class);
         final ServletInfo info = new ServletInfo("bar", "/bar", 0, null, servlet, null);
-        final ServletHandler otherHandler = new ServletHandler(null, info);
+        final ServletHandler otherHandler = new ServletHandler(null, info, info.getServlet(), info.getPatterns()[0]);
 
         Mockito.doAnswer(new Answer<Void>()
         {
@@ -113,7 +113,7 @@
         }).when(servlet).init(Mockito.any(ServletConfig.class));
 
         final ServletInfo info2 = new ServletInfo("foo", "/foo", 0, null, servlet, null);
-        ServletHandler handler = new ServletHandler(null, info2);
+        ServletHandler handler = new ServletHandler(null, info2, info2.getServlet(), info2.getPatterns()[0]);
         try
         {
             hr.addServlet(handler);
@@ -137,7 +137,7 @@
 
         Servlet otherServlet = Mockito.mock(Servlet.class);
         final ServletInfo info = new ServletInfo("bar", "/foo", 0, null, otherServlet, null);
-        final ServletHandler otherHandler = new ServletHandler(null, info);
+        final ServletHandler otherHandler = new ServletHandler(null, info, info.getServlet(), info.getPatterns()[0]);
 
         Servlet servlet = Mockito.mock(Servlet.class);
         Mockito.doAnswer(new Answer<Void>()
@@ -152,7 +152,7 @@
         }).when(servlet).init(Mockito.any(ServletConfig.class));
 
         final ServletInfo info2 = new ServletInfo("foo", "/foo", 0, null, servlet, null);
-        ServletHandler handler = new ServletHandler(null, info2);
+        ServletHandler handler = new ServletHandler(null, info2, info2.getServlet(), info2.getPatterns()[0]);
 
         try
         {
@@ -264,11 +264,11 @@
 
         Servlet servlet = Mockito.mock(Servlet.class);
         final ServletInfo info = new ServletInfo("f", "/f", 0, null, servlet, null);
-        ServletHandler servletHandler = new ServletHandler(null, info);
+        ServletHandler servletHandler = new ServletHandler(null, info, info.getServlet(), info.getPatterns()[0]);
         hr.addServlet(servletHandler);
         Servlet servlet2 = Mockito.mock(Servlet.class);
         final ServletInfo info2 = new ServletInfo("ff", "/ff", 0, null, servlet2, null);
-        ServletHandler servletHandler2 = new ServletHandler(null, info2);
+        ServletHandler servletHandler2 = new ServletHandler(null, info2, info2.getServlet(), info2.getPatterns()[0]);
         hr.addServlet(servletHandler2);
         Filter filter = Mockito.mock(Filter.class);
         final FilterInfo fi = new FilterInfo();
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java
index efabc00..0eeb909 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/ServletHandlerTest.java
@@ -231,6 +231,6 @@
     private ServletHandler createHandler(String alias)
     {
         final ServletInfo info = new ServletInfo(null, alias, 0, null, this.servlet, null);
-        return new ServletHandler(this.context, info);
+        return new ServletHandler(this.context, info, info.getServlet(), info.getPatterns()[0]);
     }
 }