This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git
The following commit(s) were added to refs/heads/master by this push:
new 0d0c5bc FELIX-6464 add web console plugin for capabilities (#109)
0d0c5bc is described below
commit 0d0c5bc80482010c092169be15b2ecd6e949f72f
Author: Konrad Windszus <[email protected]>
AuthorDate: Tue Oct 19 10:25:35 2021 +0200
FELIX-6464 add web console plugin for capabilities (#109)
* FELIX-6464 add web console plugin for capabilities
* add missing class
---
.../webconsole/internal/core/BundlesServlet.java | 18 ++-
.../internal/core/CapabilitiesPrinter.java | 140 +++++++++++++++++++++
.../core/CapabilitiesProvidedInfoProvider.java | 107 ++++++++++++++++
.../core/CapabilitiesRequiredInfoProvider.java | 76 +++++++++++
.../webconsole/internal/servlet/OsgiManager.java | 1 +
.../main/resources/OSGI-INF/l10n/bundle.properties | 9 ++
6 files changed, 350 insertions(+), 1 deletion(-)
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 723e731..69c62c1 100644
---
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
@@ -125,6 +125,10 @@ public class BundlesServlet extends SimpleWebConsolePlugin
implements OsgiManage
// templates
private final String TEMPLATE_MAIN;
+ private ServiceRegistration<BundleInfoProvider> bipCapabilitiesProvided;
+
+ private ServiceRegistration<BundleInfoProvider> bipCapabilitiesRequired;
+
/** Default constructor */
public BundlesServlet()
{
@@ -166,6 +170,8 @@ public class BundlesServlet extends SimpleWebConsolePlugin
implements OsgiManage
props.put( WebConsoleConstants.CONFIG_PRINTER_MODES, new String[] {
ConfigurationPrinter.MODE_TXT,
ConfigurationPrinter.MODE_ZIP } );
configurationPrinter = bundleContext.registerService(
ConfigurationPrinter.class, this, props );
+ bipCapabilitiesProvided = bundleContext.registerService(
BundleInfoProvider.class, new CapabilitiesProvidedInfoProvider(
bundleContext.getBundle() ), null );
+ bipCapabilitiesRequired = bundleContext.registerService(
BundleInfoProvider.class, new CapabilitiesRequiredInfoProvider(
bundleContext.getBundle() ), null );
}
@@ -181,12 +187,22 @@ public class BundlesServlet extends
SimpleWebConsolePlugin implements OsgiManage
configurationPrinter = null;
}
- if ( bundleInfoTracker != null)
+ if ( bundleInfoTracker != null )
{
bundleInfoTracker.close();
bundleInfoTracker = null;
}
+ if ( bipCapabilitiesProvided != null )
+ {
+ bipCapabilitiesProvided.unregister();
+ bipCapabilitiesProvided = null;
+ }
+ if ( bipCapabilitiesRequired != null )
+ {
+ bipCapabilitiesRequired.unregister();
+ bipCapabilitiesRequired = null;
+ }
super.deactivate();
}
diff --git
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/CapabilitiesPrinter.java
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/CapabilitiesPrinter.java
new file mode 100644
index 0000000..5c1cb34
--- /dev/null
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/CapabilitiesPrinter.java
@@ -0,0 +1,140 @@
+/*
+ * 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.core;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.felix.webconsole.internal.AbstractConfigurationPrinter;
+import org.apache.felix.webconsole.internal.misc.ConfigurationRender;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleWiring;
+
+
+public class CapabilitiesPrinter extends AbstractConfigurationPrinter
+{
+
+ private static final String TITLE = "Capabilities";
+
+ static final List<String> EXCLUDED_NAMESPACES = Arrays.asList(
PackageNamespace.PACKAGE_NAMESPACE, HostNamespace.HOST_NAMESPACE );
+ static final Predicate<String> EXCLUDED_NAMESPACES_PREDICATE = new
ExcludedNamespacesPredicate();
+
+ public String getTitle()
+ {
+ return TITLE;
+ }
+
+ static final class ExcludedNamespacesPredicate implements Predicate<String>
+ {
+ @Override
+ public boolean test(String namespace) {
+ return !EXCLUDED_NAMESPACES.contains(namespace);
+ }
+ }
+
+ public void printConfiguration( PrintWriter printWriter )
+ {
+ for ( Bundle bundle : getBundleContext().getBundles() )
+ {
+ BundleWiring wiring = bundle.adapt( BundleWiring.class );
+
+ List<BundleCapability> capabilities = wiring.getCapabilities( null
).stream()
+ .filter( c ->
EXCLUDED_NAMESPACES_PREDICATE.test(c.getNamespace()) )
+ .collect( Collectors.toList() );
+ if ( capabilities.isEmpty() )
+ {
+ // skip bundles not exporting capabilities
+ continue;
+ }
+
+ ConfigurationRender.infoLine( printWriter, null, "Bundle",
bundle.getSymbolicName() + " (" + bundle.getBundleId() + ")" );
+ for ( BundleCapability capability : capabilities )
+ {
+ ConfigurationRender.infoLine( printWriter, " ", "Capability
namespace", capability.getNamespace() );
+ String attributes = dumpTypedAttributes(
capability.getAttributes() );
+ if ( !attributes.isEmpty() )
+ {
+ ConfigurationRender.infoLine( printWriter, " ",
"Attributes", attributes );
+ }
+ String directives = dumpDirectives( capability.getDirectives()
);
+ if ( !directives.isEmpty() )
+ {
+ ConfigurationRender.infoLine( printWriter, " ",
"Directives", directives );
+ }
+ String requirerBundles = wiring.getProvidedWires(
capability.getNamespace() ).stream()
+ .map( w -> w.getRequirer().getSymbolicName() + " (" +
w.getRequirer().getBundle().getBundleId() + ") with directives: " +
dumpDirectives( w.getRequirement().getDirectives() ) )
+ .collect( Collectors.joining( ", ") );
+ if ( !requirerBundles.isEmpty() )
+ {
+ ConfigurationRender.infoLine( printWriter, " ",
"Required By", requirerBundles );
+ }
+ }
+ }
+ }
+
+ static String dumpTypedAttributes( Map<String, Object> typedAttributes )
+ {
+ StringBuilder attributes = new StringBuilder();
+ boolean isFirst = true;
+ for ( Map.Entry<String, Object> entry : typedAttributes.entrySet() )
+ {
+ String value;
+ if ( entry.getValue().getClass().isArray() )
+ {
+ StringBuilder values = new StringBuilder( "[" );
+ for ( int i=0; i<Array.getLength(entry.getValue()); i++ )
+ {
+ if ( i > 0 )
+ {
+ values.append( ", " );
+ }
+ values.append( Array.get( entry.getValue(), i ) );
+ }
+ value = values.append( "]" ).toString();
+ }
+ else
+ {
+ value = entry.getValue().toString();
+ }
+ if ( isFirst )
+ {
+ isFirst = false;
+ }
+ else
+ {
+ attributes.append( ", " );
+ }
+ attributes.append( entry.getKey() ).append( "=" ).append( value );
+ }
+ return attributes.toString();
+ }
+
+ static String dumpDirectives( Map<String, String> directives )
+ {
+ return directives.entrySet().stream().map( e -> e.getKey() + "=" +
e.getValue() ).collect( Collectors.joining( " ," ) );
+ }
+}
\ No newline at end of file
diff --git
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/CapabilitiesProvidedInfoProvider.java
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/CapabilitiesProvidedInfoProvider.java
new file mode 100644
index 0000000..0ad62b2
--- /dev/null
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/CapabilitiesProvidedInfoProvider.java
@@ -0,0 +1,107 @@
+/*
+ * 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.core;
+
+
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.felix.webconsole.bundleinfo.BundleInfo;
+import org.apache.felix.webconsole.bundleinfo.BundleInfoProvider;
+import org.apache.felix.webconsole.bundleinfo.BundleInfoType;
+import org.apache.felix.webconsole.i18n.LocalizationHelper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleWiring;
+
+
+public class CapabilitiesProvidedInfoProvider implements BundleInfoProvider
+{
+ private final LocalizationHelper localization;
+
+ // hide some namespace except if required by some other bundle
+ // TODO: add more namespaces from
http://docs.osgi.org/specification/osgi.cmpn/7.0.0/service.namespaces.html
+ private static final List<String> STANDARD_NAMESPACES = Arrays.asList(
BundleNamespace.BUNDLE_NAMESPACE, IdentityNamespace.IDENTITY_NAMESPACE );
+
+ CapabilitiesProvidedInfoProvider( Bundle bundle )
+ {
+ localization = new LocalizationHelper( bundle );
+ }
+
+ @Override
+ public String getName( Locale locale )
+ {
+ return localization.getResourceBundle( locale ).getString(
"capabilities.provided.info.name" ); //$NON-NLS-1$;
+ }
+
+ /**
+ * Hides those capabilities which are exposed in a dedicated section and
all default bundle namespaces which are not required by at least one other
bundle.
+ *
+ */
+ private static final class CapabilityFilter implements
Predicate<BundleCapability>
+ {
+ private BundleWiring wiring;
+
+ CapabilityFilter(BundleWiring wiring)
+ {
+ this.wiring = wiring;
+ }
+
+ @Override
+ public boolean test(BundleCapability t)
+ {
+ return
(CapabilitiesPrinter.EXCLUDED_NAMESPACES_PREDICATE.test(t.getNamespace())) &&
+ !(STANDARD_NAMESPACES.contains(t.getNamespace()) &&
wiring.getProvidedWires(t.getNamespace()).isEmpty());
+ }
+ }
+
+ @Override
+ public BundleInfo[] getBundleInfo( Bundle bundle, String webConsoleRoot,
Locale locale )
+ {
+ BundleWiring wiring = bundle.adapt( BundleWiring.class );
+ Predicate<BundleCapability> capabilityFilter = new CapabilityFilter(
wiring );
+ // which capabilities to filter?
+ List<BundleCapability> capabilities = wiring.getCapabilities( null
).stream()
+ .filter( capabilityFilter )
+ .collect( Collectors.toList() );
+ return capabilities.stream().map( c -> toInfo( c, wiring,
webConsoleRoot, locale ) ).toArray( BundleInfo[]::new );
+ }
+
+ private BundleInfo toInfo( BundleCapability capability, BundleWiring
wiring, String webConsoleRoot, Locale locale )
+ {
+ final String descr = localization.getResourceBundle( locale
).getString( "capabilities.provided.info.descr" ); //$NON-NLS-1$;
+ String name = localization.getResourceBundle( locale ).getString(
"capabilities.provided.info.key" ); //$NON-NLS-1$;
+ String requirerBundles = wiring.getProvidedWires(
capability.getNamespace() ).stream()
+ .map( w -> w.getRequirer().getSymbolicName() + " (" +
w.getRequirer().getBundle().getBundleId() + ")" )
+ .collect( Collectors.joining( ", ") );
+ name = MessageFormat.format( name, capability.getNamespace(),
CapabilitiesPrinter.dumpTypedAttributes( capability.getAttributes() ) );
+ if ( !requirerBundles.isEmpty() )
+ {
+ name += MessageFormat.format( localization.getResourceBundle(
locale ).getString( "capabilities.provided.info.key.addition" ),
requirerBundles );
+ }
+ // use empty link type to prevent the pattern <name>=<value> being
used for printing
+ return new BundleInfo( name, "/#", BundleInfoType.LINK, descr );
+ }
+}
\ No newline at end of file
diff --git
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/CapabilitiesRequiredInfoProvider.java
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/CapabilitiesRequiredInfoProvider.java
new file mode 100644
index 0000000..2136948
--- /dev/null
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/CapabilitiesRequiredInfoProvider.java
@@ -0,0 +1,76 @@
+/*
+ * 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.core;
+
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.Optional;
+
+import org.apache.felix.webconsole.bundleinfo.BundleInfo;
+import org.apache.felix.webconsole.bundleinfo.BundleInfoProvider;
+import org.apache.felix.webconsole.bundleinfo.BundleInfoType;
+import org.apache.felix.webconsole.i18n.LocalizationHelper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleWiring;
+
+
+public class CapabilitiesRequiredInfoProvider implements BundleInfoProvider
+{
+ private final LocalizationHelper localization;
+
+ CapabilitiesRequiredInfoProvider( Bundle bundle )
+ {
+ localization = new LocalizationHelper( bundle );
+ }
+
+ @Override
+ public String getName( Locale locale )
+ {
+ return localization.getResourceBundle( locale ).getString(
"capabilities.required.info.name" ); //$NON-NLS-1$;
+ }
+
+ @Override
+ public BundleInfo[] getBundleInfo( Bundle bundle, String webConsoleRoot,
Locale locale )
+ {
+ BundleWiring wiring = bundle.adapt( BundleWiring.class );
+ return wiring.getRequirements( null ).stream()
+ .filter( t ->
CapabilitiesPrinter.EXCLUDED_NAMESPACES_PREDICATE.test(t.getNamespace()))
+ .map( r -> toInfo( r, wiring, webConsoleRoot, locale ) ).toArray(
BundleInfo[]::new );
+ }
+
+ private BundleInfo toInfo( BundleRequirement requirement, BundleWiring
wiring, String webConsoleRoot, Locale locale )
+ {
+ final String descr = localization.getResourceBundle( locale
).getString( "capabilities.required.info.descr" ); //$NON-NLS-1$;
+ Optional<Bundle> providerBundle = wiring.getRequiredWires(
requirement.getNamespace() ).stream()
+ .map( w -> w.getProvider().getBundle() ).findFirst(); // only
the first one is returned
+ String name = localization.getResourceBundle( locale ).getString(
"capabilities.required.info.key" ); //$NON-NLS-1$;
+ name = MessageFormat.format( name, requirement.getNamespace(),
CapabilitiesPrinter.dumpDirectives( requirement.getDirectives() ) );
+ String link = "/#"; // use empty link type to prevent the pattern
<name>=<value> being used for printing
+ if ( providerBundle.isPresent() )
+ {
+ name += MessageFormat.format( localization.getResourceBundle(
locale ).getString( "capabilities.required.info.key.addition" ),
providerBundle.get().getSymbolicName(), providerBundle.get().getBundleId() );
+ if ( webConsoleRoot != null ) {
+ link = webConsoleRoot + "/bundles/" +
providerBundle.get().getBundleId(); //$NON-NLS-1$
+ }
+ }
+ return new BundleInfo( name, link, BundleInfoType.LINK, descr );
+ }
+}
\ No newline at end of file
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 9261e8d..94c7bf4 100644
---
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
+++
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/servlet/OsgiManager.java
@@ -203,6 +203,7 @@ public class OsgiManager extends GenericServlet
"org.apache.felix.webconsole.internal.compendium.PreferencesConfigurationPrinter",
//$NON-NLS-1$
"org.apache.felix.webconsole.internal.compendium.WireAdminConfigurationPrinter",
//$NON-NLS-1$
"org.apache.felix.webconsole.internal.core.BundlesConfigurationPrinter",
//$NON-NLS-1$
+ "org.apache.felix.webconsole.internal.core.CapabilitiesPrinter",
//$NON-NLS-1$
"org.apache.felix.webconsole.internal.core.PermissionsConfigurationPrinter",
//$NON-NLS-1$
"org.apache.felix.webconsole.internal.core.ServicesConfigurationPrinter",
//$NON-NLS-1$
"org.apache.felix.webconsole.internal.misc.SystemPropertiesPrinter",
//$NON-NLS-1$
diff --git a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
index 9cc31c6..7792a48 100644
--- a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
+++ b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -73,6 +73,15 @@ vmstat.mem.free=Free Memory
vmstat.gc.title=Garbage Collection
vmstat.gc.button=Run
+capabilities.provided.info.name=Provided Capabilities
+capabilities.provided.info.key=Capability "{0}" with attributes {1}
+capabilities.provided.info.key.addition=\ (required by {0})
+capabilities.provided.info.descr=Capability provided by this bundle
+capabilities.required.info.name=Required Capabilities
+capabilities.required.info.key=Capability "{0}" with directives {1}
+capabilities.required.info.key.addition=\ (provided by {0} ({1}))
+capabilities.required.info.descr=Capability required by this bundle
+
# Services plugin
services.pluginTitle=Services
services.details.hide=Hide Details