FELIX-3237 Implement a Web Console plugin

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1203903 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
index 441a153..7c81303 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
@@ -16,6 +16,8 @@
  */
 package org.apache.felix.http.whiteboard.internal;
 
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.util.tracker.ServiceTracker;
 import org.apache.felix.http.whiteboard.internal.tracker.FilterTracker;
 import org.apache.felix.http.whiteboard.internal.tracker.HttpContextTracker;
@@ -23,16 +25,18 @@
 import org.apache.felix.http.whiteboard.internal.tracker.HttpServiceTracker;
 import org.apache.felix.http.whiteboard.internal.manager.ExtenderManagerImpl;
 import org.apache.felix.http.whiteboard.internal.manager.ExtenderManager;
+import org.apache.felix.http.whiteboard.internal.manager.HttpWhiteboardWebConsolePlugin;
 import org.apache.felix.http.base.internal.AbstractActivator;
 import org.apache.felix.http.base.internal.logger.SystemLogger;
-
 import java.util.ArrayList;
+import java.util.Hashtable;
 
 public final class WhiteboardActivator
     extends AbstractActivator
 {
     private final ArrayList<ServiceTracker> trackers;
     private ExtenderManager manager;
+    private ServiceRegistration httpPlugin;
 
     public WhiteboardActivator()
     {
@@ -47,6 +51,14 @@
         addTracker(new FilterTracker(getBundleContext(), this.manager));
         addTracker(new ServletTracker(getBundleContext(), this.manager));
         addTracker(new HttpServiceTracker(getBundleContext(), this.manager));
+
+        HttpWhiteboardWebConsolePlugin plugin = new HttpWhiteboardWebConsolePlugin((ExtenderManagerImpl) this.manager);
+        Hashtable<String, Object> props = new Hashtable<String, Object>();
+        props.put("felix.webconsole.label", plugin.getLabel());
+        props.put("felix.webconsole.title", plugin.getTitle());
+        props.put(Constants.SERVICE_DESCRIPTION, "Felix Http Whiteboard WebConsole Plugin");
+        httpPlugin = getBundleContext().registerService("javax.servlet.Servlet", plugin, props);
+
         SystemLogger.info("Http service whiteboard started");
     }
 
@@ -59,6 +71,8 @@
     protected void doStop()
         throws Exception
     {
+        this.httpPlugin.unregister();
+
         for (ServiceTracker tracker : this.trackers) {
             tracker.close();
         }
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/DefaultHttpContext.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/DefaultHttpContext.java
index 435fb4d..6f02502 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/DefaultHttpContext.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/DefaultHttpContext.java
@@ -22,7 +22,7 @@
 import javax.servlet.http.HttpServletResponse;
 import java.net.URL;
 
-public final class DefaultHttpContext 
+public final class DefaultHttpContext
     implements HttpContext
 {
     private Bundle bundle;
@@ -50,4 +50,10 @@
     {
         return true;
     }
+
+    @Override
+    public String toString()
+    {
+        return getClass().getSimpleName() + " (" + Integer.toHexString(System.identityHashCode(this)) + ")";
+    }
 }
\ No newline at end of file
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java
index 4b0bda6..909ab85 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java
@@ -18,6 +18,7 @@
 
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Map;
 
 import javax.servlet.Filter;
 import javax.servlet.Servlet;
@@ -134,7 +135,7 @@
             return;
         }
 
-        ServletMapping mapping = new ServletMapping(getHttpContext(ref), service, alias); 
+        ServletMapping mapping = new ServletMapping(getHttpContext(ref), service, alias);
         addInitParams(ref, mapping);
         addMapping(service, mapping);
     }
@@ -213,4 +214,16 @@
             mapping.unregister(this.httpService);
         }
     }
+
+    Map<String, HttpContext> getHttpContexts() {
+        return this.contextManager.getHttpContexts();
+    }
+
+    Map<Object, AbstractMapping> getMappings()
+    {
+        synchronized (this)
+        {
+            return new HashMap<Object, AbstractMapping>(this.mapping);
+        }
+    }
 }
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
index 8ae62b4..275aa4f 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
@@ -38,9 +38,24 @@
         this.ranking = ranking;
     }
 
+    Filter getFilter()
+    {
+        return filter;
+    }
+
+    int getRanking()
+    {
+        return ranking;
+    }
+
+    String getPattern()
+    {
+        return pattern;
+    }
+
     public void register(HttpService httpService)
-    {    
-        if (httpService instanceof ExtHttpService) {
+    {
+       if (httpService instanceof ExtHttpService) {
             register((ExtHttpService)httpService);
         }
     }
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
index e5f105e..58d4f74 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
@@ -20,6 +20,7 @@
 import org.osgi.service.http.HttpContext;
 import org.apache.felix.http.base.internal.logger.SystemLogger;
 import java.util.HashMap;
+import java.util.Map;
 
 public final class HttpContextManager
 {
@@ -48,7 +49,7 @@
             this.contextMap.put(context, id);
             SystemLogger.debug("Added context with id [" + contextId + "]");
         } else {
-            SystemLogger.debug("Reusing context with id [" + contextId + "]");            
+            SystemLogger.debug("Reusing context with id [" + contextId + "]");
         }
 
         return context;
@@ -68,4 +69,9 @@
         this.idMap.put(id, context);
         this.contextMap.put(context, id);
     }
+
+    public synchronized Map<String, HttpContext> getHttpContexts()
+    {
+        return new HashMap<String, HttpContext>(this.idMap);
+    }
 }
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java
new file mode 100755
index 0000000..9e57b9b
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java
@@ -0,0 +1,159 @@
+/*
+ * 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.whiteboard.internal.manager;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.osgi.service.http.HttpContext;
+
+@SuppressWarnings("serial")
+public class HttpWhiteboardWebConsolePlugin extends HttpServlet
+{
+
+    private final ExtenderManagerImpl extMgr;
+
+    public String getLabel()
+    {
+        return "httpwhiteboard";
+    }
+
+    public String getTitle()
+    {
+        return "Http Whiteboard";
+    }
+
+    public HttpWhiteboardWebConsolePlugin(final ExtenderManagerImpl extMgr)
+    {
+        this.extMgr = extMgr;
+    }
+
+    @Override
+    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+    {
+        // only handle GET requests, ensure no error message for other requests
+        if ("GET".equals(req.getMethod()) || "HEAD".equals(req.getMethod()))
+        {
+            super.service(req, resp);
+        }
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
+    {
+
+        PrintWriter pw = resp.getWriter();
+
+        pw.println("<table class='content' width='100%' cellspacing='0' cellpadding='0'>");
+
+        printHttpContextServices(pw);
+
+        pw.println("<tr><td colspan='2'>&nbsp;</td></tr>");
+
+        final Map<Object, AbstractMapping> mappings = extMgr.getMappings();
+        printServletMappings(pw, mappings);
+
+        pw.println("<tr><td colspan='2'>&nbsp;</td></tr>");
+
+        printFilterMappings(pw, mappings);
+
+        pw.println("</table>");
+    }
+
+    private void printHttpContextServices(PrintWriter pw)
+    {
+        pw.println("<tr>");
+        pw.println("<th class='content container' colspan='4'>Registered HttpContext Services</td>");
+        pw.println("</tr>");
+        pw.println("<tr>");
+        pw.println("<th class='content'>Context ID</td>");
+        pw.println("<th class='content' colspan='3'>HttpContext</td>");
+        pw.println("</tr>");
+
+        final Map<String, HttpContext> contexts = extMgr.getHttpContexts();
+        for (Map.Entry<String, HttpContext> handler : contexts.entrySet())
+        {
+            pw.println("<tr class='content'>");
+            pw.println("<td class='content'>" + handler.getKey() + "</td>");
+            pw.println("<td class='content' colspan='3'>" + handler.getValue() + "</td>");
+            pw.println("</tr>");
+        }
+    }
+
+    private void printServletMappings(PrintWriter pw, Map<Object, AbstractMapping> mappings)
+    {
+        pw.println("<tr>");
+        pw.println("<th class='content container' colspan='4'>Registered Filter and Servlet Services</td>");
+        pw.println("</tr>");
+        pw.println("<tr>");
+        pw.println("<th class='content'>Alias</td>");
+        pw.println("<th class='content'>Servlet</td>");
+        pw.println("<th class='content'>Init Parameter</td>");
+        pw.println("<th class='content'>HttpContext</td>");
+        pw.println("</tr>");
+
+        for (Map.Entry<Object, AbstractMapping> handler : mappings.entrySet())
+        {
+            if (handler.getValue() instanceof ServletMapping)
+            {
+                ServletMapping sm = (ServletMapping) handler.getValue();
+                pw.println("<tr class='content'>");
+                pw.println("<td class='content'>" + sm.getAlias() + "</td>");
+                pw.println("<td class='content'>" + sm.getServlet() + "</td>");
+                pw.println("<td class='content'>" + sm.getInitParams() + "</td>");
+                pw.println("<td class='content'>" + sm.getContext() + "</td>");
+                pw.println("</tr>");
+            }
+        }
+    }
+
+    private void printFilterMappings(PrintWriter pw, Map<Object, AbstractMapping> mappings)
+    {
+        pw.println("<tr>");
+        pw.println("<th class='content container' colspan='4'>Registered Filter and Servlet Services</td>");
+        pw.println("</tr>");
+        pw.println("<tr>");
+        pw.println("<th class='content'>Pattern</td>");
+        pw.println("<th class='content'>Filter (Ranking)</td>");
+        pw.println("<th class='content'>Init Parameter</td>");
+        pw.println("<th class='content'>HttpContext</td>");
+        pw.println("</tr>");
+
+        for (Map.Entry<Object, AbstractMapping> handler : mappings.entrySet())
+        {
+            if (handler.getValue() instanceof FilterMapping)
+            {
+                FilterMapping fm = (FilterMapping) handler.getValue();
+                pw.println("<tr class='content'>");
+                pw.println("<td class='content'>" + fm.getPattern() + "</td>");
+                pw.println("<td class='content'>" + fm.getFilter() + " (" + fm.getRanking() + ")</td>");
+                pw.println("<td class='content'>" + fm.getInitParams() + "</td>");
+                pw.println("<td class='content'>" + fm.getContext() + "</td>");
+                pw.println("</tr>");
+            }
+        }
+    }
+
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
index 0a283e9..362e582 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
@@ -35,6 +35,16 @@
         this.alias = alias;
     }
 
+    String getAlias()
+    {
+        return this.alias;
+    }
+
+    Servlet getServlet()
+    {
+        return this.servlet;
+    }
+
     public void register(HttpService httpService)
     {
         try {