You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by vv...@apache.org on 2012/08/15 14:48:54 UTC

svn commit: r1373379 - in /felix/trunk/webconsole: ./ src/main/java/org/apache/felix/webconsole/bundleinfo/ src/main/java/org/apache/felix/webconsole/i18n/ src/main/java/org/apache/felix/webconsole/internal/core/ src/main/java/org/apache/felix/webconso...

Author: vvalchev
Date: Wed Aug 15 12:48:53 2012
New Revision: 1373379

URL: http://svn.apache.org/viewvc?rev=1373379&view=rev
Log:
Fixed FELIX-2896 Add support for bundle info providers
https://issues.apache.org/jira/browse/FELIX-2896

Added:
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfo.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoType.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/i18n/
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java
Modified:
    felix/trunk/webconsole/pom.xml
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java
    felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ResourceBundleCache.java
    felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
    felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle_bg.properties
    felix/trunk/webconsole/src/main/resources/res/ui/bundles.js

Modified: felix/trunk/webconsole/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/pom.xml?rev=1373379&r1=1373378&r2=1373379&view=diff
==============================================================================
--- felix/trunk/webconsole/pom.xml (original)
+++ felix/trunk/webconsole/pom.xml Wed Aug 15 12:48:53 2012
@@ -122,7 +122,9 @@
                             org.apache.felix.webconsole.internal.OsgiManagerActivator
                         </Bundle-Activator>
                         <Export-Package>
-                            org.apache.felix.webconsole;version=3.1.2;provide:=true
+                            org.apache.felix.webconsole;version=3.1.2;provide:=true,
+                            org.apache.felix.webconsole.bundleinfo;version=1.0.0;provide:=true,
+                            org.apache.felix.webconsole.i18n;version=1.0.0;provide:=true
                         </Export-Package>
                         <Import-Package>
                             org.osgi.service.metatype;resolution:=optional,

Added: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfo.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfo.java?rev=1373379&view=auto
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfo.java (added)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfo.java Wed Aug 15 12:48:53 2012
@@ -0,0 +1,98 @@
+/*
+ * 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.bundleinfo;
+
+/**
+ * This entity defines additional bundle information entry, that is provided by
+ * the {@link BundleInfoProvider}. Each information entry is featured by name,
+ * value, type and description.
+ * 
+ * @author Valentin Valchev
+ */
+public class BundleInfo 
+{
+
+    private final String name;
+    private final String description;
+    private final Object value;
+    private final BundleInfoType type;
+
+    /**
+     * Creates a new bundle information entry.
+     * 
+     * @param name
+     *            the name of the entry
+     * @param value
+     *            the value associated with that entry
+     * @param type
+     *            the type of the value
+     * @param description
+     *            additional, user-friendly description for that value.
+     */
+    public BundleInfo(String name, Object value, BundleInfoType type,
+	    String description) 
+    {
+	this.name = name;
+	this.value = value;
+	this.type = type;
+	this.description = description;
+	type.validate(value);
+    }
+
+    /**
+     * Gets the name of the information entry. The name should be localized
+     * according the requested locale.
+     * 
+     * @return the name of that information key.
+     */
+    public String getName() 
+    {
+	return name;
+    }
+
+    /**
+     * Gets user-friendly description of the key pair. The description should be
+     * localized according the requested locale.
+     * 
+     * @return the description for that information key.
+     */
+    public String getDescription() 
+    {
+	return description;
+    }
+
+    /**
+     * Gets the information value.
+     * 
+     * @return the value.
+     */
+    public Object getValue() 
+    {
+	return value;
+    }
+
+    /**
+     * Gets the type of the information value.
+     * 
+     * @return the information type.
+     */
+    public BundleInfoType getType() 
+    {
+	return type;
+    }
+
+}

Added: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java?rev=1373379&view=auto
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java (added)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java Wed Aug 15 12:48:53 2012
@@ -0,0 +1,70 @@
+/*
+ * 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.bundleinfo;
+
+import java.util.Locale;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * 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 
+{
+
+    /**
+     * This is just an utility - empty array, that could be returned when there
+     * is no additional information for a specific bundle.
+     */
+    public static final BundleInfo[] NO_INFO = new BundleInfo[0];
+
+    /**
+     * 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.
+     */
+    String getName(Locale locale);
+
+    /**
+     * Gets the associated bundle information with the specified bundle (by it's
+     * ID)
+     * 
+     * @param bundle
+     *            the bundle, for which additional information is requested.
+     * @param webConsoleRoot
+     *            the root alias of 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.
+     */
+    BundleInfo[] getBundleInfo(Bundle bundle, String webConsoleRoot,
+	    Locale locale);
+}

Added: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoType.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoType.java?rev=1373379&view=auto
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoType.java (added)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoType.java Wed Aug 15 12:48:53 2012
@@ -0,0 +1,108 @@
+/*
+ * 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.bundleinfo;
+
+import java.net.URL;
+
+/**
+ * This pre-java 5 enum defines all valid bundle information value types.
+ * 
+ * @author Valentin Valchev
+ */
+public final class BundleInfoType 
+{
+
+    /**
+     * Specifies that the value is {@link String} and is either a link to a
+     * local Servlet, or link to external HTTP server. In case the link starts
+     * with <code>&lt;protocol&gt;://</code> the link will be considered as
+     * external. Otherwise the link should be absolute link to a local Servlet
+     * and must always start with <code>/</code>.
+     * 
+     * for security reasons, the protocol cannot be <code>file</code> for
+     * external links.
+     */
+    public static final BundleInfoType LINK = new BundleInfoType("link"); //$NON-NLS-1$
+    /**
+     * This information type, specifies that the value of the information is URL
+     * object, that points to a resource. In that case the UI could consider
+     * that as a <em>download</em> link.
+     */
+    public static final BundleInfoType RESOURCE = new BundleInfoType("resource"); //$NON-NLS-1$
+    /**
+     * That information type is for normal information keys, that provide a
+     * normal (not link) value as information. The type of the value is
+     * <code>Object</code> and UI will visualize it by using it's
+     * {@link Object#toString()} method.
+     */
+    public static final BundleInfoType VALUE = new BundleInfoType("value"); //$NON-NLS-1$
+
+    private final String name;
+
+    private BundleInfoType(String name)
+    {
+	/* prevent instantiation */
+	this.name = name;
+    }
+
+    /**
+     * Returns the name of the type.
+     * 
+     * @return the type name
+     */
+    public final String getName()
+    {
+	return name;
+    }
+
+    /**
+     * That method is used to validate if the object is correct for the
+     * specified type.
+     * 
+     * @param value
+     *            the value that will be validated.
+     */
+    public final void validate(final Object value)
+    {
+	if (this == LINK)
+	{
+	    if (!(value instanceof String))
+		throw new IllegalArgumentException("Not a String");
+	    final String val = (String) value;
+	    final int idx = val.indexOf("://"); //$NON-NLS-1$
+	    // check local
+	    if (idx == -1)
+	    {
+		if (!val.startsWith("/")) //$NON-NLS-1$
+		    throw new IllegalArgumentException("Invalid local link");
+	    }
+	    else
+	    {
+		// check external link
+		if (val.substring(0, idx).equalsIgnoreCase("file")) //$NON-NLS-1$
+		    throw new IllegalArgumentException(
+			    "External link cannot use file protocol");
+	    }
+	}
+	else if (this == RESOURCE) 
+	{
+	    if (!(value instanceof URL))
+		throw new IllegalArgumentException("Invalid URL");
+	}
+    }
+
+}

Added: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java?rev=1373379&view=auto
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java (added)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java Wed Aug 15 12:48:53 2012
@@ -0,0 +1,59 @@
+/*
+ * 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.i18n;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.apache.felix.webconsole.internal.i18n.ResourceBundleCache;
+import org.osgi.framework.Bundle;
+
+/**
+ * The localization helper is supposed to be used from the bundle info
+ * providers. It will allow them to provide locale-specific names and
+ * descriptions of the bundle information entries.
+ * 
+ * @author Valentin Valchev
+ */
+public class LocalizationHelper {
+
+    private final ResourceBundleCache cache;
+
+    /**
+     * Creates a new helper instance.
+     * 
+     * @param bundle
+     *            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);
+    }
+
+    /**
+     * Used to retrieve the resource bundle for the specified locale.
+     * 
+     * @param locale
+     *            the requested locale.
+     * @return the resource bundle (could be empty, but never <code>null</code>)
+     */
+    public ResourceBundle getResourceBundle(final Locale locale) {
+	return cache.getResourceBundle(locale);
+    }
+}

Modified: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java?rev=1373379&r1=1373378&r2=1373379&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java (original)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/BundlesServlet.java Wed Aug 15 12:48:53 2012
@@ -32,6 +32,9 @@ import org.apache.felix.framework.util.V
 import org.apache.felix.utils.manifest.Clause;
 import org.apache.felix.utils.manifest.Parser;
 import org.apache.felix.webconsole.*;
+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.internal.OsgiManagerPlugin;
 import org.apache.felix.webconsole.internal.Util;
 import org.json.*;
@@ -42,6 +45,7 @@ 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;
 
 
 /**
@@ -81,6 +85,7 @@ public class BundlesServlet extends Simp
     private boolean[] bootPkgWildcards;
 
     private ServiceRegistration configurationPrinter;
+    private ServiceTracker bundleInfoTracker;
 
     // templates
     private final String TEMPLATE_MAIN;
@@ -100,6 +105,9 @@ public class BundlesServlet extends Simp
     public void activate( BundleContext bundleContext )
     {
         super.activate( bundleContext );
+        
+        bundleInfoTracker = new ServiceTracker( bundleContext, BundleInfoProvider.class.getName(), null);
+        bundleInfoTracker.open();
 
         // bootdelegation property parsing from Apache Felix R4SearchPolicyCore
         String bootDelegation = bundleContext.getProperty( Constants.FRAMEWORK_BOOTDELEGATION );
@@ -135,6 +143,12 @@ public class BundlesServlet extends Simp
             configurationPrinter.unregister();
             configurationPrinter = null;
         }
+        
+        if ( bundleInfoTracker != null)
+        {
+            bundleInfoTracker.close();
+            bundleInfoTracker = null;
+        }
 
         super.deactivate();
     }
@@ -758,10 +772,53 @@ public class BundlesServlet extends Simp
         }
 
         listHeaders( jw, bundle );
+        bundleInfoDetails(jw, bundle, pluginRoot.substring(0, pluginRoot.lastIndexOf("/")), locale);
 
         jw.endArray();
     }
 
+    
+    private final void bundleInfoDetails( JSONWriter jw, Bundle bundle, String appRoot, final Locale locale)
+	        throws JSONException
+    {
+	jw.object();
+	jw.key( "key" );
+	jw.value( "nfo" );
+	jw.key( "value");
+	jw.object();
+        final Object[] bundleInfoProviders = bundleInfoTracker.getServices();
+        for ( int i = 0; bundleInfoProviders != null && i < bundleInfoProviders.length; i++ )
+        {
+            final BundleInfoProvider infoProvider = (BundleInfoProvider) bundleInfoProviders[i];
+            final BundleInfo[] infos = infoProvider.getBundleInfo(bundle, appRoot, locale);
+            if ( null != infos && infos.length > 0)
+            {
+        	jw.key( infoProvider.getName(locale) );
+        	jw.array();
+        	for ( int j = 0; j < infos.length; j++ ) 
+        	{
+        	    bundleInfo( jw, infos[j] );
+        	}
+        	jw.endArray();
+            }
+        }
+        jw.endObject(); // value
+        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();
+    }
 
     private final Integer getStartLevel( Bundle bundle )
     {

Modified: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java?rev=1373379&r1=1373378&r2=1373379&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java (original)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesServlet.java Wed Aug 15 12:48:53 2012
@@ -38,9 +38,11 @@ import org.apache.felix.webconsole.inter
 import org.json.JSONException;
 import org.json.JSONWriter;
 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;
 
 
 /**
@@ -104,8 +106,8 @@ public class ServicesServlet extends Sim
     }
 
     /** the label for the services plugin */
-    public static final String LABEL = "services";
-    private static final String TITLE = "%services.pluginTitle";
+    public static final String LABEL = "services"; //$NON-NLS-1$
+    private static final String TITLE = "%services.pluginTitle"; //$NON-NLS-1$
     private static final String CSS[] = null;
 
     private final String TEMPLATE;
@@ -115,7 +117,24 @@ public class ServicesServlet extends Sim
         super(LABEL, TITLE, CSS);
 
         // load templates
-        TEMPLATE = readTemplateFile( "/templates/services.html" );
+        TEMPLATE = readTemplateFile( "/templates/services.html" ); //$NON-NLS-1$
+    }
+    
+    private ServiceRegistration bipReg;
+    
+    public void activate(BundleContext bundleContext) 
+    {
+        super.activate(bundleContext);
+        bipReg = new ServicesUsedInfoProvider( bundleContext.getBundle() ).register( bundleContext );
+    }
+    
+    public void deactivate() {
+	if ( null != bipReg )
+	{
+	    bipReg.unregister();
+	    bipReg = null;
+	}
+        super.deactivate();
     }
 
 

Added: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java?rev=1373379&view=auto
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java (added)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java Wed Aug 15 12:48:53 2012
@@ -0,0 +1,94 @@
+/*
+ * 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.Locale;
+
+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.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+final class ServicesUsedInfoProvider implements
+	BundleInfoProvider {
+
+    // TODO: add i18n for those entries
+    private static final String SERVICE_DESCRIPTION = "%services.info.descr.";
+    
+    private final LocalizationHelper localization;
+    
+    ServicesUsedInfoProvider(Bundle bundle) 
+    {
+	localization = new LocalizationHelper(bundle);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.felix.webconsole.bundleinfo.BundleInfoProvider#getName(java
+     * .util.Locale)
+     */
+    public String getName(Locale locale) 
+    {
+	return localization.getResourceBundle(locale).getString("services.info.name"); //$NON-NLS-1$;
+    }
+
+    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;
+    }
+
+    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);
+    }
+    
+    ServiceRegistration register( BundleContext context )
+    {
+	return context.registerService(BundleInfoProvider.class.getName(), this, null);
+    }
+
+}

Modified: felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ResourceBundleCache.java
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ResourceBundleCache.java?rev=1373379&r1=1373378&r2=1373379&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ResourceBundleCache.java (original)
+++ felix/trunk/webconsole/src/main/java/org/apache/felix/webconsole/internal/i18n/ResourceBundleCache.java Wed Aug 15 12:48:53 2012
@@ -33,12 +33,12 @@ import org.osgi.framework.Constants;
 /**
  * The <code>ResourceBundleCache</code> caches resource bundles per OSGi bundle.
  */
-class ResourceBundleCache
+public class ResourceBundleCache
 {
 
     /**
      * The default locale corresponding to the default language in the
-     * bundle.properties file, which is english.
+     * bundle.properties file, which is English.
      * (FELIX-1957 The Locale(String) constructor used before is not available
      * in the OSGi/Minimum-1.1 profile and should be prevented)
      */
@@ -51,14 +51,25 @@ class ResourceBundleCache
     private Map resourceBundleEntries;
 
 
-    ResourceBundleCache( final Bundle bundle )
+    /**
+     * Creates a new object
+     * 
+     * @param bundle the bundle which resources should be loaded.
+     */
+    public ResourceBundleCache( final Bundle bundle )
     {
         this.bundle = bundle;
         this.resourceBundles = new HashMap();
     }
 
 
-    ResourceBundle getResourceBundle( final Locale locale )
+    /**
+     * Gets the resource bundle for the specified locale.
+     * 
+     * @param locale the requested locale
+     * @return the resource bundle for the requested locale
+     */
+    public ResourceBundle getResourceBundle( final Locale locale )
     {
         if ( locale == null )
         {
@@ -161,7 +172,7 @@ class ResourceBundleCache
     }
 
 
-    private Locale getParentLocale( Locale locale )
+    private static final Locale getParentLocale( Locale locale )
     {
         if ( locale.getVariant().length() != 0 )
         {

Modified: felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties?rev=1373379&r1=1373378&r2=1373379&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties (original)
+++ felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties Wed Aug 15 12:48:53 2012
@@ -78,6 +78,9 @@ services.details.tip=Details
 services.statusline=Services information: {0} service(s) in total.
 services.caption=Services
 services.usingBundles=Using Bundles
+services.info.name=Used Services
+services.info.descr=This is a OSGi service that is used by the current bundle. Click to see more details in 'Services' plugin.
+services.info.key=Service #{0} of type(s) {1}
 
 # Log plugin
 logs.pluginTitle=Log Service

Modified: felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle_bg.properties
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle_bg.properties?rev=1373379&r1=1373378&r2=1373379&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle_bg.properties [UTF-8] (original)
+++ felix/trunk/webconsole/src/main/resources/OSGI-INF/l10n/bundle_bg.properties [UTF-8] Wed Aug 15 12:48:53 2012
@@ -78,6 +78,10 @@ services.details.tip=Детайли
 services.statusline=Информация за услугите: {0} услуги.
 services.caption=Услуги
 services.usingBundles=Използващи бъндъли
+services.info.name=Използвани услуги
+services.info.descr=Тази услуга се използва от избраният бъндъл. Кликнете за да видите повече детайли в плъгин "Услуги"
+services.info.key=Услуга #{0} от тип(ове) {1}
+
 
 # Log plugin
 logs.pluginTitle=Журнал

Modified: felix/trunk/webconsole/src/main/resources/res/ui/bundles.js
URL: http://svn.apache.org/viewvc/felix/trunk/webconsole/src/main/resources/res/ui/bundles.js?rev=1373379&r1=1373378&r2=1373379&view=diff
==============================================================================
--- felix/trunk/webconsole/src/main/resources/res/ui/bundles.js (original)
+++ felix/trunk/webconsole/src/main/resources/res/ui/bundles.js Wed Aug 15 12:48:53 2012
@@ -175,38 +175,63 @@ function renderDetails( data ) {
     var details = data.props;
     for (var idx in details) {
         var prop = details[idx];
-		var key = i18n[prop.key] ? i18n[prop.key] : prop.key;
-
-        var txt = "<tr><td class='aligntop' noWrap='true' style='border:0px none'>" + key + "</td><td class='aligntop' style='border:0px none'>";          
-        if (prop.value) {
-            if ( prop.key == 'Bundle Documentation' )  {
-                txt = txt + "<a href='" + prop.value + "' target='_blank'>" + prop.value + "</a>";
-            } else  {
-                if ( $.isArray(prop.value) ) {
-                    var i = 0;
-                    for(var pi in prop.value) {
-                        var value = prop.value[pi];
-                        if (i > 0) { txt = txt + "<br/>"; }
-		                var span;
-		                if (value.substring(0, 6) == "INFO: ") {
-		                	txt = txt + "<span class='ui-state-info-text'>" + value.substring(5) + "</span>";
-		                } else if (value.substring(0, 7) == "ERROR: ") {
-		                	txt = txt + "<span class='ui-state-error-text'>" + value.substring(6) + "</span>";
-		                } else {
-		                	txt = txt + value;
-		                }
-                        i++;
-                    }
-                } else {
-                    txt = txt + prop.value;
+        
+        if (prop.key == 'nfo') {
+        	$.each(prop.value, function(name, bundleInfo) {
+        		var txt = '';
+        		$.each(bundleInfo, function(idx, ie) {
+        			txt += '<div title="' + makeSafe(ie.description) + '">';
+        			if (ie.type == 'link' || ie.type == 'resource') {
+        				txt += '<a href="' + ie.value + '">' + ie.name + '</a>';
+        			} else {
+        				txt += ie.name + " = " + ie.value;
+        			}
+        			txt += '</div>';
+        		});
+            	$("#pluginInlineDetails" + data.id + " > table > tbody").append( 
+                		renderDetailsEntry(name, txt) );
+        	});
+        } else 
+        	$("#pluginInlineDetails" + data.id + " > table > tbody").append( 
+        		renderDetailsEntry(prop.key, prop.value) );
+    }
+}
+function makeSafe(text) {
+	return text.replace(/\W/g, function (chr) {
+		return '&#' + chr.charCodeAt(0) + ';';
+	});
+};
+
+function renderDetailsEntry(key, value) {
+	var key18 = i18n[key] ? i18n[key] : key;
+	var txt = "<tr><td class='aligntop' noWrap='true' style='border:0px none'>" + key18 + "</td><td class='aligntop' style='border:0px none'>";          
+    if (value) {
+        if ( key == 'Bundle Documentation' )  {
+            txt += "<a href='" + value + "' target='_blank'>" + value + "</a>";
+        } else  {
+            if ( $.isArray(value) ) {
+                var i = 0;
+                for(var pi in value) {
+                    var xv = value[pi];
+                    if (i > 0) { txt = txt + "<br/>"; }
+	                var span;
+	                if (xv.substring(0, 6) == "INFO: ") {
+	                	txt += "<span class='ui-state-info-text'>" + xv.substring(5) + "</span>";
+	                } else if (xv.substring(0, 7) == "ERROR: ") {
+	                	txt += "<span class='ui-state-error-text'>" + xv.substring(6) + "</span>";
+	                } else {
+	                	txt +=  xv;
+	                }
+                    i++;
                 }
+            } else {
+                txt += value;
             }
-        } else {
-            txt = txt + "\u00a0";
         }
-        txt = txt + "</td></tr>";
-        $("#pluginInlineDetails" + data.id + " > table > tbody").append(txt);
+    } else {
+        txt += "\u00a0";
     }
+    return txt + "</td></tr>";
 }