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();
