/*****************************************************************************
* Copyright (c) 2006-2013, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text of
* such license is available at www.eclipse.org.
*****************************************************************************/
package org.eclipse.buckminster.cmdline;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.buckminster.cmdline.parser.CommandLineParser;
import org.eclipse.buckminster.cmdline.parser.InvalidOptionValueException;
import org.eclipse.buckminster.cmdline.parser.ParseResult;
import org.eclipse.buckminster.runtime.Buckminster;
import org.eclipse.buckminster.runtime.BuckminsterException;
import org.eclipse.buckminster.runtime.BuckminsterPreferences;
import org.eclipse.buckminster.runtime.IOUtils;
import org.eclipse.buckminster.runtime.Logger;
import org.eclipse.buckminster.runtime.Trivial;
import org.eclipse.buckminster.runtime.URLUtils;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.osgi.util.NLS;
/**
* This class controls all aspects of the application's execution
*/
public class Headless implements IApplication, OptionValueType {
public static class Invocation {
private final String name;
private final String[] args;
public Invocation(String name, String[] args) {
this.name = name;
this.args = args == null ? Trivial.EMPTY_STRING_ARRAY : args;
}
public String[] getArgs() {
return args;
}
public String getName() {
return name;
}
@Override
public String toString() {
int nargs = args.length;
if (nargs == 0)
return name;
StringBuffer bld = new StringBuffer();
bld.append(name);
for (int idx = 0; idx < nargs; ++idx) {
bld.append(" '"); //$NON-NLS-1$
bld.append(args[idx]);
bld.append('\'');
}
return bld.toString();
}
}
enum LogType {
console, ant, eclipse
}
/**
* The plug-in ID
*/
public static final String PLUGIN_ID = "org.eclipse.buckminster.cmdline"; //$NON-NLS-1$
static final public int EXIT_FORCED = 2;
static final public int EXIT_FAIL = 1;
static final public int EXIT_OK = 0;
// be a little less user-friendly by displaying nasty stack traces on
// exception
//
private static final OptionDescriptor DISPLAY_STACKTRACE = new OptionDescriptor(null, "displaystacktrace", NONE); //$NON-NLS-1$
// help options, treated equally
//
private static final OptionDescriptor HELP = new OptionDescriptor('?', "help", OptionValueType.NONE); //$NON-NLS-1$
// logging possibilities
//
private static final OptionDescriptor LOG_LEVEL = new OptionDescriptor('L', "loglevel", REQUIRED); //$NON-NLS-1$
// logging possibilities
//
private static final OptionDescriptor FILE = new OptionDescriptor('S', "scriptfile", REQUIRED); //$NON-NLS-1$
static final OptionDescriptor DEFINE_DESCRIPTOR = new OptionDescriptor('D', "define", //$NON-NLS-1$
OptionValueType.REQUIRED);
static final OptionDescriptor PROPERTIES_DESCRIPTOR = new OptionDescriptor('P', "properties", //$NON-NLS-1$
OptionValueType.REQUIRED);
static final Pattern DEFINE_PATTERN = Pattern.compile("^([^=]+)(?:=(.+))?$"); //$NON-NLS-1$
private final ArrayList<Invocation> invocations = new ArrayList<Invocation>();
private Properties props;
private boolean displayStackTrace = false;
private boolean help = false;
private boolean usingScript = false;
private int logLevel = Logger.INFO;
private int antLogLevel = -1;
private static final Pattern commaSplit = Pattern.compile(","); //$NON-NLS-1$
public void addProperty(String key, String value) {
if (props == null)
props = new Properties(System.getProperties());
props.put(key, value);
}
protected void help(PrintStream ps) throws Exception {
PrintStream out = System.out;
InputStream is = getClass().getResourceAsStream("Headless.help"); //$NON-NLS-1$
if (is == null)
out.println(Messages.Headless_Help_is_not_available);
else {
out.println(Messages.Headless_Help_text_for_buckminster);
try {
IOUtils.copy(is, out, null);
out.flush();
} finally {
IOUtils.close(is);
}
}
}
protected void parse(String[] args) throws Exception {
ArrayList<OptionDescriptor> optionArr = new ArrayList<OptionDescriptor>();
optionArr.add(DISPLAY_STACKTRACE);
optionArr.add(FILE);
optionArr.add(HELP);
optionArr.add(LOG_LEVEL);
optionArr.add(DEFINE_DESCRIPTOR);
optionArr.add(PROPERTIES_DESCRIPTOR);
ParseResult pr = ParseResult.parse(args, optionArr);
String scriptFile = null;
Option[] options = pr.getOptions();
int top = options.length;
for (int idx = 0; idx < top; ++idx) {
Option option = options[idx];
if (option.is(HELP))
help = true;
else if (option.is(DISPLAY_STACKTRACE))
displayStackTrace = true;
else if (option.is(FILE))
scriptFile = option.getValue();
else if (option.is(LOG_LEVEL)) {
int level;
for (String levelDecl : commaSplit.split(option.getValue())) {
LogType logType = LogType.console;
int eqIdx = levelDecl.indexOf('=');
if (eqIdx > 0) {
String logTypeName = levelDecl.substring(0, eqIdx);
if ("console".equalsIgnoreCase(logTypeName)) //$NON-NLS-1$
logType = LogType.console;
else if ("ant".equalsIgnoreCase(logTypeName)) //$NON-NLS-1$
logType = LogType.ant;
else
throw new InvalidOptionValueException(option.getName(), option.getValue());
levelDecl = levelDecl.substring(eqIdx + 1);
}
if ("info".equalsIgnoreCase(levelDecl)) //$NON-NLS-1$
level = Logger.INFO;
else if ("warning".equalsIgnoreCase(levelDecl)) //$NON-NLS-1$
level = Logger.WARNING;
else if ("error".equalsIgnoreCase(levelDecl)) //$NON-NLS-1$
level = Logger.ERROR;
else if ("debug".equalsIgnoreCase(levelDecl)) //$NON-NLS-1$
level = Logger.DEBUG;
else
throw new InvalidOptionValueException(option.getName(), option.getValue());
switch (logType) {
case console:
logLevel = level;
if (level == Logger.DEBUG)
displayStackTrace = true;
break;
case ant:
antLogLevel = level;
}
}
} else if (option.is(DEFINE_DESCRIPTOR)) {
String v = option.getValue();
Matcher m = DEFINE_PATTERN.matcher(v);
if (!m.matches())
throw new IllegalArgumentException(NLS.bind(Messages.Not_a_key_value_string_0, v));
String key = m.group(1);
String value = m.group(2) == null ? "" //$NON-NLS-1$
: m.group(2);
addProperty(key, value);
} else if (option.is(PROPERTIES_DESCRIPTOR)) {
String v = option.getValue();
InputStream input = null;
try {
URL propsURL = URLUtils.normalizeToURL(v);
input = new BufferedInputStream(propsURL.openStream());
if (props == null)
props = new Properties(System.getProperties());
props.load(input);
} catch (MalformedURLException e) {
throw new IllegalArgumentException(NLS.bind(Messages.Invalid_URL_or_Path_0, v));
} finally {
IOUtils.close(input);
}
} else
throw new InternalError(Messages.Headless_Unexpected_option);
}
if (props != null)
System.setProperties(props);
String[] unparsed = pr.getUnparsed();
if (unparsed.length > 0) {
if (scriptFile != null)
throw new UsageException(Messages.Headless_The_scriptfile_option_cannot_be_combined_with_a_command, true);
String[] commandArgs = new String[unparsed.length - 1];
System.arraycopy(unparsed, 1, commandArgs, 0, commandArgs.length);
invocations.add(new Invocation(unparsed[0], commandArgs));
} else if (scriptFile != null) {
InputStream lines = null;
try {
if (scriptFile.equals("-")) //$NON-NLS-1$
lines = System.in;
else
lines = new FileInputStream(scriptFile);
LineNumberReader reader = new LineNumberReader(new InputStreamReader(lines));
String line;
while ((line = reader.readLine()) != null) {
CommandLineParser tokenParser = new CommandLineParser(line);
if (tokenParser.hasNext()) {
String command = tokenParser.next();
ArrayList<String> tokens = new ArrayList<String>();
while (tokenParser.hasNext())
tokens.add(tokenParser.next());
invocations.add(new Invocation(command, tokens.toArray(new String[tokens.size()])));
usingScript = true;
}
}
} finally {
if (lines != System.in)
IOUtils.close(lines);
}
}
}
public Object run(Object objArgs) throws Exception {
Buckminster.setHeadless();
int exitValue = EXIT_FAIL;
try {
exitValue = run((String[]) objArgs);
} catch (OperationCanceledException e) {
System.err.println(Messages.Headless_Command_canceled);
} catch (InterruptedException e) {
System.err.println(Messages.Headless_Command_was_interrupted);
} catch (SimpleErrorExitException e) {
System.err.println(e.getMessage());
exitValue = e.getExitValue();
} catch (UsageException e) {
System.err.println(e.getMessage());
if (e.isEmitHelp())
help(System.out);
} catch (Throwable e) {
BuckminsterException.deeplyPrint(e, System.err, displayStackTrace);
}
return new Integer(exitValue);
}
protected int run(String[] args) throws Exception {
int currentAntLogLevel = BuckminsterPreferences.getLogLevelAntLogger();
Properties sysProps = System.getProperties();
try {
parse(args);
Logger.setConsoleLevelThreshold(logLevel);
Logger.setEclipseLoggerLevelThreshold(logLevel);
Logger.setEclipseLoggerToConsole(true);
if (antLogLevel != -1)
BuckminsterPreferences.setLogLevelAntLogger(antLogLevel);
if (help) {
help(System.out);
return EXIT_OK;
}
final IJobManager jobMgr = Job.getJobManager();
int top = invocations.size();
if (top == 0) {
System.out.println(Messages.Headless_No_command_provided_Try_one_of);
System.out.println(Messages.Headless_buckminster__help);
System.out.println(Messages.Headless_buckminster_listcommands);
System.out.println(Messages.Headless_buckminster_command__help);
return EXIT_FAIL;
}
Logger logger = Buckminster.getLogger();
for (int idx = 0; idx < top; ++idx) {
Invocation invocation = invocations.get(idx);
String commandName = invocation.getName();
CommandInfo ci = CommandInfo.getCommand(commandName);
AbstractCommand cmd = ci.createInstance();
jobMgr.setProgressProvider(cmd.getProgressProvider());
if (logger.isDebugEnabled())
logger.debug(invocation.toString());
else if (usingScript)
logger.info(invocation.toString());
int exitValue = cmd.basicRun(commandName, ci, invocation.getArgs());
if (exitValue != EXIT_OK)
return exitValue;
}
} finally {
if (props != null)
System.setProperties(sysProps);
if (antLogLevel != -1)
BuckminsterPreferences.setLogLevelAntLogger(currentAntLogLevel);
}
return EXIT_OK;
};
@Override
public Object start(IApplicationContext context) throws Exception {
return run(context.getArguments().get(IApplicationContext.APPLICATION_ARGS));
}
@Override
public void stop() {
}
}