Fixed FELIX-2896 Add support for bundle info providers
https://issues.apache.org/jira/browse/FELIX-2896
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1373379 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/pom.xml b/webconsole/pom.xml
index a066e12..ee94278 100644
--- a/webconsole/pom.xml
+++ b/webconsole/pom.xml
@@ -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,
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfo.java b/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfo.java
new file mode 100644
index 0000000..e7f320e
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfo.java
@@ -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;
+ }
+
+}
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
new file mode 100644
index 0000000..6a1f75a
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoProvider.java
@@ -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);
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoType.java b/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoType.java
new file mode 100644
index 0000000..bab6afa
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/bundleinfo/BundleInfoType.java
@@ -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><protocol>://</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");
+ }
+ }
+
+}
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
new file mode 100644
index 0000000..a29b6c1
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/i18n/LocalizationHelper.java
@@ -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);
+ }
+}
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 2bbdf49..43ed413 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
@@ -32,6 +32,9 @@
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.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.startlevel.StartLevel;
+import org.osgi.util.tracker.ServiceTracker;
/**
@@ -81,6 +85,7 @@
private boolean[] bootPkgWildcards;
private ServiceRegistration configurationPrinter;
+ private ServiceTracker bundleInfoTracker;
// templates
private final String TEMPLATE_MAIN;
@@ -100,6 +105,9 @@
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 @@
configurationPrinter.unregister();
configurationPrinter = null;
}
+
+ if ( bundleInfoTracker != null)
+ {
+ bundleInfoTracker.close();
+ bundleInfoTracker = null;
+ }
super.deactivate();
}
@@ -758,10 +772,53 @@
}
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 )
{
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 512f674..2a41111 100644
--- 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
@@ -38,9 +38,11 @@
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 @@
}
/** 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 @@
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();
}
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
new file mode 100644
index 0000000..fb784b0
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/core/ServicesUsedInfoProvider.java
@@ -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);
+ }
+
+}
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 bf3514e..9962d20 100644
--- 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,12 +33,12 @@
/**
* 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 @@
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 @@
}
- private Locale getParentLocale( Locale locale )
+ private static final Locale getParentLocale( Locale locale )
{
if ( locale.getVariant().length() != 0 )
{
diff --git a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
index 696504c..f6b8218 100644
--- a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
+++ b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -78,6 +78,9 @@
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
diff --git a/webconsole/src/main/resources/OSGI-INF/l10n/bundle_bg.properties b/webconsole/src/main/resources/OSGI-INF/l10n/bundle_bg.properties
index 2910a8a..fbf4136 100644
--- a/webconsole/src/main/resources/OSGI-INF/l10n/bundle_bg.properties
+++ b/webconsole/src/main/resources/OSGI-INF/l10n/bundle_bg.properties
@@ -78,6 +78,10 @@
services.statusline=Информация за услугите: {0} услуги.
services.caption=Услуги
services.usingBundles=Използващи бъндъли
+services.info.name=Използвани услуги
+services.info.descr=Тази услуга се използва от избраният бъндъл. Кликнете за да видите повече детайли в плъгин "Услуги"
+services.info.key=Услуга #{0} от тип(ове) {1}
+
# Log plugin
logs.pluginTitle=Журнал
diff --git a/webconsole/src/main/resources/res/ui/bundles.js b/webconsole/src/main/resources/res/ui/bundles.js
index d316b38..703b961 100644
--- a/webconsole/src/main/resources/res/ui/bundles.js
+++ b/webconsole/src/main/resources/res/ui/bundles.js
@@ -175,39 +175,64 @@
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;
- }
- }
- } else {
- txt = txt + "\u00a0";
- }
- txt = txt + "</td></tr>";
- $("#pluginInlineDetails" + data.id + " > table > tbody").append(txt);
+
+ 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 += "\u00a0";
+ }
+ return txt + "</td></tr>";
+}
$(document).ready(function(){