FELIX-3946 Prevent NPE in BundlesServlet.bundleDetails
* BundleInfoProvider: Clarify webConsoleRoot may be null
* BundlesServlet: Guard pluginRoot against null (only get substring if not null)
* BundlesServlet: Fix rendition of "nfo" structure generated based
on BundleInforProvider data
* ServicesUsedInfoProvider: Generate BundleInfo of type VALUE if
webConsoleRoot is null
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1452202 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java b/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java
index a8b123b..62178ef 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java
@@ -25,16 +25,16 @@
/**
* The bundle info provider allows the user to supply additional information
* that will be used by the Web Console bundle plugin.
- *
+ *
* The API allows the user to register a special service, that could bind a
* custom, implementation-specific information to a bundle.
- *
+ *
* A typical use-case for that API would be the Declarative Services, that could
* provide information about the components provided by this bundle (and link to
* the component plugin too). Another usage could be the ProSyst resource
* manager, that would provide information about the memory and CPU usage of the
* bundle.
- *
+ *
* @author Valentin Valchev
*/
public interface BundleInfoProvider
@@ -49,7 +49,7 @@
/**
* Gets the name of the bundle info provider as localized string.
- *
+ *
* @param locale
* the locale in which the name should be returned
* @return the name of the bundle info provider.
@@ -60,11 +60,18 @@
/**
* Gets the associated bundle information with the specified bundle (by it's
* ID)
- *
+ *
+ * The Service may also be called outside through the new Inventory bundle
+ * due to mapping the BundlesServlet to an InventoryPrinter and for example
+ * calling it from a Gogo Shell. In this case the {@code webConsoleRoot}
+ * parameter will be null a {@link BundleInfo} objects of type
+ * {@link BundleInfoType#LINK} must not be generated.
+ *
* @param bundle
* the bundle, for which additional information is requested.
* @param webConsoleRoot
- * the root alias of the web console itself.
+ * the root alias of the web console itself or {@code null}
+ * if this method is not called through the Web Console itself.
* @param locale
* the locale in which the key-value pair should be returned.
* @return array of available {@link BundleInfo} or empty array if none.
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 3f4d07e..c8a4f48 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
@@ -219,25 +219,50 @@
if ( !props.isNull( pi ) )
{
JSONObject entry = props.getJSONObject( pi );
-
- pw.print( " " + entry.get( "key" ) + ": " );
-
- Object entryValue = entry.get( "value" );
- if ( entryValue instanceof JSONArray )
+ String key = ( String ) entry.get( "key" );
+ if ( "nfo".equals( key ) )
{
- pw.println();
- JSONArray entryArray = ( JSONArray ) entryValue;
- for ( int ei = 0; ei < entryArray.length(); ei++ )
+ // BundleInfo (see #bundleInfo & #bundleInfoDetails
+ JSONObject infos = ( JSONObject ) entry.get( "value" );
+ Iterator infoKeys = infos.keys();
+ while ( infoKeys.hasNext() )
{
- if ( !entryArray.isNull( ei ) )
+ String infoKey = ( String ) infoKeys.next();
+ pw.println( " " + infoKey + ": " );
+
+ JSONArray infoA = infos.getJSONArray( infoKey );
+ for ( int iai = 0; iai < infoA.length(); iai++ )
{
- pw.println( " " + entryArray.get( ei ) );
+ if ( !infoA.isNull( iai ) )
+ {
+ JSONObject info = infoA.getJSONObject( iai );
+ pw.println( " " + info.get( "name" ) );
+ }
}
}
}
else
{
- pw.println( entryValue );
+ // regular data
+ pw.print( " " + key + ": " );
+
+ Object entryValue = entry.get( "value" );
+ if ( entryValue instanceof JSONArray )
+ {
+ pw.println();
+ JSONArray entryArray = ( JSONArray ) entryValue;
+ for ( int ei = 0; ei < entryArray.length(); ei++ )
+ {
+ if ( !entryArray.isNull( ei ) )
+ {
+ pw.println( " " + entryArray.get( ei ) );
+ }
+ }
+ }
+ else
+ {
+ pw.println( entryValue );
+ }
}
}
}
@@ -803,7 +828,8 @@
}
listHeaders( jw, bundle );
- bundleInfoDetails(jw, bundle, pluginRoot.substring(0, pluginRoot.lastIndexOf("/")), locale);
+ final String appRoot = ( pluginRoot == null ) ? null : pluginRoot.substring( 0, pluginRoot.lastIndexOf( "/" ) );
+ bundleInfoDetails( jw, bundle, appRoot, locale );
jw.endArray();
}
@@ -837,20 +863,22 @@
jw.endObject();
}
+
private static final void bundleInfo( JSONWriter jw, BundleInfo info ) throws JSONException
{
- jw.object();
- jw.key("name");
- jw.value( info.getName() );
- jw.key("description");
- jw.value( info.getDescription() );
- jw.key("type");
- jw.value( info.getType().getName() );
- jw.key("value");
- jw.value( info.getValue() );
- jw.endObject();
+ jw.object();
+ jw.key( "name" );
+ jw.value( info.getName() );
+ jw.key( "description" );
+ jw.value( info.getDescription() );
+ jw.key( "type" );
+ jw.value( info.getType().getName() );
+ jw.key( "value" );
+ jw.value( info.getValue() );
+ jw.endObject();
}
+
private final Integer getStartLevel( Bundle bundle )
{
if ( bundle.getState() != Bundle.UNINSTALLED )
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 170f45f..8ab3588 100644
--- 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
@@ -18,6 +18,7 @@
*/
package org.apache.felix.webconsole.internal.core;
+
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Locale;
@@ -32,60 +33,67 @@
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
-final class ServicesUsedInfoProvider implements
- BundleInfoProvider {
-
+
+final class ServicesUsedInfoProvider implements BundleInfoProvider
+{
+
private final LocalizationHelper localization;
-
- ServicesUsedInfoProvider(Bundle bundle)
+
+
+ ServicesUsedInfoProvider( Bundle bundle )
{
- localization = new LocalizationHelper(bundle);
+ localization = new LocalizationHelper( bundle );
}
+
/*
* (non-Javadoc)
- *
+ *
* @see
* org.apache.felix.webconsole.bundleinfo.BundleInfoProvider#getName(java
* .util.Locale)
*/
- public String getName(Locale locale)
+ public String getName( Locale locale )
{
- return localization.getResourceBundle(locale).getString("services.info.name"); //$NON-NLS-1$;
+ return localization.getResourceBundle( locale ).getString( "services.info.name" ); //$NON-NLS-1$;
}
- public BundleInfo[] getBundleInfo(Bundle bundle, String webConsoleRoot,
- Locale locale)
+
+ public BundleInfo[] getBundleInfo( Bundle bundle, String webConsoleRoot, Locale locale )
{
- final ServiceReference[] refs = bundle.getServicesInUse();
- if (null == refs || refs.length == 0)
- return NO_INFO;
-
- BundleInfo[] ret = new BundleInfo[refs.length];
- for ( int i=0; i < refs.length; i++ )
- {
- ret[i] = toInfo(refs[i], webConsoleRoot, locale);
- }
- return ret;
+ final ServiceReference[] refs = bundle.getServicesInUse();
+ if ( null == refs || refs.length == 0 )
+ return NO_INFO;
+
+ BundleInfo[] ret = new BundleInfo[refs.length];
+ for ( int i = 0; i < refs.length; i++ )
+ {
+ ret[i] = toInfo( refs[i], webConsoleRoot, locale );
+ }
+ return ret;
}
- 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);
- final String descr = localization.getResourceBundle(locale).getString("services.info.descr"); //$NON-NLS-1$;
- String name = localization.getResourceBundle(locale).getString("services.info.key"); //$NON-NLS-1$;
- name = MessageFormat.format(name, new Object[] {
- id, Arrays.asList(classes).toString()
- });
- return new BundleInfo(name, webConsoleRoot + "/services/" + id, //$NON-NLS-1$
- BundleInfoType.LINK, descr);
+ final String[] classes = ( String[] ) ref.getProperty( Constants.OBJECTCLASS );
+ final Object id = ref.getProperty( Constants.SERVICE_ID );
+ final String descr = localization.getResourceBundle( locale ).getString( "services.info.descr" ); //$NON-NLS-1$;
+ String name = localization.getResourceBundle( locale ).getString( "services.info.key" ); //$NON-NLS-1$;
+ name = MessageFormat.format( name, new Object[]
+ { id, Arrays.asList( classes ).toString() } );
+ if ( webConsoleRoot == null )
+ {
+ return new BundleInfo( name, id, BundleInfoType.VALUE, descr );
+ }
+ return new BundleInfo( name, webConsoleRoot + "/services/" + id, //$NON-NLS-1$
+ BundleInfoType.LINK, descr );
}
-
+
+
ServiceRegistration register( BundleContext context )
{
- return context.registerService(BundleInfoProvider.class.getName(), this, null);
+ return context.registerService( BundleInfoProvider.class.getName(), this, null );
}
}