org.apache.felix.gogo.command.Basic Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.felix.gogo.command Show documentation
Show all versions of org.apache.felix.gogo.command Show documentation
Provides basic shell commands for Gogo.
The newest version!
/*
* 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.gogo.command;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Formatter;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.apache.felix.service.command.CommandSession;
import org.apache.felix.service.command.Descriptor;
import org.apache.felix.service.command.Parameter;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleReference;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.startlevel.BundleStartLevel;
import org.osgi.framework.startlevel.FrameworkStartLevel;
import org.osgi.framework.wiring.FrameworkWiring;
import org.osgi.service.log.LogEntry;
import org.osgi.service.log.LogReaderService;
import org.osgi.service.log.LogService;
public class Basic
{
private final BundleContext m_bc;
private final Bundle m_b0;
public Basic(BundleContext bc)
{
m_bc = bc;
m_b0 = m_bc.getBundle(0);
}
@Descriptor("query bundle start level")
public String bundlelevel(@Descriptor("bundle to query") Bundle bundle)
{
return bundle + " is level " + bundle.adapt(BundleStartLevel.class).getStartLevel();
}
@Descriptor("set bundle start level or initial bundle start level")
public String bundlelevel(
@Descriptor("set the bundle's start level") @Parameter(names = { "-s",
"--setlevel" }, presentValue = "true", absentValue = "false") boolean set,
@Descriptor("set the initial bundle start level") @Parameter(names = { "-i",
"--setinitial" }, presentValue = "true", absentValue = "false") boolean initial,
@Descriptor("target level") int level,
@Descriptor("target identifiers") Bundle[] bundles)
{
if (set && initial)
{
return "Cannot specify '-s' and '-i' at the same time.";
}
else if (!set && !initial)
{
return "Must specify either '-s' or '-i'.";
}
else if (level <= 0)
{
return "Specified start level must be greater than zero.";
}
// Set the initial bundle start level.
else if (initial)
{
if ((bundles != null) && (bundles.length == 0))
{
m_b0.adapt(FrameworkStartLevel.class).setInitialBundleStartLevel(level);
}
else
{
return "Cannot specify bundles when setting initial start level.";
}
}
// Set the bundle start level.
else if (set)
{
if ((bundles != null) && (bundles.length != 0))
{
for (Bundle bundle : bundles)
{
bundle.adapt(BundleStartLevel.class).setStartLevel(level);
}
}
else
{
return "Must specify target bundles.";
}
}
return null;
}
@Descriptor("query framework active start level")
public String frameworklevel()
{
return "Level is " + m_b0.adapt(FrameworkStartLevel.class).getStartLevel();
}
@Descriptor("set framework active start level")
public void frameworklevel(@Descriptor("target start level") int level)
{
m_b0.adapt(FrameworkStartLevel.class).setStartLevel(level);
}
@Descriptor("display bundle headers")
public String headers(@Descriptor("target bundles") Bundle[] bundles)
{
try (Formatter f = new Formatter()) {
bundles = ((bundles == null) || (bundles.length == 0)) ? m_bc.getBundles()
: bundles;
String prefix = "";
for (Bundle bundle : bundles)
{
String title = Util.getBundleName(bundle);
f.format("%s%s%n", prefix, title);
f.format("%s%n", Util.getUnderlineString(title.length()));
Dictionary dict = bundle.getHeaders();
Enumeration keys = dict.keys();
while (keys.hasMoreElements())
{
String k = keys.nextElement();
String v = dict.get(k);
f.format("%s = %s%n", k, v);
}
prefix = "\n";
}
return f.toString();
}
}
@Descriptor("displays available commands")
public String help()
{
try (Formatter f = new Formatter()) {
Map> commands = getCommands();
for (String name : commands.keySet())
{
f.format("%s%n", name);
}
return f.toString();
}
}
@Descriptor("displays information about a specific command")
public String help(@Descriptor("target command") String name)
{
Map> commands = getCommands();
List methods = null;
// If the specified command doesn't have a scope, then
// search for matching methods by ignoring the scope.
int scopeIdx = name.indexOf(':');
if (scopeIdx < 0)
{
for (Entry> entry : commands.entrySet())
{
String k = entry.getKey().substring(entry.getKey().indexOf(':') + 1);
if (name.equals(k))
{
name = entry.getKey();
methods = entry.getValue();
break;
}
}
}
// Otherwise directly look up matching methods.
else
{
methods = commands.get(name);
}
if ((methods == null) || (methods.size() <= 0))
{
return "No methods found matching: " + name;
}
try (Formatter f = new Formatter()) {
String prefix = "";
for (Method m : methods)
{
Descriptor d = m.getAnnotation(Descriptor.class);
if (d == null)
{
f.format("%s%s%n", prefix, m.getName());
}
else
{
f.format("%s%s - %s%n", prefix, m.getName(), d.value());
}
f.format(" scope: %s%n", name.substring(0, name.indexOf(':')));
// Get flags and options.
Class>[] paramTypes = m.getParameterTypes();
Map flags = new TreeMap<>();
Map flagDescs = new TreeMap<>();
Map options = new TreeMap<>();
Map optionDescs = new TreeMap<>();
List params = new ArrayList<>();
Annotation[][] anns = m.getParameterAnnotations();
for (int paramIdx = 0; paramIdx < anns.length; paramIdx++)
{
Class> paramType = m.getParameterTypes()[paramIdx];
if (paramType == CommandSession.class) {
/* Do not bother the user with a CommandSession. */
continue;
}
Parameter p = findAnnotation(anns[paramIdx], Parameter.class);
d = findAnnotation(anns[paramIdx], Descriptor.class);
if (p != null)
{
if (p.presentValue().equals(Parameter.UNSPECIFIED))
{
options.put(p.names()[0], p);
if (d != null)
{
optionDescs.put(p.names()[0], d.value());
}
}
else
{
flags.put(p.names()[0], p);
if (d != null)
{
flagDescs.put(p.names()[0], d.value());
}
}
}
else if (d != null)
{
params.add(paramTypes[paramIdx].getSimpleName());
params.add(d.value());
}
else
{
params.add(paramTypes[paramIdx].getSimpleName());
params.add("");
}
}
// Print flags and options.
if (flags.size() > 0)
{
f.format(" flags:%n");
for (Entry entry : flags.entrySet())
{
// Print all aliases.
String[] names = entry.getValue().names();
f.format(" %s", names[0]);
for (int aliasIdx = 1; aliasIdx < names.length; aliasIdx++)
{
f.format(", %s", names[aliasIdx]);
}
f.format(" %s%n", flagDescs.get(entry.getKey()));
}
}
if (options.size() > 0)
{
f.format(" options:%n");
for (Entry entry : options.entrySet())
{
// Print all aliases.
String[] names = entry.getValue().names();
f.format(" %s", names[0]);
for (int aliasIdx = 1; aliasIdx < names.length; aliasIdx++)
{
f.format(", %s", names[aliasIdx]);
}
f.format(" %s%s%n",
optionDescs.get(entry.getKey()),
((entry.getValue().absentValue() == null) ? "" : " [optional]"));
}
}
if (params.size() > 0)
{
f.format(" parameters:%n");
for (Iterator it = params.iterator(); it.hasNext();)
{
f.format(" %s %s%n", it.next(), it.next());
}
}
prefix = "\n";
}
return f.toString();
}
}
private static T findAnnotation(Annotation[] anns,
Class clazz)
{
for (int i = 0; (anns != null) && (i < anns.length); i++)
{
if (clazz.isInstance(anns[i]))
{
return clazz.cast(anns[i]);
}
}
return null;
}
private Map> getCommands()
{
ServiceReference>[] refs = null;
try
{
refs = m_bc.getAllServiceReferences(null, "(osgi.command.scope=*)");
}
catch (InvalidSyntaxException ex)
{
// This should never happen.
}
Map> commands = new TreeMap<>();
for (ServiceReference> ref : refs)
{
Object svc = m_bc.getService(ref);
if (svc != null)
{
String scope = (String) ref.getProperty("osgi.command.scope");
Object ofunc = ref.getProperty("osgi.command.function");
String[] funcs = (ofunc instanceof String[]) ? (String[]) ofunc
: new String[] { String.valueOf(ofunc) };
for (String func : funcs)
{
commands.put(scope + ":" + func, new ArrayList());
}
if (!commands.isEmpty())
{
Method[] methods = svc.getClass().getMethods();
for (Method method : methods)
{
List commandMethods = commands.get(scope + ":"
+ method.getName());
if (commandMethods != null)
{
commandMethods.add(method);
}
}
}
// Remove any missing commands.
Iterator>> it = commands.entrySet().iterator();
while (it.hasNext())
{
if (it.next().getValue().size() == 0)
{
it.remove();
}
}
}
}
return commands;
}
@Descriptor("install bundle using URLs")
public String install(@Descriptor("command session")CommandSession session,
@Descriptor("target URLs") String[] urls) throws IOException
{
try (Formatter f = new Formatter()) {
StringBuilder sb = new StringBuilder();
for (String url : urls)
{
String location = Util.resolveUri(session, url.trim());
Bundle bundle = null;
try
{
bundle = m_bc.installBundle(location, null);
}
catch (IllegalStateException ex)
{
f.format("%s%n", ex.toString());
}
catch (BundleException ex)
{
if (ex.getNestedException() != null)
{
f.format("%s%n", ex.getNestedException().toString());
}
else
{
f.format("%s%n", ex.toString());
}
}
catch (Exception ex)
{
f.format("%s%n", ex.toString());
}
if (bundle != null)
{
if (sb.length() > 0)
{
sb.append(", ");
}
sb.append(bundle.getBundleId());
}
}
if (sb.toString().indexOf(',') > 0)
{
return "Bundle IDs: " + sb.toString();
}
else if (sb.length() > 0)
{
return "Bundle ID: " + sb.toString();
}
return f.toString();
}
}
@Descriptor("list all installed bundles")
public String lb(
@Descriptor("show location") @Parameter(names = { "-l", "--location" }, presentValue = "true", absentValue = "false") boolean showLoc,
@Descriptor("show symbolic name") @Parameter(names = { "-s", "--symbolicname" }, presentValue = "true", absentValue = "false") boolean showSymbolic,
@Descriptor("show update location") @Parameter(names = { "-u", "--updatelocation" }, presentValue = "true", absentValue = "false") boolean showUpdate)
{
return lb(showLoc, showSymbolic, showUpdate, null);
}
@Descriptor("list installed bundles matching a substring")
public String lb(
@Descriptor("show location") @Parameter(names = { "-l", "--location" }, presentValue = "true", absentValue = "false") boolean showLoc,
@Descriptor("show symbolic name") @Parameter(names = { "-s", "--symbolicname" }, presentValue = "true", absentValue = "false") boolean showSymbolic,
@Descriptor("show update location") @Parameter(names = { "-u", "--updatelocation" }, presentValue = "true", absentValue = "false") boolean showUpdate,
@Descriptor("subtring matched against name or symbolic name") String pattern)
{
if ((showLoc && showSymbolic && showUpdate) ||
(showLoc && showSymbolic) ||
(showSymbolic && showUpdate) ||
(showLoc && showUpdate)) {
return "Only one of -l, -s, -u should be used.";
}
List found = new ArrayList<>();
if (pattern == null)
{
found.addAll(Arrays.asList(m_bc.getBundles()));
}
else
{
Bundle[] bundles = m_bc.getBundles();
for (Bundle bundle : bundles) {
String name = bundle.getHeaders().get(Constants.BUNDLE_NAME);
if (matchBundleName(bundle.getSymbolicName(), pattern)
|| matchBundleName(name, pattern)) {
found.add(bundle);
}
}
}
if (found.size() > 0)
{
try (Formatter f = new Formatter()) {
printBundleList(found.toArray(new Bundle[found.size()]),
showLoc, showSymbolic, showUpdate, m_b0, f);
return f.toString();
}
}
else
{
return "No matching bundles found.";
}
}
private boolean matchBundleName(String name, String pattern)
{
return (name != null) && name.toLowerCase().contains(pattern.toLowerCase());
}
@Descriptor("display all matching log entries")
public String log(
@Descriptor("minimum log level [ debug | info | warn | error ]") String logLevel)
{
return log(-1, logLevel);
}
@Descriptor("display some matching log entries")
public String log(@Descriptor("maximum number of entries") int maxEntries,
@Descriptor("minimum log level [ debug | info | warn | error ]") String logLevel)
{
// Keep track of service references.
List> refs = new ArrayList<>();
// Get start level service.
try {
LogReaderService lrs = Util.getService(m_bc, LogReaderService.class, refs);
if (lrs == null)
{
return "Log reader service is unavailable.";
}
else
{
try (Formatter f = new Formatter()) {
@SuppressWarnings("unchecked")
Enumeration entries = lrs.getLog();
List select = new ArrayList<>();
int minLevel = logLevelAsInt(logLevel);
int index = 0;
while (entries.hasMoreElements() && (maxEntries < 0 || index < maxEntries))
{
LogEntry entry = entries.nextElement();
if (entry.getLevel() <= minLevel)
{
select.add(0, entry);
index++;
}
}
for (LogEntry e : select) {
display(e,f);
}
Util.ungetServices(m_bc, refs);
return f.toString();
}
}
}
catch (NoClassDefFoundError ncdfe) {
return "Log reader service is unavailable.";
}
}
private void display(LogEntry entry, Formatter f)
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
StringBuilder buffer = new StringBuilder();
buffer.append(sdf.format(new Date(entry.getTime()))).append(" ");
buffer.append(logLevelAsString(entry.getLevel())).append(" - ");
buffer.append("Bundle: ").append(entry.getBundle().getSymbolicName());
if (entry.getServiceReference() != null)
{
buffer.append(" - ");
buffer.append(entry.getServiceReference().toString());
}
buffer.append(" - ").append(entry.getMessage());
if (entry.getException() != null)
{
buffer.append(" - ");
StringWriter writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
entry.getException().printStackTrace(pw);
buffer.append(writer.toString());
}
f.format("%s%n", buffer.toString());
}
private static int logLevelAsInt(String logLevel)
{
if ("error".equalsIgnoreCase(logLevel))
{
return LogService.LOG_ERROR;
}
else if ("warn".equalsIgnoreCase(logLevel))
{
return LogService.LOG_WARNING;
}
else if ("info".equalsIgnoreCase(logLevel))
{
return LogService.LOG_INFO;
}
return LogService.LOG_DEBUG;
}
private static String logLevelAsString(int level)
{
switch (level)
{
case LogService.LOG_ERROR:
return "ERROR";
case LogService.LOG_WARNING:
return "WARNING";
case LogService.LOG_INFO:
return "INFO";
default:
return "DEBUG";
}
}
@Descriptor("refresh bundles")
public void refresh(
@Descriptor("target bundles (can be null or empty)") Bundle[] bundles)
{
if ((bundles != null) && (bundles.length == 0))
{
bundles = null;
}
m_b0.adapt(FrameworkWiring.class).refreshBundles(Arrays.asList(bundles));
}
@Descriptor("resolve bundles")
public String resolve(
@Descriptor("target bundles (can be null or empty)") Bundle[] bundles)
{
if (m_b0.adapt(FrameworkWiring.class).resolveBundles(bundles != null ? Arrays.asList(bundles) : null))
{
return "Not all bundles could be resolved.";
}
return null;
}
@Descriptor("start bundles")
public String start(
@Descriptor("start bundle transiently") @Parameter(names = { "-t", "--transient" }, presentValue = "true", absentValue = "false") boolean trans,
@Descriptor("use declared activation policy") @Parameter(names = { "-p",
"--policy" }, presentValue = "true", absentValue = "false") boolean policy,
@Descriptor("target bundle identifiers or URLs") String[] ss)
{
if ((ss == null) || (ss.length < 1)) {
return "Please specify the bundles to start.";
}
int options = 0;
// Check for "transient" switch.
if (trans)
{
options |= Bundle.START_TRANSIENT;
}
// Check for "start policy" switch.
if (policy)
{
options |= Bundle.START_ACTIVATION_POLICY;
}
try (Formatter f = new Formatter()) {
for (String s : ss)
{
String id = s.trim();
try
{
Bundle bundle = null;
// The id may be a number or a URL, so check.
if (Character.isDigit(id.charAt(0)))
{
long l = Long.parseLong(id);
bundle = m_bc.getBundle(l);
}
else
{
bundle = m_bc.installBundle(id);
}
if (bundle != null)
{
bundle.start(options);
}
else
{
f.format("Bundle ID '%s' is invalid.%n", id);
}
}
catch (NumberFormatException ex)
{
f.format("Unable to parse id '%s'.%n", id);
}
catch (BundleException ex)
{
if (ex.getNestedException() != null)
{
f.format("%s%n", ex.getNestedException().toString());
}
else
{
f.format("%s%n", ex.toString());
}
}
catch (Exception ex)
{
f.format("%s%n", ex.toString());
}
}
return f.toString();
}
}
@Descriptor("stop bundles")
public String stop(@Descriptor("stop bundle transiently") @Parameter(names = { "-t",
"--transient" }, presentValue = "true", absentValue = "false") boolean trans,
@Descriptor("target bundles") Bundle[] bundles)
{
if ((bundles == null) || (bundles.length == 0))
{
return "Please specify the bundles to stop.";
}
int options = 0;
// Check for "transient" switch.
if (trans)
{
options |= Bundle.STOP_TRANSIENT;
}
try (Formatter f = new Formatter()) {
for (Bundle bundle : bundles)
{
try
{
bundle.stop(options);
}
catch (BundleException ex)
{
if (ex.getNestedException() != null)
{
f.format("%s%n", ex.getNestedException().toString());
}
else
{
f.format("%s%n", ex.toString());
}
}
catch (Exception ex)
{
f.format("%s%n", ex.toString());
}
}
return f.toString();
}
}
@Descriptor("uninstall bundles")
public String uninstall(@Descriptor("target bundles") Bundle[] bundles)
{
if ((bundles == null) || (bundles.length == 0))
{
return "Please specify the bundles to uninstall.";
}
try (Formatter f = new Formatter()) {
for (Bundle bundle : bundles)
{
try
{
bundle.uninstall();
}
catch (BundleException ex)
{
if (ex.getNestedException() != null)
{
f.format("%s%n", ex.getNestedException().toString());
}
else {
f.format("%s%n", ex.toString());
}
}
catch (Exception ex)
{
f.format("%s%n", ex.toString());
}
}
return f.toString();
}
}
@Descriptor("update bundle")
public String update(@Descriptor("target bundle") Bundle bundle)
{
if (bundle == null)
{
return "Must specify a bundle.";
}
try
{
bundle.update();
return null;
}
catch (BundleException ex)
{
if (ex.getNestedException() != null)
{
return ex.getNestedException().toString();
}
return ex.toString();
}
catch (Exception ex)
{
return ex.toString();
}
}
@Descriptor("update bundle from URL")
public String update(
@Descriptor("command session") CommandSession session,
@Descriptor("target bundle") Bundle bundle,
@Descriptor("URL from where to retrieve bundle") String location) throws IOException {
if (bundle == null)
{
return "Must specify a bundle.";
}
if (location == null)
{
return "Must specify a location.";
}
try
{
location = Util.resolveUri(session, location.trim());
InputStream is = new URL(location).openStream();
bundle.update(is);
return null;
}
catch (MalformedURLException ex)
{
return "Unable to parse URL";
}
catch (IOException ex)
{
return "Unable to open input stream: " + ex;
}
catch (BundleException ex)
{
if (ex.getNestedException() != null)
{
return ex.getNestedException().toString();
}
return ex.toString();
}
catch (Exception ex)
{
return ex.toString();
}
}
@Descriptor("determines from where a bundle loads a class")
public String which(@Descriptor("target bundle") Bundle bundle,
@Descriptor("target class name") String className)
{
if (bundle == null)
{
return "Please specify a bundle";
}
Class> clazz = null;
try
{
clazz = bundle.loadClass(className);
if (clazz.getClassLoader() == null)
{
return "Loaded from: boot class loader";
}
else if (clazz.getClassLoader() instanceof BundleReference)
{
Bundle p = ((BundleReference) clazz.getClassLoader()).getBundle();
return "Loaded from: " + p;
}
else
{
return "Loaded from: " + clazz.getClassLoader();
}
}
catch (ClassNotFoundException ex)
{
return "Class not found";
}
}
private static void printBundleList(Bundle[] bundles,
boolean showLoc, boolean showSymbolic, boolean showUpdate, Bundle b0, Formatter f)
{
f.format("START LEVEL %s%n", b0.adapt(FrameworkStartLevel.class).getStartLevel());
// Determine last column.
String lastColumn = "Name";
if (showLoc)
{
lastColumn = "Location";
}
else if (showSymbolic)
{
lastColumn = "Symbolic name";
}
else if (showUpdate)
{
lastColumn = "Update location";
}
f.format("%5s|%-11s|%5s|%s%n", "ID", "State", "Level", lastColumn);
for (Bundle bundle : bundles)
{
// Get the bundle name or location.
String name = bundle.getHeaders().get(Constants.BUNDLE_NAME);
// If there is no name, then default to symbolic name.
name = (name == null) ? bundle.getSymbolicName() : name;
// If there is no symbolic name, resort to location.
name = (name == null) ? bundle.getLocation() : name;
// Overwrite the default value is the user specifically
// requested to display one or the other.
if (showLoc)
{
name = bundle.getLocation();
}
else if (showSymbolic)
{
name = bundle.getSymbolicName();
name = (name == null) ? "" : name;
}
else if (showUpdate)
{
name = bundle.getHeaders().get(Constants.BUNDLE_UPDATELOCATION);
name = (name == null) ? bundle.getLocation() : name;
}
// Show bundle version if not showing location.
name = (!showLoc && !showUpdate) ? name + " (" + bundle.getVersion() + ")"
: name;
// Get the bundle's start level.
int level = bundle.adapt(BundleStartLevel.class).getStartLevel();
f.format("%5d|%-11s|%5d|%s|%s%n",
bundle.getBundleId(), getStateString(bundle), level, name,
bundle.getVersion());
}
}
private static String getStateString(Bundle bundle)
{
int state = bundle.getState();
if (state == Bundle.ACTIVE)
{
return "Active ";
}
else if (state == Bundle.INSTALLED)
{
return "Installed ";
}
else if (state == Bundle.RESOLVED)
{
return "Resolved ";
}
else if (state == Bundle.STARTING)
{
return "Starting ";
}
else if (state == Bundle.STOPPING)
{
return "Stopping ";
}
else
{
return "Unknown ";
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy