/*******************************************************************************
* 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.esc.util;
import java.util.regex.Pattern;
import org.cloudifysource.domain.cloud.RemoteExecutionModes;
/*******
* A simple wrapper around a StringBuilder. Used to generate the command line
* required to run the remote Cloudify agent.
*
* @author dank, barakme
* @since 1.0
*
*/
public class ShellCommandBuilder {
private static final String CIFS_ABSOLUTE_PATH_WITH_DRIVE_REGEX = "/[a-zA-Z][$]/.*";
private static final String SSH_COMMAND_SEPARATOR = ";";
private static final String POWERSHELL_COMMAND_SEPARATOR = ";"; // System.getProperty("line.separator");
private static Pattern pattern;
private final StringBuilder sb = new StringBuilder();
private String separator;
/******
* Enum for supported script languages.
*
* @author barakme
*
*/
public enum ScriptLanguages {
/**
* Linux shell script.
*/
LINUX_SHELL,
/****
* Windows powershell.
*/
WINDOWS_POWERSHELL
}
private boolean runInBackground = false;
private RemoteExecutionModes mode = RemoteExecutionModes.SSH;
/********
* Constructor.
*
* @param mode
* the execution mode.
*/
public ShellCommandBuilder(final RemoteExecutionModes mode) {
this.mode = mode;
switch (mode) {
case SSH:
this.separator = SSH_COMMAND_SEPARATOR;
break;
case WINRM:
this.separator = POWERSHELL_COMMAND_SEPARATOR;
break;
default:
throw new UnsupportedOperationException(
"Unsupported execution mode: " + mode);
}
}
/******
* Default constructor, using LINUX_SHELL.
*/
public ShellCommandBuilder() {
this(RemoteExecutionModes.SSH);
}
/*******
* Adds a command to the command line.
*
* @param str
* the command to add.
* @return this.
*/
public ShellCommandBuilder call(final String str) {
if (this.mode == RemoteExecutionModes.SSH) {
sb.append(str);
} else {
final String normalizedPathCommand = normalizeCifsPath(str);
sb.append(normalizedPathCommand);
}
return this;
}
/****************
* Given a path of the type /C$/PATH - indicating an absolute cifs path,
* returns /PATH. If the string does not match, returns the original
* unmodified string.
*
* @param str
* the input path.
* @return the input path, adjusted to remove the cifs drive letter, if it
* exists, or the original path if the drive letter is not present.
*/
public static String normalizeCifsPath(final String str) {
final String expression = CIFS_ABSOLUTE_PATH_WITH_DRIVE_REGEX;
if (pattern == null) {
pattern = Pattern.compile(expression);
}
if (str == null) {
return null;
}
if (pattern.matcher(str).matches()) {
final char drive = str.charAt(1);
return drive + ":\\"
+ str.substring("/c$/".length()).replace('/', '\\');
}
return str;
}
/********
* Adds a separator.
*
* @return this.
*/
public ShellCommandBuilder separate() {
sb.append(this.separator);
return this;
}
private static final java.util.logging.Logger logger = java.util.logging.Logger
.getLogger(ShellCommandBuilder.class.getName());
/*********
* Adds an environment variable to the command line.
*
* @param name
* variable name.
* @param value
* variable value.
* @return this.
*/
public ShellCommandBuilder exportVar(final String name, final String value) {
logger.fine("exporting var: " + name + " with value " + value);
String actualValue = value;
if (value == null) {
actualValue = "";
}
switch (this.mode) {
case SSH:
sb.append("export ").append(name).append("=").append(actualValue);
break;
case WINRM:
String normalizedValue = normalizeCifsPath(actualValue);
if (normalizedValue.startsWith("\"") && normalizedValue.endsWith("\"")) {
normalizedValue = normalizedValue.replace("\"", "'");
} else {
if (!(normalizedValue.startsWith("\"") && normalizedValue.endsWith("\""))) {
normalizedValue = "'" + normalizedValue + "'";
}
}
sb.append("$ENV:").append(name).append("=").append(normalizedValue);
break;
default:
// not possible.
break;
}
separate();
return this;
}
/******
* Marks a file as executable.
*
* @param path
* the file path.
* @return this.
*/
public ShellCommandBuilder chmodExecutable(final String path) {
switch (this.mode) {
case SSH:
sb.append("chmod +x ").append(path);
separate();
break;
default:
break;
}
return this;
}
/*****
* Marks a command line to be executed in the background.
*
* @return this.
*/
public ShellCommandBuilder runInBackground() {
switch (this.mode) {
case SSH:
sb.append(" &");
break;
default:
break;
}
this.runInBackground = true;
return this;
}
@Override
public String toString() {
return sb.toString();
}
public boolean isRunInBackground() {
return runInBackground;
}
}