/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive_grid_cloud_portal.cli; import static com.google.common.base.Throwables.getStackTraceAsString; import static org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES; import static org.ow2.proactive_grid_cloud_portal.cli.CLIException.REASON_UNAUTHORIZED_ACCESS; import static org.ow2.proactive_grid_cloud_portal.cli.RestConstants.DFLT_REST_SCHEDULER_URL; import static org.ow2.proactive_grid_cloud_portal.cli.cmd.AbstractCommand.writeDebugModeUsageWithBreakEndLine; import static org.ow2.proactive_grid_cloud_portal.cli.cmd.AbstractLoginCommand.PROP_PERSISTED_SESSION; import static org.ow2.proactive_grid_cloud_portal.cli.cmd.AbstractLoginCommand.PROP_RENEW_SESSION; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.codehaus.jackson.map.ObjectMapper; import org.ow2.proactive_grid_cloud_portal.cli.cmd.AbstractLoginCommand; import org.ow2.proactive_grid_cloud_portal.cli.cmd.Command; import org.ow2.proactive_grid_cloud_portal.cli.console.AbstractDevice; import org.ow2.proactive_grid_cloud_portal.cli.console.JLineDevice; import com.google.common.collect.ObjectArrays; public abstract class EntryPoint { protected abstract String resourceType(); protected int run(String... args) { CommandFactory commandFactory = null; CommandLine cli = null; AbstractDevice console; ApplicationContext currentContext = new ApplicationContextImpl().currentContext(); // Cannot rely on AbstractCommand#isDebugModeEnabled // because at this step SetDebugModeCommand#execute has not yet been executed // Consequently, SetDebugModeCommand.PROP_DEBUG_MODE is not set even if debug mode is enabled. boolean isDebugModeEnabled = isDebugModeEnabled(args); try { commandFactory = getCommandFactory(currentContext); Options options = commandFactory.supportedOptions(); cli = parseArgs(options, args); } catch (IOException ioe) { System.err.println("An error occurred."); ioe.printStackTrace(System.err); return 1; } catch (ParseException pe) { writeError(currentContext, pe.getMessage(), pe, isDebugModeEnabled); // print usage Command help = commandFactory.commandForOption(new Option("h", null)); if (help != null) { help.execute(currentContext); } return 1; } currentContext.setObjectMapper(new ObjectMapper().configure(FAIL_ON_UNKNOWN_PROPERTIES, false)); currentContext.setRestServerUrl(DFLT_REST_SCHEDULER_URL); currentContext.setResourceType(resourceType()); // retrieve the (ordered) command list corresponding to command-line // arguments List<Command> commands; try { commands = commandFactory.getCommandList(cli, currentContext); } catch (CLIException e) { writeError(currentContext, "An error occurred.", e, isDebugModeEnabled); return 1; } boolean retryLogin = false; try { executeCommandList(commands, currentContext); } catch (CLIException error) { if (REASON_UNAUTHORIZED_ACCESS == error.reason() && hasLoginCommand(commands)) { retryLogin = true; } else { writeError(currentContext, "An error occurred.", error, isDebugModeEnabled); return 1; } } catch (Throwable e) { writeError(currentContext, "An error occurred.", e, isDebugModeEnabled); return 1; } /* * in case of an existing session-id, the REST CLI reuses it without * obtaining a new session-id even if a login with credentials * specified. However if the REST server responds with an authorization * error (e.g. due to session timeout), it re-executes the commands list * with AbstractLoginCommand.PROP_RENEW_SESSION property set to 'true'. * This will effectively re-execute the user command with a new * session-id from server. */ if (retryLogin && currentContext.getProperty(PROP_PERSISTED_SESSION, Boolean.TYPE, false)) { try { currentContext.setProperty(PROP_RENEW_SESSION, true); executeCommandList(commands, currentContext); } catch (Throwable error) { writeError(currentContext, "An error occurred while execution.", error, isDebugModeEnabled); return 1; } } return 0; } protected static CommandFactory getCommandFactory(ApplicationContext currentContext) throws IOException { CommandFactory commandFactory; AbstractDevice console; commandFactory = getCommandFactory(); console = AbstractDevice.getConsole(AbstractDevice.JLINE); ((JLineDevice) console).setCommands(ObjectArrays.concat(commandFactory.supportedCommandEntries(), CommandSet.INTERACTIVE_COMMANDS, CommandSet.Entry.class)); currentContext.setDevice(console); return commandFactory; } private static CommandFactory getCommandFactory() { return CommandFactory.getCommandFactory(CommandFactory.Type.ALL); } protected static CommandLine parseArgs(Options options, String[] args) throws ParseException { return new DefaultParser().parse(options, args); } /* * The arguments are parsed manually because in case of * parsing error (i.e. ParseException is raised), CommandLine object will be null. * However, it is required in that case to know whether debug mode is enabled or not. */ private boolean isDebugModeEnabled(String[] args) { String shortOption = "-" + CommandSet.DEBUG.opt(); String longOption = "--" + CommandSet.DEBUG.longOpt(); for (String arg : args) { if (arg.equals(shortOption) || arg.equals(longOption)) { return true; } } return false; } private void executeCommandList(List<Command> commandList, ApplicationContext currentContext) throws CLIException { for (Command command : commandList) { command.execute(currentContext); } } private void writeError(ApplicationContext currentContext, String errorMsg, Throwable cause, boolean isDebugModeEnabled) { PrintWriter writer = new PrintWriter(currentContext.getDevice().getWriter(), true); writer.printf("%s", errorMsg); if (cause != null) { if (cause.getMessage() != null) { writer.printf("%n%nError message: %s%n", cause.getMessage()); } if (isDebugModeEnabled) { if (cause instanceof CLIException && ((CLIException) cause).stackTrace() != null) { writer.printf("%nStack trace: %s%n", ((CLIException) cause).stackTrace()); } else { writer.printf("%nStack trace: %s%n", getStackTraceAsString(cause)); } } else { writeDebugModeUsageWithBreakEndLine(currentContext); } } } private boolean hasLoginCommand(List<Command> commandList) { for (Command c : commandList) { if (c instanceof AbstractLoginCommand) { return true; } } return false; } }