/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.command.internal.handlers; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.LogFactory; import org.osgi.framework.Version; import de.rcenvironment.core.command.common.CommandException; import de.rcenvironment.core.command.spi.CommandContext; import de.rcenvironment.core.command.spi.CommandDescription; import de.rcenvironment.core.command.spi.CommandPlugin; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.core.toolkitbridge.transitional.StatsCounter; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.common.VersionUtils; import de.rcenvironment.core.utils.common.textstream.receivers.CapturingTextOutReceiver; import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription; /** * Provides built-in console commands, like printing "help" or version information. * * @author Robert Mischke */ public class BuiltInCommandPlugin implements CommandPlugin { private static final String CMD_DEV = "dev"; private static final String CMD_DUMMY = "dummy"; private static final String CMD_HELP = "help"; private static final String CMD_HELP_DEV = "help --dev"; private static final String CMD_OSGI = "osgi"; private static final String CMD_STATS = "stats"; private static final String CMD_TASKS = "tasks"; private static final String CMD_VERSION = "version"; private static final String CMD_CRASH = "force-crash"; @Override public Collection<CommandDescription> getCommandDescriptions() { final Collection<CommandDescription> contributions = new ArrayList<CommandDescription>(); contributions.add(new CommandDescription(CMD_HELP, "", false, "list available commands")); contributions.add(new CommandDescription(CMD_VERSION, "[--detailed]", false, "print version information")); // developer commands contributions.add(new CommandDescription(CMD_DEV, "", true, "alias of \"help --dev\" [deprecated]")); contributions.add(new CommandDescription(CMD_DUMMY, "", true, "prints a test message")); contributions.add(new CommandDescription(CMD_CRASH, "<delay>", true, "\"kills\" the instance without proper shutdown at <delay> milliseconds after the command is executed")); contributions.add(new CommandDescription(CMD_HELP_DEV, "", true, "list available commands (including developer commands)")); contributions.add(new CommandDescription(CMD_STATS, "", true, "show internal statistics")); contributions.add(new CommandDescription(CMD_OSGI, "[-o <filename>] <command>", true, "executes an OSGi/Equinox console command; use -o to write text output to a file")); contributions.add(new CommandDescription(CMD_TASKS, "[-a] [-i]", true, "show information about internal tasks", "-a - Show all tasks, including inactive ones", "-i - Extended information: list tasks with a unique id")); // // Only the command description of the 'saveto' command is specified here. The functionality of the command is implemented in // {@link // // MultiCommandHandler} // contributions.add(new CommandDescription(MultiCommandHandler.SAVETO, "[-m] (<filename>|--auto) <command(s)>", false, // "saves the output of the command(s) to a file", // "-m: mirrors the output to the command console", // "<filename>: name of the file into which the output should be stored", // "--auto: auto-generates a filename", // "<command(s)>: list of commands which should be executed")); return contributions; } @Override public void execute(CommandContext context) throws CommandException { String cmd = context.consumeNextToken(); if (CMD_HELP.equals(cmd)) { boolean devOption = context.consumeNextTokenIfEquals("--dev"); performHelp(context, devOption); } else if (CMD_VERSION.equals(cmd)) { performVersion(context); } else if (CMD_OSGI.equals(cmd)) { performOsgi(context); } else if (CMD_CRASH.equals(cmd)) { performCrash(context); } else if (CMD_DEV.equals(cmd)) { // deprecated alias of "help --dev" performHelp(context, true); } else if (CMD_STATS.equals(cmd)) { performStats(context); } else if (CMD_TASKS.equals(cmd)) { performTasks(context); } else if (CMD_DUMMY.equals(cmd)) { performDummy(context); // } else if (MultiCommandHandler.SAVETO.equals(cmd)) { // throw CommandException.syntaxError("The 'saveto' command can only be used as a prefix of a list of commands!", context); } else { throw new IllegalStateException(); } } private void performHelp(CommandContext context, boolean devOption) throws CommandException { context.setDeveloperCommandSetEnabled(devOption); // "rce" or "rce help" -> print user help throw CommandException.requestHelp(context); } /** * Dummy command for testing. * * @throws CommandException */ private void performDummy(final CommandContext context) throws CommandException { context.println("Dummy command executing"); } private void performOsgi(CommandContext context) throws CommandException { final EquinoxConsoleCommandInvoker commandInvoker = new EquinoxConsoleCommandInvoker(); final boolean logToFile = context.consumeNextTokenIfEquals("-o"); if (!logToFile) { commandInvoker.execute(context); } else { String outputFilename = context.consumeNextToken(); if (outputFilename == null) { throw CommandException.syntaxError("Missing filename after -o parameter", context); } final CapturingTextOutReceiver outputReceiver = new CapturingTextOutReceiver(""); CommandContext wrappedContext = new CommandContext(context.consumeRemainingTokens(), outputReceiver, context.getInvokerInformation()); commandInvoker.execute(wrappedContext); File outputFile = new File(outputFilename); try { FileUtils.write(outputFile, outputReceiver.getBufferedOutput()); context.println("Logged output to " + outputFile.getAbsolutePath()); } catch (IOException e) { context.println("Internal error: Failed to write to output file " + outputFile.getAbsolutePath()); } } } private void performVersion(CommandContext context) { if (context.consumeNextTokenIfEquals("--detailed")) { context.println("RCE platform version: " + VersionUtils.getVersionOfPlatformBundles()); context.println("RCE core version: " + VersionUtils.getVersionOfCoreBundles()); context.println("RCE product version: " + VersionUtils.getVersionOfProduct()); } else { Version version = VersionUtils.getVersionOfProduct(); String buildId = VersionUtils.getBuildIdAsString(version); if (buildId == null) { buildId = "-"; } context.println(StringUtils.format("%s (build ID: %s)", VersionUtils.getVersionAsString(version), buildId)); } } private void performCrash(CommandContext context) throws CommandException { String token = context.consumeNextToken(); if (token == null || context.hasRemainingTokens()) { throw CommandException.wrongNumberOfParameters(context); } try { int delayMsec = Integer.parseInt(token); LogFactory.getLog(getClass()).warn(StringUtils.format("Killing the instance (using System.exit(1)) in %,d msec...", delayMsec)); ConcurrencyUtils.getAsyncTaskService().scheduleAfterDelay(new Runnable() { @Override @TaskDescription("Simulate an instance crash (triggered by console command)") public void run() { System.exit(1); } }, delayMsec); } catch (NumberFormatException e) { throw CommandException.syntaxError( StringUtils.format("You need to specify the delay in milliseconds. %s is not a valid number.", token), context); } } /** * Prints statistics about asynchronous tasks. * * @param context * * @return String the console output * @throws CommandException on syntax error */ private void performTasks(CommandContext context) throws CommandException { boolean addTaskIds = false; boolean includeInactive = false; String token; while ((token = context.consumeNextToken()) != null) { switch (token) { case "-a": // "all" includeInactive = true; break; case "-i": // "ids" addTaskIds = true; break; default: throw CommandException.syntaxError("Unknown parameter: " + token, context); } } context.println(ConcurrencyUtils.getThreadPoolManagement().getFormattedStatistics(addTaskIds, includeInactive)); } private void performStats(CommandContext context) { for (String line : StatsCounter.getFullReportAsStandardTextRepresentation()) { context.println(line); } } }