FELIX-3045 Apply patch by Justin Edelson (thanks alot) adding dependency finder functionality
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1187506 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 7c0d715..44c9b36 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
@@ -24,19 +24,24 @@
import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
-public class Activator implements BundleActivator
-{
+public class Activator implements BundleActivator {
private ServiceTracker pkgAdminTracker;
private ServiceRegistration pkgAdminPlugin;
- public void start(final BundleContext context) throws Exception
- {
+ private ServiceRegistration depFinderPlugin;
+
+ public void start(final BundleContext context) throws Exception {
this.pkgAdminTracker = new ServiceTracker(context,
"org.osgi.service.packageadmin.PackageAdmin", null);
this.pkgAdminTracker.open();
+ registerPackageAdminPlugin(context);
+ registerDependencyFinderPlugin(context);
+ }
+
+ private void registerPackageAdminPlugin(final BundleContext context) {
final PackageAdminPlugin plugin = new PackageAdminPlugin(context,
pkgAdminTracker);
final Dictionary<String, Object> props = new Hashtable<String, Object>();
@@ -47,15 +52,26 @@
plugin, props);
}
- public void stop(final BundleContext context) throws Exception
- {
- if ( this.pkgAdminPlugin != null )
- {
+ 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);
+ }
+
+ public void stop(final BundleContext context) throws Exception {
+ if ( this.pkgAdminPlugin != null ) {
this.pkgAdminPlugin.unregister();
this.pkgAdminPlugin = null;
}
- if ( this.pkgAdminTracker != null )
- {
+ if ( this.depFinderPlugin != null ) {
+ this.depFinderPlugin.unregister();
+ this.depFinderPlugin = null;
+ }
+ if ( this.pkgAdminTracker != null ) {
this.pkgAdminTracker.close();
this.pkgAdminTracker = 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
new file mode 100644
index 0000000..c021cd4
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/DependencyFinderPlugin.java
@@ -0,0 +1,213 @@
+/*
+ * 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.ServletException;
+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 class='content'>");
+ pw.println("<td class='content'>Packages/Classes</td>");
+ pw.print("<td class='content' 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='content' cellpadding='0' cellspacing='0' width='100%'>");
+ }
+
+ private void titleHtml(PrintWriter pw, String title, String description) {
+ pw.println("<tr class='content'>");
+ pw.println("<th colspan='3'class='content container'>" + title
+ + "</th>");
+ pw.println("</tr>");
+
+ if (description != null) {
+ pw.println("<tr class='content'>");
+ pw.println("<td colspan='3'class='content'>" + 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 ServletException, IOException {
+ final PrintWriter pw = resp.getWriter();
+
+ final PackageAdmin pa = (PackageAdmin) this.pkgAdminTracker.getService();
+ if (pa == null) {
+ printStatLine(pw, "PackageAdmin Service not registered");
+ return;
+ }
+
+ 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 class='content'>");
+ pw.print("<td class='content'>");
+ pw.print(packageName);
+ pw.print("</td>");
+
+ pw.print("<td class='content' 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 class='content'>");
+ pw.print("<td class='content' 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/PackageAdminPlugin.java b/webconsole-plugins/packageadmin/src/main/java/org/apache/felix/webconsole/plugins/packageadmin/internal/PackageAdminPlugin.java
index 19f1889..c39b23d 100644
--- 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
@@ -32,6 +32,7 @@
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.util.tracker.ServiceTracker;
+@SuppressWarnings("serial")
public class PackageAdminPlugin extends GenericServlet {
static final String LABEL = "pkgadmin";
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
new file mode 100644
index 0000000..db1b05f
--- /dev/null
+++ b/webconsole-plugins/packageadmin/src/test/java/org/apache/felix/webconsole/plugins/packageadmin/internal/DependencyFinderPluginTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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