/*******************************************************************************
* 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.gogo.commands.Argument;
import org.apache.felix.gogo.commands.Command;
import org.apache.felix.gogo.commands.Option;
import org.cloudifysource.dsl.internal.CloudifyConstants;
import org.cloudifysource.dsl.internal.CloudifyConstants.InvocationStatus;
import org.cloudifysource.dsl.rest.request.InvokeCustomCommandRequest;
import org.cloudifysource.dsl.rest.response.InvokeInstanceCommandResponse;
import org.cloudifysource.dsl.rest.response.InvokeServiceCommandResponse;
import org.cloudifysource.restclient.InvocationResult;
import org.cloudifysource.restclient.RestClient;
import org.cloudifysource.shell.exceptions.CLIStatusException;
import org.cloudifysource.shell.rest.RestAdminFacade;
/**
* @author rafi, adaml, barakm
* @since 2.0.0
*
* Invokes a custom command on a specific service over REST.
*
* Required arguments: service-name - The service to invoke the command on command-name - The name of the command
* to invoke params - Command parameters
*
* Optional arguments: beanname - Bean name instanceid - If provided, the command will be invoked only on that
* specific instance
*
* Command syntax: invoke [-instanceid instanceid] service-name command-name params
*/
@Command(scope = "cloudify", name = "invoke", description = "invokes a custom command")
public class Invoke extends AdminAwareCommand implements NewRestClientCommand {
@Argument(index = 0, name = "service-name", required = true, description = " the service to invoke the command on")
private String serviceName;
@Argument(index = 1, name = "command-name", required = true, description = "the name of the command to invoke")
private String commandName;
@Option(name = "-instanceid", description = "If provided, the command will be invoked only on that specific "
+ "instance")
private Integer instanceId;
@Argument(index = 2, multiValued = true, name = "params", required = false, description = "Command Custom "
+ "parameters.")
private final List<String> params = new ArrayList<String>();
/**
* {@inheritDoc}
*/
@Override
protected Object doExecute()
throws Exception {
// Containing all the success invocation messages.
final StringBuilder invocationSuccessStringBuilder = new StringBuilder("Invocation results: ");
invocationSuccessStringBuilder.append(System.getProperty("line.separator"));
String applicationName = this.getCurrentApplicationName();
if (applicationName == null) {
applicationName = "default";
}
Map<String, String> paramsMap = new HashMap<String, String>();
if (params != null) {
paramsMap = getParamsMap(params);
}
// Containing all the failed invocation messages.
final StringBuilder invocationFailedStringBuilder = new StringBuilder();
if (instanceId == null) { // Invoking command on all of the service's instances.
final Map<String, InvocationResult> result = adminFacade.invokeServiceCommand(applicationName,
serviceName, CloudifyConstants.INVOCATION_PARAMETER_BEAN_NAME_USM, commandName, paramsMap);
final Collection<InvocationResult> values = result.values();
final List<InvocationResult> valuesList = new ArrayList<InvocationResult>(values);
Collections.sort(valuesList);
for (final InvocationResult invocationResult : valuesList) {
if (invocationResult.isSuccess()) {
final String successMessage = getFormattedMessage("invocation_success",
invocationResult.getInstanceId(), invocationResult.getInstanceName(),
invocationResult.getResult());
invocationSuccessStringBuilder.append(successMessage).append(System.getProperty("line.separator"));
} else {
final String failedMessage = getFormattedMessage("invocation_failed",
invocationResult.getInstanceId(), invocationResult.getInstanceName(),
invocationResult.getExceptionMessage());
invocationFailedStringBuilder.append(failedMessage).append(System.getProperty("line.separator"));
}
}
} else { // instanceID specified. invoking command on specific instance.
final InvocationResult invocationResult = adminFacade.invokeInstanceCommand(applicationName, serviceName,
CloudifyConstants.INVOCATION_PARAMETER_BEAN_NAME_USM, instanceId, commandName, paramsMap);
if (invocationResult.isSuccess()) {
final String successMessage = getFormattedMessage("invocation_success",
invocationResult.getInstanceId(), invocationResult.getInstanceName(),
invocationResult.getResult());
invocationSuccessStringBuilder.append(successMessage).append(System.getProperty("line.separator"));
} else {
final String failedMessage = getFormattedMessage("invocation_failed",
invocationResult.getInstanceId(), invocationResult.getInstanceName(),
invocationResult.getExceptionMessage());
invocationFailedStringBuilder.append(failedMessage).append(System.getProperty("line.separator"));
}
}
// print the success messages to the screen.
logger.info(invocationSuccessStringBuilder.toString());
if (invocationFailedStringBuilder.length() != 0) {
throw new CLIStatusException("some_invocations_failed", this.serviceName,
invocationFailedStringBuilder.toString());
}
return getFormattedMessage("all_invocations_completed_successfully");
}
@Override
public Object doExecuteNewRestClient() throws Exception {
String successMessages = "";
String unexpectedMessages = "";
String failureMessages = "";
logger.fine("Invoking command " + commandName + " using the new rest client");
final RestClient newRestClient = ((RestAdminFacade) getRestAdminFacade()).getNewRestClient();
InvokeCustomCommandRequest request = new InvokeCustomCommandRequest();
request.setCommandName(commandName);
request.setParameters(params);
String applicationName = this.getCurrentApplicationName();
if (applicationName == null) {
applicationName = "default";
}
if (instanceId == null) { // Invoking command on all of the service's instances.
InvokeServiceCommandResponse response = newRestClient.invokeServiceCommand(applicationName, serviceName,
request);
final Map<String, InvocationResult> invocationResults = parseInvocationResults(
response.getInvocationResultPerInstance());
final List<InvocationResult> resultsList = new ArrayList<InvocationResult>(invocationResults.values());
Collections.sort(resultsList);
successMessages = getAllSuccessMessages(resultsList);
unexpectedMessages = getAllUnexpectedMessages(resultsList);
failureMessages = getAllFailureMessages(resultsList);
} else {
// instanceID specified. invoking command on specific instance.
InvokeInstanceCommandResponse response = newRestClient.invokeInstanceCommand(applicationName, serviceName,
instanceId, request);
final InvocationResult invocationResult = parseInvocationResult(response.getInvocationResult());
if (invocationResult.getInvocationStatus() == InvocationStatus.SUCCESS) {
successMessages = getSuccessMessage(invocationResult);
} else if (invocationResult.getInvocationStatus() == InvocationStatus.UNEXPECTED) {
unexpectedMessages = getUnexpectedMessage(invocationResult);
} else {
failureMessages = getFailureMessage(invocationResult);
}
}
// if invocations failed - throw exception
if (StringUtils.isNotBlank(failureMessages)) {
throw new CLIStatusException("some_invocations_failed", this.serviceName,
System.getProperty("line.separator") + failureMessages);
}
// if invocations returned unexpected value - throw exception
if (StringUtils.isNotBlank(unexpectedMessages)) {
//logger.severe("Received an unexpected return value to the invoke command. Key: "
// + instanceName + ", value: " + restInvocationResult);
throw new CLIStatusException("some_invocations_failed", this.serviceName,
System.getProperty("line.separator") + unexpectedMessages);
}
// print the success messages to the screen
if (StringUtils.isNotBlank(successMessages)) {
logger.info("Invocation results: " + System.getProperty("line.separator") + successMessages);
}
return getFormattedMessage("all_invocations_completed_successfully");
}
private Map<String, String> getParamsMap(final List<String> parameters) {
int index = 0;
final Map<String, String> returnMap = new HashMap<String, String>();
if (parameters == null) {
return returnMap;
}
for (final String param : parameters) {
returnMap.put(CloudifyConstants.INVOCATION_PARAMETERS_KEY + index, param);
++index;
}
return returnMap;
}
private Map<String, InvocationResult> parseInvocationResults(
final Map<String, Map<String, String>> restInvocationResultsPerInstance) {
final Map<String, InvocationResult> invocationResultsMap = new LinkedHashMap<String, InvocationResult>();
for (final Map.Entry<String, Map<String, String>> entry : restInvocationResultsPerInstance.entrySet()) {
final String instanceName = entry.getKey();
final Map<String, String> restInvocationResult = entry.getValue();
invocationResultsMap.put(instanceName, parseInvocationResult(restInvocationResult));
}
return invocationResultsMap;
}
private InvocationResult parseInvocationResult(final Map<String, String> restInvocationResult) {
InvocationResult invocationResult = null;
if (restInvocationResult != null) {
invocationResult = InvocationResult.createInvocationResult(restInvocationResult);
}
return invocationResult;
}
private String getAllSuccessMessages(final List<InvocationResult> resultsList) {
final StringBuilder successMessagesText = new StringBuilder();
for (final InvocationResult invocationResult : resultsList) {
if (invocationResult.getInvocationStatus() == InvocationStatus.SUCCESS) {
String successMessage = getSuccessMessage(invocationResult);
successMessagesText.append(successMessage).append(System.getProperty("line.separator"));
}
}
return successMessagesText.toString();
}
private String getAllUnexpectedMessages(final List<InvocationResult> resultsList) {
final StringBuilder unexpectedMessagesText = new StringBuilder();
for (final InvocationResult invocationResult : resultsList) {
if (invocationResult.getInvocationStatus() == InvocationStatus.UNEXPECTED) {
String unexpectedMessage = getUnexpectedMessage(invocationResult);
unexpectedMessagesText.append(unexpectedMessage).append(System.getProperty("line.separator"));
}
}
return unexpectedMessagesText.toString();
}
private String getAllFailureMessages(final List<InvocationResult> resultsList) {
final StringBuilder failureMessagesText = new StringBuilder();
for (final InvocationResult invocationResult : resultsList) {
if (invocationResult.getInvocationStatus() == InvocationStatus.FAILURE) {
String failureMessage = getFailureMessage(invocationResult);
failureMessagesText.append(failureMessage).append(System.getProperty("line.separator"));
}
}
return failureMessagesText.toString();
}
private String getSuccessMessage(final InvocationResult invocationResult) {
return getFormattedMessage("invocation_success",
invocationResult.getInstanceId(), invocationResult.getInstanceName(),
invocationResult.getResult());
}
private String getUnexpectedMessage(final InvocationResult invocationResult) {
return getFormattedMessage("invocation_unexpected_result",
invocationResult.getInstanceName(),
invocationResult.getResult());
}
private String getFailureMessage(final InvocationResult invocationResult) {
return getFormattedMessage("invocation_failed",
invocationResult.getInstanceId(), invocationResult.getInstanceName(),
invocationResult.getExceptionMessage());
}
}