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

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

commit 1d5b52db1c64aee0de3e7d16bcff64373f10a822
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Mon Aug 21 15:25:10 2023 +0200

    Port changes from main branch
---
 webconsole/pom.xml                                 |   7 +
 .../felix/webconsole/i18n/LocalizationHelper.java  |  10 +-
 .../internal/WebConsolePluginAdapter.java          |   1 +
 .../internal/configuration/ConfigManager.java      |  31 +--
 .../webconsole/internal/core/BundlesServlet.java   |  45 ++--
 .../core/PermissionsConfigurationPrinter.java      |   6 +-
 .../core/ServicesConfigurationPrinter.java         |  64 +-----
 .../webconsole/internal/core/ServicesServlet.java  |  44 ++--
 .../internal/core/ServicesUsedInfoProvider.java    |   9 +-
 .../internal/filter/FilteringResponseWrapper.java  |  39 ++--
 .../internal/filter/ResourceFilteringWriter.java   |   9 +-
 .../internal/i18n/CombinedEnumeration.java         |  26 +--
 .../internal/i18n/CombinedResourceBundle.java      |   2 +-
 .../i18n/ConsolePropertyResourceBundle.java        |  26 +--
 .../internal/i18n/ResourceBundleCache.java         |  87 +++-----
 .../webconsole/internal/misc/LicenseServlet.java   |   5 +-
 .../internal/misc/SystemPropertiesPrinter.java     |   9 +-
 .../JakartaServletAdapter.java}                    | 231 ++++++++++-----------
 .../internal/servlet/JakartaServletTracker.java    | 106 ++++++++++
 .../webconsole/internal/servlet/OsgiManager.java   |  12 ++
 .../felix/webconsole/internal/servlet/Plugin.java  |   6 +-
 .../webconsole/internal/servlet/PluginHolder.java  |  43 ++--
 .../webconsole/internal/system/VMStatPlugin.java   |   6 +-
 .../felix/webconsole/servlet/AbstractServlet.java  | 201 ++++++++++++++++++
 .../servlet/RequestVariableResolver.java           |  62 ++++++
 .../felix/webconsole/servlet/ServletConstants.java |  75 +++++++
 .../felix/webconsole/servlet/package-info.java     |  21 ++
 .../webconsole/AbstractWebConsolePluginTest.java   |   4 +
 .../internal/servlet/OsgiManagerTest.java          |   4 +-
 29 files changed, 799 insertions(+), 392 deletions(-)

diff --git a/webconsole/pom.xml b/webconsole/pom.xml
index 6e76474344..3026269816 100644
--- a/webconsole/pom.xml
+++ b/webconsole/pom.xml
@@ -42,6 +42,7 @@
             org.apache.felix.webconsole;provide:=true,
             org.apache.felix.webconsole.bundleinfo;provide:=true,
             org.apache.felix.webconsole.i18n;provide:=true,
+            org.apache.felix.webconsole.servlet,
             org.apache.felix.webconsole.spi
           </webconsole.exports>
           <felix.java.version>11</felix.java.version>
@@ -384,6 +385,12 @@
             <version>6.0.0</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.owasp.encoder</groupId>
+            <artifactId>encoder</artifactId>
+            <version>1.2.3</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java
index a29b6c1c04..b830e38caa 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java
@@ -40,10 +40,10 @@ public class LocalizationHelper {
      *            the bundle that provides the localization resources. See the
      *            standard OSGi-type localization support.
      */
-    public LocalizationHelper(Bundle bundle) {
-       if (null == bundle)
-           throw new NullPointerException();
-       this.cache = new ResourceBundleCache(bundle);
+    public LocalizationHelper(final Bundle bundle) {
+           if (null == bundle)
+               throw new NullPointerException();
+           this.cache = new ResourceBundleCache(bundle);
     }
 
     /**
@@ -54,6 +54,6 @@ public class LocalizationHelper {
      * @return the resource bundle (could be empty, but never 
<code>null</code>)
      */
     public ResourceBundle getResourceBundle(final Locale locale) {
-       return cache.getResourceBundle(locale);
+           return cache.getResourceBundle(locale);
     }
 }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/WebConsolePluginAdapter.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/WebConsolePluginAdapter.java
index 362453a571..4eb6742585 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/WebConsolePluginAdapter.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/WebConsolePluginAdapter.java
@@ -239,6 +239,7 @@ public class WebConsolePluginAdapter extends 
AbstractWebConsolePlugin
         }
     }
 
+
     //---------- internal
 
     @SuppressWarnings("rawtypes")
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
index b9a82702bc..efed40249b 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
@@ -35,6 +35,7 @@ import org.apache.felix.webconsole.WebConsoleUtil;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
 import org.apache.felix.webconsole.internal.Util;
 import org.apache.felix.webconsole.internal.misc.ServletSupport;
+import org.apache.felix.webconsole.servlet.RequestVariableResolver;
 import org.apache.felix.webconsole.spi.ConfigurationHandler;
 import org.apache.felix.webconsole.spi.ValidationException;
 import org.osgi.framework.BundleContext;
@@ -423,9 +424,9 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
             cas.getJsonSupport().listFactoryConfigurations( jw, pidFilter, 
locale );
         }
         if ( !hasConfigs && !hasMetatype && cas != null ) {
-            jw.key("noconfigs").value(true); //$NON-NLS-1$
+            jw.key("noconfigs").value(true);
         } else {
-            jw.key("noconfigs").value(false); //$NON-NLS-1$
+            jw.key("noconfigs").value(false);
         }
 
         jw.endObject();
@@ -438,19 +439,19 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
 
         // prepare variables
         final String referer = request.getParameter( REFERER );
-        final boolean factoryCreate = "true".equals( 
request.getParameter(FACTORY_CREATE) ); //$NON-NLS-1$
-        @SuppressWarnings("unchecked")
-        final Map<String, Object> vars = ( ( Map<String, Object> ) 
WebConsoleUtil.getVariableResolver( request ) );
-        vars.put( "__data__", json.toString() ); //$NON-NLS-1$
-        vars.put( "selectedPid", pid != null ? pid : "" ); //$NON-NLS-1$ 
//$NON-NLS-2$
-        vars.put( "configurationReferer", referer != null ? referer : "" ); 
//$NON-NLS-1$ //$NON-NLS-2$
-        vars.put( "factoryCreate", Boolean.valueOf(factoryCreate) ); 
//$NON-NLS-1$
-        vars.put( "param.apply", ACTION_APPLY ); //$NON-NLS-1$
-        vars.put( "param.create", ACTION_CREATE ); //$NON-NLS-1$
-        vars.put( "param.unbind", ACTION_UNBIND ); //$NON-NLS-1$
-        vars.put( "param.delete", ACTION_DELETE ); //$NON-NLS-1$
-        vars.put( "param.propertylist", PROPERTY_LIST ); //$NON-NLS-1$
-        vars.put( "param.pidFilter", PID_FILTER ); //$NON-NLS-1$
+        final boolean factoryCreate = "true".equals( 
request.getParameter(FACTORY_CREATE) );
+
+        final RequestVariableResolver vars = 
WebConsoleUtil.getRequestVariableResolver(request);
+        vars.put( "__data__", json.toString() ); 
+        vars.put( "selectedPid", pid != null ? pid : "" );
+        vars.put( "configurationReferer", referer != null ? referer : "" );
+        vars.put( "factoryCreate", Boolean.valueOf(factoryCreate) );
+        vars.put( "param.apply", ACTION_APPLY );
+        vars.put( "param.create", ACTION_CREATE );
+        vars.put( "param.unbind", ACTION_UNBIND );
+        vars.put( "param.delete", ACTION_DELETE );
+        vars.put( "param.propertylist", PROPERTY_LIST );
+        vars.put( "param.pidFilter", PID_FILTER );
 
         response.getWriter().print(TEMPLATE);
     }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
index 85d1089348..7c585b58e6 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
@@ -52,7 +52,6 @@ import org.apache.felix.utils.json.JSONWriter;
 import org.apache.felix.utils.manifest.Clause;
 import org.apache.felix.utils.manifest.Parser;
 import org.apache.felix.webconsole.ConfigurationPrinter;
-import org.apache.felix.webconsole.DefaultVariableResolver;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;
 import org.apache.felix.webconsole.WebConsoleConstants;
 import org.apache.felix.webconsole.WebConsoleUtil;
@@ -60,6 +59,7 @@ import org.apache.felix.webconsole.bundleinfo.BundleInfo;
 import org.apache.felix.webconsole.bundleinfo.BundleInfoProvider;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
 import org.apache.felix.webconsole.internal.Util;
+import org.apache.felix.webconsole.servlet.RequestVariableResolver;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
@@ -70,12 +70,14 @@ import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.Version;
 import org.osgi.framework.VersionRange;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.framework.wiring.FrameworkWiring;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.log.LogService;
 import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.service.startlevel.StartLevel;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
@@ -517,16 +519,17 @@ public class BundlesServlet extends 
SimpleWebConsolePlugin implements OsgiManage
      * @see 
org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(jakarta.servlet.http.HttpServletRequest,
 jakarta.servlet.http.HttpServletResponse)
      */
     @Override
-    @SuppressWarnings("unchecked")
     protected void renderContent( HttpServletRequest request, 
HttpServletResponse response ) throws IOException
     {
         // get request info from request attribute
         final RequestInfo reqInfo = getRequestInfo(request);
 
-        final int startLevel = getStartLevel().getInitialBundleStartLevel();
+        final Bundle systemBundle = 
this.getBundleContext().getBundle(Constants.SYSTEM_BUNDLE_LOCATION);
+        final FrameworkStartLevel fsl = 
systemBundle.adapt(FrameworkStartLevel.class);
+        final int startLevel = fsl.getInitialBundleStartLevel();
 
         // prepare variables
-        DefaultVariableResolver vars = ( ( DefaultVariableResolver ) 
WebConsoleUtil.getVariableResolver( request ) );
+        final RequestVariableResolver vars = 
WebConsoleUtil.getRequestVariableResolver(request);
         vars.put( "startLevel", String.valueOf(startLevel));
         vars.put( "drawDetails", reqInfo.bundleRequested ? Boolean.TRUE : 
Boolean.FALSE );
         vars.put( "currentBundle", (reqInfo.bundleRequested && reqInfo.bundle 
!= null ? String.valueOf(reqInfo.bundle.getBundleId()) : "null"));
@@ -754,15 +757,13 @@ public class BundlesServlet extends 
SimpleWebConsolePlugin implements OsgiManage
     }
 
 
-    private final boolean isFragmentBundle( Bundle bundle )
-    {
+    private final boolean isFragmentBundle(final Bundle bundle ) {
         // Workaround for FELIX-3670
-        if ( bundle.getState() == Bundle.UNINSTALLED )
-        {
+        if ( bundle.getState() == Bundle.UNINSTALLED ) {
             return bundle.getHeaders().get( Constants.FRAGMENT_HOST ) != null;
         }
-
-        return getPackageAdmin().getBundleType( bundle ) == 
PackageAdmin.BUNDLE_TYPE_FRAGMENT;
+        final BundleRevision rev = bundle.adapt(BundleRevision.class);
+        return rev != null && (rev.getTypes() & BundleRevision.TYPE_FRAGMENT) 
== BundleRevision.TYPE_FRAGMENT;
     }
 
     private void keyVal(final List<Map<String, Object>> props, final String 
key, final Object val)
@@ -864,14 +865,11 @@ public class BundlesServlet extends 
SimpleWebConsolePlugin implements OsgiManage
     }
 
 
-    private final Integer getStartLevel( Bundle bundle )
-    {
-        if ( bundle.getState() != Bundle.UNINSTALLED )
-        {
-            StartLevel sl = getStartLevel();
-            if ( sl != null )
-            {
-                return sl.getBundleStartLevel( bundle );
+    private final Integer getStartLevel( Bundle bundle ) {
+        if ( bundle.getState() != Bundle.UNINSTALLED ){
+            final BundleStartLevel bsl = bundle.adapt( BundleStartLevel.class 
);
+            if (bsl != null ) {
+                return bsl.getStartLevel();
             }
         }
 
@@ -880,8 +878,7 @@ public class BundlesServlet extends SimpleWebConsolePlugin 
implements OsgiManage
     }
 
 
-    private void listImportExport( List<Map<String, Object>> props, Bundle 
bundle, final String pluginRoot )
-    {
+    private void listImportExport( List<Map<String, Object>> props, Bundle 
bundle, final String pluginRoot ) {
         PackageAdmin packageAdmin = getPackageAdmin();
         if ( packageAdmin == null )
         {
@@ -1466,12 +1463,6 @@ public class BundlesServlet extends 
SimpleWebConsolePlugin implements OsgiManage
         return ( PackageAdmin ) getService( PackageAdmin.class.getName() );
     }
 
-    private final StartLevel getStartLevel()
-    {
-        return ( StartLevel ) getService( StartLevel.class.getName() );
-    }
-
-
     //---------- Bundle Installation handler (former InstallAction)
 
     private void installBundles( final HttpServletRequest request )
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/PermissionsConfigurationPrinter.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/PermissionsConfigurationPrinter.java
index 1701350edb..f1083874e0 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/PermissionsConfigurationPrinter.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/PermissionsConfigurationPrinter.java
@@ -59,8 +59,8 @@ public final class PermissionsConfigurationPrinter extends 
AbstractConfiguration
     public final void printConfiguration(PrintWriter pw)
     {
         final BundleContext bc = getBundleContext();
-        final ServiceReference paRef = bc.getServiceReference( 
PERMISSION_ADMIN_NAME );
-        final ServiceReference cpaRef = bc.getServiceReference( 
CONDITIONAL_PERMISSION_ADMIN_NAME );
+        final ServiceReference<?> paRef = bc.getServiceReference( 
PERMISSION_ADMIN_NAME );
+        final ServiceReference<?> cpaRef = bc.getServiceReference( 
CONDITIONAL_PERMISSION_ADMIN_NAME );
         final Object paSvc = paRef != null ? bc.getService(paRef) : null;
         final Object cpaSvc = cpaRef != null ? bc.getService(cpaRef) : null;
 
@@ -112,7 +112,7 @@ public final class PermissionsConfigurationPrinter extends 
AbstractConfiguration
                 boolean hasPermissions = false;
                 //final java.util.List list = 
cpa.newConditionalPermissionUpdate().getConditionalPermissionInfos();
                 //for (int i = 0; list != null && i < list.size(); i++)
-                for (Enumeration e = cpa.getConditionalPermissionInfos(); 
e.hasMoreElements();)
+                for (Enumeration<ConditionalPermissionInfo> e = 
cpa.getConditionalPermissionInfos(); e.hasMoreElements();)
                 {
                     hasPermissions = true;
                     //final ConditionalPermissionInfo info = 
(ConditionalPermissionInfo) list.get(i);
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesConfigurationPrinter.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesConfigurationPrinter.java
index 56d11870d4..11af2fcc19 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesConfigurationPrinter.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesConfigurationPrinter.java
@@ -47,7 +47,7 @@ public class ServicesConfigurationPrinter extends 
AbstractConfigurationPrinter
         "  Using Bundle {0} - {1} ({2}), version {3}");
 
     // don't create empty reference array all the time, create it only once - 
it is immutable
-    private static final ServiceReference[] NO_REFS = new ServiceReference[0];
+    private static final ServiceReference<?>[] NO_REFS = new 
ServiceReference[0];
 
     /**
      * @see org.apache.felix.webconsole.ConfigurationPrinter#getTitle()
@@ -65,7 +65,7 @@ public class ServicesConfigurationPrinter extends 
AbstractConfigurationPrinter
     public final void printConfiguration(PrintWriter pw)
     {
         final Object[] data = new Object[4]; // used as message formatter 
parameters
-        final ServiceReference refs[] = getServices();
+        final ServiceReference<?> refs[] = getServices();
         pw.print("Status: ");
         pw.println(ServicesServlet.getStatusLine(refs));
 
@@ -118,7 +118,7 @@ public class ServicesConfigurationPrinter extends 
AbstractConfigurationPrinter
         return data;
     }
 
-    private static final Object[] params(ServiceReference ref, Object[] data)
+    private static final Object[] params(ServiceReference<?> ref, Object[] 
data)
     {
         data[0] = ServicesServlet.propertyAsString(ref, Constants.SERVICE_ID);
         data[1] = ServicesServlet.propertyAsString(ref, Constants.OBJECTCLASS);
@@ -127,64 +127,20 @@ public class ServicesConfigurationPrinter extends 
AbstractConfigurationPrinter
         return data;
     }
 
-    private final ServiceReference[] getServices()
-    {
-        ServiceReference[] refs = null;
-        try
-        {
+    private final ServiceReference<?>[] getServices() {
+        ServiceReference<?>[] refs = null;
+        try {
             refs = 
BundleContextUtil.getWorkingBundleContext(getBundleContext()).getAllServiceReferences(null,
 null);
-        }
-        catch (InvalidSyntaxException e)
-        {
+        } catch (InvalidSyntaxException e) {
             // ignore
         }
 
         // no services or invalid filter syntax (unlikely)
-        if (refs != null)
-        {
-            Arrays.sort(refs, new ServiceReferenceComparator());
-        }
-        else
-        {
+        if (refs != null) {
+            Arrays.sort(refs);
+        } else {
             refs = NO_REFS;
         }
         return refs;
     }
-
-}
-
-class ServiceReferenceComparator implements Comparator
-{
-    private static final Long ZERO = new Long(0);
-
-    public int compare(ServiceReference p1, ServiceReference p2)
-    {
-        Long id1 = null;
-        if (p1 != null)
-        {
-            id1 = (Long) p1.getProperty(Constants.SERVICE_ID);
-        }
-        if (id1 == null)
-        {
-            id1 = ZERO;
-        }
-
-        Long id2 = null;
-        if (p2 != null)
-        {
-            id2 = (Long) p2.getProperty(Constants.SERVICE_ID);
-        }
-        if (id2 == null)
-        {
-            id2 = ZERO;
-        }
-
-        return id1.compareTo(id2);
-    }
-
-    @Override
-    public int compare(Object o1, Object o2)
-    {
-        return compare((ServiceReference) o1, (ServiceReference) o2);
-    }
 }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java
index a8fd5e205b..284f3bcda1 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java
@@ -30,18 +30,20 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.felix.utils.json.JSONWriter;
-import org.apache.felix.webconsole.DefaultVariableResolver;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;
 import org.apache.felix.webconsole.WebConsoleConstants;
 import org.apache.felix.webconsole.WebConsoleUtil;
+import org.apache.felix.webconsole.bundleinfo.BundleInfoProvider;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
 import org.apache.felix.webconsole.internal.Util;
+import org.apache.felix.webconsole.servlet.RequestVariableResolver;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
+import org.owasp.encoder.Encode;
 
 
 /**
@@ -50,12 +52,12 @@ import org.osgi.framework.ServiceRegistration;
 public class ServicesServlet extends SimpleWebConsolePlugin implements 
OsgiManagerPlugin
 {
     // don't create empty reference array all the time, create it only once - 
it is immutable
-    private static final ServiceReference[] NO_REFS = new ServiceReference[0];
+    private static final ServiceReference<?>[] NO_REFS = new 
ServiceReference[0];
 
     private final class RequestInfo
     {
         public final String extension;
-        public final ServiceReference service;
+        public final ServiceReference<?> service;
         public final boolean serviceRequested;
 
 
@@ -122,7 +124,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
         TEMPLATE = readTemplateFile( "/templates/services.html" ); 
//$NON-NLS-1$
     }
 
-    private ServiceRegistration bipReg;
+    private ServiceRegistration<BundleInfoProvider> bipReg;
 
     public void activate(BundleContext bundleContext)
     {
@@ -140,7 +142,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
     }
 
 
-    final ServiceReference getServiceById( String pathInfo )
+    final ServiceReference<?> getServiceById( String pathInfo )
     {
         // only use last part of the pathInfo
         pathInfo = pathInfo.substring( pathInfo.lastIndexOf( '/' ) + 1 );
@@ -151,7 +153,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
         String filterStr = filter.toString();
         try
         {
-            ServiceReference[] refs = 
BundleContextUtil.getWorkingBundleContext(this.getBundleContext()).getAllServiceReferences(
 null, filterStr );
+            ServiceReference<?>[] refs = 
BundleContextUtil.getWorkingBundleContext(this.getBundleContext()).getAllServiceReferences(
 null, filterStr );
             if ( refs == null || refs.length != 1 )
             {
                 return null;
@@ -167,7 +169,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
     }
 
 
-    private final ServiceReference[] getServices(String filter)
+    private final ServiceReference<?>[] getServices(String filter)
     {
         // empty filter string will return nothing, must set it to null to 
return all services
         if (filter != null && filter.trim().length() == 0) {
@@ -175,7 +177,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
         }
         try
         {
-            final ServiceReference[] refs = 
BundleContextUtil.getWorkingBundleContext(this.getBundleContext()).getAllServiceReferences(
 null, filter );
+            final ServiceReference<?>[] refs = 
BundleContextUtil.getWorkingBundleContext(this.getBundleContext()).getAllServiceReferences(
 null, filter );
             if ( refs != null )
             {
                 return refs;
@@ -191,7 +193,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
     }
 
 
-    static final String getStatusLine( final ServiceReference[] services )
+    static final String getStatusLine( final ServiceReference<?>[] services )
     {
         final int count = services.length;
         final StringBuilder buffer = new StringBuilder();
@@ -204,14 +206,14 @@ public class ServicesServlet extends 
SimpleWebConsolePlugin implements OsgiManag
     }
 
 
-    static final String propertyAsString( ServiceReference ref, String name )
+    static final String propertyAsString( ServiceReference<?> ref, String name 
)
     {
         final Object value = ref.getProperty( name );
         return WebConsoleUtil.toString( value );
     }
 
 
-    private void renderJSON( final HttpServletResponse response, final 
ServiceReference service, final Locale locale )
+    private void renderJSON( final HttpServletResponse response, final 
ServiceReference<?> service, final Locale locale )
             throws IOException
     {
         response.setContentType( "application/json" );
@@ -232,7 +234,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
         }
     }
 
-    private void serviceDetails( JSONWriter jw, ServiceReference service ) 
throws IOException
+    private void serviceDetails( JSONWriter jw, ServiceReference<?> service ) 
throws IOException
     {
         String[] keys = service.getPropertyKeys();
 
@@ -266,7 +268,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
     }
 
 
-    private void usingBundles( JSONWriter jw, ServiceReference service, Locale 
locale ) throws IOException
+    private void usingBundles( JSONWriter jw, ServiceReference<?> service, 
Locale locale ) throws IOException
     {
         jw.key( "usingBundles" );
         jw.array();
@@ -287,7 +289,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
     }
 
 
-    private void serviceInfo( JSONWriter jw, ServiceReference service, boolean 
details, final Locale locale )
+    private void serviceInfo( JSONWriter jw, ServiceReference<?> service, 
boolean details, final Locale locale )
             throws IOException
     {
         jw.object();
@@ -333,19 +335,19 @@ public class ServicesServlet extends 
SimpleWebConsolePlugin implements OsgiManag
     }
 
 
-    private void writeJSON(final Writer pw, final ServiceReference service, 
final Locale locale, final String filter) throws IOException
+    private void writeJSON(final Writer pw, final ServiceReference<?> service, 
final Locale locale, final String filter) throws IOException
     {
         writeJSON( pw, service, false, locale, filter );
     }
 
 
-    private void writeJSON( final Writer pw, final ServiceReference service, 
final boolean fullDetails, final Locale locale, final String filter )
+    private void writeJSON( final Writer pw, final ServiceReference<?> 
service, final boolean fullDetails, final Locale locale, final String filter )
             throws IOException
     {
-        final ServiceReference[] allServices = this.getServices(filter);
+        final ServiceReference<?>[] allServices = this.getServices(filter);
         final String statusLine = getStatusLine( allServices );
 
-        final ServiceReference[] services = ( service != null ) ? new 
ServiceReference[]
+        final ServiceReference<?>[] services = ( service != null ) ? new 
ServiceReference[]
                 { service } : allServices;
 
                 final JSONWriter jw = new JSONWriter( pw );
@@ -375,7 +377,7 @@ public class ServicesServlet extends SimpleWebConsolePlugin 
implements OsgiManag
 
 
     /**
-     * @see 
org.apache.felix.webconsole.AbstractWebConsolePlugin#doGet(jakarta.servlet.http.HttpServletRequest,
 jakarta.servlet.http.HttpServletResponse)
+     * @see 
org.apache.felix.webconsole.AbstractWebConsolePlugin#doGet(jakarta.servlet.http.HttpServletRequest,
 jalarta.servlet.http.HttpServletResponse)
      */
     protected void doGet( HttpServletRequest request, HttpServletResponse 
response ) throws ServletException,
     IOException
@@ -415,11 +417,11 @@ public class ServicesServlet extends 
SimpleWebConsolePlugin implements OsgiManag
         writeJSON(w, reqInfo.service, request.getLocale(), filter);
 
         // prepare variables
-        DefaultVariableResolver vars = ( ( DefaultVariableResolver ) 
WebConsoleUtil.getVariableResolver( request ) );
+        final RequestVariableResolver vars = 
WebConsoleUtil.getRequestVariableResolver(request);
         vars.put( "bundlePath", appRoot +  "/" + BundlesServlet.NAME + "/" );
         vars.put( "drawDetails", String.valueOf(reqInfo.serviceRequested));
         vars.put( "__data__", w.toString() );
-        vars.put( "filter", filter == null ? "" : 
WebConsoleUtil.escapeHtml(filter));
+        vars.put( "filter", filter == null ? "" : 
Encode.forHtmlContent(filter) );
 
         response.getWriter().print( TEMPLATE );
     }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java
index 8ab35886d9..2139a6117c 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java
@@ -61,7 +61,7 @@ final class ServicesUsedInfoProvider implements 
BundleInfoProvider
 
     public BundleInfo[] getBundleInfo( Bundle bundle, String webConsoleRoot, 
Locale locale )
     {
-        final ServiceReference[] refs = bundle.getServicesInUse();
+        final ServiceReference<?>[] refs = bundle.getServicesInUse();
         if ( null == refs || refs.length == 0 )
             return NO_INFO;
 
@@ -74,7 +74,7 @@ final class ServicesUsedInfoProvider implements 
BundleInfoProvider
     }
 
 
-    private BundleInfo toInfo( ServiceReference ref, String webConsoleRoot, 
Locale locale )
+    private BundleInfo toInfo( ServiceReference<?> ref, String webConsoleRoot, 
Locale locale )
     {
         final String[] classes = ( String[] ) ref.getProperty( 
Constants.OBJECTCLASS );
         final Object id = ref.getProperty( Constants.SERVICE_ID );
@@ -91,9 +91,8 @@ final class ServicesUsedInfoProvider implements 
BundleInfoProvider
     }
 
 
-    ServiceRegistration register( BundleContext context )
-    {
-        return context.registerService( BundleInfoProvider.class.getName(), 
this, null );
+    ServiceRegistration<BundleInfoProvider> register( BundleContext context ) {
+        return context.registerService( BundleInfoProvider.class, this, null );
     }
 
 }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/filter/FilteringResponseWrapper.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/filter/FilteringResponseWrapper.java
index ab4610e74d..e262be2954 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/filter/FilteringResponseWrapper.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/filter/FilteringResponseWrapper.java
@@ -27,8 +27,8 @@ import jakarta.servlet.ServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpServletResponseWrapper;
 
-import org.apache.felix.webconsole.VariableResolver;
 import org.apache.felix.webconsole.WebConsoleUtil;
+import org.apache.felix.webconsole.servlet.RequestVariableResolver;
 
 
 /**
@@ -40,8 +40,7 @@ import org.apache.felix.webconsole.WebConsoleUtil;
  * exists in the resource bundle, the text is written unmodifed (except the
  * wrapping <code>${}</code> characters are removed.
  */
-public class FilteringResponseWrapper extends HttpServletResponseWrapper
-{
+public class FilteringResponseWrapper extends HttpServletResponseWrapper {
 
     // the resource bundle providing translations for the output
     private final ResourceBundle locale;
@@ -62,36 +61,30 @@ public class FilteringResponseWrapper extends 
HttpServletResponseWrapper
      * @param locale a resource bundle, that will be used for translation of 
the strings
      * @param request the original request - used to obtain the variable 
resolver
      */
-    public FilteringResponseWrapper( final HttpServletResponse response, final 
ResourceBundle locale,
-        final ServletRequest request )
-    {
+    public FilteringResponseWrapper( final HttpServletResponse response, 
+        final ResourceBundle locale,
+        final ServletRequest request ) {
         super( response );
         this.locale = locale;
         this.request = request;
     }
 
-
     /**
      * Returns a <code>PrintWriter</code> for the response. If 
<code>text/html</code>
      * is being generated a filtering writer is returned which translates
      * strings enclosed in <code>${}</code> according to the resource bundle
      * configured for this response.
      *
-     * @see jakarta.servlet.ServletResponseWrapper#getWriter()
+     * @see javax.servlet.ServletResponseWrapper#getWriter()
      */
-    public PrintWriter getWriter() throws IOException
-    {
-        if ( writer == null )
-        {
+    public PrintWriter getWriter() throws IOException {
+        if ( writer == null ) {
             final PrintWriter base = super.getWriter();
-            if ( doWrap() )
-            {
-                final VariableResolver resolver = 
WebConsoleUtil.getVariableResolver( request );
-                final ResourceFilteringWriter filter = new 
ResourceFilteringWriter( base, locale, resolver );
+            if ( doWrap() ) {
+                final RequestVariableResolver vars = 
WebConsoleUtil.getRequestVariableResolver(request);
+                final ResourceFilteringWriter filter = new 
ResourceFilteringWriter( base, locale, vars );
                 writer = new PrintWriter( filter );
-            }
-            else
-            {
+            } else {
                 writer = base;
             }
         }
@@ -99,11 +92,7 @@ public class FilteringResponseWrapper extends 
HttpServletResponseWrapper
         return writer;
     }
 
-
-    private final boolean doWrap()
-    {
-        boolean doWrap = getContentType() != null && getContentType().indexOf( 
"text/html" ) >= 0;
-        return doWrap;
+    private final boolean doWrap() {
+        return getContentType() != null && getContentType().indexOf( 
"text/html" ) >= 0;
     }
-
 }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/filter/ResourceFilteringWriter.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/filter/ResourceFilteringWriter.java
index e201dd03b0..1379114707 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/filter/ResourceFilteringWriter.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/filter/ResourceFilteringWriter.java
@@ -25,8 +25,7 @@ import java.io.Writer;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
-import org.apache.felix.webconsole.DefaultVariableResolver;
-import org.apache.felix.webconsole.VariableResolver;
+import org.apache.felix.webconsole.servlet.RequestVariableResolver;
 
 
 /**
@@ -75,7 +74,7 @@ class ResourceFilteringWriter extends FilterWriter
      */
     private final ResourceBundle locale;
 
-    private final VariableResolver variables;
+    private final RequestVariableResolver variables;
 
     /**
      * The buffer to gather the text to be translated
@@ -88,11 +87,11 @@ class ResourceFilteringWriter extends FilterWriter
     private int state = STATE_NULL;
 
 
-    ResourceFilteringWriter( final Writer out, final ResourceBundle locale, 
final VariableResolver variables )
+    ResourceFilteringWriter( final Writer out, final ResourceBundle locale, 
final RequestVariableResolver variables )
     {
         super( out );
         this.locale = locale;
-        this.variables = ( variables != null ) ? variables : new 
DefaultVariableResolver();
+        this.variables = variables;
     }
 
 
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/CombinedEnumeration.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/CombinedEnumeration.java
index 5d0441b1c9..c23db58f45 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/CombinedEnumeration.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/CombinedEnumeration.java
@@ -32,28 +32,28 @@ import java.util.Set;
  * So if both enumerations would produce the same result, say "123", only the
  * first would be returned.
  */
-class CombinedEnumeration implements Enumeration
+class CombinedEnumeration implements Enumeration<String>
 {
 
     // the first enumeration to iterate
-    private final Enumeration first;
+    private final Enumeration<String> first;
 
     // the second enumeration to iterate once the first is exhausted
-    private final Enumeration second;
+    private final Enumeration<String> second;
 
     // the set of values already returned to prevent duplicate entries
-    private final Set seenKeys;
+    private final Set<String> seenKeys;
 
     // preview to the next return value for nextElement(), null at the end
-    private Object nextKey;
+    private String nextKey;
 
 
-    CombinedEnumeration( final Enumeration first, final Enumeration second )
+    CombinedEnumeration( final Enumeration<String> first, final 
Enumeration<String> second )
     {
         this.first = first;
         this.second = second;
 
-        this.seenKeys = new HashSet();
+        this.seenKeys = new HashSet<>();
         this.nextKey = seek();
     }
 
@@ -64,14 +64,14 @@ class CombinedEnumeration implements Enumeration
     }
 
 
-    public Object nextElement()
+    public String nextElement()
     {
         if ( !hasMoreElements() )
         {
             throw new NoSuchElementException();
         }
 
-        Object result = nextKey;
+        String result = nextKey;
         nextKey = seek();
         return result;
     }
@@ -82,11 +82,11 @@ class CombinedEnumeration implements Enumeration
      * (unique) element is available, null is returned. The element returned
      * is also added to the set of seen elements to prevent duplicate provision
      */
-    private Object seek()
+    private String seek()
     {
         while ( first.hasMoreElements() )
         {
-            final Object next = first.nextElement();
+            final String next = first.nextElement();
             if ( !seenKeys.contains( next ) )
             {
                 seenKeys.add( next );
@@ -95,7 +95,7 @@ class CombinedEnumeration implements Enumeration
         }
         while ( second.hasMoreElements() )
         {
-            final Object next = second.nextElement();
+            final String next = second.nextElement();
             if ( !seenKeys.contains( next ) )
             {
                 seenKeys.add( next );
@@ -104,4 +104,4 @@ class CombinedEnumeration implements Enumeration
         }
         return null;
     }
-}
\ No newline at end of file
+}
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/CombinedResourceBundle.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/CombinedResourceBundle.java
index eafc68eecf..19130c6fc5 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/CombinedResourceBundle.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/CombinedResourceBundle.java
@@ -48,7 +48,7 @@ class CombinedResourceBundle extends ResourceBundle
     }
 
 
-    public Enumeration getKeys()
+    public Enumeration<String> getKeys()
     {
         return new CombinedEnumeration( resourceBundle.getKeys(), 
defaultResourceBundle.getKeys() );
     }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ConsolePropertyResourceBundle.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ConsolePropertyResourceBundle.java
index c1d389b3e6..82d028bfd3 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ConsolePropertyResourceBundle.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ConsolePropertyResourceBundle.java
@@ -27,14 +27,11 @@ import java.util.Properties;
 import java.util.ResourceBundle;
 
 
-class ConsolePropertyResourceBundle extends ResourceBundle
-{
+class ConsolePropertyResourceBundle extends ResourceBundle {
 
     private final Properties props;
 
-
-    ConsolePropertyResourceBundle( final ResourceBundle parent, final URL 
source )
-    {
+    ConsolePropertyResourceBundle( final ResourceBundle parent, final URL 
source ) {
         setParent( parent );
 
         props = new Properties();
@@ -47,21 +44,18 @@ class ConsolePropertyResourceBundle extends ResourceBundle
         }
     }
 
-
-    public Enumeration getKeys()
-    {
-        Enumeration keysEnum = null;
-        if (parent == null) {
-            keysEnum = props.keys();
-        } else {
-            keysEnum = new CombinedEnumeration( props.keys(), parent.getKeys() 
);
+    @Override
+    public Enumeration<String> getKeys() {
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        Enumeration<String> keysEnum = (Enumeration) props.keys();
+        if (parent != null) {
+            keysEnum = new CombinedEnumeration( keysEnum, parent.getKeys() );
         }
         return keysEnum;
     }
 
-
-    protected Object handleGetObject( String key )
-    {
+    @Override
+    protected Object handleGetObject( String key ) {
         return props.get( key );
     }
 }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ResourceBundleCache.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ResourceBundleCache.java
index 9962d209c5..19bb78d1c4 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ResourceBundleCache.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ResourceBundleCache.java
@@ -33,8 +33,7 @@ import org.osgi.framework.Constants;
 /**
  * The <code>ResourceBundleCache</code> caches resource bundles per OSGi 
bundle.
  */
-public class ResourceBundleCache
-{
+public class ResourceBundleCache {
 
     /**
      * The default locale corresponding to the default language in the
@@ -46,9 +45,9 @@ public class ResourceBundleCache
 
     private final Bundle bundle;
 
-    private final Map resourceBundles;
+    private final Map<Locale, ResourceBundle> resourceBundles;
 
-    private Map resourceBundleEntries;
+    private volatile Map<String, URL> resourceBundleEntries;
 
 
     /**
@@ -56,10 +55,9 @@ public class ResourceBundleCache
      * 
      * @param bundle the bundle which resources should be loaded.
      */
-    public ResourceBundleCache( final Bundle bundle )
-    {
+    public ResourceBundleCache( final Bundle bundle ) {
         this.bundle = bundle;
-        this.resourceBundles = new HashMap();
+        this.resourceBundles = new HashMap<>();
     }
 
 
@@ -69,10 +67,8 @@ public class ResourceBundleCache
      * @param locale the requested locale
      * @return the resource bundle for the requested locale
      */
-    public ResourceBundle getResourceBundle( final Locale locale )
-    {
-        if ( locale == null )
-        {
+    public ResourceBundle getResourceBundle( final Locale locale ) {
+        if ( locale == null ) {
             return getResourceBundleInternal( DEFAULT_LOCALE );
         }
 
@@ -80,26 +76,21 @@ public class ResourceBundleCache
     }
 
 
-    ResourceBundle getResourceBundleInternal( final Locale locale )
-    {
-        if ( locale == null )
-        {
+    ResourceBundle getResourceBundleInternal( final Locale locale ) {
+        if ( locale == null ) {
             return null;
         }
 
-        synchronized ( resourceBundles )
-        {
-            ResourceBundle bundle = ( ResourceBundle ) resourceBundles.get( 
locale );
-            if ( bundle != null )
-            {
+        synchronized ( resourceBundles ) {
+            ResourceBundle bundle = resourceBundles.get( locale );
+            if ( bundle != null ) {
                 return bundle;
             }
         }
 
         ResourceBundle parent = getResourceBundleInternal( getParentLocale( 
locale ) );
         ResourceBundle bundle = loadResourceBundle( parent, locale );
-        synchronized ( resourceBundles )
-        {
+        synchronized ( resourceBundles ) {
             resourceBundles.put( locale, bundle );
         }
 
@@ -107,43 +98,36 @@ public class ResourceBundleCache
     }
 
 
-    private ResourceBundle loadResourceBundle( final ResourceBundle parent, 
final Locale locale )
-    {
-        final String path = "_" + locale.toString(); //$NON-NLS-1$
-        final URL source = ( URL ) getResourceBundleEntries().get( path );
+    private ResourceBundle loadResourceBundle( final ResourceBundle parent, 
final Locale locale ) {
+        final String path = "_" + locale.toString();
+        final URL source = getResourceBundleEntries().get( path );
         return new ConsolePropertyResourceBundle( parent, source );
     }
 
 
-    private synchronized Map getResourceBundleEntries()
-    {
-        if ( this.resourceBundleEntries == null )
-        {
+    private synchronized Map<String, URL> getResourceBundleEntries() {
+        if ( this.resourceBundleEntries == null ) {
             String file = ( String ) bundle.getHeaders().get( 
Constants.BUNDLE_LOCALIZATION );
-            if ( file == null )
-            {
+            if ( file == null ) {
                 file = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
             }
 
             // remove leading slash
-            if ( file.startsWith( "/" ) ) //$NON-NLS-1$
-            {
+            if ( file.startsWith( "/" ) ) {
                 file = file.substring( 1 );
             }
 
             // split path and base name
             int slash = file.lastIndexOf( '/' );
             String fileName = file.substring( slash + 1 );
-            String path = ( slash <= 0 ) ? "/" : file.substring( 0, slash ); 
//$NON-NLS-1$
+            String path = ( slash <= 0 ) ? "/" : file.substring( 0, slash );
 
-            HashMap resourceBundleEntries = new HashMap();
+            HashMap<String, URL> resourceBundleEntries = new HashMap<>();
 
-            Enumeration locales = bundle.findEntries( path, fileName + 
"*.properties", false ); //$NON-NLS-1$
-            if ( locales != null )
-            {
-                while ( locales.hasMoreElements() )
-                {
-                    URL entry = ( URL ) locales.nextElement();
+            Enumeration<URL> locales = bundle.findEntries( path, fileName + 
"*.properties", false );
+            if ( locales != null ) {
+                while ( locales.hasMoreElements() ) {
+                    URL entry = locales.nextElement();
 
                     // calculate the key
                     String entryPath = entry.getPath();
@@ -154,7 +138,7 @@ public class ResourceBundleCache
                     // the default language is "name.properties" thus the entry
                     // path is empty and must default to "_"+DEFAULT_LOCALE
                     if (entryPath.length() == 0) {
-                        entryPath = "_" + DEFAULT_LOCALE; //$NON-NLS-1$
+                        entryPath = "_" + DEFAULT_LOCALE;
                     }
 
                     // only add this entry, if the "language" is not provided
@@ -172,23 +156,16 @@ public class ResourceBundleCache
     }
 
 
-    private static final Locale getParentLocale( Locale locale )
-    {
-        if ( locale.getVariant().length() != 0 )
-        {
+    private static final Locale getParentLocale( Locale locale ) {
+        if ( locale.getVariant().length() != 0 ) {
             return new Locale( locale.getLanguage(), locale.getCountry() );
-        }
-        else if ( locale.getCountry().length() != 0 )
-        {
-            return new Locale( locale.getLanguage(), "" ); //$NON-NLS-1$
-        }
-        else if ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) 
)
-        {
+        } else if ( locale.getCountry().length() != 0 ) {
+            return new Locale( locale.getLanguage(), "" );
+        } else if ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() 
) ) {
             return DEFAULT_LOCALE;
         }
 
         // no more parents
         return null;
     }
-
 }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java
index 826cf18aa3..639abf2c3d 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/LicenseServlet.java
@@ -42,11 +42,11 @@ import jakarta.servlet.http.HttpServletResponse;
 import org.apache.felix.utils.json.JSONWriter;
 import org.apache.felix.utils.manifest.Clause;
 import org.apache.felix.utils.manifest.Parser;
-import org.apache.felix.webconsole.DefaultVariableResolver;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;
 import org.apache.felix.webconsole.WebConsoleUtil;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
 import org.apache.felix.webconsole.internal.Util;
+import org.apache.felix.webconsole.servlet.RequestVariableResolver;
 import org.osgi.framework.Bundle;
 
 
@@ -109,14 +109,13 @@ public final class LicenseServlet extends 
SimpleWebConsolePlugin implements Osgi
     /**
      * @see 
org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(jakarta.servlet.http.HttpServletRequest,
 jakarta.servlet.http.HttpServletResponse)
      */
-    @SuppressWarnings("unchecked")
     protected void renderContent( HttpServletRequest request, 
HttpServletResponse res ) throws IOException
     {
         Bundle[] bundles = getBundleContext().getBundles();
         Util.sort( bundles, request.getLocale() );
 
         // prepare variables
-        DefaultVariableResolver vars = ( ( DefaultVariableResolver ) 
WebConsoleUtil.getVariableResolver( request ) );
+        final RequestVariableResolver vars = 
WebConsoleUtil.getRequestVariableResolver(request);
         vars.put( "__data__", getBundleData( bundles, request.getLocale() ));
 
         res.getWriter().print(TEMPLATE);
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/SystemPropertiesPrinter.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/SystemPropertiesPrinter.java
index dbd71c6963..5279eb9df7 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/SystemPropertiesPrinter.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/SystemPropertiesPrinter.java
@@ -28,11 +28,11 @@ import java.util.TreeSet;
 import org.apache.felix.webconsole.internal.AbstractConfigurationPrinter;
 
 
-public class SystemPropertiesPrinter extends AbstractConfigurationPrinter
-{
+public class SystemPropertiesPrinter extends AbstractConfigurationPrinter {
 
     private static final String TITLE = "System Properties";
 
+    @SuppressWarnings("unused")
     private static final String LABEL = "_systemproperties";
 
 
@@ -42,8 +42,7 @@ public class SystemPropertiesPrinter extends 
AbstractConfigurationPrinter
     }
 
 
-    public void printConfiguration( PrintWriter printWriter )
-    {
+    public void printConfiguration( PrintWriter printWriter ) {
         Properties props = System.getProperties();
         SortedSet keys = new TreeSet( props.keySet() );
         for ( Iterator ki = keys.iterator(); ki.hasNext(); )
@@ -54,4 +53,4 @@ public class SystemPropertiesPrinter extends 
AbstractConfigurationPrinter
 
     }
 
-}
\ No newline at end of file
+}
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/WebConsolePluginAdapter.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServletAdapter.java
similarity index 50%
copy from 
webconsole/src/main/java/org/apache/felix/webconsole/internal/WebConsolePluginAdapter.java
copy to 
webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServletAdapter.java
index 362453a571..a4c1d1d86d 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/WebConsolePluginAdapter.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServletAdapter.java
@@ -16,32 +16,34 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.felix.webconsole.internal;
+package org.apache.felix.webconsole.internal.servlet;
 
 
 import java.io.IOException;
 import java.util.*;
-import org.apache.felix.webconsole.AbstractWebConsolePlugin;
-import org.apache.felix.webconsole.WebConsoleConstants;
-import org.osgi.framework.ServiceReference;
 
-import jakarta.servlet.Servlet;
-import jakarta.servlet.ServletConfig;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.ServletRequest;
-import jakarta.servlet.ServletResponse;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
+import org.apache.felix.http.jakartawrappers.HttpServletRequestWrapper;
+import org.apache.felix.http.jakartawrappers.HttpServletResponseWrapper;
+import org.apache.felix.http.jakartawrappers.ServletConfigWrapper;
+import org.apache.felix.http.javaxwrappers.ServletExceptionUtil;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.apache.felix.webconsole.servlet.AbstractServlet;
+import org.apache.felix.webconsole.servlet.ServletConstants;
+import org.osgi.framework.ServiceReference;
 
 /**
- * The <code>WebConsolePluginAdapter</code> is an adapter to the
+ * The <code>JakartaServletAdapter</code> is an adapter to the
  * {@link AbstractWebConsolePlugin} for regular servlets registered with the
  * {@link org.apache.felix.webconsole.WebConsoleConstants#PLUGIN_TITLE}
- * service attribute.
+ * service attribute using jakarta.servlet.Servlet
  */
-public class WebConsolePluginAdapter extends AbstractWebConsolePlugin
-{
+public class JakartaServletAdapter extends AbstractWebConsolePlugin {
 
     /** serial UID */
     private static final long serialVersionUID = 1L;
@@ -49,8 +51,11 @@ public class WebConsolePluginAdapter extends 
AbstractWebConsolePlugin
     // the plugin label (aka address)
     private final String label;
 
+    // the plugin title
+    private final String title;
+
     // the actual plugin to forward rendering requests to
-    private final Servlet plugin;
+    private final AbstractServlet plugin;
 
     // the CSS references (null if none)
     private final String[] cssReferences;
@@ -62,11 +67,11 @@ public class WebConsolePluginAdapter extends 
AbstractWebConsolePlugin
      * @param plugin the plugin itself
      * @param serviceReference reference to the plugin
      */
-    public WebConsolePluginAdapter( String label, Servlet plugin, 
ServiceReference<Servlet> serviceReference )
-    {
-        this.label = label;
+    public JakartaServletAdapter( final AbstractServlet plugin, 
ServiceReference<Servlet> serviceReference ) {
+        this.label = (String) serviceReference.getProperty( 
ServletConstants.PLUGIN_LABEL );
+        this.title = (String) serviceReference.getProperty( 
ServletConstants.PLUGIN_TITLE );
         this.plugin = plugin;
-        this.cssReferences = toStringArray( serviceReference.getProperty( 
WebConsoleConstants.PLUGIN_CSS_REFERENCES ) );
+        this.cssReferences = toStringArray( serviceReference.getProperty( 
ServletConstants.PLUGIN_CSS_REFERENCES ) );
 
         // activate this abstract plugin (mainly to set the bundle context)
         activate( serviceReference.getBundle().getBundleContext() );
@@ -75,58 +80,32 @@ public class WebConsolePluginAdapter extends 
AbstractWebConsolePlugin
 
     //---------- AbstractWebConsolePlugin API
 
-    /**
-     * Returns the label of this plugin page as defined in the constructor.
-     *
-     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getLabel()
-     */
-    public String getLabel()
-    {
+    @Override
+    public String getLabel() {
         return label;
     }
 
-
-    /**
-     * Returns the title of this plugin page as defined by the
-     * {@link WebConsoleConstants#PLUGIN_TITLE} service registration attribute
-     * which is exposed as the servlet name in the servlet configuration.
-     *
-     * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#getTitle()
-     */
-    public String getTitle()
-    {
-        // return the servlet name from the configuration but don't call
-        // the base class implementation, which calls getTitle()
-        return getServletConfig().getServletName();
+    @Override
+    public String getTitle() {
+        return this.title;
     }
 
-
-    /**
-     * Returns the CSS references from the
-     * {@link WebConsoleConstants#PLUGIN_CSS_REFERENCES felix.webconsole.css}
-     * service registration property of the plugin.
-     *
-     * @see 
org.apache.felix.webconsole.AbstractWebConsolePlugin#getCssReferences()
-     */
-    protected String[] getCssReferences()
-    {
+    @Override
+    protected String[] getCssReferences() {
         return cssReferences;
     }
 
-
-    /**
-     * Call the plugin servlet's service method to render the content of this
-     * page.
-     *
-     * @see 
org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(jakarta.servlet.http.HttpServletRequest,
 jakarta.servlet.http.HttpServletResponse)
-     */
-    protected void renderContent( HttpServletRequest req, HttpServletResponse 
res ) throws ServletException,
-        IOException
-    {
-        plugin.service( req, res );
+    @Override
+    protected void renderContent( final HttpServletRequest req, final 
HttpServletResponse res )
+    throws ServletException, IOException {
+        try {
+            plugin.renderContent( 
(jakarta.servlet.http.HttpServletRequest)HttpServletRequestWrapper.getWrapper(req),
 
+                
(jakarta.servlet.http.HttpServletResponse)HttpServletResponseWrapper.getWrapper(res)
 );
+        } catch (final jakarta.servlet.ServletException s) {
+            throw ServletExceptionUtil.getServletException(s);
+        }
     }
 
-
     /**
      * Returns the registered plugin class to be able to call the
      * <code>getResource()</code> method on that object for this plugin to
@@ -134,8 +113,7 @@ public class WebConsolePluginAdapter extends 
AbstractWebConsolePlugin
      *
      * @see 
org.apache.felix.webconsole.AbstractWebConsolePlugin#getResourceProvider()
      */
-    protected Object getResourceProvider()
-    {
+    protected Object getResourceProvider() {
         return plugin;
     }
 
@@ -145,22 +123,22 @@ public class WebConsolePluginAdapter extends 
AbstractWebConsolePlugin
     /**
      * Initializes this servlet as well as the plugin servlet.
      *
-     * @see jakarta.servlet.GenericServlet#init(jakarta.servlet.ServletConfig)
+     * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
      */
-    public void init( ServletConfig config ) throws ServletException
-    {
+    public void init( ServletConfig config ) throws ServletException {
         // no need to activate the plugin, this has already been done
         // when the instance was setup
-        try
-        {
+        try {
             // base classe initialization
             super.init( config );
 
             // plugin initialization
-            plugin.init( config );
-        }
-        catch ( ServletException se )
-        {
+            try {
+                plugin.init( new ServletConfigWrapper(config) );
+            } catch ( final jakarta.servlet.ServletException s) {
+                throw ServletExceptionUtil.getServletException(s);
+            }
+        } catch ( ServletException se ) {
             // if init fails, the plugin will not be destroyed and thus
             // the plugin not deactivated. Do it here
             deactivate();
@@ -170,32 +148,53 @@ public class WebConsolePluginAdapter extends 
AbstractWebConsolePlugin
         }
     }
 
+    private static final class CheckHttpServletResponse extends 
HttpServletResponseWrapper {
 
-    /**
-     * Detects whether this request is intended to have the headers and
-     * footers of this plugin be rendered or not. The decision is taken based
-     * on whether and what extension the request URI has: If the request URI
-     * has no extension or the the extension is <code>.html</code>, the request
-     * is assumed to be rendered with header and footer. Otherwise the
-     * headers and footers are omitted and the
-     * {@link #renderContent(HttpServletRequest, HttpServletResponse)}
-     * method is called without any decorations and without setting any
-     * response headers.
-     *
-     * @see 
org.apache.felix.webconsole.AbstractWebConsolePlugin#isHtmlRequest(jakarta.servlet.http.HttpServletRequest)
-     */
-    protected boolean isHtmlRequest( final HttpServletRequest request )
-    {
-        final String requestUri = request.getRequestURI();
-        if ( requestUri.endsWith( ".html" ) ) {
-            return true;
+        private boolean done = false;
+        public CheckHttpServletResponse(HttpServletResponse response) {
+            super(response);
+        }
+
+        public boolean isDone() {
+            return this.done;
+        }
+
+        @Override
+        public void reset() {
+            this.done = false;
+            super.reset();
+        }
+
+        @Override
+        public void sendError(final int sc) throws IOException {
+            this.done = true;
+            super.sendError(sc);
+        }
+
+        @Override
+        public void sendError(final int sc, final String msg) throws 
IOException {
+            this.done = true;
+            super.sendError(sc, msg);
+        }
+
+        @Override
+        public void sendRedirect(final String location) throws IOException {
+            this.done = true;
+            super.sendRedirect(location);
+        }
+
+        @Override
+        public void setStatus(final int sc) {
+            this.done = true;
+            super.setStatus(sc);
         }
-        // check if there is an extension
-        final int lastSlash = requestUri.lastIndexOf('/');
-        final int lastDot = requestUri.indexOf('.', lastSlash + 1);
-        return lastDot < 0;
-    }
 
+        @Override
+        public void setStatus(final int sc, final String sm) {
+            this.done = true;
+            super.setStatus(sc, sm);
+        }        
+    }
 
     /**
      * Directly refer to the plugin's service method unless the request method
@@ -204,46 +203,44 @@ public class WebConsolePluginAdapter extends 
AbstractWebConsolePlugin
      * {@link #renderContent(HttpServletRequest, HttpServletResponse)}
      * method.
      *
-     * @see 
jakarta.servlet.http.HttpServlet#service(jakarta.servlet.ServletRequest, 
jakarta.servlet.ServletResponse)
+     * @see 
javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest, 
javax.servlet.ServletResponse)
      */
-    public void service( ServletRequest req, ServletResponse resp ) throws 
ServletException, IOException
-    {
-        if ( ( req instanceof HttpServletRequest ) && ( ( HttpServletRequest ) 
req ).getMethod().equals( "GET" ) )
-        {
+    public void service( HttpServletRequest req, HttpServletResponse resp )
+    throws ServletException, IOException {
+        final CheckHttpServletResponse checkResponse = new 
CheckHttpServletResponse(resp);
+        // call plugin first
+        try {
+            plugin.service( 
(jakarta.servlet.http.HttpServletRequest)HttpServletRequestWrapper.getWrapper(req),
 
+                
(jakarta.servlet.http.HttpServletResponse)HttpServletResponseWrapper.getWrapper(resp)
 );
+        } catch (final jakarta.servlet.ServletException s) {
+            throw ServletExceptionUtil.getServletException(s);
+        }
+        // if a GET request and plugin did not create a response yet, call 
super to get full HTML response
+        if ( !checkResponse.isDone() && req.getMethod().equals( "GET" ) ) {
             // handle the GET request here and call into plugin on 
renderContent
             super.service( req, resp );
         }
-        else
-        {
-            // not a GET request, have the plugin handle it directly
-            plugin.service( req, resp );
-        }
     }
 
-
     /**
      * Destroys this servlet as well as the plugin servlet.
      *
-     * @see jakarta.servlet.GenericServlet#destroy()
+     * @see javax.servlet.GenericServlet#destroy()
      */
-    public void destroy()
-    {
-        try
-        {
+    public void destroy() {
+        try {
             plugin.destroy();
             super.destroy();
-        }
-        finally
-        {
+        } finally {
             deactivate();
         }
     }
 
+
     //---------- internal
 
     @SuppressWarnings("rawtypes")
-    private String[] toStringArray( final Object value )
-    {
+    private String[] toStringArray( final Object value ) {
         if ( value instanceof String )
         {
             return new String[]
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServletTracker.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServletTracker.java
new file mode 100755
index 0000000000..b0e1a140a1
--- /dev/null
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/JakartaServletTracker.java
@@ -0,0 +1,106 @@
+/*
+ * 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.webconsole.internal.servlet;
+
+
+import java.io.Closeable;
+
+import org.apache.felix.http.javaxwrappers.ServletWrapper;
+import org.apache.felix.webconsole.WebConsoleConstants;
+import org.apache.felix.webconsole.internal.Util;
+import org.apache.felix.webconsole.internal.servlet.Plugin.ServletPlugin;
+import org.apache.felix.webconsole.servlet.AbstractServlet;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import jakarta.servlet.Servlet;
+
+public class JakartaServletTracker implements Closeable, 
ServiceTrackerCustomizer<Servlet, JakartaServletTracker.JakartaServletPlugin> {
+
+    private final ServiceTracker<Servlet, JakartaServletPlugin> servletTracker;
+
+    private final PluginHolder pluginHolder;
+
+    public JakartaServletTracker( final PluginHolder pluginHolder, final 
BundleContext context ) {
+        this.pluginHolder = pluginHolder;
+        Filter filter = null;
+        try {
+            filter = context.createFilter("(&(" + Constants.OBJECTCLASS + "=" 
+ Servlet.class.getName() + 
+                ")(" + WebConsoleConstants.PLUGIN_LABEL + "=*))");
+        } catch (final InvalidSyntaxException e) {
+            // not expected, thus fail hard
+            throw new InternalError( "Failed creating filter: " + 
e.getMessage() );
+        }
+        this.servletTracker = new ServiceTracker<>(context, filter, this);
+        servletTracker.open();
+    }
+
+    @Override
+    public void close() {
+        servletTracker.close();
+    }
+
+    @Override
+    public JakartaServletPlugin addingService( final 
org.osgi.framework.ServiceReference<Servlet> reference ) {
+        final String label = Util.getStringProperty( reference, 
WebConsoleConstants.PLUGIN_LABEL );
+        if ( label != null ) {
+            final JakartaServletPlugin plugin = new 
JakartaServletPlugin(this.pluginHolder, reference, label);
+            pluginHolder.addPlugin(plugin);
+            return plugin;
+        }
+        return null;
+    }
+
+    @Override
+    public void modifiedService( final 
org.osgi.framework.ServiceReference<Servlet> reference, final 
JakartaServletPlugin service ) {
+        this.removedService(reference, service);
+        this.addingService(reference);
+    }
+
+    @Override
+    public void removedService( final 
org.osgi.framework.ServiceReference<Servlet> reference, final 
JakartaServletPlugin service ) {
+        this.pluginHolder.removePlugin(service);
+    }
+
+    public static class JakartaServletPlugin extends ServletPlugin {
+            
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        public JakartaServletPlugin(PluginHolder holder, 
ServiceReference<jakarta.servlet.Servlet> serviceReference,
+                String label) {
+            super(holder, (ServiceReference)serviceReference, label);
+        }
+
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        protected javax.servlet.Servlet getService() {
+            final Servlet servlet = (Servlet) 
getHolder().getBundleContext().getService( 
(ServiceReference)this.getServiceReference() );
+            if (servlet != null) {
+                if ( servlet instanceof AbstractServlet ) {
+                    return new JakartaServletAdapter((AbstractServlet)servlet, 
this.getServiceReference());
+                }
+                return new ServletWrapper(servlet);
+            }
+            return 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 758935eb3d..ea763fe8e1 100755
--- 
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
@@ -46,12 +46,14 @@ import org.apache.felix.webconsole.WebConsoleConstants;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider2;
 import org.apache.felix.webconsole.WebConsoleSecurityProvider3;
+import org.apache.felix.webconsole.WebConsoleUtil;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
 import org.apache.felix.webconsole.internal.Util;
 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.servlet.RequestVariableResolver;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
@@ -628,9 +630,19 @@ public class OsgiManager extends GenericServlet {
         request = wrapRequest(request, locale);
         response = wrapResponse(request, response, plugin);
 
+        // make sure to set the variable resolver
+        initRequestVariableResolver(request);
+
         plugin.service(request, response);
     }
 
+    private void initRequestVariableResolver(final HttpServletRequest request) 
{
+        final RequestVariableResolver resolver = new RequestVariableResolver();
+        request.setAttribute(RequestVariableResolver.REQUEST_ATTRIBUTE, 
resolver);
+        resolver.put( "appRoot", (String) request.getAttribute( 
WebConsoleConstants.ATTR_APP_ROOT ) );
+        resolver.put( "pluginRoot", (String) request.getAttribute( 
WebConsoleConstants.ATTR_PLUGIN_ROOT ) );
+    }
+
     private final void logout(HttpServletRequest request, HttpServletResponse 
response)
         throws IOException {
         final Object securityProvider = securityProviderTracker.getService();
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 67c181c4ef..c9c5e32ef7 100755
--- 
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
@@ -194,8 +194,12 @@ public abstract class Plugin implements ServletConfig, 
Comparable<Plugin> {
             return this.getServiceReference().toString();
         }
 
+        protected Servlet getService() {
+            return getHolder().getBundleContext().getService( 
this.getServiceReference() );
+        }
+
         protected AbstractWebConsolePlugin doGetConsolePlugin() {
-            final Servlet service = getHolder().getBundleContext().getService( 
this.getServiceReference() );
+            final Servlet service = getService();
             if ( service != null ) {
                 this.title = Util.getStringProperty( 
this.getServiceReference(), WebConsoleConstants.PLUGIN_TITLE );
                 this.category = Util.getStringProperty( 
this.getServiceReference(), WebConsoleConstants.PLUGIN_CATEGORY );
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 5a30de010c..f4671525c9 100755
--- 
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
@@ -18,6 +18,8 @@
  */
 package org.apache.felix.webconsole.internal.servlet;
 
+import java.io.Closeable;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -67,6 +69,8 @@ class PluginHolder implements 
ServiceTrackerCustomizer<Servlet, Plugin> {
 
     private final ServiceTracker<Servlet, Plugin> servletTracker;
 
+    private volatile Closeable jakartaTracker;
+
     PluginHolder( final OsgiManager osgiManager, final BundleContext context ) 
{
         this.osgiManager = osgiManager;
         this.bundleContext = context;
@@ -91,6 +95,13 @@ class PluginHolder implements 
ServiceTrackerCustomizer<Servlet, Plugin> {
      */
     void open() {
         this.servletTracker.open();
+        try {
+            this.jakartaTracker = new JakartaServletTracker(this, 
this.getBundleContext());
+            this.osgiManager.log(LogService.LOG_INFO, "Jakarta Servlet bridge 
enabled");
+        } catch ( final Throwable t) {
+            // ignore
+            this.osgiManager.log(LogService.LOG_INFO, "Jakarta Servlet bridge 
not enabled");
+        }
     }
 
     /**
@@ -99,6 +110,14 @@ class PluginHolder implements 
ServiceTrackerCustomizer<Servlet, Plugin> {
      * held plugin services.
      */
     void close() {
+        if (this.jakartaTracker != null) {
+            try {
+                this.jakartaTracker.close();
+            } catch (final IOException e) {
+                // ignore
+            }
+            this.jakartaTracker = null;
+        }
         this.servletTracker.close();
 
         this.plugins.clear();
@@ -223,29 +242,21 @@ class PluginHolder implements 
ServiceTrackerCustomizer<Servlet, Plugin> {
             // support only one level for now
             Map categoryMap = null;
             String category = plugin.getCategory();
-            if ( category == null || category.trim().length() == 0 )
-            {
+            if ( category == null || category.trim().length() == 0 ) {
                 // FELIX-3798 configured default category
                 category = defaultCategory;
             }
 
-            // TODO: FELIX-3769; translate the Category
-
             categoryMap = findCategoryMap( map, category );
 
             final String label = plugin.getLabel();
             String title = plugin.getTitle();
-            if ( title.startsWith( "%" ) )
-            {
-                try
-                {
-                    final ResourceBundle resourceBundle = 
resourceBundleManager.getResourceBundle( plugin.getBundle(),
-                        locale );
+            if ( title.startsWith( "%" ) ) {
+                try {
+                    final ResourceBundle resourceBundle = 
resourceBundleManager.getResourceBundle( plugin.getBundle(), locale );
                     title = resourceBundle.getString( title.substring( 1 ) );
-                }
-                catch ( Throwable e )
-                {
-                    /* ignore missing resource - use default title */
+                } catch ( Throwable e ) {
+                    // ignore missing resource - use default title
                 }
             }
 
@@ -350,7 +361,7 @@ class PluginHolder implements 
ServiceTrackerCustomizer<Servlet, Plugin> {
         removePlugin( plugin );
     }
 
-    private void addPlugin( final Plugin plugin ) {
+    void addPlugin( final Plugin plugin ) {
         synchronized ( plugins ) {
             final List<Plugin> list = 
plugins.computeIfAbsent(plugin.getLabel(), k -> new ArrayList<>());
             final Plugin oldPlugin = list.isEmpty() ? null : list.get(0);
@@ -375,7 +386,7 @@ class PluginHolder implements 
ServiceTrackerCustomizer<Servlet, Plugin> {
         }
     }
 
-    private void removePlugin( final Plugin plugin ) {
+    void removePlugin( final Plugin plugin ) {
         synchronized ( plugins ) {
             final List<Plugin> list = plugins.get( plugin.getLabel() );
             if ( list != null ) {
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/system/VMStatPlugin.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/system/VMStatPlugin.java
index 195266ec0e..2f48a2c7d6 100755
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/system/VMStatPlugin.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/system/VMStatPlugin.java
@@ -31,12 +31,12 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.felix.utils.json.JSONWriter;
-import org.apache.felix.webconsole.DefaultVariableResolver;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;
 import org.apache.felix.webconsole.WebConsoleConstants;
 import org.apache.felix.webconsole.WebConsoleUtil;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
 import org.apache.felix.webconsole.internal.servlet.OsgiManager;
+import org.apache.felix.webconsole.servlet.RequestVariableResolver;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.service.startlevel.StartLevel;
@@ -190,7 +190,7 @@ public class VMStatPlugin extends SimpleWebConsolePlugin 
implements OsgiManagerP
                 jw.endObject();
                 jw.flush();
 
-                DefaultVariableResolver vars = ( ( DefaultVariableResolver ) 
WebConsoleUtil.getVariableResolver( request ) );
+                final RequestVariableResolver vars = 
WebConsoleUtil.getRequestVariableResolver(request);
                 vars.put( "data", json.toString() );
 
                 body = TPL_VM_RESTART;
@@ -250,7 +250,7 @@ public class VMStatPlugin extends SimpleWebConsolePlugin 
implements OsgiManagerP
 
         jw.flush();
 
-        DefaultVariableResolver vars = ( ( DefaultVariableResolver ) 
WebConsoleUtil.getVariableResolver( request ) );
+        final RequestVariableResolver vars = 
WebConsoleUtil.getRequestVariableResolver(request);
         vars.put( "startData", json.toString() );
 
         response.getWriter().print( body );
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/servlet/AbstractServlet.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/AbstractServlet.java
new file mode 100755
index 0000000000..031ad59cdc
--- /dev/null
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/AbstractServlet.java
@@ -0,0 +1,201 @@
+/*
+ * 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.webconsole.servlet;
+
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+
+/**
+ * This class can be used as a base class for a web console plugin.
+ * The plugin (servlet) needs to be registered as a servlet service
+ * with at least the label property.
+ *
+ * @see ServletConstants#PLUGIN_LABEL
+ * @see ServletConstants#PLUGIN_TITLE
+ * @see ServletConstants#PLUGIN_CATEGORY
+ */
+public abstract class AbstractServlet extends HttpServlet {
+
+    /**
+     * Called to identify resources.
+     * By default, if the path starts with "/res/" this is treated as a 
resource
+     * and the URL to the resource is tried to be loaded via the class loader.
+     * @param path the path
+     * @return the URL of the resource or <code>null</code> if not found.
+     */
+    protected URL getResource( final String path ) {
+        final int index = path.indexOf( '/', 1 );
+        if (index != -1) {
+            if (path.substring(index).startsWith("/res/") ) {
+                return getClass().getResource( path.substring(index) );
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Handle get requests. This method can be used to return resources, like 
JSON responses etc.
+     * If the plugin is serving a resource, this method call {@link 
HttpServletResponse#setStatus(int)}.
+     * This method is also allowed to send a redirect or an error.
+     * If none of the three applies, the webconsole will call {@link 
#renderContent(HttpServletRequest, HttpServletResponse)}
+     */
+    @Override
+    protected void doGet( final HttpServletRequest request, final 
HttpServletResponse response )
+    throws ServletException, IOException {
+        this.spoolResource( request, response );
+    }
+
+    /**
+     * This method is used to render the main contents of the plugin
+     * @param request The request
+     * @param response The response
+     * @throws ServletException If an error occurs
+     * @throws IOException If writing the response fails
+     */
+    public void renderContent( final HttpServletRequest request, final 
HttpServletResponse response)
+    throws ServletException, IOException {
+        throw new ServletException("Render method not implemented");
+    }
+
+   /**
+     * If the request addresses a resource , this method serves it
+     * and returns <code>true</code>. Otherwise <code>false</code> is returned.
+     * <p>
+     * If <code>true</code> is returned, the request is considered complete and
+     * request processing terminates. Otherwise request processing continues
+     * with normal plugin rendering.
+     *
+     * @param request The request object
+     * @param response The response object
+     * @return <code>true</code> if the request causes a resource to be sent 
back.
+     *
+     * @throws IOException If an error occurs accessing or spooling the 
resource.
+     */
+    protected final void spoolResource(final HttpServletRequest request, final 
HttpServletResponse response) 
+    throws IOException {
+        // check for a resource, fail if none
+        final URL url = this.getResource(request.getPathInfo());
+        if ( url == null ) {
+            return;
+        }
+
+        // open the connection and the stream (we use the stream to be able
+        // to at least hint to close the connection because there is no
+        // method to explicitly close the conneciton, unfortunately)
+        final URLConnection connection = url.openConnection();
+        try ( final InputStream ins = connection.getInputStream()) {
+            // FELIX-2017 Equinox may return an URL for a non-existing
+            // resource but then (instead of throwing) return null on
+            // getInputStream. We should account for this situation and
+            // just assume a non-existing resource in this case.
+            if (ins == null) {
+                return;
+            }
+
+            // check whether we may return 304/UNMODIFIED
+            long lastModified = connection.getLastModified();
+            if ( lastModified > 0 ) {
+                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;
+                }
+
+                // have to send, so set the last modified header now
+                response.setDateHeader( "Last-Modified", lastModified ); 
//$NON-NLS-1$
+            }
+
+            // describe the contents
+            response.setContentType( getServletContext().getMimeType( 
request.getPathInfo() ) );
+            if (connection.getContentLength() != -1) {
+                response.setContentLength( connection.getContentLength() );
+            }
+            response.setStatus( HttpServletResponse.SC_OK);
+
+            // spool the actual contents
+            final OutputStream out = response.getOutputStream();
+            final byte[] buf = new byte[2048];
+            int rd;
+            while ( ( rd = ins.read( buf ) ) >= 0 ) {
+                out.write( buf, 0, rd );
+            }
+        }
+    }
+
+    /**
+     * Reads the <code>templateFile</code> as a resource through the class
+     * loader of this class converting the binary data into a string using
+     * UTF-8 encoding.
+     *
+     * @param templateFile The absolute path to the template file to read.
+     * @return The contents of the template file as a string 
+     *
+     * @throws NullPointerException if <code>templateFile</code> is
+     *      <code>null</code>
+     * @throws FileNotFoundException If template file cannot be found
+     * @throws IOException On any other error reading the template file
+     */
+    protected final String readTemplateFile( final String templateFile ) 
throws IOException {
+        return readTemplateFile( getClass(), templateFile );
+    }
+
+    private final String readTemplateFile( final Class<?> clazz, final String 
templateFile) throws IOException {
+        try(final InputStream templateStream = clazz.getResourceAsStream( 
templateFile )) {
+            if ( templateStream != null ) {
+                try ( final StringWriter w = new StringWriter()) {
+                    final byte[] buf = new byte[2048];
+                    int l;
+                    while ( ( l = templateStream.read(buf)) > 0 ) {
+                        w.write(new String(buf, 0, l, StandardCharsets.UTF_8));
+                    }
+                    String str = w.toString();
+                    switch ( str.charAt(0) ) { // skip BOM
+                        case 0xFEFF: // UTF-16/UTF-32, big-endian
+                        case 0xFFFE: // UTF-16, little-endian
+                        case 0xEFBB: // UTF-8
+                            return str.substring(1);
+                    }
+                    return str;
+                }
+            }
+        }
+
+        throw new FileNotFoundException("Template " + templateFile + " not 
found");
+    }
+
+    protected RequestVariableResolver getVariableResolver(final 
HttpServletRequest request) {
+        return (RequestVariableResolver) 
request.getAttribute(RequestVariableResolver.REQUEST_ATTRIBUTE);
+    }
+}
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/servlet/RequestVariableResolver.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/RequestVariableResolver.java
new file mode 100755
index 0000000000..4efdc3aacd
--- /dev/null
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/RequestVariableResolver.java
@@ -0,0 +1,62 @@
+/*
+ * 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.webconsole.servlet;
+
+import java.util.HashMap;
+
+/**
+ * The <code>RequestVariableResolver</code> is a <code>HashMap</code> that
+ * is used by the webconsole to process variables in the template.
+ * The resolver is stored as a request attribute with the name
+ * {@link #REQUEST_ATTRIBUTE}.
+ */
+public class RequestVariableResolver extends HashMap<String, Object> {
+
+    /**
+     * The name of the request attribute holding the {@link 
RequestVariableResolver}
+     * for the request (value is "felix.webconsole.variable.resolver").
+     *
+     * @since 3.0
+     */
+    public static final String REQUEST_ATTRIBUTE = 
"felix.webconsole.variable.resolver";
+
+    /**
+     * Creates a new variable resolver with default capacity.
+     */
+    public RequestVariableResolver() {
+        super();
+    }
+
+    /**
+     * Returns the string representation of the value stored under the variable
+     * name in this map. If no value is stored under the variable name,
+     * <code>null</code> is returned.
+     *
+     * @param variable The name of the variable whose value is to be returned.
+     * @return The variable value or <code>null</code> if there is no entry
+     *      with the given name in this map.
+     */
+    public String resolve( final String variable ) {
+        final Object value = this.get(variable);
+        if ( value != null ) {
+            return value.toString();
+        }
+        return null;
+    }
+}
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/servlet/ServletConstants.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/ServletConstants.java
new file mode 100755
index 0000000000..7b5f2f1431
--- /dev/null
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/ServletConstants.java
@@ -0,0 +1,75 @@
+/*
+ * 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.webconsole.servlet;
+
+/**
+ * Constants for servlets registered with the web console.
+ */
+public abstract class ServletConstants {
+
+    /**
+     * The URI address label under which the servlet is called by
+     * the web console (value is "felix.webconsole.label").
+     * <p>
+     * This service registration property must be set to a single non-empty
+     * String value. Otherwise the Servlet services will
+     * be ignored by the web console and not be used as a plugin.
+     */
+    public static final String PLUGIN_LABEL = "felix.webconsole.label";
+
+    /**
+     * The title under which the servlet is called by
+     * the web console (value is "felix.webconsole.title").
+     * <p>
+     * This property is required for the service to be used as a plugin. 
+     * Otherwise the service is just ignored by the web console.
+     * <p>
+     */
+    public static final String PLUGIN_TITLE = "felix.webconsole.title";
+
+    /**
+     * The category under which the servlet is listed in the top
+     * navigation by the web console (value is "felix.webconsole.category").
+     * <p>
+     * If not specified, the servlet is put into the default category.
+     */
+    public static final String PLUGIN_CATEGORY = "felix.webconsole.category";
+
+    /**
+     * The name of the service registration properties providing references
+     * to addition CSS files that should be loaded when rendering the header
+     * for a registered plugin.
+     * <p>
+     * This property is expected to be a single string value, array of string
+     * values or a Collection (or Vector) of string values.
+     */
+    public static final String PLUGIN_CSS_REFERENCES = "felix.webconsole.css";
+
+    /**
+     * The name of the request attribute providing the absolute path of the
+     * Web Console root (value is "felix.webconsole.appRoot"). This consists of
+     * the servlet context path (from 
<code>HttpServletRequest.getContextPath()</code>)
+     * and the Web Console servlet path (from
+     * <code>HttpServletRequest.getServletPath()</code>,
+     * <code>/system/console</code> by default).
+     * <p>
+     * The type of this request attribute is <code>String</code>.
+     */
+    public static final String ATTR_APP_ROOT = "felix.webconsole.appRoot";
+}
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/servlet/package-info.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/package-info.java
new file mode 100755
index 0000000000..6792c7703b
--- /dev/null
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/servlet/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
[email protected]("1.0.0")
+package org.apache.felix.webconsole.servlet;
+
diff --git 
a/webconsole/src/test/java/org/apache/felix/webconsole/AbstractWebConsolePluginTest.java
 
b/webconsole/src/test/java/org/apache/felix/webconsole/AbstractWebConsolePluginTest.java
index 93cc070ae2..f69505e82e 100755
--- 
a/webconsole/src/test/java/org/apache/felix/webconsole/AbstractWebConsolePluginTest.java
+++ 
b/webconsole/src/test/java/org/apache/felix/webconsole/AbstractWebConsolePluginTest.java
@@ -122,6 +122,7 @@ public class AbstractWebConsolePluginTest extends TestCase
 
     private static class PrivateTestPlugin extends TestPlugin
     {
+        @SuppressWarnings("unused")
         private URL getResource( String name )
         {
             return null;
@@ -130,6 +131,7 @@ public class AbstractWebConsolePluginTest extends TestCase
 
     private static class ProtectedTestPlugin extends TestPlugin
     {
+        @SuppressWarnings("unused")
         protected URL getResource( String name )
         {
             return null;
@@ -138,6 +140,7 @@ public class AbstractWebConsolePluginTest extends TestCase
 
     private static class PackageTestPlugin extends TestPlugin
     {
+        @SuppressWarnings("unused")
         URL getResource( String name )
         {
             return null;
@@ -146,6 +149,7 @@ public class AbstractWebConsolePluginTest extends TestCase
 
     private static class PublicTestPlugin extends TestPlugin
     {
+        @SuppressWarnings("unused")
         public URL getResource( String name )
         {
             return null;
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 f3e243b336..248bc172e0 100755
--- 
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
@@ -69,7 +69,7 @@ public class OsgiManagerTest {
                 OsgiManager.splitCommaSeparatedString(" abc , x.y.z,123"));
     }
 
-    @SuppressWarnings({ "unchecked", "rawtypes", "serial" })
+    @SuppressWarnings({ "unchecked", "rawtypes"})
     @Test
     public void testUpdateDependenciesCustomizerAdd() throws Exception {
         BundleContext bc = mockBundleContext();
@@ -97,7 +97,7 @@ public class OsgiManagerTest {
         assertEquals(1, updateCalled.size());
     }
 
-    @SuppressWarnings({ "unchecked", "rawtypes", "serial" })
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Test
     public void testUpdateDependenciesCustomzerRemove() throws Exception {
         BundleContext bc = mockBundleContext();

Reply via email to