Fixed FELIX-3476 Make package admin to use SimpleWebConsolePlugin
https://issues.apache.org/jira/browse/FELIX-3476
Fixed FELIX-3474 Make package admin work with J9 and other embedded VMs
https://issues.apache.org/jira/browse/FELIX-3474
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1330237 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/Activator.java b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/Activator.java
index 01fc800..0f2cd85 100644
--- a/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/Activator.java
+++ b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/Activator.java
@@ -21,64 +21,106 @@
import java.util.Dictionary;
import java.util.Hashtable;
-import org.osgi.framework.*;
+import org.apache.felix.webconsole.ConfigurationPrinter;
+import org.apache.felix.webconsole.SimpleWebConsolePlugin;
+import org.apache.felix.webconsole.WebConsoleConstants;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
-public class Activator implements BundleActivator
+/**
+ * This is the main starting class of the bundle.
+ */
+public class Activator implements BundleActivator, ServiceTrackerCustomizer
{
private ServiceTracker pkgAdminTracker;
- private ServiceRegistration pkgAdminPlugin;
+ private BundleContext context;
+ private SimpleWebConsolePlugin plugin;
+ private ServiceRegistration printerReg;
- private ServiceRegistration depFinderPlugin;
-
+ /**
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
public void start(final BundleContext context) throws Exception
{
- this.pkgAdminTracker = new ServiceTracker(context, "org.osgi.service.packageadmin.PackageAdmin", null);
+ this.context = context;
+ this.pkgAdminTracker = new ServiceTracker(context,
+ "org.osgi.service.packageadmin.PackageAdmin", this); //$NON-NLS-1$
this.pkgAdminTracker.open();
- registerPackageAdminPlugin(context);
- registerDependencyFinderPlugin(context);
+ // register configuration printer
+ final Dictionary/*<String, Object>*/props = new Hashtable/*<String, Object>*/();
+ props.put(WebConsoleConstants.CONFIG_PRINTER_MODES, new String[] {
+ ConfigurationPrinter.MODE_ZIP, ConfigurationPrinter.MODE_TXT });
+ printerReg = context.registerService(
+ "org.apache.felix.webconsole.ConfigurationPrinter", //$NON-NLS-1$
+ new WebConsolePrinter(context, pkgAdminTracker), props);
}
- private void registerPackageAdminPlugin(final BundleContext context)
- {
- final PackageAdminPlugin plugin = new PackageAdminPlugin(context, pkgAdminTracker);
- final Dictionary<String, Object> props = new Hashtable<String, Object>();
- props.put("felix.webconsole.label", PackageAdminPlugin.LABEL);
- props.put("felix.webconsole.title", PackageAdminPlugin.TITLE);
- props.put("felix.webconsole.configprinter.modes", new String[]
- { "zip", "txt" });
- this.pkgAdminPlugin = context.registerService("javax.servlet.Servlet", plugin, props);
- }
-
- private void registerDependencyFinderPlugin(final BundleContext context)
- {
- final DependencyFinderPlugin plugin = new DependencyFinderPlugin(context, pkgAdminTracker);
- final Dictionary<String, Object> props = new Hashtable<String, Object>();
- props.put("felix.webconsole.label", DependencyFinderPlugin.LABEL);
- props.put("felix.webconsole.title", DependencyFinderPlugin.TITLE);
- this.depFinderPlugin = context.registerService("javax.servlet.Servlet", plugin, props);
- }
-
+ /**
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
public void stop(final BundleContext context) throws Exception
{
- if (this.pkgAdminPlugin != null)
+ if (printerReg != null)
{
- this.pkgAdminPlugin.unregister();
- this.pkgAdminPlugin = null;
+ printerReg.unregister();
+ printerReg = null;
}
- if (this.depFinderPlugin != null)
- {
- this.depFinderPlugin.unregister();
- this.depFinderPlugin = null;
- }
+
if (this.pkgAdminTracker != null)
{
this.pkgAdminTracker.close();
this.pkgAdminTracker = null;
}
+
+ this.context = null;
+ }
+
+ // - begin tracker
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference,
+ * java.lang.Object)
+ */
+ public final void modifiedService(ServiceReference reference, Object service)
+ {/* unused */
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
+ */
+ public final Object addingService(ServiceReference reference)
+ {
+ SimpleWebConsolePlugin plugin = this.plugin;
+ Object ret = null;
+ if (plugin == null)
+ {
+ ret = context.getService(reference);
+ this.plugin = plugin = new WebConsolePlugin(context, ret).register(context);
+ }
+
+ return ret;
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(org.osgi.framework.ServiceReference,
+ * java.lang.Object)
+ */
+ public final void removedService(ServiceReference reference, Object service)
+ {
+ SimpleWebConsolePlugin plugin = this.plugin;
+
+ if (pkgAdminTracker.size() <= 1 && plugin != null)
+ {
+ plugin.unregister();
+ this.plugin = null;
+ }
+
}
}
diff --git a/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/DependencyFinderPlugin.java b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/DependencyFinderPlugin.java
deleted file mode 100644
index 3f2e614..0000000
--- a/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/DependencyFinderPlugin.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * 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.plugins.packageadmin.internal;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.util.tracker.ServiceTracker;
-
-@SuppressWarnings("serial")
-public class DependencyFinderPlugin extends HttpServlet
-{
-
- static final String LABEL = "depfinder";
-
- static final String TITLE = "Dependency Finder";
-
- private static final String PARAM_FIND = "plugin.find";
-
- private static final String PARAM_SUBMIT = "plugin.submit";
-
- private static final String INDENT = " ";
-
- private final BundleContext bundleContext;
-
- private final ServiceTracker pkgAdminTracker;
-
- DependencyFinderPlugin(final BundleContext bundleContext, final ServiceTracker pkgAdminTracker)
- {
- this.bundleContext = bundleContext;
- this.pkgAdminTracker = pkgAdminTracker;
- }
-
- private void drawForm(final PrintWriter pw, String findField)
- {
- titleHtml(pw, "Dependency Finder", "Enter a list of package or class names");
- pw.println("<tr>");
- pw.println("<td>Packages/Classes</td>");
- pw.print("<td colspan='2'>");
- pw.print("<form method='GET'>");
- pw.println("<textarea rows='10' cols='80' name='" + PARAM_FIND + "'>" + (findField != null ? findField : "")
- + "</textarea>");
- pw.println(" <input type='submit' name='" + PARAM_SUBMIT + "' value='Find' class='submit'>");
- pw.print("</form>");
- pw.print("</td>");
- pw.println("</tr>");
- }
-
- private void endTable(final PrintWriter pw)
- {
- pw.println("</table>");
- }
-
- private void startTable(final PrintWriter pw)
- {
- pw.println("<table class='nicetable'>");
- }
-
- private void titleHtml(PrintWriter pw, String title, String description)
- {
- pw.println("<tr>");
- pw.println("<th colspan='3'>" + title + "</th>");
- pw.println("</tr>");
-
- if (description != null)
- {
- pw.println("<tr>");
- pw.println("<td colspan='3'>" + description + "</th>");
- pw.println("</tr>");
- }
- }
-
- private void printStatLine(final PrintWriter pw, final String format, final Object... args)
- {
- final String message = String.format(format, args);
- pw.printf("<p class=\"statline\">%s</p>%n", message);
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
- {
- final PrintWriter pw = resp.getWriter();
-
- final PackageAdmin pa = (PackageAdmin) this.pkgAdminTracker.getService();
- if (pa == null)
- {
- printStatLine(pw, "PackageAdmin Service not registered");
- return;
- }
-
- printStatLine(pw, "Find exported packages and classes");
-
- startTable(pw);
- String findField = req.getParameter(PARAM_FIND);
- if (findField != null)
- {
- SortedSet<String> packageNames = getPackageNames(findField);
- if (!packageNames.isEmpty())
- {
- Set<Bundle> exportingBundles = new LinkedHashSet<Bundle>();
-
- titleHtml(pw, "Packages", "Here are the packages you entered and by which bundle they are exported.");
- for (String packageName : packageNames)
- {
- pw.println("<tr>");
- pw.print("<td>");
- pw.print(packageName);
- pw.print("</td>");
-
- pw.print("<td colspan='2'>");
- ExportedPackage[] exports = pa.getExportedPackages(packageName);
- ExportedPackage export = pa.getExportedPackage(packageName);
- if (export == null)
- {
- pw.print("<span style='color: red;'>NOT EXPORTED</span>");
- }
- else
- {
- Bundle exportingBundle = export.getExportingBundle();
- pw.printf(
- "<a href='/system/console/bundles/%s' style='text-decoration: none; color: #555;'>%s (%s)</a>",
- exportingBundle.getBundleId(), exportingBundle.getSymbolicName(),
- exportingBundle.getBundleId());
- if (exports.length > 1)
- {
- pw.printf(" and %s others.", exports.length - 1);
- }
- exportingBundles.add(exportingBundle);
-
- }
- pw.print("</td>");
-
- pw.println("</tr>");
- }
-
- titleHtml(pw, "Maven Dependencies", "Here are the bundles listed above as Maven dependencies");
- pw.println("<tr>");
- pw.print("<td colspan='3'>");
- pw.println("<pre>");
- for (Bundle bundle : exportingBundles)
- {
- Enumeration<?> entries = bundle.findEntries("META-INF/maven", "pom.properties", true);
- if (entries != null)
- {
- URL u = (URL) entries.nextElement();
- java.util.Properties props = new java.util.Properties();
- InputStream is = null;
- try
- {
- is = u.openStream();
- props.load(u.openStream());
-
- indent(pw, 2, "<dependency>");
- indent(pw, 3, String.format("<groupId>%s</groupId>", props.get("groupId")));
- indent(pw, 3, String.format("<artifactId>%s</artifactId>", props.get("artifactId")));
- indent(pw, 3, String.format("<version>%s</version>", props.get("version")));
- indent(pw, 3, "<scope>provided</scope>");
- indent(pw, 2, "</dependency>");
- }
- catch (IOException e)
- {
- }
- finally
- {
- if (is != null)
- {
- is.close();
- }
- }
- }
- }
- pw.print("</pre>");
- pw.print("</td>");
- pw.println("</tr>");
- }
- }
- drawForm(pw, findField);
- endTable(pw);
- }
-
- private void indent(PrintWriter pw, int count, String string)
- {
- for (int i = 0; i < count; i++)
- {
- pw.print(INDENT);
- }
- pw.println(string.replace("<", "<").replace(">", ">"));
- }
-
- static SortedSet<String> getPackageNames(String findField)
- {
- String[] parts = findField.split("\\s");
- SortedSet<String> result = new TreeSet<String>();
- for (String part : parts)
- {
- part = part.trim();
- if (part.length() > 0)
- {
- int idx = part.lastIndexOf('.');
- if (idx != -1)
- {
- char firstCharAfterLastDot = part.charAt(idx + 1);
- if (Character.isUpperCase(firstCharAfterLastDot))
- {
- result.add(part.substring(0, idx));
- }
- else
- {
- result.add(part);
- }
- }
- }
- }
- return result;
- }
-
-}
diff --git a/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/ExportedPackageComparator.java b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/ExportedPackageComparator.java
new file mode 100644
index 0000000..1d63fc3
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/ExportedPackageComparator.java
@@ -0,0 +1,54 @@
+/*
+ * 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.plugins.packageadmin.internal;
+
+import java.util.Comparator;
+
+import org.osgi.service.packageadmin.ExportedPackage;
+
+final class ExportedPackageComparator implements Comparator/*<ExportedPackage>*/
+{
+
+ public int compare(Object _o1, Object _o2)
+ {
+ ExportedPackage o1 = (ExportedPackage) _o1;
+ ExportedPackage o2 = (ExportedPackage) _o2;
+ if (o1 == o2)
+ {
+ return 0;
+ }
+
+ int name = o1.getName().compareTo(o2.getName());
+ if (name != 0)
+ {
+ return name;
+ }
+
+ int version = o1.getVersion().compareTo(o2.getVersion());
+ if (version != 0)
+ {
+ return version;
+ }
+
+ final long o1bid = o1.getExportingBundle().getBundleId();
+ final long o2bid = o2.getExportingBundle().getBundleId();
+ return (o1bid < o2bid ? -1 : (o1bid == o2bid ? 0 : 1));
+ }
+
+}
\ No newline at end of file
diff --git a/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/PackageAdminPlugin.java b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/PackageAdminPlugin.java
deleted file mode 100644
index df886f6..0000000
--- a/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/PackageAdminPlugin.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * 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.plugins.packageadmin.internal;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.*;
-import java.util.Map.Entry;
-
-import javax.servlet.*;
-import javax.servlet.http.HttpServletResponse;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.util.tracker.ServiceTracker;
-
-@SuppressWarnings("serial")
-public class PackageAdminPlugin extends GenericServlet
-{
-
- static final String LABEL = "pkgadmin";
-
- static final String TITLE = "Package Admin";
-
- private static final Comparator<ExportedPackage> EXPORT_PACKAGE_COMPARATOR = new ExportedPackageComparator();
-
- private final BundleContext bundleContext;
-
- private final ServiceTracker pkgAdminTracker;
-
- PackageAdminPlugin(final BundleContext bundleContext, final ServiceTracker pkgAdminTracker)
- {
- this.bundleContext = bundleContext;
- this.pkgAdminTracker = pkgAdminTracker;
- }
-
- public void service(ServletRequest req, ServletResponse res) throws IOException
- {
-
- final PrintWriter pw = ((HttpServletResponse) res).getWriter();
-
- final PackageAdmin pa = (PackageAdmin) this.pkgAdminTracker.getService();
- if (pa == null)
- {
- printStatLine(pw, "PackageAdmin Service not registered");
- return;
- }
-
- try
- {
- Map<String, Set<ExportedPackage>> exports = collectExportedPackages(pa);
- printStatLine(pw, "PackageAdmin service reports %d exported packages", exports.size());
- dumpDuplicates(pw, exports);
- }
- catch (Exception e)
- {
- pw.println("failure ...." + e);
- e.printStackTrace(pw);
- }
- }
-
- private Map<String, Set<ExportedPackage>> collectExportedPackages(final PackageAdmin pa)
- {
- Map<String, Set<ExportedPackage>> exports = new TreeMap<String, Set<ExportedPackage>>();
-
- for (Bundle bundle : this.bundleContext.getBundles())
- {
- final ExportedPackage[] bundleExports = pa.getExportedPackages(bundle);
- if (bundleExports != null)
- {
- for (ExportedPackage exportedPackage : bundleExports)
- {
- Set<ExportedPackage> exportSet = exports.get(exportedPackage.getName());
- if (exportSet == null)
- {
- exportSet = new TreeSet<ExportedPackage>(EXPORT_PACKAGE_COMPARATOR);
- exports.put(exportedPackage.getName(), exportSet);
- }
- exportSet.add(exportedPackage);
- }
- }
- }
-
- return exports;
- }
-
- private void printStatLine(final PrintWriter pw, final String format, final Object... args)
- {
- final String message = String.format(format, args);
- pw.printf("<p class=\"statline\">%s</p>%n", message);
- }
-
- private void printTitle(final PrintWriter pw, final String format, final Object... args)
- {
- pw.println("<div class=\"ui-widget-header ui-corner-top\">");
- pw.printf(format, args);
- pw.println("</div>");
- }
-
- private void dumpDuplicates(final PrintWriter pw, final Map<String, Set<ExportedPackage>> exports)
- {
- printTitle(pw, "Duplicate Exported Packages");
- pw.println("<table class=\"nicetable\">");
- pw.println("<tr><th>Package</th><th>Exports</th><th>Imports</th></tr>");
- for (Entry<String, Set<ExportedPackage>> exportEntry : exports.entrySet())
- {
- Set<ExportedPackage> exportSet = exportEntry.getValue();
- if (exportSet.size() > 1)
- {
- String firstCol = String.format("<td rowspan=\"%s\">%s</td>", exportSet.size(), exportEntry.getKey());
- for (ExportedPackage exportedPackage : exportSet)
- {
- Bundle[] importers = exportedPackage.getImportingBundles();
- pw.printf("<tr>%s<td>version=%s, Bundle %s</td><td>", firstCol, exportedPackage.getVersion(),
- exportedPackage.getExportingBundle());
- if (importers != null && importers.length > 0)
- {
- for (Bundle bundle : importers)
- {
- pw.printf("%s<br>", bundle);
- }
- }
- else
- {
- pw.print(" ");
- }
- pw.println("</td></tr>");
- firstCol = "";
- }
- }
- }
- pw.println("</table>");
- }
-
- private void dumpDuplicatesAsTxt(final PrintWriter pw, final Map<String, Set<ExportedPackage>> exports)
- {
- pw.println("Duplicate Exported Packages");
- pw.println("---------------------------");
- final List<String[]> lines = new ArrayList<String[]>();
- lines.add(new String[]
- { "Package", "Exports", "Imports" });
-
- for (Entry<String, Set<ExportedPackage>> exportEntry : exports.entrySet())
- {
- final Set<ExportedPackage> exportSet = exportEntry.getValue();
- if (exportSet.size() > 1)
- {
- String firstCol = exportEntry.getKey();
- for (ExportedPackage exportedPackage : exportSet)
- {
- final Bundle[] importers = exportedPackage.getImportingBundles();
- final String secondCol = "version=" + exportedPackage.getVersion() + ", Bundle "
- + exportedPackage.getExportingBundle();
- if (importers != null && importers.length > 0)
- {
- boolean first = true;
- for (Bundle bundle : importers)
- {
- if (first)
- {
- lines.add(new String[]
- { firstCol, secondCol, bundle.toString() });
- first = false;
- }
- else
- {
- lines.add(new String[]
- { "", "", bundle.toString() });
-
- }
- }
- }
- else
- {
- lines.add(new String[]
- { firstCol, secondCol, "" });
- }
- firstCol = "";
- }
- }
- }
- int maxFirst = 0, maxSecond = 0;
- for (final String[] entry : lines)
- {
- if (entry[0].length() > maxFirst)
- {
- maxFirst = entry[0].length();
- }
- if (entry[1].length() > maxSecond)
- {
- maxSecond = entry[1].length();
- }
- }
- maxFirst += 2;
- maxSecond += 2;
- for (final String[] entry : lines)
- {
- padText(pw, entry[0], maxFirst);
- padText(pw, entry[1], maxSecond);
- pw.println(entry[2]);
- }
- }
-
- private void padText(final PrintWriter pw, final String text, final int length)
- {
- pw.print(text);
- final int padLength = length - text.length();
- for (int i = 0; i < padLength; i++)
- {
- pw.print(' ');
- }
- }
-
- private static class ExportedPackageComparator implements Comparator<ExportedPackage>
- {
-
- public int compare(ExportedPackage o1, ExportedPackage o2)
- {
- if (o1 == o2)
- {
- return 0;
- }
-
- int name = o1.getName().compareTo(o2.getName());
- if (name != 0)
- {
- return name;
- }
-
- int version = o1.getVersion().compareTo(o2.getVersion());
- if (version != 0)
- {
- return version;
- }
-
- return Long.valueOf(o1.getExportingBundle().getBundleId()).compareTo(o2.getExportingBundle().getBundleId());
- }
-
- }
-
- /**
- * Configuration printer
- */
- public void printConfiguration(final PrintWriter pw)
- {
- final PackageAdmin pa = (PackageAdmin) this.pkgAdminTracker.getService();
- if (pa == null)
- {
- pw.println("PackageAdmin Service not registered");
- return;
- }
-
- try
- {
- Map<String, Set<ExportedPackage>> exports = collectExportedPackages(pa);
-
- pw.print("PackageAdmin service reports ");
- pw.print(String.valueOf(exports.size()));
- pw.println(" exported packages.");
- pw.println();
-
- dumpDuplicatesAsTxt(pw, exports);
- }
- catch (Exception e)
- {
- pw.println("failure ...." + e);
- }
- }
-}
diff --git a/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/WebConsolePlugin.java b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/WebConsolePlugin.java
new file mode 100644
index 0000000..f85132c
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/WebConsolePlugin.java
@@ -0,0 +1,300 @@
+/*
+ * 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.plugins.packageadmin.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.felix.webconsole.SimpleWebConsolePlugin;
+import org.apache.felix.webconsole.WebConsoleUtil;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Provides a Web bases interface to the Packages Admin service, allowing
+ * the user to find package / maven information and identify duplicate exports.
+ */
+class WebConsolePlugin extends SimpleWebConsolePlugin
+{
+
+ private static final String LABEL = "depfinder"; //$NON-NLS-1$
+ private static final String TITLE = "%pluginTitle"; //$NON-NLS-1$
+ private static final String CSS[] = { "/" + LABEL + "/res/plugin.css" }; //$NON-NLS-1$ //$NON-NLS-2$
+
+ private static final Comparator/*<ExportedPackage>*/EXPORT_PACKAGE_COMPARATOR = new ExportedPackageComparator();
+
+ private final PackageAdmin pa;
+ private final BundleContext bc;
+
+ // templates
+ private final String TEMPLATE;
+
+ WebConsolePlugin(BundleContext bc, Object pa)
+ {
+ super(LABEL, TITLE, CSS);
+
+ this.pa = (PackageAdmin) pa;
+ this.bc = bc;
+
+ // load templates
+ TEMPLATE = readTemplateFile("/res/plugin.html"); //$NON-NLS-1$
+ }
+
+ /**
+ * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(HttpServletRequest, HttpServletResponse)
+ */
+ protected final void renderContent(HttpServletRequest req,
+ HttpServletResponse response) throws ServletException, IOException
+ {
+ response.getWriter().print(TEMPLATE);
+ }
+
+ /**
+ * @see HttpServlet#doPost(HttpServletRequest, HttpServletResponse)
+ */
+ protected final void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException
+ {
+ final Object json;
+
+ try
+ {
+ String action = req.getParameter("action"); //$NON-NLS-1$
+ if ("deps".equals(action)) { //$NON-NLS-1$
+ json = doFindDependencies(req, pa);
+ }
+ else if ("dups".equals(action)) { //$NON-NLS-1$
+ Map/*<String, Set<ExportedPackage>>*/packages = collectExportedPackages(
+ pa, bc);
+ json = doFindDuplicates(packages);
+ }
+ else
+ {
+ throw new ServletException("Invalid action: " + action);
+ }
+ }
+ catch (JSONException e)
+ {
+ throw new ServletException(e);
+ }
+
+ WebConsoleUtil.setNoCache(resp);
+ resp.setContentType("application/json; utf-8"); //$NON-NLS-1$
+ resp.getWriter().println(json);
+ }
+
+ static final Map/*<String, Set<ExportedPackage>>*/collectExportedPackages(
+ final PackageAdmin pa, final BundleContext bundleContext)
+ {
+ Map/*<String, Set<ExportedPackage>>*/exports = new TreeMap/*<String, Set<ExportedPackage>>*/();
+
+ Bundle[] bundles = bundleContext.getBundles();
+ for (int i = 0; bundles != null && i < bundles.length; i++)
+ {
+ final Bundle bundle = bundles[i];
+ final ExportedPackage[] bundleExports = pa.getExportedPackages(bundle);
+ for (int j = 0; bundleExports != null && j < bundleExports.length; j++)
+ {
+ final ExportedPackage exportedPackage = bundleExports[j];
+ Set/*<ExportedPackage>*/exportSet = (Set) exports.get(exportedPackage.getName());
+ if (exportSet == null)
+ {
+ exportSet = new TreeSet/*<ExportedPackage>*/(
+ EXPORT_PACKAGE_COMPARATOR);
+ exports.put(exportedPackage.getName(), exportSet);
+ }
+ exportSet.add(exportedPackage);
+ }
+ }
+
+ return exports;
+ }
+
+ private static final JSONObject doFindDependencies(HttpServletRequest req,
+ PackageAdmin pa) throws JSONException
+ {
+ final JSONObject json = new JSONObject();
+
+ final String findField = req.getParameter("plugin.find"); //$NON-NLS-1$
+ if (findField != null)
+ {
+ Set/*<String>*/packageNames = getPackageNames(findField);
+ Set/*<Bundle>*/exportingBundles = new LinkedHashSet/*<Bundle>*/();
+
+ for (Iterator/*<String>*/i = packageNames.iterator(); i.hasNext();)
+ {
+ String name = (String) i.next();
+ json.append("packages", getPackageInfo(name, pa, exportingBundles)); //$NON-NLS-1$
+ }
+
+ final JSONObject mavenJson = new JSONObject();
+ json.put("maven", mavenJson); //$NON-NLS-1$
+ for (Iterator/*<Bundle>*/i = exportingBundles.iterator(); i.hasNext();)
+ {
+ Bundle bundle = (Bundle) i.next();
+ mavenJson.putOpt(//
+ String.valueOf(bundle.getBundleId()), //
+ getMavenInfo(bundle));
+ }
+ }
+
+ return json;
+
+ }
+
+ private static final JSONArray doFindDuplicates(
+ final Map/*<String, Set<ExportedPackage>>*/exports) throws JSONException
+ {
+ final JSONArray ret = new JSONArray();
+ for (Iterator entryIter = exports.entrySet().iterator(); entryIter.hasNext();)
+ {
+ Entry/*<String, Set<ExportedPackage>>*/exportEntry = (Entry) entryIter.next();
+ Set/*<ExportedPackage>*/exportSet = (Set) exportEntry.getValue();
+ if (exportSet.size() > 1)
+ {
+ final JSONObject container = new JSONObject();
+ ret.put(container);
+ for (Iterator packageIter = exportSet.iterator(); packageIter.hasNext();)
+ {
+ ExportedPackage exportedPackage = (ExportedPackage) packageIter.next();
+ final JSONObject json = toJSON(exportedPackage);
+ Bundle[] importers = exportedPackage.getImportingBundles();
+ for (int j = 0; importers != null && j < importers.length; j++)
+ {
+ json.append("importers", toJSON(importers[j], new JSONObject())); //$NON-NLS-1$
+ }
+ container//
+ .put("name", exportedPackage.getName()) //$NON-NLS-1$
+ .append("entries", json); //$NON-NLS-1$
+ }
+ }
+ }
+ return ret;
+ }
+
+ private static final JSONObject toJSON(Bundle bundle, JSONObject json)
+ throws JSONException
+ {
+ json.put("bid", bundle.getBundleId()); //$NON-NLS-1$
+ json.putOpt("bsn", bundle.getSymbolicName()); //$NON-NLS-1$
+ return json;
+ }
+
+ private static final JSONObject toJSON(final ExportedPackage pkg)
+ throws JSONException
+ {
+ final JSONObject ret = new JSONObject();
+ ret.put("version", pkg.getVersion()); //$NON-NLS-1$
+ return toJSON(pkg.getExportingBundle(), ret);
+ }
+
+ private static final JSONObject getPackageInfo(String packageName, PackageAdmin pa,
+ Set/*<Bundle>*/exportingBundles) throws JSONException
+ {
+ final JSONObject ret = new JSONObject();
+ final ExportedPackage[] exports = pa.getExportedPackages(packageName);
+ for (int i = 0; exports != null && i < exports.length; i++)
+ {
+ final ExportedPackage x = exports[i];
+ ret.append("exporters", toJSON(x)); //$NON-NLS-1$
+ exportingBundles.add(x.getExportingBundle());
+ }
+ return ret.put("name", packageName); //$NON-NLS-1$
+ }
+
+ private static final JSONObject getMavenInfo(Bundle bundle)
+ {
+ JSONObject ret = null;
+
+ Enumeration entries = bundle.findEntries("META-INF/maven", "pom.properties", true); //$NON-NLS-1$ //$NON-NLS-2$
+ if (entries != null)
+ {
+ URL u = (URL) entries.nextElement();
+ java.util.Properties props = new java.util.Properties();
+ InputStream is = null;
+ try
+ {
+ is = u.openStream();
+ props.load(u.openStream());
+
+ ret = new JSONObject(props);
+ }
+ catch (IOException e)
+ {
+ // ignore
+ }
+ finally
+ {
+ IOUtils.closeQuietly(is);
+ }
+ }
+ return ret;
+ }
+
+ static final Set/*<String>*/getPackageNames(String findField)
+ {
+ StringTokenizer tok = new StringTokenizer(findField, " \t\n\f\r"); //$NON-NLS-1$
+ SortedSet/*<String>*/result = new TreeSet/*<String>*/();
+ while (tok.hasMoreTokens())
+ {
+ final String part = tok.nextToken().trim();
+ if (part.length() > 0)
+ {
+ int idx = part.lastIndexOf('.');
+ if (idx != -1)
+ {
+ char firstCharAfterLastDot = part.charAt(idx + 1);
+ if (Character.isUpperCase(firstCharAfterLastDot))
+ {
+ result.add(part.substring(0, idx));
+ }
+ else
+ {
+ result.add(part);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/WebConsolePrinter.java b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/WebConsolePrinter.java
new file mode 100644
index 0000000..aba1264
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/WebConsolePrinter.java
@@ -0,0 +1,170 @@
+/*
+ * 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.plugins.packageadmin.internal;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.apache.felix.webconsole.ConfigurationPrinter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+class WebConsolePrinter implements ConfigurationPrinter
+{
+
+ private final ServiceTracker tracker;
+ private final BundleContext bc;
+
+ WebConsolePrinter(BundleContext bc, ServiceTracker tracker)
+ {
+ this.bc = bc;
+ this.tracker = tracker;
+ }
+
+ /**
+ * @see org.apache.felix.webconsole.ConfigurationPrinter#printConfiguration(java.io.PrintWriter)
+ */
+ public void printConfiguration(PrintWriter pw)
+ {
+ final PackageAdmin pa = (PackageAdmin) tracker.getService();
+ if (pa == null)
+ {
+ pw.println("Status: PackageAdmin Service not registered");
+ return;
+ }
+
+ try
+ {
+ Map/*<String, Set<ExportedPackage>>*/exports = WebConsolePlugin.collectExportedPackages(
+ pa, bc);
+
+ pw.print("Status: PackageAdmin service reports ");
+ pw.print(String.valueOf(exports.size()));
+ pw.println(" exported packages.");
+ pw.println();
+
+ dumpDuplicatesAsTxt(pw, exports);
+ }
+ catch (Exception e)
+ {
+ pw.println("failure ...." + e);
+ }
+ }
+
+ private void dumpDuplicatesAsTxt(final PrintWriter pw,
+ final Map/*<String, Set<ExportedPackage>>*/exports)
+ {
+ pw.println("Duplicate Exported Packages");
+ pw.println("---------------------------");
+ final List/*<String[]>*/lines = new ArrayList/*<String[]>*/();
+ lines.add(new String[] { "Package", "Exports", "Imports" });
+
+ for (Iterator/*<Entry<String, Set<ExportedPackage>>>*/entriesIter = exports.entrySet().iterator(); entriesIter.hasNext();)
+ {
+ Entry/*<String, Set<ExportedPackage>>*/exportEntry = (Entry) entriesIter.next();
+
+ final Set/*<ExportedPackage>*/exportSet = (Set) exportEntry.getValue();
+ if (exportSet.size() > 1)
+ {
+ String firstCol = (String) exportEntry.getKey();
+ for (Iterator packageIter = exportSet.iterator(); packageIter.hasNext();)
+ {
+ ExportedPackage exportedPackage = (ExportedPackage) packageIter.next();
+ final Bundle[] importers = exportedPackage.getImportingBundles();
+ final String secondCol = "version=" + exportedPackage.getVersion()
+ + ", Bundle " + exportedPackage.getExportingBundle();
+ if (importers != null && importers.length > 0)
+ {
+ boolean first = true;
+ for (int j = 0; j < importers.length; j++)
+ {
+ final Bundle bundle = importers[j];
+ if (first)
+ {
+ lines.add(new String[] { firstCol, secondCol,
+ bundle.toString() });
+ first = false;
+ }
+ else
+ {
+ lines.add(new String[] { "", "", bundle.toString() });
+
+ }
+ }
+ }
+ else
+ {
+ lines.add(new String[] { firstCol, secondCol, "" });
+ }
+ firstCol = "";
+ }
+ }
+ }
+ int maxFirst = 0, maxSecond = 0;
+ for (int i = 0; i < lines.size(); i++)
+ {
+ final String[] entry = (String[]) lines.get(i);
+ if (entry[0].length() > maxFirst)
+ {
+ maxFirst = entry[0].length();
+ }
+ if (entry[1].length() > maxSecond)
+ {
+ maxSecond = entry[1].length();
+ }
+ }
+ maxFirst += 2;
+ maxSecond += 2;
+ for (int i = 0; i < lines.size(); i++)
+ {
+ final String[] entry = (String[]) lines.get(i);
+ padText(pw, entry[0], maxFirst);
+ padText(pw, entry[1], maxSecond);
+ pw.println(entry[2]);
+ }
+ }
+
+ private static final void padText(final PrintWriter pw, final String text,
+ final int length)
+ {
+ pw.print(text);
+ final int padLength = length - text.length();
+ for (int i = 0; i < padLength; i++)
+ {
+ pw.print(' ');
+ }
+ }
+
+ /**
+ * @see org.apache.felix.webconsole.ConfigurationPrinter#getTitle()
+ */
+ public String getTitle()
+ {
+ return "Duplicate Exports";
+ }
+
+}
diff --git a/webconsole-plugins/packageadmin/src/main/resources/OSGI-INF/l10n/bundle.properties b/webconsole-plugins/packageadmin/src/main/resources/OSGI-INF/l10n/bundle.properties
new file mode 100644
index 0000000..91c4163
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -0,0 +1,46 @@
+#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.
+
+#
+# Web Console strings for reference all strings here are commented.
+# This file may be used to produce a translation of the strings
+#
+# Note that properties files are ISO-8859-1 encoded. To provide translations
+# for languages requiring different character encodings, you may use the
+# native2ascii Maven Plugin from http://mojo.codehaus.org/native2ascii-maven-plugin/
+# to translate the natively encoded files to ISO-8859-1 during bundle build
+#
+# Translations requiring non-ISO-8859-1 encoding are placed in the
+# src/main/native2ascii/OSGI-INF/l10n folder and are converted using said
+# plugin while building the bundle
+#
+pluginTitle=Packages
+find.label=Packages/Classes:
+find.btn=Find
+find.tip=Enter a list of package or class names
+
+header.package.name=Package
+header.package.ver=Version
+header.exporting.bundle=Exported by
+header.maven.deps=Maven Dependency
+no.exporters.found=No exporting bundle(s) found
+no.maven.found=No maven information available
+status.initial=Please enter a list of package or class names and click the find button
+status.find={0} result(s) found
+
+# duplicates
+find.dups=Find Duplicate Exports
+status.dups={0} duplicate package(s) found
+header.importing.bundle=Used by bundle(s)
diff --git a/webconsole-plugins/packageadmin/src/main/resources/OSGI-INF/l10n/bundle_bg.properties b/webconsole-plugins/packageadmin/src/main/resources/OSGI-INF/l10n/bundle_bg.properties
new file mode 100644
index 0000000..8b96e6a
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/main/resources/OSGI-INF/l10n/bundle_bg.properties
@@ -0,0 +1,46 @@
+#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.
+
+#
+# Web Console strings for reference all strings here are commented.
+# This file may be used to produce a translation of the strings
+#
+# Note that properties files are ISO-8859-1 encoded. To provide translations
+# for languages requiring different character encodings, you may use the
+# native2ascii Maven Plugin from http://mojo.codehaus.org/native2ascii-maven-plugin/
+# to translate the natively encoded files to ISO-8859-1 during bundle build
+#
+# Translations requiring non-ISO-8859-1 encoding are placed in the
+# src/main/native2ascii/OSGI-INF/l10n folder and are converted using said
+# plugin while building the bundle
+#
+pluginTitle=Java пакети
+find.label=Пакети/класове:
+find.btn=Търсене
+find.tip=Въведете списък с пакети или имена на класове
+
+header.package.name=Пакет
+header.package.ver=Версия
+header.exporting.bundle=Предостъпен от
+header.maven.deps=Maven информация
+no.exporters.found=Няма бъндъли предотставящи този пакет
+no.maven.found=Няма достъпна Maven информация
+status.initial=Моля въведете списък с пакети или имена на класове и натиснете бутона "Търсене"
+status.find=Открити са {0} резултата
+
+# duplicates
+find.dups=Търсене на дублирани пакети
+status.dups=Открити са {0} дуплирани пакета
+header.importing.bundle=Използван от бъндъл(и)
diff --git a/webconsole-plugins/packageadmin/src/main/resources/res/plugin.css b/webconsole-plugins/packageadmin/src/main/resources/res/plugin.css
new file mode 100644
index 0000000..474a443
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/main/resources/res/plugin.css
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+td.mvn { white-space: pre-wrap }
+#findField { width: 50% }
diff --git a/webconsole-plugins/packageadmin/src/main/resources/res/plugin.html b/webconsole-plugins/packageadmin/src/main/resources/res/plugin.html
new file mode 100644
index 0000000..d4fbae3
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/main/resources/res/plugin.html
@@ -0,0 +1,63 @@
+<script type="text/javascript" src="${pluginRoot}/res/plugin.js"></script>
+<script type="text/javascript">
+// <![CDATA[
+var i18n = {
+ statusFind : '${status.find}',
+ statusDups : '${status.dups}'
+}
+// ]]>
+</script>
+
+<!-- status line -->
+<p class="statline">${status.initial}</p>
+
+<!-- table caption -->
+<form method="post" action="${pluginRoot}">
+ <div class="ui-widget-header ui-corner-top buttonGroup">
+ <span>${find.label}</span>
+ <input type="text" id="findField" title="${find.tip}" />
+ <button id="findButton">${find.btn}</button>
+ <button id="findDups">${find.dups}</button>
+ </div>
+</form>
+
+<!-- table find results -->
+<table class="tablesorter nicetable noauto" id="findTable">
+ <thead>
+ <tr>
+ <th>${header.package.name}</th>
+ <th>${header.package.ver}</th>
+ <th>${header.exporting.bundle}</th>
+ <th>${header.maven.deps}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="pkg"> </td>
+ <td class="ver">-</td>
+ <td class="bnd">${no.exporters.found}</td>
+ <td class="mvn">${no.maven.found}</td>
+ </tr>
+ </tbody>
+</table>
+
+<!-- duplicates -->
+<table class="nicetable noauto ui-helper-hidden" id="dupsTable">
+ <thead>
+ <tr>
+ <th>${header.package.name}</th>
+ <th>${header.package.ver}</th>
+ <th>${header.exporting.bundle}</th>
+ <th>${header.importing.bundle}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="pkg">-</td>
+ <td class="ver">-</td>
+ <td class="exp">-</td>
+ <td class="imp"> </td>
+ </tr>
+ </tbody>
+</table>
+
diff --git a/webconsole-plugins/packageadmin/src/main/resources/res/plugin.js b/webconsole-plugins/packageadmin/src/main/resources/res/plugin.js
new file mode 100644
index 0000000..8468103
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/main/resources/res/plugin.js
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+var findField = false;
+var findButton = false;
+var findTable = false;
+var findTableBody = false;
+var findTableTemplate = false;
+var dupsTable = false;
+var dupsTableBody = false;
+var dupsTableTemplate = false;
+var statline = false;
+
+function linkBundle(bnd) { return '<a href="{0}/bundles/{1}">{2} ({3})</a>'.msgFormat(appRoot, bnd.bid, bnd.bsn, bnd.bid) }
+
+$(function() {
+ findField = $('#findField');
+ statline = $('.statline');
+
+ findTable = $('#findTable').tablesorter({
+ headers:{
+ 1:{sorter: false},
+ 3:{sorter: false}
+ },
+ textExtraction:mixedLinksExtraction
+ });
+ findTableBody = findTable.find('tbody');
+ findTableTemplate = findTableBody.find('tr').clone();
+ findTableBody.empty();
+
+ dupsTable = $('#dupsTable');
+ dupsTableBody = dupsTable.find('tbody');
+ dupsTableTemplate = dupsTableBody.find('tr').clone();
+
+ $('#findButton').click(function() {
+ if(!findField.val()) {
+ findField.addClass('ui-state-error');
+ } else {
+ findField.removeClass('ui-state-error');
+
+ $.post(pluginRoot, { 'action': 'deps', 'plugin.find' : findField.val() }, function(response) {
+ dupsTable.addClass('ui-helper-hidden')
+ findTable.removeClass('ui-helper-hidden');
+ findTableBody.empty();
+ if (response.packages) for(var i in response.packages) {
+ var pkg = response.packages[i];
+ if (pkg.exporters) for(var i in pkg.exporters) {
+ var exp = pkg.exporters[i];
+ var tr = findTableTemplate.clone()
+ .find('td.pkg').text(pkg.name).end()
+ .find('td.ver').text(exp.version).end()
+ .find('td.bnd').html(linkBundle(exp)).end()
+ .appendTo(findTableBody);
+ if (response.maven && response.maven[exp.bid]) {
+ var mvn = response.maven[exp.bid];
+ mvn['scope'] = 'provided';
+ var txt = ''; for (var p in mvn) {
+ txt += '\t<' + p + '>' + mvn[p] + '</' + p + '>\n';
+ }
+ tr.find('td.mvn').text('<dependency>\n' + txt + '</dependency>');
+ }
+ } else {
+ var tr = findTableTemplate.clone()
+ .find('td.pkg').text(pkg.name).end()
+ .appendTo(findTableBody);
+ }
+ }
+ statline.text(i18n.statusFind.msgFormat(response.packages ? response.packages.length : 0));
+ findTable.trigger('update').trigger('applyWidgets')
+ }, 'json');
+ }
+ return false;
+ });
+
+ $('#findDups').click(function() {
+ $.post(pluginRoot, { 'action': 'dups' }, function(response) {
+ findTable.addClass('ui-helper-hidden');
+ dupsTable.removeClass('ui-helper-hidden')
+ dupsTableBody.empty();
+ if (response) for(var i in response) {
+ var pkg = response[i];
+ if (pkg.entries) for (var i in pkg.entries) {
+ var exp = pkg.entries[i];
+ var td = dupsTableTemplate.clone()
+ .find('td.pkg').text(pkg.name).end()
+ .find('td.ver').text(exp.version).end()
+ .find('td.exp').html(linkBundle(exp)).end();
+ if (exp.importers) {
+ var txt = ''; for(var j in exp.importers) txt += linkBundle(exp.importers[j]) + '<br/>';
+ td.find('td.imp').html( txt );
+ }
+ if (i==0) {
+ td.find('td.pkg').attr('rowspan', pkg.entries.length);
+ } else {
+ td.find('td.pkg').remove();
+ }
+ td.appendTo(dupsTableBody);
+ }
+ }
+ statline.text(i18n.statusDups.msgFormat(response ? response.length : 0));
+ }, 'json');
+
+ return false;
+ });
+
+})
diff --git a/webconsole-plugins/packageadmin/src/test/java/org/apache/felix/webconsole/plugins/packageadmin/internal/DependencyFinderPluginTest.java b/webconsole-plugins/packageadmin/src/test/java/org/apache/felix/webconsole/plugins/packageadmin/internal/DependencyFinderPluginTest.java
deleted file mode 100644
index 56531fa..0000000
--- a/webconsole-plugins/packageadmin/src/test/java/org/apache/felix/webconsole/plugins/packageadmin/internal/DependencyFinderPluginTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.plugins.packageadmin.internal;
-
-import java.util.Iterator;
-import java.util.SortedSet;
-
-import static org.junit.Assert.*;
-import org.junit.Test;
-
-public class DependencyFinderPluginTest
-{
-
- @Test
- public void single()
- {
- SortedSet<String> r = DependencyFinderPlugin.getPackageNames("com.foo.bar");
- assertEquals(1, r.size());
- assertEquals("com.foo.bar", r.first());
- }
-
- @Test
- public void singleClass()
- {
- SortedSet<String> r = DependencyFinderPlugin.getPackageNames("com.foo.bar.MyClass");
- assertEquals(1, r.size());
- assertEquals("com.foo.bar", r.first());
- }
-
- @Test
- public void multiple()
- {
- SortedSet<String> r = DependencyFinderPlugin
- .getPackageNames("com.foo.bar\ncom.foo.bar2\ncom.foo.bar3.MyClass\ncom.foo.bar.MyClass");
- assertEquals(3, r.size());
- Iterator<String> it = r.iterator();
- assertEquals("com.foo.bar", it.next());
- assertEquals("com.foo.bar2", it.next());
- assertEquals("com.foo.bar3", it.next());
- }
-
-}
\ No newline at end of file