Java tutorial
/* * Copyright 2010 DTO Labs, Inc. (http://dtolabs.com) * * 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 com.dtolabs.rundeck.core.cli; import com.dtolabs.rundeck.core.*; import com.dtolabs.rundeck.core.cli.project.ProjectToolException; import com.dtolabs.rundeck.core.cli.queue.ConsoleExecutionFollowReceiver; import com.dtolabs.rundeck.core.cli.queue.QueueTool; import com.dtolabs.rundeck.core.common.*; import com.dtolabs.rundeck.core.dispatcher.CentralDispatcherException; import com.dtolabs.rundeck.core.dispatcher.IDispatchedScript; import com.dtolabs.rundeck.core.dispatcher.QueuedItem; import com.dtolabs.rundeck.core.dispatcher.QueuedItemResult; import com.dtolabs.rundeck.core.execution.script.ScriptfileUtils; import com.dtolabs.rundeck.core.utils.*; import org.apache.commons.cli.*; import org.apache.log4j.PropertyConfigurator; import org.apache.tools.ant.Project; import java.io.*; import java.util.*; /** * Main class for <code>dispatch</code> command line tool. This command will dispatch the command either locally or * remotely. */ public class ExecTool implements CLITool, IDispatchedScript, CLILoggerParams { /** * Short option value for filter exclude precedence option */ static final String FILTER_EXCLUDE_PRECEDENCE_OPT = "Z"; /** * Long option for filter exclude precedence option */ static final String FILTER_EXCLUDE_PRECEDENCE_LONG = "filter-exclude-precedence"; private boolean argVerbose = false; private boolean argDebug = false; private boolean argQuiet = false; private boolean argKeepgoing; private Integer nodeThreadcount = 1; private String argProject; private boolean argFollow; private boolean argProgress; private boolean shouldExit = false; protected PrintStream err = System.err; protected PrintStream out = System.out; /** * Reference to command line params */ protected CommandLine cli; /** * reference to the command line {@link org.apache.commons.cli.Options} instance. */ protected static final Options options = new Options(); static { options.addOption("h", "help", false, "print usage"); options.addOption("v", "verbose", false, "verbose mode"); options.addOption("V", "debug", false, "Debug output mode"); options.addOption("q", "quiet", false, "quiet mode"); options.addOption("K", "keepgoing", false, "keep going if there are errors"); options.addOption("C", "threadcount", true, "number of threads"); options.addOption("F", "filter", true, "node filter string"); options.addOption("I", "nodes", true, "include node list (deprecated, use --filter)"); options.addOption("X", "xnodes", true, "exclude node list (deprecated, use --filter)"); options.addOption("p", "project", true, "project name"); options.addOption(FILTER_EXCLUDE_PRECEDENCE_OPT, FILTER_EXCLUDE_PRECEDENCE_LONG, true, "true/false. if true, exclusion filters have precedence over inclusion filters"); options.addOption("s", "scriptfile", true, "scriptfile script file"); options.addOption("u", "url", true, "script URL"); options.addOption("S", "stdin", false, "read script from stdin"); options.addOption("f", "follow", false, "Follow queued execution output"); options.addOption("r", "progress", false, "In follow mode, print progress indicator chars"); } /** * Reference to the framework instance */ private Framework framework; private String baseDir; protected NodeFormatter nodeFormatter; private String nodeFilter; void setFramework(Framework framework) { this.framework = framework; } private boolean nodeExcludePrecedence = true; private String scriptpath; private InputStream scriptAsStream; private boolean inlineScript; private String inlineScriptContent; private String scriptURLString; /** * Create a new ExecTool initialized at the RDECK_BASE location via System property */ ExecTool() { this(Constants.getSystemBaseDir()); } /** * Create a new ExecTool with the given RDECK_BASE location * * @param baseDir path to RDECK_BASE */ ExecTool(String baseDir) { this(Framework.getInstanceWithoutProjectsDir(baseDir)); } /** * Create a new ExecTool with the given framework instance. * * @param framework framework instance */ public ExecTool(Framework framework) { this.framework = framework; this.nodeFormatter = new NodeYAMLFormatter(); } /** * array containing args to the Cli instance (e,g args after the "--" ) */ private String[] argsDeferred; private Map excludeMap = new HashMap(); private Map includeMap = new HashMap(); /** * Reads the argument vector and constructs a {@link org.apache.commons.cli.CommandLine} object containing params * * @param args the cli arg vector * * @return a new instance of CommandLine */ public CommandLine parseArgs(String[] args) { int lastArg = -1; for (int i = 0; i < args.length; i++) { if ("--".equals(args[i])) { lastArg = i; break; } } if (lastArg >= 0) { final int argslen = args.length - (lastArg + 1); argsDeferred = new String[argslen]; System.arraycopy(args, lastArg + 1, argsDeferred, 0, argslen); } final CommandLineParser parser = new PosixParser(); try { cli = parser.parse(options, args); } catch (ParseException e) { help(); throw new ProjectToolException(e); } if (cli.hasOption("K")) { argKeepgoing = true; } if (cli.hasOption("v")) { argVerbose = true; } if (cli.hasOption("V")) { argDebug = true; } if (cli.hasOption("q")) { argQuiet = true; } if (cli.hasOption("S") && cli.hasOption("s")) { throw new CoreException("-s and -S options are mutually exclusive"); } if (cli.hasOption("s")) { scriptpath = new File(cli.getOptionValue("s")).getAbsolutePath(); } if (cli.hasOption("u")) { scriptURLString = cli.getOptionValue("u"); } if (cli.hasOption("S")) { inlineScript = true; } if (cli.hasOption("f")) { argFollow = true; if (cli.hasOption("r")) { argProgress = true; } } if (cli.hasOption("F")) { setNodeFilter(cli.getOptionValue("F")); } if (cli.hasOption('p')) { argProject = cli.getOptionValue("p"); } else if (!cli.hasOption("p") && framework.getFrameworkProjectMgr().listFrameworkProjects().size() == 1) { final FrameworkProject project = (FrameworkProject) framework.getFrameworkProjectMgr() .listFrameworkProjects().iterator().next(); argProject = project.getName(); } if (cli.hasOption("C")) { try { setNodeThreadcount(Integer.valueOf(cli.getOptionValue("C"))); } catch (NumberFormatException e) { throw new IllegalArgumentException("threadcount must be an integer"); } } if (cli.hasOption("h")) { help(); exit(1); } //if failedNodes file exists, parse it for a list of node names to include. final String[] keys = NodeSet.FILTER_KEYS_LIST.toArray(new String[NodeSet.FILTER_KEYS_LIST.size()]); excludeMap = parseExcludeArgs(keys); includeMap = parseIncludeArgs(keys); setNodeExcludePrecedence(determineExclusionPrecedenceForArgs(args, cli)); if (null == argProject) { throw new IllegalArgumentException("project parameter not specified"); } return cli; } /** * Generates the commandline execution string used. If filterArgs are specified, they are used in place of the * commandline filter args when the -F option is not present. * * @param filterArgs set of filter commandline arguments to insert * * @return String of the previously executed commandline, modified with the given filterArgs if present */ public String generateExecLine(Map<String, String> filterArgs) { ArrayList<String> list = new ArrayList<String>(); if (argKeepgoing) { list.add("-K"); } if (argVerbose) { list.add("-v"); } if (argDebug) { list.add("-V"); } if (argQuiet) { list.add("-q"); } if (null != scriptpath) { list.add("-s"); list.add(scriptpath); } if (null != scriptURLString) { list.add("-u"); list.add(scriptURLString); } if (inlineScript) { list.add("-S"); } if (null != argProject) { list.add("-p"); list.add(argProject); } if (1 != getNodeThreadcount()) { list.add("-C"); list.add(Integer.toString(getNodeThreadcount())); } if (null != filterArgs) { for (final Map.Entry<String, String> entry : filterArgs.entrySet()) { list.add(entry.getKey()); list.add(entry.getValue()); } } if (null != argsDeferred && argsDeferred.length > 0) { list.add("--"); list.addAll(Arrays.asList(argsDeferred)); } //generate string list.add(0, "dispatch"); return StringArrayUtil.asString(list.toArray(new String[list.size()]), " "); } /** * Parse the value of the -X option * * @param keys * * @return */ protected Map parseExcludeArgs(String[] keys) { return parseFilterArgs(keys, cli, "X"); } /** * Parse the value of the -X option. * * @param keys * * @return */ protected Map parseIncludeArgs(String[] keys) { return parseFilterArgs(keys, cli, "I"); } /** * The run method carries out the lifecycle of the tool, parsing args, handling exceptions, and exiting with a * suitable exit code. * * @param args the cli arg vector */ public void run(String[] args) { int exitCode = 1; //pessimistic initial value ThreadBoundOutputStream.bindSystemOut(); ThreadBoundOutputStream.bindSystemErr(); out = System.out; err = System.err; try { parseArgs(args); configurePrintStream(argQuiet); if (!hasNecessaryRunArgs()) { listAction(); } else { queueAction(); } exitCode = 0; } catch (Throwable t) { if (null == t.getMessage() || argVerbose || "true".equals(System.getProperty("rdeck.traceExceptions"))) { t.printStackTrace(); } error(t); if (t instanceof NodesetEmptyException) { exitCode = NODESET_EMPTY_EXIT_CODE; } } exit(exitCode); } /** * Return true if any necessary args for dispatch are present. */ private boolean hasNecessaryRunArgs() { return hasArgsDeferred() || null != getScript() || null != getServerScriptFilePath() || isInlineScript() || null != getScriptURLString(); } private boolean hasArgsDeferred() { return null != argsDeferred && 0 != argsDeferred.length; } /** * Exit code for a Nodeset Empty failure exception */ public static final int NODESET_EMPTY_EXIT_CODE = 3; /** * List the nodes */ void listAction() { try { log((argVerbose ? getNodeFormatter() : new DefaultNodeFormatter()) .formatNodes(filterNodes(false).getNodes()).toString()); } catch (Exception e) { e.printStackTrace(); } } NodeFormatter getNodeFormatter() { return nodeFormatter; } /** * Call the CentralDispatcherMgr to submit the execution params to the central dispatch queue instead of executing * locally. */ private void queueAction() { final QueuedItemResult result; try { if (inlineScript && null == inlineScriptContent) { //pass input stream as the script stream so that the Queue action can serialize it for the remote // request setScriptAsStream(instream); } result = framework.getCentralDispatcherMgr().queueDispatcherScript(this); } catch (CentralDispatcherException e) { throw new CoreException("Unable to queue the execution: " + e.getMessage(), e); } if (null == result || !result.isSuccessful()) { throw new CoreException( "Queued job request failed: " + (null != result ? result.getMessage() : "Result was null")); } if (null != result.getMessage()) { out.println(result.getMessage()); } out.println("Queued Execution ID: " + result.getItem().getId() + " <" + result.getItem().getUrl() + ">"); if (argFollow) { followOutput(result.getItem(), argQuiet, argProgress); } } /** * Perform follow action for the execution, using QueueTool implementation. */ private void followOutput(QueuedItem item, final boolean quiet, final boolean progress) throws CoreException { boolean successful = false; try { ConsoleExecutionFollowReceiver.Mode mode = ConsoleExecutionFollowReceiver.Mode.output; if (quiet) { mode = ConsoleExecutionFollowReceiver.Mode.quiet; } else if (progress) { mode = ConsoleExecutionFollowReceiver.Mode.progress; } successful = QueueTool.followAction(item.getId(), true, mode, framework, System.out, this); } catch (CentralDispatcherException e) { throw new CoreException("Failed following output for execution: " + item.getId(), e); } if (!successful) { exit(3); } } public static final String DEFAULT_LOG_FORMAT = "[%user@%node %command][%level] %message"; /** * Crete a NodeSet using the parsed argument values * * @return NodeSet */ protected NodeSet createFilterNodeSelector() { if (null != nodeFilter) { NodeSet nodeSet = NodeSet.fromFilter(nodeFilter); nodeSet.setKeepgoing(isKeepgoing()); nodeSet.setThreadCount(getNodeThreadcount()); nodeSet.getInclude().setDominant(!getNodeExcludePrecedence()); nodeSet.getExclude().setDominant(getNodeExcludePrecedence()); return nodeSet; } return createNodeSet(includeMap, excludeMap, getNodeExcludePrecedence(), getNodeThreadcount(), argKeepgoing, null); } /** * Create a NodeSet using the included maps, where exclusion has precedence * * @param includeMap include map * @param excludeMap exclude map * * @return NodeSet */ protected NodeSet createNodeSet(final Map includeMap, final Map excludeMap) { return createNodeSet(includeMap, excludeMap, true, getNodeThreadcount(), argKeepgoing, null); } /** * Create a NodeSet using the included maps, and boolean exclude value * * @param includeMap include map * @param excludeMap exclude map * @param excludePrecedence if true, exclusion has precedence * @param threadCount the threadcount * @param keepgoing keepgoing boolean * @param failedNodesfile file indicating list of failed nodes * * @return NodeSet */ protected static NodeSet createNodeSet(final Map includeMap, final Map excludeMap, final boolean excludePrecedence, final Integer threadCount, final boolean keepgoing, final File failedNodesfile) { final NodeSet nodeset = new NodeSet(); nodeset.createExclude(excludeMap).setDominant(excludePrecedence); nodeset.createInclude(includeMap).setDominant(!excludePrecedence); nodeset.setThreadCount(threadCount); nodeset.setKeepgoing(keepgoing); nodeset.setFailedNodesfile(failedNodesfile); return nodeset; } /** * Creates an instance and executes {@link #run(String[])}. * * @param args * * @throws Exception */ public static void main(final String[] args) throws Exception { /** * Initialize the log4j logger */ File configDir = Constants.getFrameworkConfigFile(); PropertyConfigurator.configure(new File(configDir, "log4j.properties").getAbsolutePath()); File systemBaseDir = new File(Constants.getSystemBaseDir()); final ExecTool ExecTool = new ExecTool(systemBaseDir.getAbsolutePath()); ExecTool.shouldExit = true; ExecTool.run(args); } /** * Calls the exit method * * @param exitcode return code to exit with */ public void exit(int exitcode) { if (shouldExit) { System.exit(exitcode); } else if (0 != exitcode) { warn("ExecTool.exit() called while in embedded usage, not exitting."); } } /** * Writes help message to implementation specific output channel. */ public void help() { final HelpFormatter formatter = new HelpFormatter(); formatter.printHelp(80, "dispatch [-h] [-v] [-V] [-q] [-p project] " + "[-F node-filter] " + "[--threadcount <1>] [--keepgoing] " + "[[-S] | [-s <>] | [-u <url>] | [-- command-args]]", null, options, "Examples:\n" + "| dispatch\n | => Prints all nodes\n" + "| dispatch -p default -f -- whoami\n | => Runs the whoami command on all nodes\n" + "| dispatch -F '!name: node1' -f -- uptime\n | => Runs the uptime command on all nodes except node1\n" + "| dispatch -s myscript.sh -f\n | => Copies and then runs myscript.sh to matching nodes\n" + "| dispatch -u http://server/script.sh\n | => Downloads script URL, then runs on matching nodes\n" + "\n" + "[RUNDECK version " + VersionConstants.VERSION + " (" + VersionConstants.BUILD + ")]"); } /** * Logs message via implementation specific log facility * * @param message message to log */ public void log(String message) { System.out.println(message); } /** * Logs warning message via implementation specific log facility * * @param message message to log */ public void warn(String message) { System.err.println("warn: " + message); } /** * Logs error message via implementation specific log facility * * @param message message to log */ public void error(String message) { System.err.println("error: " + message); } private void error(final Throwable t) { error(t.getMessage()); } public void debug(String message) { if (argDebug) { log("debug: " + message); } } /** * Logs verbose message via implementation specific log facility * * @param message message to log */ public void verbose(String message) { if (argVerbose) { log("verbose: " + message); } } /** * Reconfigures the System.out PrintStream to write to a file output stream. * * @param quiet If true, configures to write to a file. */ protected void configurePrintStream(boolean quiet) { if (quiet) { final String logpath = framework.getProperty("framework.logs.dir"); if (null == logpath || "".equals(logpath)) { throw new CoreException( "Cannot configure print stream to a file. " + "framework.logs.dir property not set"); } final File logsdir = new File(logpath); if (!logsdir.isDirectory() || !logsdir.exists()) { throw new CoreException("Cannot configure print stream to a file. " + "Path does not exist or is not a directory: " + logpath); } try { final File logfile = File.createTempFile("dispatch-", ".log", logsdir); ThreadBoundOutputStream.bindSystemOut().installThreadStream(new FileOutputStream(logfile)); } catch (IOException e) { throw new CoreException( "Cannot configure print stream to a file. " + "Failed created log file: " + e.getMessage()); } } } public String getArgFrameworkProject() { return argProject; } public String[] getArgsDeferred() { return null != argsDeferred ? argsDeferred.clone() : null; } public String getScriptpath() { return scriptpath; } public boolean isInlineScript() { return inlineScript; } public String getInlineScriptContent() { return inlineScriptContent; } public Framework getFramework() { return framework; } public NodesSelector getNodeSelector() { return createFilterNodeSelector().nodeSelectorWithDefault(framework.getFrameworkNodeName()); } public INodeSet getNodes() { return filterNodes(true); } public int getThreadCount() { return getNodeThreadcount(); } public String getNodeRankAttribute() { return null; } public boolean isNodeRankOrderAscending() { return true; } public Boolean isKeepgoing() { return argKeepgoing; } public String getFrameworkProject() { return getArgFrameworkProject(); } public String getScript() { return getInlineScriptContent(); } public InputStream getScriptAsStream() { return scriptAsStream; } public void setScriptAsStream(final InputStream scriptAsStream) { this.scriptAsStream = scriptAsStream; } public String getServerScriptFilePath() { return getScriptpath(); } public NodeSet getNodeSet() { return createFilterNodeSelector(); } public String[] getArgs() { return getArgsDeferred(); } public int getLoglevel() { if (argDebug) { return Constants.DEBUG_LEVEL; } if (argQuiet && argVerbose) { return Constants.WARN_LEVEL; } if (argVerbose) { return Constants.VERBOSE_LEVEL; } if (argQuiet) { return Constants.ERR_LEVEL; } return Constants.INFO_LEVEL; } public Map<String, Map<String, String>> getDataContext() { return null; } /** * Return true if exclusion should have precedence in node filter args * * @param args all commandline args * @param cli parsed CommandLine * * @return true if --filter-exclusion-precedence is true, or -I is not specified before -X */ static boolean determineExclusionPrecedenceForArgs(String[] args, final CommandLine cli) { if (cli.hasOption(FILTER_EXCLUDE_PRECEDENCE_OPT)) { return "true".equals(cli.getOptionValue(FILTER_EXCLUDE_PRECEDENCE_OPT)); } else { //determine if -X or -I appears first in args list, and set precendence for first item for (int i = 0; i < args.length; i++) { String option = args[i]; if ("-X".equals(option) || "--xnodes".equals(option)) { return true; } else if ("-I".equals(option) || "--nodes".equals(option)) { return false; } } } return true; } /** * Parse the values as key=value pairs, using the set of allowed keys. If there is only one entry in the values * array without a key, then the first key of the allowed keys is used as the default * * @param keys allowed keys for the key=value strings, the first key is used as the default key * @param values array of key=value strings, or merely 1 value string if the array is size 1 * * @return map of the key to values */ protected static Map<String, String> parseMultiNodeArgs(String[] keys, String[] values) { HashMap<String, String> map = new HashMap<String, String>(); if (null != values && values.length > 0) { for (String exclude : values) { int i1 = exclude.indexOf("="); if (i1 > 0 && i1 <= exclude.length() - 1) { String k = exclude.substring(0, i1); String v = exclude.substring(i1 + 1); map.put(k, v); } else if (i1 < 0) { map.put(keys[0], exclude); } } } return map; } protected static Map<String, String> parseFilterArgs(String[] keys, CommandLine cli, String opt) { String[] strings = cli.getOptionValues(opt); if (null == strings || strings.length == 0) { if (null != cli.getOptionValue(opt)) { strings = new String[] { cli.getOptionValue(opt) }; } } return parseMultiNodeArgs(keys, strings); } /** * Looks up node registrations in nodes.properties * * @return Nodes object */ private INodeSet readNodesFile() { FrameworkProject project = framework.getFrameworkProjectMgr().getFrameworkProject(argProject); try { return project.getNodeSet(); } catch (NodeFileParserException e) { throw new CoreException("Error parsing nodes resource file: " + e.getMessage(), e); } } INodeSet filterNodes() { return filterNodes(false); } INodeSet filterNodes(final boolean singleNodeDefault) { /** * Read the nodes.properties file */ final INodeSet n = readNodesFile(); debug("total unfiltered nodes=" + n.getNodeNames().size()); final NodeSet filterNodeSelector = createFilterNodeSelector(); if (0 == n.getNodeNames().size()) { verbose("Empty node list"); } else { /** * Apply the include/exclude filters to the list */ debug("applying nodeset filter... " + getNodeSelector().toString()); /** * Reset collection to filter results */ return NodeFilter.filterNodes( singleNodeDefault ? filterNodeSelector.nodeSelectorWithDefault(framework.getFrameworkNodeName()) : filterNodeSelector.nodeSelectorWithDefaultAll(), n); } /** * Retrieve the complete list of node entries */ return n; } void setNodeFormatter(final NodeFormatter nodeFormatter) { this.nodeFormatter = nodeFormatter; } public String getScriptURLString() { return scriptURLString; } public String getNodeFilter() { if (null != nodeFilter) { return nodeFilter; } NodeSet nodeSet = getNodeSet(); if (null != nodeSet) { return NodeSet.generateFilter(nodeSet); } return null; } public void setNodeFilter(String nodeFilter) { this.nodeFilter = nodeFilter; } /** * boolean value specifying that exclusion node filters have precedence over inclusion filters */ public Boolean getNodeExcludePrecedence() { return nodeExcludePrecedence; } public void setNodeExcludePrecedence(boolean nodeExcludePrecedence) { this.nodeExcludePrecedence = nodeExcludePrecedence; } public int getNodeThreadcount() { return nodeThreadcount; } public void setNodeThreadcount(Integer nodeThreadcount) { this.nodeThreadcount = nodeThreadcount; } /** * Action to display matching nodes */ static class DefaultNodeFormatter implements NodeFormatter { public StringBuffer formatNodes(Collection nodes) throws Exception { return formatResults(nodes); } StringBuffer formatResults(Collection c) { StringBuffer sb = new StringBuffer(); int i = 0; for (Object aC : c) { INodeEntry node = (INodeEntry) aC; sb.append(node.getNodename()); if (i < c.size() - 1) { sb.append(" "); } i++; } return sb; } } public static interface NodeFormatter { public StringBuffer formatNodes(Collection nodes) throws Exception; } /** * Action to display matching nodes */ static class NodeYAMLFormatter implements NodeFormatter { public StringBuffer formatNodes(final Collection nodes) throws Exception { return generate(nodes); } StringBuffer generate(final Collection c) throws NodesGeneratorException, IOException { final StringWriter writer = new StringWriter(); NodesYamlGenerator gen = new NodesYamlGenerator(writer); for (Object aC : c) { gen.addNode((INodeEntry) aC); } gen.generate(); return writer.getBuffer(); } } InputStream instream = System.in; protected File writeInputToFile(final InputStream input) { verbose("reading stdin and saving it to file..."); final File tempfile; try { tempfile = ScriptfileUtils.writeScriptTempfile(framework, input); } catch (IOException e) { throw new CoreException("error while reading script input from stdin: " + e.getMessage()); } verbose("Wrote stdin input to file: " + tempfile); try { ScriptfileUtils.setExecutePermissions(tempfile); } catch (IOException e) { warn("Failed to set execute permissions on tempfile, execution may fail: " + tempfile.getAbsolutePath()); } return tempfile; } /** * Return the Ant loglevel equivalent to the input flags (verbose,debug,quiet). * * @return loglevel */ public int getAntLoglevel() { if (argDebug) { return Project.MSG_DEBUG; } if (argQuiet && argVerbose) { return Project.MSG_WARN; } if (argVerbose) { return Project.MSG_VERBOSE; } if (argQuiet) { return Project.MSG_ERR; } return Project.MSG_INFO; } public boolean isDebug() { return argDebug; } public boolean isVerbose() { return argVerbose; } public boolean isQuiet() { return argQuiet; } public void log(String message, Map<String, String> context) { if (getAntLoglevel() >= Project.MSG_INFO) { log(message); } } public void error(String message, Map<String, String> context) { if (getAntLoglevel() >= Project.MSG_ERR) { log(message); } } public void warn(String message, Map<String, String> context) { if (getAntLoglevel() >= Project.MSG_WARN) { log(message); } } public void verbose(String message, Map<String, String> context) { if (getAntLoglevel() >= Project.MSG_VERBOSE) { log(message); } } public void debug(String message, Map<String, String> context) { if (getAntLoglevel() >= Project.MSG_DEBUG) { log(message); } } }