/******************************************************************************* * Copyright (c) 2011 GigaSpaces Technologies Ltd. All rights reserved * * 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 org.cloudifysource.shell.commands; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.felix.gogo.commands.Action; import org.apache.felix.gogo.commands.Option; import org.apache.felix.service.command.CommandSession; import org.apache.karaf.shell.console.CloseShellException; import org.cloudifysource.restclient.exceptions.RestClientException; import org.cloudifysource.restclient.exceptions.RestClientHttpException; import org.cloudifysource.restclient.utils.NewRestClientUtils; import org.cloudifysource.shell.AdminFacade; import org.cloudifysource.shell.Constants; import org.cloudifysource.shell.ShellUtils; import org.cloudifysource.shell.exceptions.CLIException; import org.cloudifysource.shell.exceptions.CLIStatusException; import org.cloudifysource.shell.exceptions.CLIValidationException; import org.cloudifysource.shell.exceptions.handlers.CLIExceptionHandler; import org.cloudifysource.shell.exceptions.handlers.CLIStatusExceptionHandler; import org.cloudifysource.shell.exceptions.handlers.CLIValidationExceptionHandler; import org.cloudifysource.shell.exceptions.handlers.ClientSideExceptionHandler; import org.cloudifysource.shell.exceptions.handlers.InterruptedExceptionHandler; import org.cloudifysource.shell.exceptions.handlers.RestClientExceptionHandler; import org.cloudifysource.shell.exceptions.handlers.RestClientHttpExceptionHandler; import org.cloudifysource.shell.exceptions.handlers.ThrowableHandler; import org.fusesource.jansi.Ansi.Color; /** * @author rafi, barakm * @since 2.0.0 * <p/> * Abstract implementation of the {@link org.apache.felix.gogo.commands.Action} interface. This abstraction can * be extended by Cloudify aware commands to get support for: - Formatted messages from the message bundle - * Logging (with verbose mode - true/false) - AdminFacade such as the RestAdminFacade, when required by the * command (AKA admin-aware commands). */ public abstract class AbstractGSCommand implements Action { protected static final Logger logger = Logger.getLogger(AbstractGSCommand.class.getName()); @Option(required = false, name = "--verbose", description = "show detailed execution result including exception stack trace") protected boolean verbose; protected CommandSession session; protected ResourceBundle messages; protected boolean adminAware = false; protected AdminFacade adminFacade; /** * Initializes the messages bundle, and takes the admin facade objects from the session when command is admin-aware. * Calls doExecute (must be implemented separately in the extending classes). * * @param session * The command session to be used. * @return Object The object returned from the call to doExecute * @throws Exception * Reporting a failure to execute this command */ @Override public Object execute(final CommandSession session) throws Exception { // setUpLoggingLevel(); //see CLOUDIFY-1558 this.session = session; messages = ShellUtils.getMessageBundle(); try { if (adminAware) { adminFacade = (AdminFacade) session.get(Constants.ADMIN_FACADE); if (!adminFacade.isConnected()) { throw new CLIStatusException("not_connected"); } } // check if this command implements NewRestClientCommand and if the new rest client is enabled. // if so, execute this command using the new rest client. if (this instanceof NewRestClientCommand && NewRestClientUtils.isNewRestClientEnabled()) { if (logger.isLoggable(Level.FINE)) { logger.fine("executing " + this.getClass().getName() + " command using the new rest client."); } return ((NewRestClientCommand) this).doExecuteNewRestClient(); } // else, execute this command using the old rest client. if (logger.isLoggable(Level.FINE)) { logger.fine("executing " + this.getClass().getName() + " command using the old rest client."); } return doExecute(); } catch (final CLIStatusException e) { handle(new CLIStatusExceptionHandler(e), e); } catch (final CLIValidationException e) { handle(new CLIValidationExceptionHandler(e), e); } catch (final CLIException e) { handle(new CLIExceptionHandler(e), e); } catch (final RestClientHttpException e) { handle(new RestClientHttpExceptionHandler(e), e); } catch (final RestClientException e) { handle(new RestClientExceptionHandler(e), e); } catch (final InterruptedException e) { handle(new InterruptedExceptionHandler(), e); } catch (final Throwable t) { handle(new ThrowableHandler(t), t); } return getFormattedMessage("op_failed", Color.RED, ""); } private void handle(final ClientSideExceptionHandler clientSideExceptionHandler, final Throwable t) throws CloseShellException { if (logger.isLoggable(clientSideExceptionHandler.getLoggingLevel())) { logger.log(clientSideExceptionHandler.getLoggingLevel(), clientSideExceptionHandler.getMessage(verbose)); } if (t instanceof CLIValidationException) { System.exit(((CLIValidationException) t).getExitCode()); } else { raiseCloseShellExceptionIfNonInteractive(session, t); } } /** * If not using the CLI in interactive mode - the method adds the given throwable to the session and throws a * CloseShellException. * * @param session * current command session * @param t * The throwable to add to the session * @throws CloseShellException * Indicates the console to close. */ private static void raiseCloseShellExceptionIfNonInteractive(final CommandSession session, final Throwable t) throws CloseShellException { if (!(Boolean) session.get(Constants.INTERACTIVE_MODE)) { session.put(Constants.LAST_COMMAND_EXCEPTION, t); throw new CloseShellException(); } } /** * Gets the name of the current application. * * @return Name of the current application, as a String. */ protected final String getCurrentApplicationName() { if (session == null) { return null; } return (String) session.get(Constants.ACTIVE_APP); } /** * Gets a message from the message bundle, without argument. If the message cannot be retrieved the message name is * returned and the failure is logged. * * @param msgName * the message name used to retrieve from the message bundle * @return formatted message as a String */ protected final String getFormattedMessage(final String msgName) { return getFormattedMessage( msgName, new Object[0]); } /** * Gets a message from the message bundle, embedded with the given arguments if supplied. If the message cannot be * retrieved the message name is returned and the failure is logged. * * @param msgName * the message name used to retrieve from the message bundle * @param color * The color to be used when displaying the message in the console * @param arguments * arguments to embed in the message text * @return formatted message as a String */ protected final String getFormattedMessage(final String msgName, final Color color, final Object... arguments) { final String outputMessage = getFormattedMessage(msgName, arguments); return ShellUtils.getColorMessage(outputMessage, color); } /** * Gets a message from the message bundle, embedded with the given arguments if supplied. If the message cannot be * retrieved the message name is returned and the failure is logged. * * @param msgName * the message name used to retrieve from the message bundle * @param arguments * arguments to embed in the message text * @return formatted message as a String */ protected final String getFormattedMessage(final String msgName, final Object... arguments) { return ShellUtils.getFormattedMessage(msgName, arguments); } /** * The main method to be implemented by all extending classes. * * @return Object Return value from the executed command * @throws Exception * Reporting a failure to execute this command */ protected abstract Object doExecute() throws Exception; /** * Gets the restAdminFacade. * * @return The AdminFacade object used by rest commands */ protected AdminFacade getRestAdminFacade() { return (AdminFacade) session.get(Constants.ADMIN_FACADE); } }