| /* |
| * Licensed 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.karaf.subsystem.commands; |
| |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.TreeMap; |
| |
| import org.apache.felix.utils.manifest.Attribute; |
| import org.apache.felix.utils.manifest.Clause; |
| import org.apache.felix.utils.manifest.Directive; |
| import org.apache.felix.utils.manifest.Parser; |
| import org.apache.karaf.shell.api.action.Action; |
| import org.apache.karaf.shell.api.action.Argument; |
| import org.apache.karaf.shell.api.action.Command; |
| import org.apache.karaf.shell.api.action.Completion; |
| import org.apache.karaf.shell.api.action.Option; |
| import org.apache.karaf.shell.api.action.lifecycle.Reference; |
| import org.apache.karaf.shell.api.action.lifecycle.Service; |
| import org.apache.karaf.shell.api.console.Terminal; |
| import org.apache.karaf.shell.support.ShellUtil; |
| import org.osgi.service.subsystem.Subsystem; |
| import org.osgi.service.subsystem.SubsystemConstants; |
| |
| @Command(scope = "subsystem", name = "info", description = "Display information about subsystems") |
| @Service |
| public class InfoAction extends SubsystemSupport implements Action { |
| |
| protected final static String SUBSYSTEM_PREFIX = "Subsystem-"; |
| protected final static String PACKAGE_SUFFFIX = "-Package"; |
| protected final static String SERVICE_SUFFIX = "-Service"; |
| protected final static String CAPABILITY_SUFFIX = "-Capability"; |
| protected final static String IMPORT_PACKAGES_ATTRIB = "Import-Package"; |
| protected final static String REQUIRE_BUNDLE_ATTRIB = "Require-Bundle"; |
| |
| @Option(name = "--indent", description = "Indentation method") |
| int indent = -1; |
| |
| @Argument(description = "Subsystem names or ids") |
| @Completion(SubsystemCompleter.class) |
| String id; |
| |
| @Reference |
| Terminal terminal; |
| |
| @Override |
| public Object execute() throws Exception { |
| for (Subsystem ss : getSubsystems(id)) { |
| printHeaders(ss); |
| } |
| return null; |
| } |
| |
| protected void printHeaders(Subsystem subsystem) throws Exception { |
| String title = getSubsystemName(subsystem); |
| System.out.println("\n" + title); |
| System.out.println(ShellUtil.getUnderlineString(title)); |
| if (indent == 0) { |
| Map<String, String> dict = subsystem.getSubsystemHeaders(null); |
| for (String k : dict.keySet()) { |
| Object v = dict.get(k); |
| System.out.println(k + " = " + ShellUtil.getValueString(v)); |
| } |
| } else { |
| System.out.println(generateFormattedOutput(subsystem)); |
| } |
| } |
| |
| public static String getSubsystemName(Subsystem subsystem) { |
| String name = subsystem.getSubsystemHeaders(null).get(SubsystemConstants.SUBSYSTEM_NAME); |
| return (name == null) |
| ? "Subsystem " + Long.toString(subsystem.getSubsystemId()) |
| : name + " (" + Long.toString(subsystem.getSubsystemId()) + ")"; |
| } |
| |
| protected String generateFormattedOutput(Subsystem subsystem) { |
| StringBuilder output = new StringBuilder(); |
| Map<String, Object> otherAttribs = new TreeMap<String, Object>(); |
| Map<String, Object> subsystemAttribs = new TreeMap<String, Object>(); |
| Map<String, Object> serviceAttribs = new TreeMap<String, Object>(); |
| Map<String, Object> packagesAttribs = new TreeMap<String, Object>(); |
| Map<String, String> dict = subsystem.getSubsystemHeaders(null); |
| |
| // do an initial loop and separate the attributes in different groups |
| for (String k : dict.keySet()) { |
| Object v = dict.get(k); |
| if (k.startsWith(SUBSYSTEM_PREFIX)) { |
| // starts with Bundle-xxx |
| subsystemAttribs.put(k, v); |
| } else if (k.endsWith(SERVICE_SUFFIX) || k.endsWith(CAPABILITY_SUFFIX)) { |
| // ends with xxx-Service |
| serviceAttribs.put(k, v); |
| } else if (k.endsWith(PACKAGE_SUFFFIX)) { |
| // ends with xxx-Package |
| packagesAttribs.put(k, v); |
| } else if (k.endsWith(REQUIRE_BUNDLE_ATTRIB)) { |
| // require bundle statement |
| packagesAttribs.put(k, v); |
| } else { |
| // the remaining attribs |
| otherAttribs.put(k, v); |
| } |
| } |
| |
| // we will display the formatted result like this: |
| // Bundle-Name (ID) |
| // ----------------------- |
| // all other attributes |
| // |
| // all Subsystem attributes |
| // |
| // all Service attributes |
| // |
| // all Package attributes |
| Iterator<Map.Entry<String, Object>> it = otherAttribs.entrySet().iterator(); |
| while (it.hasNext()) { |
| Map.Entry<String, Object> e = it.next(); |
| output.append(String.format("%s = %s\n", e.getKey(), ShellUtil.getValueString(e.getValue()))); |
| } |
| if (otherAttribs.size() > 0) { |
| output.append('\n'); |
| } |
| |
| it = subsystemAttribs.entrySet().iterator(); |
| while (it.hasNext()) { |
| Map.Entry<String, Object> e = it.next(); |
| output.append(e.getKey()); |
| output.append(" = \n"); |
| formatHeader(ShellUtil.getValueString(e.getValue()), null, output, indent); |
| output.append("\n"); |
| } |
| if (subsystemAttribs.size() > 0) { |
| output.append('\n'); |
| } |
| |
| it = serviceAttribs.entrySet().iterator(); |
| while (it.hasNext()) { |
| Map.Entry<String, Object> e = it.next(); |
| output.append(e.getKey()); |
| output.append(" = \n"); |
| formatHeader(ShellUtil.getValueString(e.getValue()), null, output, indent); |
| output.append("\n"); |
| } |
| if (serviceAttribs.size() > 0) { |
| output.append('\n'); |
| } |
| |
| Map<String, ClauseFormatter> formatters = new HashMap<String, ClauseFormatter>(); |
| /* |
| formatters.put(REQUIRE_BUNDLE_ATTRIB, new ClauseFormatter() { |
| public void pre(Clause clause, StringBuilder output) { |
| boolean isSatisfied = checkBundle(clause.getName(), clause.getAttribute("bundle-version")); |
| Ansi.ansi(output).fg(isSatisfied ? Ansi.Color.DEFAULT : Ansi.Color.RED).a(""); |
| } |
| public void post(Clause clause, StringBuilder output) { |
| Ansi.ansi(output).reset().a(""); |
| } |
| }); |
| formatters.put(IMPORT_PACKAGES_ATTRIB, new ClauseFormatter() { |
| public void pre(Clause clause, StringBuilder output) { |
| boolean isSatisfied = checkPackage(clause.getName(), clause.getAttribute("version")); |
| boolean isOptional = "optional".equals(clause.getDirective("resolution")); |
| Ansi.ansi(output).fg(isSatisfied ? Ansi.Color.DEFAULT : Ansi.Color.RED) |
| .a(isSatisfied || isOptional ? Ansi.Attribute.INTENSITY_BOLD_OFF : Ansi.Attribute.INTENSITY_BOLD) |
| .a(""); |
| } |
| public void post(Clause clause, StringBuilder output) { |
| Ansi.ansi(output).reset().a(""); |
| } |
| }); |
| */ |
| |
| it = packagesAttribs.entrySet().iterator(); |
| while (it.hasNext()) { |
| Map.Entry<String, Object> e = it.next(); |
| output.append(e.getKey()); |
| output.append(" = \n"); |
| formatHeader(ShellUtil.getValueString(e.getValue()), formatters.get(e.getKey()), output, indent); |
| output.append("\n"); |
| } |
| if (packagesAttribs.size() > 0) { |
| output.append('\n'); |
| } |
| |
| return output.toString(); |
| } |
| |
| protected interface ClauseFormatter { |
| void pre(Clause clause, StringBuilder output); |
| void post(Clause clause, StringBuilder output); |
| } |
| |
| protected void formatHeader(String header, ClauseFormatter formatter, StringBuilder builder, int indent) { |
| Clause[] clauses = Parser.parseHeader(header); |
| formatClauses(clauses, formatter, builder, indent); |
| } |
| |
| protected void formatClauses(Clause[] clauses, ClauseFormatter formatter, StringBuilder builder, int indent) { |
| boolean first = true; |
| for (Clause clause : clauses) { |
| if (first) { |
| first = false; |
| } else { |
| builder.append(",\n"); |
| } |
| formatClause(clause, formatter, builder, indent); |
| } |
| } |
| |
| protected void formatClause(Clause clause, ClauseFormatter formatter, StringBuilder builder, int indent) { |
| builder.append("\t"); |
| if (formatter != null) { |
| formatter.pre(clause, builder); |
| } |
| formatClause(clause, builder, indent); |
| if (formatter != null) { |
| formatter.post(clause, builder); |
| } |
| } |
| |
| protected int getTermWidth() { |
| return terminal.getWidth(); |
| |
| } |
| |
| protected void formatClause(Clause clause, StringBuilder builder, int indent) { |
| if (indent < 0) { |
| if (clause.toString().length() < getTermWidth() - 8) { // -8 for tabs |
| indent = 1; |
| } else { |
| indent = 3; |
| } |
| } |
| String name = clause.getName(); |
| Directive[] directives = clause.getDirectives(); |
| Attribute[] attributes = clause.getAttributes(); |
| Arrays.sort(directives, new Comparator<Directive>() { |
| public int compare(Directive o1, Directive o2) { |
| return o1.getName().compareTo(o2.getName()); |
| } |
| }); |
| Arrays.sort(attributes, new Comparator<Attribute>() { |
| public int compare(Attribute o1, Attribute o2) { |
| return o1.getName().compareTo(o2.getName()); |
| } |
| }); |
| builder.append(name); |
| for (int i = 0; directives != null && i < directives.length; i++) { |
| builder.append(";"); |
| if (indent > 1) { |
| builder.append("\n\t\t"); |
| } |
| builder.append(directives[i].getName()).append(":="); |
| String v = directives[i].getValue(); |
| if (v.contains(",")) { |
| if (indent > 2 && v.length() > 20) { |
| v = v.replace(",", ",\n\t\t\t"); |
| } |
| builder.append("\"").append(v).append("\""); |
| } else { |
| builder.append(v); |
| } |
| } |
| for (int i = 0; attributes != null && i < attributes.length; i++) { |
| builder.append(";"); |
| if (indent > 1) { |
| builder.append("\n\t\t"); |
| } |
| builder.append(attributes[i].getName()).append("="); |
| String v = attributes[i].getValue(); |
| if (v.contains(",")) { |
| if (indent > 2 && v.length() > 20) { |
| v = v.replace(",", ",\n\t\t\t"); |
| } |
| builder.append("\"").append(v).append("\""); |
| } else { |
| builder.append(v); |
| } |
| } |
| } |
| |
| |
| /* |
| private boolean checkBundle(String bundleName, String version) { |
| VersionRange vr = VersionRange.parseVersionRange(version); |
| Bundle[] bundles = bundleContext.getBundles(); |
| for (int i = 0; (bundles != null) && (i < bundles.length); i++) { |
| String sym = bundles[i].getSymbolicName(); |
| if ((sym != null) && sym.equals(bundleName)) { |
| if (vr.contains(bundles[i].getVersion())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private boolean checkPackage(String packageName, String version) { |
| VersionRange range = VersionRange.parseVersionRange(version); |
| Bundle[] bundles = bundleContext.getBundles(); |
| for (int i = 0; (bundles != null) && (i < bundles.length); i++) { |
| BundleWiring wiring = bundles[i].adapt(BundleWiring.class); |
| List<BundleCapability> caps = wiring != null ? wiring.getCapabilities(BundleRevision.PACKAGE_NAMESPACE) : null; |
| if (caps != null) { |
| for (BundleCapability cap : caps) { |
| String n = getAttribute(cap, BundleRevision.PACKAGE_NAMESPACE); |
| String v = getAttribute(cap, Constants.VERSION_ATTRIBUTE); |
| if (packageName.equals(n) && range.contains(VersionTable.getVersion(v))) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| private String getAttribute(BundleCapability cap, String name) { |
| Object obj = cap.getAttributes().get(name); |
| return obj != null ? obj.toString() : null; |
| } |
| */ |
| |
| } |