blob: 38adaa551b1cceb86cd63cb658ad13116f6b3a98 [file] [log] [blame]
/*
* 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(optional = true)
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 != null ? terminal.getWidth() : 80;
}
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;
}
*/
}