This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new 77c5f4041f FELIX-6623 : Use Http Whiteboard for Web Console 
registration
77c5f4041f is described below

commit 77c5f4041f0ac7a9e0f2572b38ebef943c0e9917
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Mon Aug 14 11:10:08 2023 +0200

    FELIX-6623 : Use Http Whiteboard for Web Console registration
---
 webconsole/pom.xml                                 |  38 +-
 .../felix/webconsole/AbstractWebConsolePlugin.java |  17 +-
 .../felix/webconsole/DefaultVariableResolver.java  |   1 +
 .../felix/webconsole/SimpleWebConsolePlugin.java   |  52 +-
 .../webconsole/WebConsoleSecurityProvider3.java    |   4 +-
 .../webconsole/internal/OsgiManagerActivator.java  |   6 +-
 .../internal/configuration/ConfigurationUtil.java  |  33 +-
 .../servlet/BasicWebConsoleSecurityProvider.java   |   6 +-
 .../servlet/ConfigurationMetatypeSupport.java      |  13 +-
 .../internal/servlet/ConfigurationUtil.java        |   6 +-
 .../webconsole/internal/servlet/OsgiManager.java   | 554 ++++++++-------------
 .../internal/servlet/OsgiManagerHttpContext.java   |  80 ++-
 .../felix/webconsole/internal/servlet/Plugin.java  |   6 +-
 .../webconsole/internal/servlet/PluginHolder.java  |   8 +-
 .../configuration/ConfigJsonSupportTest.java       |   2 -
 .../servlet/OsgiManagerHttpContextTest.java        |  10 +-
 .../internal/servlet/OsgiManagerTest.java          | 215 +++-----
 17 files changed, 433 insertions(+), 618 deletions(-)

diff --git a/webconsole/pom.xml b/webconsole/pom.xml
index 5e17cedb52..7fd4e313b8 100644
--- a/webconsole/pom.xml
+++ b/webconsole/pom.xml
@@ -94,7 +94,7 @@
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
-                <version>5.1.1</version>
+                <version>5.1.9</version>
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
@@ -372,13 +372,43 @@
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>osgi.core</artifactId>
-            <version>6.0.0</version>
+            <version>8.0.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
-            <artifactId>osgi.cmpn</artifactId>
-            <version>6.0.0</version>
+            <artifactId>org.osgi.service.http.whiteboard</artifactId>
+            <version>1.1.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.cm</artifactId>
+            <version>1.6.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.metatype</artifactId>
+            <version>1.4.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.prefs</artifactId>
+            <version>1.1.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.log</artifactId>
+            <version>1.3.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.wireadmin</artifactId>
+            <version>1.0.2</version>
             <scope>provided</scope>
         </dependency>
 
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
index 5b15f9a487..434ea6031b 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
@@ -47,7 +47,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.felix.webconsole.internal.servlet.OsgiManager;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
-import org.osgi.service.log.LogService;
+import org.osgi.service.log.LogLevel;
 
 
 /**
@@ -389,7 +389,7 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
         {
             try
             {
-                Class cl = resourceProvider.getClass();
+                Class<?> cl = resourceProvider.getClass();
                 while ( tmpGetResourceMethod == null && cl != Object.class )
                 {
                     Method[] methods = cl.getDeclaredMethods();
@@ -633,6 +633,7 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
      * @throws IOException on I/O error
      * @see #endResponse(PrintWriter)
      */
+    @SuppressWarnings({ "unchecked" })
     protected PrintWriter startResponse( HttpServletRequest request, 
HttpServletResponse response ) throws IOException
     {
         response.setCharacterEncoding( "utf-8" ); //$NON-NLS-1$
@@ -674,6 +675,7 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
      * @param request the HTTP request coming from the user
      * @param pw the writer, where the HTML data is rendered
      */
+    @SuppressWarnings({ "rawtypes" })
     protected void renderTopNavigation( HttpServletRequest request, 
PrintWriter pw )
     {
         // assume pathInfo to not be null, else this would not be called
@@ -730,6 +732,7 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
     }
 
 
+    @SuppressWarnings({ "rawtypes" })
     protected void renderMenu( Map menuMap, String appRoot, PrintWriter pw )
     {
         if ( menuMap != null )
@@ -745,6 +748,7 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
     }
 
 
+    @SuppressWarnings({ "rawtypes" })
     private void renderMenu( Map menuMap, String appRoot, PrintWriter pw, int 
level )
     {
         pw.println( "<ul class=\"navMenuLevel-" + level + "\">" );
@@ -753,6 +757,7 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
     }
 
 
+    @SuppressWarnings({ "rawtypes" })
     private void renderSubmenu( Map menuMap, String appRoot, PrintWriter pw, 
int level )
     {
         String liStyleClass = " class=\"navMenuItem-" + level + "\"";
@@ -935,7 +940,7 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
         return readTemplateFile( getClass(), templateFile );
     }
 
-    private final String readTemplateFile( final Class clazz, final String 
templateFile) {
+    private final String readTemplateFile( final Class<?> clazz, final String 
templateFile) {
         
         try(InputStream templateStream = clazz.getResourceAsStream( 
templateFile )) {
             if ( templateStream != null ) {
@@ -964,7 +969,7 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
         }
 
         // template file does not exist, return an empty string
-        log( LogService.LOG_ERROR, "readTemplateFile: File '" + templateFile + 
"' not found through class " + clazz ); //$NON-NLS-1$ //$NON-NLS-2$
+        log( LogLevel.ERROR.ordinal(), "readTemplateFile: File '" + 
templateFile + "' not found through class " + clazz ); //$NON-NLS-1$ 
//$NON-NLS-2$
         return ""; //$NON-NLS-1$
     }
 
@@ -1013,6 +1018,7 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
     }
 
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     private SortedMap sortMenuCategoryMap( Map map, String appRoot )
     {
         SortedMap sortedMap = new TreeMap<>( String.CASE_INSENSITIVE_ORDER );
@@ -1054,18 +1060,17 @@ public abstract class AbstractWebConsolePlugin extends 
HttpServlet
         return sortedMap;
     }
 
+    @SuppressWarnings({ "rawtypes" })
     private static class MenuItem
     {
     private String link;
         private Map subMenu;
 
-
         public MenuItem( String link )
         {
             this.link = link;
         }
 
-
         public MenuItem( String link, Map subMenu )
         {
             super();
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/DefaultVariableResolver.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/DefaultVariableResolver.java
index dfdb7fab7b..c0bafb52d8 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/DefaultVariableResolver.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/DefaultVariableResolver.java
@@ -31,6 +31,7 @@ import java.util.Map;
  * {@link WebConsoleUtil#getVariableResolver(javax.servlet.ServletRequest)}
  * as the variable resolver if none has yet been assigned to the request.
  */
+@SuppressWarnings({ "rawtypes" })
 public class DefaultVariableResolver extends HashMap implements 
VariableResolver
 {
 
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java
index 3c69d3d5bb..133928b203 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/SimpleWebConsolePlugin.java
@@ -20,6 +20,7 @@ package org.apache.felix.webconsole;
 
 
 import java.net.URL;
+import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Iterator;
@@ -27,6 +28,8 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.ResourceBundle;
 
+import javax.servlet.Servlet;
+
 import org.apache.felix.webconsole.i18n.LocalizationHelper;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -61,10 +64,10 @@ public abstract class SimpleWebConsolePlugin extends 
AbstractWebConsolePlugin
 
     // used for service registration
     private final Object regLock = new Object();
-    private ServiceRegistration reg;
+    private ServiceRegistration<Servlet> reg;
 
     // used to obtain services. Structure is: service name -> ServiceTracker
-    private final Map services = new HashMap();
+    private final Map<String, ServiceTracker<?, ?>> services = new HashMap<>();
 
     // localized title as servlet name
     private String servletName;
@@ -218,20 +221,17 @@ public abstract class SimpleWebConsolePlugin extends 
AbstractWebConsolePlugin
      * @param bc the bundle context used for service registration.
      * @return self
      */
-    public final SimpleWebConsolePlugin register( BundleContext bc )
-    {
-        synchronized ( regLock )
-        {
+    public final SimpleWebConsolePlugin register( BundleContext bc ) {
+        synchronized ( regLock ) {
             activate( bc ); // don't know why this is needed!
 
-            Hashtable props = new Hashtable();
+            final Dictionary<String, Object> props = new Hashtable<>();
             props.put( WebConsoleConstants.PLUGIN_LABEL, getLabel() );
             props.put( WebConsoleConstants.PLUGIN_TITLE, getTitle() );
-            if ( getCategory() != null )
-            {
+            if ( getCategory() != null ) {
                 props.put( WebConsoleConstants.PLUGIN_CATEGORY, getCategory() 
);
             }
-            reg = bc.registerService( "javax.servlet.Servlet", this, props ); 
//$NON-NLS-1$
+            reg = bc.registerService( Servlet.class, this, props ); 
//$NON-NLS-1$
         }
         return this;
     }
@@ -241,14 +241,17 @@ public abstract class SimpleWebConsolePlugin extends 
AbstractWebConsolePlugin
      * An utility method that removes the service, registered by the
      * {@link #register(BundleContext)} method.
      */
-    public final void unregister()
-    {
-        synchronized ( regLock )
-        {
+    public final void unregister() {
+        synchronized ( regLock ) {
             deactivate(); // is this needed?
 
-            if ( reg != null )
-                reg.unregister();
+            if ( reg != null ) {
+                try {
+                    reg.unregister();
+                } catch ( final IllegalStateException ise ) {
+                    // ignore, bundle context already invalid
+                }
+            }
             reg = null;
         }
     }
@@ -265,11 +268,9 @@ public abstract class SimpleWebConsolePlugin extends 
AbstractWebConsolePlugin
      * @param serviceName the service name to obtain
      * @return the service or <code>null</code> if missing.
      */
-    public final Object getService( String serviceName )
-    {
-        ServiceTracker serviceTracker = ( ServiceTracker ) services.get( 
serviceName );
-        if ( serviceTracker == null )
-        {
+    public final Object getService( String serviceName ) {
+        ServiceTracker<?,?> serviceTracker = services.get( serviceName );
+        if ( serviceTracker == null ) {
             serviceTracker = new ServiceTracker( getBundleContext(), 
serviceName, new ServiceTrackerCustomizer() {
                     public Object addingService( ServiceReference reference ) {
                         return getBundleContext().getService( reference );
@@ -303,15 +304,12 @@ public abstract class SimpleWebConsolePlugin extends 
AbstractWebConsolePlugin
      *
      * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#deactivate()
      */
-    public void deactivate()
-    {
-        for ( Iterator ti = services.values().iterator(); ti.hasNext(); )
-        {
-            ServiceTracker tracker = ( ServiceTracker ) ti.next();
+    public void deactivate() {
+        for ( Iterator<ServiceTracker<?, ?>> ti = 
services.values().iterator(); ti.hasNext(); ) {
+            ServiceTracker<?, ?> tracker = ti.next();
             tracker.close();
             ti.remove();
         }
         super.deactivate();
     }
-
 }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider3.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider3.java
index a0c470ee84..0b96f5b9d5 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider3.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/WebConsoleSecurityProvider3.java
@@ -21,7 +21,7 @@ package org.apache.felix.webconsole;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.context.ServletContextHelper;
 
 /**
  * The <code>WebConsoleSecurityProvider3</code> extends the
@@ -34,7 +34,7 @@ import org.osgi.service.http.HttpContext;
  * If this service is missing and basic authentication is used, then new 
authentication is requested.
  * 
  * In any case, the logout procedure will invalidate the current session and 
will remove the 
- * {@link HttpContext#REMOTE_USER}, {@link HttpContext#AUTHORIZATION} 
attributes from the request and the session.
+ * {@link ServletContextHelper#REMOTE_USER}, {@link 
ServletContextHelper#AUTHORIZATION} attributes from the request and the session.
  * 
  * @since 4.2.8; Web Console Bundle 4.2.8
  */
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerActivator.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerActivator.java
index 9a564c0d2b..a9000ee419 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerActivator.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/OsgiManagerActivator.java
@@ -22,12 +22,14 @@ package org.apache.felix.webconsole.internal;
 import org.apache.felix.webconsole.internal.servlet.OsgiManager;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
+import org.osgi.service.http.whiteboard.annotations.RequireHttpWhiteboard;
 
 
 /**
  * This is the main, starting class of the Bundle. It initializes and disposes
  * the Apache Web Console upon bundle lifecycle requests.
  */
+@RequireHttpWhiteboard
 public class OsgiManagerActivator implements BundleActivator
 {
 
@@ -45,8 +47,8 @@ public class OsgiManagerActivator implements BundleActivator
         osgiManager = new OsgiManager( bundleContext );
         try
         {
-            final Class activatorClass = 
bundleContext.getBundle().loadClass(STATUS_ACTIVATOR);
-            this.statusActivator = (BundleActivator) 
activatorClass.newInstance();
+            final Class<?> activatorClass = 
bundleContext.getBundle().loadClass(STATUS_ACTIVATOR);
+            this.statusActivator = (BundleActivator) 
activatorClass.getDeclaredConstructor().newInstance();
 
         }
         catch (Throwable t)
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationUtil.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationUtil.java
index 71ae43551c..3df506a765 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationUtil.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationUtil.java
@@ -20,13 +20,16 @@ package org.apache.felix.webconsole.internal.configuration;
 
 
 import java.io.IOException;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.felix.webconsole.spi.ConfigurationHandler;
 import org.apache.felix.webconsole.spi.ValidationException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 
@@ -109,13 +112,11 @@ public class ConfigurationUtil {
             this.factoryPid = factoryPid;
         }
 
-
         @Override
         public String getPid() {
             return PLACEHOLDER_PID;
         }
 
-
         @Override
         public String getFactoryPid() {
             return factoryPid;
@@ -157,5 +158,33 @@ public class ConfigurationUtil {
             // dummy configuration always returns 0
             return 0;
         }
+
+        @Override
+        public void addAttributes(ConfigurationAttribute... attrs) throws 
IOException {            
+            // no attributes
+        }
+
+        @Override
+        public Set<ConfigurationAttribute> getAttributes() {
+            // no attributes
+            return Collections.emptySet();
+        }
+
+        @Override
+        public Dictionary<String, Object> 
getProcessedProperties(ServiceReference<?> reference) {
+            // dummy configuration has no properties
+            return null;
+        }
+
+        @Override
+        public void removeAttributes(ConfigurationAttribute... attrs) throws 
IOException {            
+            // no attributes
+        }
+
+        @Override
+        public boolean updateIfDifferent(Dictionary<String, ?> properties) 
throws IOException {
+            // dummy configuration has no properties
+            return false;
+        }
     }
 }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/BasicWebConsoleSecurityProvider.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/BasicWebConsoleSecurityProvider.java
index c17e530c5f..2a83284ec3 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/BasicWebConsoleSecurityProvider.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/BasicWebConsoleSecurityProvider.java
@@ -24,7 +24,7 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.webconsole.WebConsoleSecurityProvider2;
 import org.osgi.framework.BundleContext;
-import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.context.ServletContextHelper;
 
 /**
  * Basic implementation of WebConsoleSecurityProvider to replace logic that
@@ -103,8 +103,8 @@ public class BasicWebConsoleSecurityProvider implements 
WebConsoleSecurityProvid
                         if ( authenticate( username, toString(userPass[1]) ) 
!= null )
                         {
                             // as per the spec, set attributes
-                            request.setAttribute( 
HttpContext.AUTHENTICATION_TYPE, HttpServletRequest.BASIC_AUTH );
-                            request.setAttribute( HttpContext.REMOTE_USER, 
username );
+                            request.setAttribute( 
ServletContextHelper.AUTHENTICATION_TYPE, HttpServletRequest.BASIC_AUTH );
+                            request.setAttribute( 
ServletContextHelper.REMOTE_USER, username );
 
                             // set web console user attribute
                             request.setAttribute( 
WebConsoleSecurityProvider2.USER_ATTRIBUTE, username );
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationMetatypeSupport.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationMetatypeSupport.java
index cac5440ea0..8c218e9aa4 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationMetatypeSupport.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationMetatypeSupport.java
@@ -35,13 +35,12 @@ import org.osgi.service.metatype.ObjectClassDefinition;
 class ConfigurationMetatypeSupport extends ConfigurationSupport implements 
MetaTypeProvider
 {
     private static final String[] CONF_PROPS = new String[]
-        { OsgiManager.PROP_MANAGER_ROOT, OsgiManager.DEFAULT_MANAGER_ROOT, //
-            OsgiManager.PROP_HTTP_SERVICE_SELECTOR, 
OsgiManager.DEFAULT_HTTP_SERVICE_SELECTOR, //
-            OsgiManager.PROP_DEFAULT_RENDER, OsgiManager.DEFAULT_PAGE, //
-            OsgiManager.PROP_REALM, OsgiManager.DEFAULT_REALM, //
-            OsgiManager.PROP_USER_NAME, OsgiManager.DEFAULT_USER_NAME, //
-            OsgiManager.PROP_CATEGORY, OsgiManager.DEFAULT_CATEGORY, //
-            OsgiManager.PROP_LOCALE, "", //$NON-NLS-1$
+        { OsgiManager.PROP_MANAGER_ROOT, OsgiManager.DEFAULT_MANAGER_ROOT,
+            OsgiManager.PROP_DEFAULT_RENDER, OsgiManager.DEFAULT_PAGE,
+            OsgiManager.PROP_REALM, OsgiManager.DEFAULT_REALM,
+            OsgiManager.PROP_USER_NAME, OsgiManager.DEFAULT_USER_NAME,
+            OsgiManager.PROP_CATEGORY, OsgiManager.DEFAULT_CATEGORY,
+            OsgiManager.PROP_LOCALE, "",
         };
 
     private final Object ocdLock = new Object();
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationUtil.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationUtil.java
index 1c4ffdecba..ef5d1840b1 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationUtil.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/ConfigurationUtil.java
@@ -91,7 +91,7 @@ public class ConfigurationUtil
      * @return The value of the named property as a string or <code>def</code>
      *         if the property does not exist
      */
-    public static final String getProperty(Map config, String name, String def)
+    public static final String getProperty(Map<String, Object> config, String 
name, String def)
     {
         Object value = config.get(name);
         if ( value instanceof String )
@@ -118,7 +118,7 @@ public class ConfigurationUtil
      * @return The value of the named property as a string or <code>def</code>
      *         if the property does not exist
      */
-    public static final int getProperty(Map config, String name, int def)
+    public static final int getProperty(Map<String, Object> config, String 
name, int def)
     {
         Object value = config.get(name);
         if (value instanceof Number)
@@ -150,7 +150,7 @@ public class ConfigurationUtil
      * @param name The name of the property to return
      * @return the property value as string array - no matter if originally it 
was other kind of array, collection or comma-separated string. Returns 
<code>null</code> if the property is not set.
      */
-    public static final String[] getStringArrayProperty(Map config, String 
name)
+    public static final String[] getStringArrayProperty(Map<String, Object> 
config, String name)
     {
         Object value = config.get(name);
         if (value == null)
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
index b42633e1bd..7aa869937a 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
@@ -17,7 +17,10 @@
 package org.apache.felix.webconsole.internal.servlet;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.URL;
+import java.net.URLConnection;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
@@ -34,13 +37,13 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentSkipListSet;
 
 import javax.servlet.GenericServlet;
+import javax.servlet.Servlet;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
@@ -64,28 +67,24 @@ import 
org.apache.felix.webconsole.internal.core.BundlesServlet;
 import org.apache.felix.webconsole.internal.filter.FilteringResponseWrapper;
 import org.apache.felix.webconsole.internal.i18n.ResourceBundleManager;
 import org.apache.felix.webconsole.internal.servlet.Plugin.InternalPlugin;
-import org.apache.felix.webconsole.internal.system.VMStatPlugin;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
-import org.osgi.framework.Filter;
-import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceFactory;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.http.HttpService;
-import org.osgi.service.log.LogService;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+import org.osgi.service.log.LogLevel;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
 /**
- * The <code>OSGi Manager</code> is the actual Web Console Servlet which
- * is registered with the OSGi Http Service and which maintains registered
+ * The <code>OSGi Manager</code> is the actual Web Console Servlet. It is
+ * registered with the OSGi Http Whiteboard Service and it manages registered
  * console plugins.
  */
-public class OsgiManager extends GenericServlet
-{
+public class OsgiManager extends GenericServlet {
 
     /** Pseudo class version ID to keep the IDE quite. */
     private static final long serialVersionUID = 1L;
@@ -179,7 +178,7 @@ public class OsgiManager extends GenericServlet
     /** The timeout for VMStat plugin page reload */
     public static final String PROP_RELOAD_TIMEOUT = "reload.timeout";
 
-    public static final int DEFAULT_LOG_LEVEL = LogService.LOG_WARNING;
+    public static final int DEFAULT_LOG_LEVEL = LogLevel.WARN.ordinal();
 
     static final String DEFAULT_PAGE = BundlesServlet.NAME;
 
@@ -191,8 +190,6 @@ public class OsgiManager extends GenericServlet
 
     static final String DEFAULT_CATEGORY = "Main"; //$NON-NLS-1$
 
-    static final String DEFAULT_HTTP_SERVICE_SELECTOR = ""; //$NON-NLS-1$
-
     static final int DEFAULT_SHUTDOWN_TIMEOUT = 5; //$NON-NLS-1$
 
     static final int DEFAULT_RELOAD_TIMEOUT = 40; //$NON-NLS-1$
@@ -234,15 +231,13 @@ public class OsgiManager extends GenericServlet
             "org.apache.felix.webconsole.internal.system.VMStatPlugin", 
"vmstat", //$NON-NLS-1$ //$NON-NLS-2$
     };
 
+    private static final String SERVLEXT_CONTEXT_NAME = 
"org.apache.felix.webconsole";
+
     /** Flag to control whether secret heuristics is enabled */
     public static volatile boolean ENABLE_SECRET_HEURISTICS = 
OsgiManager.DEFAULT_ENABLE_SECRET_HEURISTIC;
 
     private BundleContext bundleContext;
 
-    private HttpServiceTracker httpServiceTracker;
-
-    private volatile HttpService httpService;
-
     private PluginHolder holder;
 
     private ServiceTracker<BrandingPlugin, BrandingPlugin> brandingTracker;
@@ -253,19 +248,19 @@ public class OsgiManager extends GenericServlet
 
     // list of OsgiManagerPlugin instances activated during init. All these
     // instances will have to be deactivated during destroy
-    private List<OsgiManagerPlugin> osgiManagerPlugins = new ArrayList<>();
+    private volatile List<OsgiManagerPlugin> osgiManagerPlugins = new 
ArrayList<>();
 
-    private String webManagerRoot;
+    private volatile String webManagerRoot;
 
     // not-null when the BasicWebConsoleSecurityProvider service is registered
     private ServiceRegistration<WebConsoleSecurityProvider> 
basicSecurityServiceRegistration;
 
-    // true if the OsgiManager is registered as a Servlet with the HttpService
-    private boolean httpServletRegistered;
-
-    // true if the resources have been registered with the HttpService
-    private boolean httpResourcesRegistered;
-
+    // not-null when the ServletContextHelper service is registered
+    private volatile ServiceRegistration<ServletContextHelper> 
servletContextRegistration;
+    
+    // not-null when the main servlet and the resources are registered
+    private volatile ServiceRegistration<Servlet> servletRegistration;
+    
     // default configuration from framework properties
     private Map<String, Object> defaultConfiguration;
 
@@ -335,12 +330,12 @@ public class OsgiManager extends GenericServlet
                     // message is just a class name, try to be more descriptive
                     message = "Class " + message + " missing";
                 }
-                log(LogService.LOG_INFO, pluginClassName + " not enabled. 
Reason: "
+                log(LogLevel.INFO.ordinal(), pluginClassName + " not enabled. 
Reason: "
                     + message);
             }
             catch (Throwable t)
             {
-                log(LogService.LOG_INFO, "Failed to instantiate plugin "
+                log(LogLevel.INFO.ordinal(), "Failed to instantiate plugin "
                     + pluginClassName + ". Reason: " + t);
             }
         }
@@ -428,18 +423,15 @@ public class OsgiManager extends GenericServlet
     }
 
     void updateRegistrationState() {
-        if (this.httpService != null) {
-            if 
(this.registeredSecurityProviders.containsAll(this.requiredSecurityProviders)) {
-                // register HTTP service
-                registerHttpService();
-                return;
-            } else {
-                log(LogService.LOG_INFO, "Not all requirements met for the Web 
Console. Required security providers: "
-                        + this.registeredSecurityProviders + " Registered 
security providers: " + this.registeredSecurityProviders);
-            }
+        if 
(this.registeredSecurityProviders.containsAll(this.requiredSecurityProviders)) {
+            // register servlet context helper, servlet, resources
+            this.registerHttpWhiteboardServices();
+        } else {
+            log(LogLevel.INFO.ordinal(), "Not all requirements met for the Web 
Console. Required security providers: "
+                    + this.registeredSecurityProviders + " Registered security 
providers: " + this.registeredSecurityProviders);
+            // Not all requirements met, unregister services
+            this.unregisterHttpWhiteboardServices();
         }
-        // Not all requirements met, unregister service.
-        unregisterHttpService();
     }
 
     public void dispose()
@@ -472,11 +464,7 @@ public class OsgiManager extends GenericServlet
         this.osgiManagerPlugins.clear();
 
         // now drop the HttpService and continue with further destroyals
-        if (httpServiceTracker != null)
-        {
-            httpServiceTracker.close();
-            httpServiceTracker = null;
-        }
+        this.unregisterHttpWhiteboardServices();
 
         // stop listening for configuration
         if (configurationListener != null)
@@ -525,7 +513,18 @@ public class OsgiManager extends GenericServlet
                 @Override
                 public Object run() throws Exception
                 {
-                    service((HttpServletRequest) req, (HttpServletResponse) 
res);
+                    final HttpServletRequest wrapper = new 
HttpServletRequestWrapper((HttpServletRequest) req) {
+                        @Override
+                        public String getServletPath() {
+                            return "";
+                        }
+
+                        @Override
+                        public String getPathInfo() {
+                            return super.getServletPath();
+                        }
+                    };
+                    service(wrapper, (HttpServletResponse) res);
                     return null;
                 }
             });
@@ -566,16 +565,13 @@ public class OsgiManager extends GenericServlet
     }
 
     void service(HttpServletRequest request, HttpServletResponse response)
-        throws ServletException, IOException
-    {
+        throws ServletException, IOException {
         // check whether we are not at .../{webManagerRoot}
         final String pathInfo = request.getPathInfo();
-        if (pathInfo == null || pathInfo.equals("/")) //$NON-NLS-1$
-        {
+        if (pathInfo == null || pathInfo.isEmpty() || pathInfo.equals("/"))  {
             String path = request.getRequestURI();
-            if (!path.endsWith("/")) //$NON-NLS-1$
-            {
-                path = path.concat("/"); //$NON-NLS-1$
+            if (!path.endsWith("/")) {
+                path = path.concat("/");
             }
             path = path.concat(holder.getDefaultPluginLabel());
             response.setContentLength(0);
@@ -583,11 +579,21 @@ public class OsgiManager extends GenericServlet
             return;
         }
 
-        if (pathInfo.equals("/logout")) { //$NON-NLS-1$
+        if (pathInfo.equals("/logout")) {
             logout(request, response);
             return;
         }
 
+        if (pathInfo.startsWith("/res/")) {
+            URL url = this.getBundleContext().getBundle().getResource( 
pathInfo );
+            if ( url == null && pathInfo.endsWith( "/" ) ) {
+                url = this.getBundleContext().getBundle().getResource( 
pathInfo.substring( 0, pathInfo.length() - 1 ) );
+            }
+            if ( url != null && this.spool(request, response, url)) {
+                return;
+            }
+        }
+
         int slash = pathInfo.indexOf("/", 1); //$NON-NLS-1$
         if (slash < 2)
         {
@@ -613,6 +619,7 @@ public class OsgiManager extends GenericServlet
             return;
         }
 
+        @SuppressWarnings("rawtypes")
         final Map labelMap = holder.getLocalizedLabelMap( 
resourceBundleManager, locale, this.defaultCategory );
         final Object flatLabelMap = labelMap.remove( 
WebConsoleConstants.ATTR_LABEL_MAP );
 
@@ -641,60 +648,92 @@ public class OsgiManager extends GenericServlet
         plugin.service(request, response);
     }
 
-    private final void logout(HttpServletRequest request, HttpServletResponse 
response)
-        throws IOException
-    {
-        // check if special logout cookie is set, this is used to prevent
-        // from an endless loop with basic auth
-        Cookie[] cookies = request.getCookies();
-        boolean found = false;
-        if ( cookies != null )
-        {
-            for(int i=0;i<cookies.length;i++)
-            {
-                if ( cookies[i].getName().equals("logout") ) //$NON-NLS-1$
-                {
-                    found = true;
-                    break;
+    private boolean spool(final HttpServletRequest request, final 
HttpServletResponse response, final URL url) 
+    throws IOException {
+        final URLConnection connection = url.openConnection();
+        try ( InputStream ins = connection.getInputStream()) {
+            if (ins == null) {
+                return false;
+            }
+
+            // check whether we may return 304/UNMODIFIED
+            final long lastModified = connection.getLastModified();
+            if ( lastModified > 0 ) {
+                final long ifModifiedSince = request.getDateHeader( 
"If-Modified-Since" );
+                if ( ifModifiedSince >= ( lastModified / 1000 * 1000 ) ) {
+                    // Round down to the nearest second for a proper compare
+                    // A ifModifiedSince of -1 will always be less
+                    response.setStatus( HttpServletResponse.SC_NOT_MODIFIED );
+
+                    return true;
                 }
+
+                // have to send, so set the last modified header now
+                response.setDateHeader( "Last-Modified", lastModified );
             }
+
+            response.setContentType( getServletContext().getMimeType( 
request.getPathInfo() ) );
+            response.setIntHeader( "Content-Length", 
connection.getContentLength() );
+
+            // spool the actual contents
+            try (final OutputStream out = response.getOutputStream()) {
+                byte[] buf = new byte[2048];
+                int rd;
+                while ( ( rd = ins.read( buf ) ) >= 0 ) {
+                    out.write( buf, 0, rd );
+                }
+            }
+
+            return true;
         }
-        if ( found )
-        {
-            // redirect to main page
-            String url = request.getRequestURI();
-            final int lastSlash = url.lastIndexOf('/');
-            final Cookie c = new Cookie("logout", "true"); //$NON-NLS-1$ 
//$NON-NLS-2$
-            c.setMaxAge(0);
-            response.addCookie(c);
-            response.sendRedirect(url.substring(0, lastSlash));
-            return;
-        }
-        Object securityProvider = securityProviderTracker.getService();
-        if (securityProvider instanceof WebConsoleSecurityProvider3)
-        {
+    }
+
+    private final void logout(HttpServletRequest request, HttpServletResponse 
response)
+        throws IOException {
+        final Object securityProvider = securityProviderTracker.getService();
+        if (securityProvider instanceof WebConsoleSecurityProvider3) {
             ((WebConsoleSecurityProvider3) securityProvider).logout(request, 
response);
-        }
-        else
-        {
-            // if the security provider doesn't support logout, we try to
-            // logout the default basic authentication mechanism
-            // See https://issues.apache.org/jira/browse/FELIX-3006
-
-            // check for basic authentication
-            String auth = request.getHeader(HEADER_AUTHORIZATION); 
//$NON-NLS-1$
-            if (null != auth && auth.toLowerCase().startsWith("basic ")) { 
//$NON-NLS-1$
-                Map<String, Object> config = getConfiguration();
-                String realm = ConfigurationUtil.getProperty(config, 
PROP_REALM, DEFAULT_REALM);
-                response.setHeader(HEADER_WWW_AUTHENTICATE, "Basic realm=\"" + 
 realm + "\""); //$NON-NLS-1$ //$NON-NLS-2$
-                response.addCookie(new Cookie("logout", "true")); 
//$NON-NLS-1$ //$NON-NLS-2$
-                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        } else {
+            // check if special logout cookie is set, this is used to prevent
+            // from an endless loop with basic auth
+            final Cookie[] cookies = request.getCookies();
+            boolean found = false;
+            if ( cookies != null ) {
+                for(final Cookie c : cookies) {
+                    if (c.getName().equals("logout") ) {
+                        found = true;
+                        break;
+                    }
+                }
+            }
+            if ( found ) {
+                // redirect to main page
+                final String url = request.getRequestURI();
+                final int lastSlash = url.lastIndexOf('/');
+                final Cookie c = new Cookie("logout", "true");
+                c.setMaxAge(0);
+                response.addCookie(c);
+                response.sendRedirect(url.substring(0, lastSlash));
+            } else {
+                // if the security provider doesn't support logout, we try to
+                // logout the default basic authentication mechanism
+                // See https://issues.apache.org/jira/browse/FELIX-3006
+
+                // check for basic authentication
+                final String auth = request.getHeader(HEADER_AUTHORIZATION);
+                if (null != auth && auth.toLowerCase().startsWith("basic ")) {
+                    Map<String, Object> config = getConfiguration();
+                    String realm = ConfigurationUtil.getProperty(config, 
PROP_REALM, DEFAULT_REALM);
+                    response.setHeader(HEADER_WWW_AUTHENTICATE, "Basic 
realm=\"" +  realm + "\"");
+                    response.addCookie(new Cookie("logout", "true"));
+                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+                }
             }
         }
 
         // clean-up
-        request.removeAttribute(HttpContext.REMOTE_USER);
-        request.removeAttribute(HttpContext.AUTHORIZATION);
+        request.removeAttribute(ServletContextHelper.REMOTE_USER);
+        request.removeAttribute(ServletContextHelper.AUTHORIZATION);
         request.removeAttribute(WebConsoleSecurityProvider2.USER_ATTRIBUTE);
         request.removeAttribute(User.USER_ATTRIBUTE);
     }
@@ -879,80 +918,6 @@ public class OsgiManager extends GenericServlet
         return new FilteringResponseWrapper(response, resourceBundle, request);
     }
 
-    private static class HttpServiceTracker extends 
ServiceTracker<HttpService, HttpService>
-    {
-        private final OsgiManager osgiManager;
-
-        private final String httpServiceSelector;
-
-        static HttpServiceTracker create(OsgiManager osgiManager,
-            String httpServiceSelector)
-        {
-            // got a service selector filter
-            if (httpServiceSelector != null && httpServiceSelector.length() > 
0)
-            {
-                try
-                {
-                    final String filterString = "(&(" + Constants.OBJECTCLASS 
+ "=" //$NON-NLS-1$ //$NON-NLS-2$
-                        + HttpService.class.getName() + ")(" + 
httpServiceSelector + "))"; //$NON-NLS-1$ //$NON-NLS-2$
-                    Filter filter = 
osgiManager.getBundleContext().createFilter(
-                        filterString);
-                    return new HttpServiceTracker(osgiManager, 
httpServiceSelector,
-                        filter);
-                }
-                catch (InvalidSyntaxException ise)
-                {
-                    // TODO: log or throw or ignore ....
-                }
-            }
-
-            // no filter or illegal filter string
-            return new HttpServiceTracker(osgiManager);
-        }
-
-        private HttpServiceTracker(final OsgiManager osgiManager)
-        {
-            super(osgiManager.getBundleContext(), HttpService.class, null);
-            this.osgiManager = osgiManager;
-            this.httpServiceSelector = null;
-        }
-
-        private HttpServiceTracker(final OsgiManager osgiManager, final String 
httpServiceSelector, final Filter httpServiceFilter)
-        {
-            super(osgiManager.getBundleContext(), httpServiceFilter, null);
-            this.osgiManager = osgiManager;
-            this.httpServiceSelector = httpServiceSelector;
-        }
-
-        boolean isSameSelector(final String newHttpServiceSelector)
-        {
-            if (newHttpServiceSelector != null)
-            {
-                return newHttpServiceSelector.equals(httpServiceSelector);
-            }
-            return httpServiceSelector == null;
-        }
-
-        @Override
-        public HttpService addingService(ServiceReference<HttpService> 
reference)
-        {
-            HttpService service = super.addingService(reference);
-            osgiManager.bindHttpService(service);
-            return service;
-        }
-
-        @Override
-        public void removedService(ServiceReference<HttpService> reference, 
HttpService service)
-        {
-            osgiManager.unbindHttpService(service);
-            try {
-                super.removedService(reference, service);
-            } catch ( final IllegalStateException ise) {
-                // ignore this as the service is already invalid
-            }
-        }
-    }
-
     private static class BrandingServiceTracker extends 
ServiceTracker<BrandingPlugin, BrandingPlugin>
     {
         BrandingServiceTracker(OsgiManager osgiManager)
@@ -981,158 +946,113 @@ public class OsgiManager extends GenericServlet
 
     }
 
-    protected void bindHttpService(HttpService httpService)
-    {
-        // do not bind service, when we are already bound
-        if (this.httpService != null)
-        {
-            log(LogService.LOG_DEBUG,
-                "bindHttpService: Already bound to an HTTP Service, ignoring 
further services");
-            return;
-        }
-
-        this.httpService = httpService;
-        updateRegistrationState();
-    }
-
-    synchronized void registerHttpService() {
-        Map<String, Object> config = getConfiguration();
+    synchronized void registerHttpWhiteboardServices() {
+        final String realm = 
ConfigurationUtil.getProperty(this.getConfiguration(), PROP_REALM, 
DEFAULT_REALM);
 
-        // get authentication details
-        String realm = ConfigurationUtil.getProperty(config, PROP_REALM, 
DEFAULT_REALM);
-        String userId = ConfigurationUtil.getProperty(config, PROP_USER_NAME, 
DEFAULT_USER_NAME);
-        String password = ConfigurationUtil.getProperty(config, PROP_PASSWORD, 
DEFAULT_PASSWORD);
+        try{
+            final String httpServiceSelector = 
ConfigurationUtil.getProperty(this.getConfiguration(), 
PROP_HTTP_SERVICE_SELECTOR, null);
 
-        // register the servlet and resources
-        try
-        {
-            HttpContext httpContext = new OsgiManagerHttpContext(httpService,
-                securityProviderTracker, realm);
-
-            Dictionary<String, String> servletConfig = toStringConfig(config);
-
-            if (basicSecurityServiceRegistration == null) {
+            if (this.basicSecurityServiceRegistration == null) {
                 //register this component
-                BasicWebConsoleSecurityProvider service = new 
BasicWebConsoleSecurityProvider(bundleContext,
+                final String userId = 
ConfigurationUtil.getProperty(this.getConfiguration(), PROP_USER_NAME, 
DEFAULT_USER_NAME);
+                final String password = 
ConfigurationUtil.getProperty(this.getConfiguration(), PROP_PASSWORD, 
DEFAULT_PASSWORD);
+                final BasicWebConsoleSecurityProvider service = new 
BasicWebConsoleSecurityProvider(bundleContext,
                         userId, password, realm);
-                Dictionary<String, Object> serviceProperties = new 
Hashtable<>(); // NOSONAR
+                final Dictionary<String, Object> serviceProperties = new 
Hashtable<>(); // NOSONAR
                 // this is a last resort service, so use a low service ranking 
to prefer all other services over this one
                 serviceProperties.put(Constants.SERVICE_RANKING, 
Integer.MIN_VALUE);
-                basicSecurityServiceRegistration = 
bundleContext.registerService(WebConsoleSecurityProvider.class,
+                this.basicSecurityServiceRegistration = 
bundleContext.registerService(WebConsoleSecurityProvider.class,
                         service, serviceProperties);
             }
 
-            if (!httpServletRegistered) {
-                // register this servlet and take note of this
-                httpService.registerServlet(this.webManagerRoot, this, 
servletConfig,
-                    httpContext);
-                httpServletRegistered = true;
-            }
+            if (this.servletContextRegistration == null) {
+                final ServletContextHelper httpContext = new 
OsgiManagerHttpContext(this.bundleContext.getBundle(),
+                    securityProviderTracker, realm);
+                final Dictionary<String, Object> props = new Hashtable<>();
+                if (httpServiceSelector != null) {
+                    props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, 
httpServiceSelector);
+                }
+                
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, 
SERVLEXT_CONTEXT_NAME);
+                
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, 
this.webManagerRoot);
 
-            if (!httpResourcesRegistered) {
-                // register resources and take of this
-                httpService.registerResources(this.webManagerRoot + "/res", 
"/res",
-                    httpContext);
-                httpResourcesRegistered = true;
+                this.servletContextRegistration = 
getBundleContext().registerService(ServletContextHelper.class,
+                    httpContext, props);
             }
-        }
-        catch (Exception e)
-        {
-            log(LogService.LOG_ERROR, "bindHttpService: Problem setting up", 
e);
-        }
-    }
 
-    protected void unbindHttpService(HttpService httpService)
-    {
-        if (this.httpService != httpService)
-        {
-            log(LogService.LOG_DEBUG,
-                "unbindHttpService: Ignoring unbind of an HttpService to which 
we are not registered");
-            return;
-        }
+            if (this.servletRegistration == null) {
+                // register this servlet and take note of this
+                final Dictionary<String, Object> props = new Hashtable<>();
+                for(final Map.Entry<String, Object> entry : 
this.getConfiguration().entrySet()) {
+                    props.put(entry.getKey(), 
String.valueOf(entry.getValue()));
+                }
+                if (httpServiceSelector != null) {
+                    props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, 
httpServiceSelector);
+                }
 
-        unregisterHttpService();
+                
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/");
+                
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(" + 
HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=" + 
SERVLEXT_CONTEXT_NAME + ")");
 
-        // drop the service reference
-        this.httpService = null;
+                this.servletRegistration = 
getBundleContext().registerService(Servlet.class, this, props);                
+            }
+        } catch (final Exception e) {
+            log(LogLevel.ERROR.ordinal(), "registerHttpWhiteboardServices: 
Problem setting up", e);
+            this.unregisterHttpWhiteboardServices();
+        }
     }
 
-    synchronized void unregisterHttpService() {
-        if (httpService == null)
-            return;
-
-        if (basicSecurityServiceRegistration != null) {
+    synchronized void unregisterHttpWhiteboardServices() {
+        if (this.basicSecurityServiceRegistration != null) {
             try {
-                basicSecurityServiceRegistration.unregister();
-            } catch (Throwable t) {
-                log(LogService.LOG_WARNING,
-                        "unbindHttpService: Failed unregistering basic 
WebConsoleSecurityProvider", t);
+                this.basicSecurityServiceRegistration.unregister();
+            } catch (final IllegalStateException ignore) {
+                // ignore
             }
-            basicSecurityServiceRegistration = null;
+            this.basicSecurityServiceRegistration = null;
         }
 
-        if (httpResourcesRegistered)
-        {
-            try
-            {
-                httpService.unregister(this.webManagerRoot + "/res");
-            }
-            catch (Throwable t)
-            {
-                log(LogService.LOG_WARNING,
-                    "unbindHttpService: Failed unregistering Resources", t);
+        if (this.servletRegistration != null) {
+            try {
+                this.servletRegistration.unregister();
+            } catch (final IllegalStateException ignore) {
+                // ignore
             }
-            httpResourcesRegistered = false;
+            this.servletRegistration = null;
         }
 
-        if (httpServletRegistered)
-        {
-            try
-            {
-                httpService.unregister(this.webManagerRoot);
-            }
-            catch (Throwable t)
-            {
-                log(LogService.LOG_WARNING,
-                    "unbindHttpService: Failed unregistering Servlet", t);
+        if (this.servletContextRegistration != null) {
+            try {
+                this.servletContextRegistration.unregister();
+            } catch (final IllegalStateException ignore) {
+                // ignore
             }
-            httpServletRegistered = false;
+            this.servletContextRegistration = null;
         }
     }
 
-
-    private Map<String, Object> getConfiguration()
-    {
+    private Map<String, Object> getConfiguration() {
         return configuration;
     }
 
-
-    Map<String, Object> getDefaultConfiguration()
-    {
+    Map<String, Object> getDefaultConfiguration() {
         return defaultConfiguration;
     }
 
+    synchronized void updateConfiguration( final Dictionary<String, Object> 
osgiConfig) {
+        final Map<String, Object> config = new HashMap<String, Object>( 
this.defaultConfiguration );
 
-    synchronized void updateConfiguration( Dictionary<String, Object> 
osgiConfig )
-    {
-        Map<String, Object> config = new HashMap<String, Object>( 
this.defaultConfiguration );
-
-        if ( osgiConfig != null )
-        {
-            for ( Enumeration<String> keys = osgiConfig.keys(); 
keys.hasMoreElements(); )
-            {
+        if ( osgiConfig != null ) {
+            for ( Enumeration<String> keys = osgiConfig.keys(); 
keys.hasMoreElements(); ) {
                 final String key = keys.nextElement();
                 config.put( key, osgiConfig.get( key ) );
             }
         }
 
-        configuration = config;
+        this.configuration = config;
 
         final Object locale = config.get(PROP_LOCALE);
-        configuredLocale = locale == null || locale.toString().trim().length() 
== 0 //
-        ? null : Util.parseLocaleString(locale.toString().trim());
+        this.configuredLocale = locale == null || 
locale.toString().trim().length() == 0 ? null : 
Util.parseLocaleString(locale.toString().trim());
 
-        logLevel = ConfigurationUtil.getProperty(config, PROP_LOG_LEVEL, 
DEFAULT_LOG_LEVEL);
+        this.logLevel = ConfigurationUtil.getProperty(config, PROP_LOG_LEVEL, 
DEFAULT_LOG_LEVEL);
         AbstractWebConsolePlugin.setLogLevel(logLevel);
 
         // default plugin page configuration
@@ -1140,67 +1060,33 @@ public class OsgiManager extends GenericServlet
 
         // get the web manager root path
         String newWebManagerRoot = ConfigurationUtil.getProperty(config, 
PROP_MANAGER_ROOT, DEFAULT_MANAGER_ROOT);
-        if (!newWebManagerRoot.startsWith("/")) //$NON-NLS-1$
-        {
-            newWebManagerRoot = "/" + newWebManagerRoot; //$NON-NLS-1$
+        if (!newWebManagerRoot.startsWith("/")) { //$NON-NLS-1$
+            newWebManagerRoot = "/".concat(newWebManagerRoot); //$NON-NLS-1$
         }
 
         // default category
         this.defaultCategory = ConfigurationUtil.getProperty( config, 
PROP_CATEGORY, DEFAULT_CATEGORY );
 
-        // get the HTTP Service selector (and dispose tracker for later
-        // recreation)
-        final String newHttpServiceSelector = 
ConfigurationUtil.getProperty(config,
-            PROP_HTTP_SERVICE_SELECTOR, DEFAULT_HTTP_SERVICE_SELECTOR);
-        if (httpServiceTracker != null
-            && !httpServiceTracker.isSameSelector(newHttpServiceSelector))
-        {
-            httpServiceTracker.close();
-            httpServiceTracker = null;
-        }
-
         // secret heuristics
         final boolean enableHeuristics = ConfigurationUtil.getProperty(config, 
PROP_ENABLE_SECRET_HEURISTIC, DEFAULT_ENABLE_SECRET_HEURISTIC);
         OsgiManager.ENABLE_SECRET_HEURISTICS = enableHeuristics;
 
         // get enabled plugins
-        String[] plugins = ConfigurationUtil.getStringArrayProperty(config, 
PROP_ENABLED_PLUGINS);
-        enabledPlugins = null == plugins ? null : new 
HashSet<String>(Arrays.asList(plugins));
+        final String[] plugins = 
ConfigurationUtil.getStringArrayProperty(config, PROP_ENABLED_PLUGINS);
+        this.enabledPlugins = null == plugins ? null : new 
HashSet<String>(Arrays.asList(plugins));
         // check for moved config manager class (see FELIX-4074)
-        if ( enabledPlugins != null )
-        {
-            if ( enabledPlugins.remove(OLD_CONFIG_MANAGER_CLASS) )
-            {
+        if ( enabledPlugins != null ) {
+            if ( enabledPlugins.remove(OLD_CONFIG_MANAGER_CLASS) ) {
                 enabledPlugins.add(NEW_CONFIG_MANAGER_CLASS);
             }
         }
         initInternalPlugins();
 
-        // might update HTTP service registration
-        HttpService httpService = this.httpService;
-        if (httpService != null)
-        {
-            // unbind old location first
-            unbindHttpService(httpService);
-
-            // switch location
-            this.webManagerRoot = newWebManagerRoot;
-
-            // bind new location now
-            bindHttpService(httpService);
-        }
-        else
-        {
-            // just set the configured location (FELIX-2034)
-            this.webManagerRoot = newWebManagerRoot;
-        }
-
-        // create or recreate the HTTP service tracker with the new selector
-        if (httpServiceTracker == null)
-        {
-            httpServiceTracker = HttpServiceTracker.create(this, 
newHttpServiceSelector);
-            httpServiceTracker.open();
-        }
+        // update http service registrations.
+        this.unregisterHttpWhiteboardServices();
+        // switch location
+        this.webManagerRoot = newWebManagerRoot;
+        this.registerHttpWhiteboardServices();
     }
 
     private void initInternalPlugins()
@@ -1239,18 +1125,6 @@ public class OsgiManager extends GenericServlet
         return enabledPlugins != null && !enabledPlugins.contains( pluginClass 
);
     }
 
-
-    private Dictionary<String, String> toStringConfig( Map<String, Object> 
config )
-    {
-        Dictionary<String, String> stringConfig = new Hashtable<>();
-        for ( Iterator<Map.Entry<String, Object>> ei = 
config.entrySet().iterator(); ei.hasNext(); )
-        {
-            Entry<String, Object> entry = ei.next();
-            stringConfig.put( entry.getKey(), String.valueOf( entry.getValue() 
) );
-        }
-        return stringConfig;
-    }
-
     static Set<String> splitCommaSeparatedString(final String str) {
         if (str == null)
             return Collections.emptySet();
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContext.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContext.java
index f8a4e4917e..e206756945 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContext.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContext.java
@@ -22,71 +22,55 @@ import static 
org.apache.felix.webconsole.internal.servlet.BasicWebConsoleSecuri
 import static 
org.apache.felix.webconsole.internal.servlet.BasicWebConsoleSecurityProvider.HEADER_WWW_AUTHENTICATE;
 
 import java.io.IOException;
-import java.net.URL;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.webconsole.User;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider2;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.http.HttpService;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.context.ServletContextHelper;
 import org.osgi.util.tracker.ServiceTracker;
 
 
-final class OsgiManagerHttpContext implements HttpContext
-{
-
-    private final HttpContext base;
+final class OsgiManagerHttpContext extends ServletContextHelper {
 
     private final ServiceTracker<WebConsoleSecurityProvider, 
WebConsoleSecurityProvider> tracker;
 
     private final String realm;
 
-    OsgiManagerHttpContext(final HttpService httpService,
+    OsgiManagerHttpContext(final Bundle webConsoleBundle,
             final ServiceTracker<WebConsoleSecurityProvider, 
WebConsoleSecurityProvider> tracker,
-            final String realm)
-    {
+            final String realm) {
+        super(webConsoleBundle);
         this.tracker = tracker;
         this.realm = realm;
-        this.base = httpService.createDefaultHttpContext();
     }
 
+    @Override
+    public boolean handleSecurity( final HttpServletRequest r, final 
HttpServletResponse response ) {
+        final WebConsoleSecurityProvider provider = tracker.getService();
 
-    public String getMimeType( String name )
-    {
-        return this.base.getMimeType( name );
-    }
-
+        // for compatibility we have to adjust a few methods on the request
+        final HttpServletRequest request = new HttpServletRequestWrapper(r) {
 
-    public URL getResource( String name )
-    {
-        URL url = this.base.getResource( name );
-        if ( url == null && name.endsWith( "/" ) )
-        {
-            return this.base.getResource( name.substring( 0, name.length() - 1 
) );
-        }
-        return url;
-    }
+            @Override
+            public String getContextPath() {
+                return "";
+            }
 
+            @Override
+            public String getServletPath() {
+                return r.getContextPath();
+            }
 
-    /**
-     * Checks the <code>Authorization</code> header of the request for Basic
-     * authentication user name and password. If contained, the credentials are
-     * compared to the user name and password set for the OSGi Console.
-     * <p>
-     * If no user name is set, the <code>Authorization</code> header is
-     * ignored and the client is assumed to be authenticated.
-     *
-     * @param request The HTTP request used to get the
-     *            <code>Authorization</code> header.
-     * @param response The HTTP response used to send the authentication 
request
-     *            if authentication is required but not satisfied.
-     * @return {@code} true if authentication is required and not satisfied by 
the request.
-     */
-    public boolean handleSecurity( final HttpServletRequest request, final 
HttpServletResponse response ) {
-        final WebConsoleSecurityProvider provider = tracker.getService();
+            @Override
+            public String getPathInfo() {
+                return r.getServletPath();
+            }
+        };
 
         // check whether the security provider can fully handle the request
         final boolean result;
@@ -156,8 +140,8 @@ final class OsgiManagerHttpContext implements HttpContext
                         if ( authenticate( provider, username, userPass[1] ) )
                         {
                             // as per the spec, set attributes
-                            request.setAttribute( 
HttpContext.AUTHENTICATION_TYPE, HttpServletRequest.BASIC_AUTH );
-                            request.setAttribute( HttpContext.REMOTE_USER, 
username );
+                            request.setAttribute( AUTHENTICATION_TYPE, 
HttpServletRequest.BASIC_AUTH );
+                            request.setAttribute( REMOTE_USER, username );
 
                             // set web console user attribute
                             request.setAttribute( 
WebConsoleSecurityProvider2.USER_ATTRIBUTE, username );
@@ -175,15 +159,12 @@ final class OsgiManagerHttpContext implements HttpContext
         }
 
         // request authentication
-        try
-        {
+        try {
             response.setHeader( HEADER_WWW_AUTHENTICATE, 
AUTHENTICATION_SCHEME_BASIC + " realm=\"" + this.realm + "\"" );
             response.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
             response.setContentLength( 0 );
             response.flushBuffer();
-        }
-        catch ( IOException ioe )
-        {
+        } catch ( IOException ioe ) {
             // failed sending the response ... cannot do anything about it
         }
 
@@ -191,8 +172,7 @@ final class OsgiManagerHttpContext implements HttpContext
         return false;
     }
 
-    private boolean authenticate( WebConsoleSecurityProvider provider, String 
username, byte[] password )
-    {
+    private boolean authenticate( WebConsoleSecurityProvider provider, String 
username, byte[] password ) {
         if ( provider != null )
         {
             return provider.authenticate( username, 
BasicWebConsoleSecurityProvider.toString( password ) ) != null;
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/Plugin.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/Plugin.java
index 9ea34a5adf..5ad14c3144 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/Plugin.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/Plugin.java
@@ -35,7 +35,7 @@ import 
org.apache.felix.webconsole.internal.WebConsolePluginAdapter;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
-import org.osgi.service.log.LogService;
+import org.osgi.service.log.LogLevel;
 
 public abstract class Plugin implements ServletConfig, Comparable<Plugin> {
 
@@ -279,7 +279,7 @@ public abstract class Plugin implements ServletConfig, 
Comparable<Plugin> {
         protected AbstractWebConsolePlugin doGetConsolePlugin() {
             if (!isEnabled()) {
                 if (doLog) {
-                    osgiManager.log( LogService.LOG_INFO, "Ignoring plugin " + 
pluginClassName + ": Disabled by configuration" );
+                    osgiManager.log( LogLevel.INFO.ordinal(), "Ignoring plugin 
" + pluginClassName + ": Disabled by configuration" );
                     doLog = false;
                 }
                 return null;
@@ -299,7 +299,7 @@ public abstract class Plugin implements ServletConfig, 
Comparable<Plugin> {
             } catch (final Throwable t) {
                 plugin = null; // in case only activate has faled!
                 if (doLog) {
-                    osgiManager.log( LogService.LOG_WARNING, "Failed to 
instantiate plugin " + pluginClassName, t );
+                    osgiManager.log( LogLevel.WARN.ordinal(), "Failed to 
instantiate plugin " + pluginClassName, t );
                     doLog = false;
                 }
             }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
index 05e69fd861..17d305bd6d 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/PluginHolder.java
@@ -40,7 +40,7 @@ import org.osgi.framework.Constants;
 import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
-import org.osgi.service.log.LogService;
+import org.osgi.service.log.LogLevel;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
@@ -209,6 +209,7 @@ class PluginHolder implements 
ServiceTrackerCustomizer<Servlet, Plugin> {
      *
      * @return The localized map of labels to titles
      */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     Map getLocalizedLabelMap( final ResourceBundleManager 
resourceBundleManager, final Locale locale, final String defaultCategory )
     {
         final Map map = new HashMap();
@@ -259,6 +260,7 @@ class PluginHolder implements 
ServiceTrackerCustomizer<Servlet, Plugin> {
     }
 
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     private Map findCategoryMap( Map map, String categoryPath )
     {
         Map categoryMap = null;
@@ -360,14 +362,14 @@ class PluginHolder implements 
ServiceTrackerCustomizer<Servlet, Plugin> {
                 if (!first.init()) {
                     list.remove(plugin);
                 } else if (oldPlugin != null) {
-                    osgiManager.log(LogService.LOG_WARNING, "Overwriting 
existing plugin " + oldPlugin.getId() 
+                    osgiManager.log(LogLevel.WARN.ordinal(), "Overwriting 
existing plugin " + oldPlugin.getId() 
                             + " having label " + plugin.getLabel() + " with 
new plugin " + plugin.getId()
                             + " due to higher ranking " );
                     oldPlugin.dispose();
                 }
             }
             if (first == oldPlugin) {
-                osgiManager.log(LogService.LOG_WARNING, "Ignoring new plugin " 
+ plugin.getId()
+                osgiManager.log(LogLevel.WARN.ordinal(), "Ignoring new plugin 
" + plugin.getId()
                         + " having existing label " + plugin.getLabel() + " 
due to lower ranking than old plugin " + oldPlugin.getId() );
             }
         }
diff --git 
a/webconsole/src/test/java/org/apache/felix/webconsole/internal/configuration/ConfigJsonSupportTest.java
 
b/webconsole/src/test/java/org/apache/felix/webconsole/internal/configuration/ConfigJsonSupportTest.java
index a6f0dfe856..ea682fda44 100644
--- 
a/webconsole/src/test/java/org/apache/felix/webconsole/internal/configuration/ConfigJsonSupportTest.java
+++ 
b/webconsole/src/test/java/org/apache/felix/webconsole/internal/configuration/ConfigJsonSupportTest.java
@@ -19,14 +19,12 @@
 package org.apache.felix.webconsole.internal.configuration;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Dictionary;
 import java.util.Hashtable;
-import java.util.List;
 
 import org.apache.felix.webconsole.spi.ConfigurationHandler;
 import org.apache.felix.webconsole.spi.ValidationException;
diff --git 
a/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContextTest.java
 
b/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContextTest.java
index 70fae7e4cf..6b9a6c7848 100644
--- 
a/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContextTest.java
+++ 
b/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerHttpContextTest.java
@@ -21,8 +21,8 @@ package org.apache.felix.webconsole.internal.servlet;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider;
 import org.junit.Test;
 import org.mockito.Mockito;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
-import org.osgi.service.http.HttpService;
 
 import java.lang.reflect.Method;
 
@@ -32,8 +32,8 @@ public class OsgiManagerHttpContextTest {
     @Test
     public void testAuthenticate() throws Exception {
         BundleContext bc = Mockito.mock(BundleContext.class);
-        HttpService svc = Mockito.mock(HttpService.class);
-        OsgiManagerHttpContext ctx = new OsgiManagerHttpContext(svc, null, 
"blah");
+        Bundle bundle = Mockito.mock(Bundle.class);
+        OsgiManagerHttpContext ctx = new OsgiManagerHttpContext(bundle, null, 
"blah");
 
         Method authenticateMethod = 
OsgiManagerHttpContext.class.getDeclaredMethod(
                 "authenticate", new Class [] 
{WebConsoleSecurityProvider.class, String.class, byte[].class});
@@ -54,8 +54,8 @@ public class OsgiManagerHttpContextTest {
         BundleContext bc = Mockito.mock(BundleContext.class);
         
Mockito.when(bc.getProperty(OsgiManager.FRAMEWORK_PROP_SECURITY_PROVIDERS)).thenReturn("a");
 
-        HttpService svc = Mockito.mock(HttpService.class);
-        OsgiManagerHttpContext ctx = new OsgiManagerHttpContext(svc, null, 
"blah");
+        Bundle bundle = Mockito.mock(Bundle.class);
+        OsgiManagerHttpContext ctx = new OsgiManagerHttpContext(bundle, null, 
"blah");
 
         Method authenticateMethod = 
OsgiManagerHttpContext.class.getDeclaredMethod(
                 "authenticate", new Class [] 
{WebConsoleSecurityProvider.class, String.class, byte[].class});
diff --git 
a/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerTest.java
 
b/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerTest.java
index efe73eec46..70b8574d18 100644
--- 
a/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerTest.java
+++ 
b/webconsole/src/test/java/org/apache/felix/webconsole/internal/servlet/OsgiManagerTest.java
@@ -19,9 +19,7 @@
 package org.apache.felix.webconsole.internal.servlet;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import java.lang.reflect.Field;
@@ -35,10 +33,12 @@ import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.Servlet;
 
 import org.apache.felix.webconsole.WebConsoleSecurityProvider;
 import org.junit.Test;
-import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -49,8 +49,8 @@ import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.http.HttpService;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.context.ServletContextHelper;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
 public class OsgiManagerTest {
@@ -171,38 +171,27 @@ public class OsgiManagerTest {
     }
 
 
-    @SuppressWarnings("serial")
     @Test
     public void testUpdateRegistrationStateNoRequiredProviders() throws 
Exception {
         BundleContext bc = mockBundleContext();
 
-        final List<String> invocations = new ArrayList<String>();
-        OsgiManager mgr = new OsgiManager(bc) {
+        final AtomicReference<String> invocation = new 
AtomicReference<String>();
+        new OsgiManager(bc) {
             @Override
-            protected synchronized void registerHttpService() {
-                invocations.add("register");
+            protected synchronized void registerHttpWhiteboardServices() {
+                invocation.set("register");
             }
 
             @Override
-            protected synchronized void unregisterHttpService() {
-                invocations.add("unregister");
+            protected synchronized void unregisterHttpWhiteboardServices() {
+                invocation.set("unregister");
             }
         };
 
-        // HTTP Service not present -> unregister
-        mgr.updateRegistrationState();
-        assertEquals(Collections.singletonList("unregister"), invocations);
-
-        // HTTP Service present, no required providers, no registered 
providers -> register
-        invocations.clear();
-        mgr.registeredSecurityProviders.clear();
-        mgr.requiredSecurityProviders.clear();
-        setPrivateField(OsgiManager.class, mgr, "httpService", 
Mockito.mock(HttpService.class));
-        mgr.updateRegistrationState();
-        assertEquals(Collections.singletonList("register"), invocations);
+        // services are registered by default
+        assertEquals("register", invocation.get());
     }
 
-    @SuppressWarnings("serial")
     @Test
     public void testUpdateRegistrationStateSomeRequiredProviders() throws 
Exception {
         BundleContext bc = mockBundleContext();
@@ -212,186 +201,92 @@ public class OsgiManagerTest {
         final List<String> invocations = new ArrayList<String>();
         OsgiManager mgr = new OsgiManager(bc) {
             @Override
-            protected synchronized void registerHttpService() {
+            protected synchronized void registerHttpWhiteboardServices() {
                 invocations.add("register");
             }
 
             @Override
-            protected synchronized void unregisterHttpService() {
+            protected synchronized void unregisterHttpWhiteboardServices() {
                 invocations.add("unregister");
             }
         };
 
-        // HTTP Service present, some required providers, no registered 
providers -> unregister
+        // some required providers, no registered providers -> unregister
         invocations.clear();
         mgr.registeredSecurityProviders.clear();
-        setPrivateField(OsgiManager.class, mgr, "httpService", 
Mockito.mock(HttpService.class));
         mgr.updateRegistrationState();
         assertEquals(Collections.singletonList("unregister"), invocations);
 
-        // HTTP Service present, some required providers, more registered ones 
-> register
+        // some required providers, more registered ones -> register
         invocations.clear();
         mgr.registeredSecurityProviders.addAll(Arrays.asList("foo", "bar", 
"blah"));
-        setPrivateField(OsgiManager.class, mgr, "httpService", 
Mockito.mock(HttpService.class));
         mgr.updateRegistrationState();
         assertEquals(Collections.singletonList("register"), invocations);
 
-        // HTTP Service present, some required providers, different registered 
ones -> unregister
+        // some required providers, different registered ones -> unregister
         invocations.clear();
         mgr.registeredSecurityProviders.clear();
         mgr.registeredSecurityProviders.addAll(Arrays.asList("foo", "bar"));
-        setPrivateField(OsgiManager.class, mgr, "httpService", 
Mockito.mock(HttpService.class));
         mgr.updateRegistrationState();
         assertEquals(Collections.singletonList("unregister"), invocations);
 
-        // HTTP Service not present, some required providers, more registered 
ones -> unregister
+        // some required providers, more registered ones -> unregister
         invocations.clear();
         mgr.registeredSecurityProviders.addAll(Arrays.asList("foo", "bar", 
"blah"));
-        setPrivateField(OsgiManager.class, mgr, "httpService", null);
         mgr.updateRegistrationState();
-        assertEquals(Collections.singletonList("unregister"), invocations);
+        assertEquals(Collections.singletonList("register"), invocations);
     }
 
-    @SuppressWarnings("serial")
-    @Test
-    public void testBindService() throws Exception {
-        BundleContext bc = mockBundleContext();
 
-        final List<Boolean> updateCalled = new ArrayList<Boolean>();
-        OsgiManager mgr = new OsgiManager(bc) {
-            @Override
-            void updateRegistrationState() {
-                updateCalled.add(true);
-            }
-        };
-
-        assertEquals("Precondition", 0, updateCalled.size());
-
-        HttpService svc = Mockito.mock(HttpService.class);
-        mgr.bindHttpService(svc);
-        assertSame(svc, getPrivateField(OsgiManager.class, mgr, 
"httpService"));
-        assertEquals(1, updateCalled.size());
-
-        updateCalled.clear();
-        mgr.bindHttpService(null);
-        assertSame(svc, getPrivateField(OsgiManager.class, mgr, 
"httpService"));
-        assertEquals(0, updateCalled.size());
-    }
-
-    @SuppressWarnings("serial")
+    @SuppressWarnings({ "unchecked" })
     @Test
-    public void testUnbindService() throws Exception {
-        BundleContext bc = mockBundleContext();
+    public void testRegisterHttpWhiteboardServices() throws Exception {
+        final BundleContext bc = mockBundleContext();
+        final OsgiManager mgr = new OsgiManager(bc);
 
-        final List<Boolean> updateCalled = new ArrayList<Boolean>();
-        final List<Boolean> unregisterCalled = new ArrayList<Boolean>();
-        OsgiManager mgr = new OsgiManager(bc) {
-            @Override
-            void updateRegistrationState() {
-                updateCalled.add(true);
-            }
-
-            @Override
-            synchronized void unregisterHttpService() {
-                try {
-                    if (getPrivateField(OsgiManager.class, this, 
"httpService") != null) {
-                        unregisterCalled.add(true);
-                    }
-                } catch (Exception e) {
-                }
-            }
-        };
-
-        HttpService svc = Mockito.mock(HttpService.class);
-        mgr.bindHttpService(svc);
-        assertSame(svc, getPrivateField(OsgiManager.class, mgr, 
"httpService"));
-        assertEquals(1, updateCalled.size());
-        assertEquals(0, unregisterCalled.size());
-
-        updateCalled.clear();
-        mgr.unbindHttpService(null);
-        assertEquals(0, updateCalled.size());
-        assertSame(svc, getPrivateField(OsgiManager.class, mgr, 
"httpService"));
-        assertEquals(0, unregisterCalled.size());
-
-        updateCalled.clear();
-        // unbind a different service, this should be ignored
-        mgr.unbindHttpService(Mockito.mock(HttpService.class));
-        assertEquals(0, updateCalled.size());
-        assertSame(svc, getPrivateField(OsgiManager.class, mgr, 
"httpService"));
-        assertEquals(0, unregisterCalled.size());
-
-        updateCalled.clear();
-        // unbind the bound service, this should remove it
-        mgr.unbindHttpService(svc);
-        assertEquals(0, updateCalled.size());
-        assertEquals(1, unregisterCalled.size());
-        assertNull(getPrivateField(OsgiManager.class, mgr, "httpService"));
-    }
-
-    @Test
-    public void testRegisterHttpService() throws Exception {
-        BundleContext bc = mockBundleContext();
-        OsgiManager mgr = new OsgiManager(bc);
+        Mockito.verify(bc, Mockito.times(1))
+            .registerService(Mockito.eq(WebConsoleSecurityProvider.class), 
Mockito.isA(WebConsoleSecurityProvider.class), Mockito.isA(Dictionary.class));
+        Mockito.verify(bc, Mockito.times(1))
+            .registerService(Mockito.eq(ServletContextHelper.class), 
Mockito.isA(ServletContextHelper.class), Mockito.isA(Dictionary.class));
+        Mockito.verify(bc, Mockito.times(1))
+            .registerService(Mockito.eq(Servlet.class), 
Mockito.isA(Servlet.class), Mockito.isA(Dictionary.class));
 
-        HttpService httpSvc = Mockito.mock(HttpService.class);
-        setPrivateField(OsgiManager.class, mgr, "httpService", httpSvc);
-
-        assertFalse((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpServletRegistered"));
-        assertFalse((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpResourcesRegistered"));
-        mgr.registerHttpService();
-        assertTrue((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpServletRegistered"));
-        assertTrue((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpResourcesRegistered"));
-
-        Mockito.verify(httpSvc, 
Mockito.times(1)).registerServlet(Mockito.eq("/system/console"), 
Mockito.same(mgr),
-                Mockito.isA(Dictionary.class),
-                Mockito.isA(HttpContext.class));
-        Mockito.verify(httpSvc, 
Mockito.times(1)).registerResources(Mockito.eq("/system/console/res"), 
Mockito.eq("/res"),
-                Mockito.isA(HttpContext.class));
-
-        mgr.registerHttpService();
+        mgr.registerHttpWhiteboardServices();
 
         // Should not re-register the services, as they were already registered
-        Mockito.verify(httpSvc, 
Mockito.times(1)).registerServlet(Mockito.eq("/system/console"), 
Mockito.same(mgr),
-                Mockito.isA(Dictionary.class),
-                Mockito.isA(HttpContext.class));
-        Mockito.verify(httpSvc, 
Mockito.times(1)).registerResources(Mockito.eq("/system/console/res"), 
Mockito.eq("/res"),
-                Mockito.isA(HttpContext.class));
+        Mockito.verify(bc, Mockito.times(1))
+            .registerService(Mockito.eq(WebConsoleSecurityProvider.class), 
Mockito.isA(WebConsoleSecurityProvider.class), Mockito.isA(Dictionary.class));
+        Mockito.verify(bc, Mockito.times(1))
+            .registerService(Mockito.eq(ServletContextHelper.class), 
Mockito.isA(ServletContextHelper.class), Mockito.isA(Dictionary.class));
+        Mockito.verify(bc, Mockito.times(1))
+            .registerService(Mockito.eq(Servlet.class), 
Mockito.isA(Servlet.class), Mockito.isA(Dictionary.class));
     }
 
+    @SuppressWarnings({ "rawtypes" })
     @Test
     public void testUnregisterHttpService() throws Exception {
-        BundleContext bc = mockBundleContext();
-        OsgiManager mgr = new OsgiManager(bc);
-
-        HttpService httpSvc = Mockito.mock(HttpService.class);
-        setPrivateField(OsgiManager.class, mgr, "httpService", httpSvc);
-        setPrivateField(OsgiManager.class, mgr, "httpServletRegistered", true);
-        setPrivateField(OsgiManager.class, mgr, "httpResourcesRegistered", 
true);
-
-        mgr.unregisterHttpService();
-        assertFalse((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpServletRegistered"));
-        assertFalse((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpResourcesRegistered"));
+        final BundleContext bc = mockBundleContext();
+        final OsgiManager mgr = new OsgiManager(bc);
 
-        Mockito.verify(httpSvc, 
Mockito.times(1)).unregister("/system/console");
-        Mockito.verify(httpSvc, 
Mockito.times(1)).unregister("/system/console/res");
+        final ServiceRegistration reg1 = 
Mockito.mock(ServiceRegistration.class);
+        final ServiceRegistration reg2 = 
Mockito.mock(ServiceRegistration.class);
 
-        mgr.unregisterHttpService();
-        assertFalse((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpServletRegistered"));
-        assertFalse((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpResourcesRegistered"));
+        setPrivateField(OsgiManager.class, mgr, "servletContextRegistration", 
reg1);
+        setPrivateField(OsgiManager.class, mgr, "servletRegistration", reg2);
 
-        Mockito.verify(httpSvc, 
Mockito.times(1)).unregister("/system/console");
-        Mockito.verify(httpSvc, 
Mockito.times(1)).unregister("/system/console/res");
+        mgr.unregisterHttpWhiteboardServices();
+        assertNull(getPrivateField(OsgiManager.class, mgr, 
"servletContextRegistration"));
+        assertNull(getPrivateField(OsgiManager.class, mgr, 
"servletRegistration"));
 
-        // Unset the http service
-        setPrivateField(OsgiManager.class, mgr, "httpService", null);
+        Mockito.verify(reg1, Mockito.times(1)).unregister();
+        Mockito.verify(reg2, Mockito.times(1)).unregister();
 
-        mgr.unregisterHttpService();
-        assertFalse((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpServletRegistered"));
-        assertFalse((Boolean) getPrivateField(OsgiManager.class, mgr, 
"httpResourcesRegistered"));
+        mgr.unregisterHttpWhiteboardServices();
+        assertNull(getPrivateField(OsgiManager.class, mgr, 
"servletContextRegistration"));
+        assertNull(getPrivateField(OsgiManager.class, mgr, 
"servletRegistration"));
 
-        Mockito.verify(httpSvc, 
Mockito.times(1)).unregister("/system/console");
-        Mockito.verify(httpSvc, 
Mockito.times(1)).unregister("/system/console/res");
+        Mockito.verify(reg1, Mockito.times(1)).unregister();
+        Mockito.verify(reg2, Mockito.times(1)).unregister();
     }
 
     private Object getPrivateField(Class<?> cls, Object obj, String field) 
throws Exception {
@@ -429,6 +324,8 @@ public class OsgiManagerTest {
                                return 
Collections.enumeration(Collections.singleton(rbUrl));
                        }
         });
+        Mockito.when(bc.registerService((Class)Mockito.any(), 
(Object)Mockito.any(), (Dictionary)Mockito.any()))
+            .thenReturn(Mockito.mock(ServiceRegistration.class));
         return bc;
     }
 }


Reply via email to