/*****************************************************************************
* Copyright (c) 2012-2015 VMware, Inc. 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 com.vmware.bdd.cli.commands;
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jline.console.ConsoleReader;
import jline.internal.Configuration;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;
import org.fusesource.jansi.internal.Kernel32;
import org.springframework.shell.core.JLineShell;
import org.springframework.shell.support.util.OsUtils;
import org.springframework.util.ClassUtils;
import com.vmware.bdd.apitypes.NodeGroupRead;
import com.vmware.bdd.apitypes.NodeRead;
import com.vmware.bdd.utils.CommonUtil;
public class CommandsUtils {
static final Logger logger = Logger.getLogger(CommandsUtils.class);
public static List<String> inputsConvert(String inputs) {
return CommonUtil.inputsConvert(inputs);
}
public static Set<String> inputsConvertSet(String inputs) {
return CommonUtil.inputsConvertSet(inputs);
}
public static String prettyRoleOutput(List<String> roles,
final String delimiter) {
StringBuilder roleStr = new StringBuilder();
if (roles != null) {
for (String role : roles) {
roleStr.append(role + delimiter);
}
}
if (roleStr.length() > 0) {
roleStr.deleteCharAt(roleStr.length() - 1);
}
return roleStr.toString();
}
public static String dataFromFile(String filePath) throws IOException,
FileNotFoundException {
StringBuilder dataStringBuffer = new StringBuilder();
FileInputStream fis = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
try {
fis = new FileInputStream(filePath);
inputStreamReader = new InputStreamReader(fis, "UTF-8");
bufferedReader = new BufferedReader(inputStreamReader);
String line = "";
while ((line = bufferedReader.readLine()) != null) {
dataStringBuffer.append(line);
dataStringBuffer.append("\n");
}
} finally {
if (fis != null) {
fis.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (bufferedReader != null) {
bufferedReader.close();
}
}
return dataStringBuffer.toString();
}
public static <T> T getObjectByJsonString(Class<T> entityType, String jsonString) throws JsonParseException,
JsonMappingException, IOException {
ObjectMapper mapper = getMapper();
T mappedObject = null;
mappedObject = mapper.readValue(jsonString, entityType);
return mappedObject;
}
public static void prettyJsonOutput(Object object, String fileName)
throws Exception {
OutputStream out = null;
try {
if (fileName != null) {
out = new FileOutputStream(fileName);
} else {
out = System.out;
}
JsonFactory factory = new JsonFactory();
JsonGenerator generator = factory.createJsonGenerator(out);
ObjectMapper mapper = getMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
generator.setCodec(mapper);
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
DefaultPrettyPrinter.Indenter indenter = new DefaultPrettyPrinter.Lf2SpacesIndenter();
prettyPrinter.indentArraysWith(indenter);
generator.setPrettyPrinter(prettyPrinter);
generator.writeObject(object);
writeEndingMsgToScreen(fileName);
} finally {
if (out != null && !(out instanceof PrintStream)) {
out.close();
}
}
}
/*
* Determine if it is the window OS
*/
public static boolean isJansiAvailable() {
return ClassUtils.isPresent("org.fusesource.jansi.AnsiConsole",
JLineShell.class.getClassLoader())
&& OsUtils.isWindows()
&& System.getProperty("jline.terminal") == null;
}
/**
* Check whether the given String has actual text. More specifically, returns
* <code>false</code> if the string not <code>null</code>, its length is
* greater than 0, and it contains at least one non-whitespace character.
* <p>
*
* <pre>
* CommandsUtils.isBlank(null) = true
* CommandsUtils.isBlank("") = true
* CommandsUtils.isBlank(" ") = true
* CommandsUtils.isBlank("12345") = false
* CommandsUtils.isBlank(" 12345 ") = false
* </pre>
*
* @param str
* the String to check(may be null).
* @return the opposite of
*/
public static boolean isBlank(final String str) {
return CommonUtil.isBlank(str);
}
public static String notNull(final String str, final String desStr) {
return CommonUtil.notNull(str, desStr);
}
/**
* Show a table(include table column names and table contents) by left
* justifying. More specifically, the {@code columnNamesWithGetMethodNames}
* argument is a map struct, the key is table column name and value is method
* name list which it will be invoked by reflection. The {@code entities}
* argument is traversed entity array.It is source of table data. In
* addition,the method name must be each of the {@code entities} argument 's
* member. The {@code spacesBeforeStart} argument is whitespace in the front
* of the row.
* <p>
*
* @param columnNamesWithGetMethodNames
* the container of table column name and invoked method name.
* @param entities
* the traversed entity array.
* @param spacesBeforeStart
* the whitespace in the front of the row.
* @throws Exception
*/
public static void printInTableFormat(
LinkedHashMap<String, List<String>> columnNamesWithGetMethodNames,
Object[] entities, String spacesBeforeStart) throws Exception {
if (entities != null && entities.length > 0) {
// get number of columns
int columnNum = columnNamesWithGetMethodNames.size();
String[][] table = new String[entities.length + 1][columnNum];
//build table header: column names
String[] tableHeader = new String[columnNum];
Set<String> columnNames = columnNamesWithGetMethodNames.keySet();
columnNames.toArray(tableHeader);
//put table column names into the first row
table[0] = tableHeader;
//build table contents
Collection<List<String>> getMethodNamesCollect =
columnNamesWithGetMethodNames.values();
int i = 1;
for (Object entity : entities) {
int j = 0;
for (List<String> getMethodNames : getMethodNamesCollect) {
Object tempValue = null;
int k = 0;
for (String methodName : getMethodNames) {
if (tempValue == null)
tempValue = entity;
Object value =
tempValue.getClass().getMethod(methodName)
.invoke(tempValue);
if (k == getMethodNames.size() - 1) {
table[i][j] =
value == null ? ""
: ((value instanceof Double) ? String
.valueOf(round(
((Double) value).doubleValue(), 2,
BigDecimal.ROUND_FLOOR)) : value
.toString());
if (isJansiAvailable() && !isBlank(table[i][j])) {
table[i][j] = transferEncoding(table[i][j]);
}
j++;
} else {
tempValue = value;
k++;
}
}
}
i++;
}
printTable(table, spacesBeforeStart);
}
}
public static void printInTableFormat(LinkedHashMap<String, List<String>> columnNamesWithKeys,
List<Map> entities, String spacesBeforeStart) throws Exception {
if(MapUtils.isNotEmpty(columnNamesWithKeys)) {
int columnNum = columnNamesWithKeys.size();
if(CollectionUtils.isNotEmpty(entities)) {
String[][] table = new String[entities.size() + 1][columnNum];
//build table header: column names
String[] tableHeader = table[0];
int rowIndex = 1;
int columnIndex = 0;
for (Map<String, String> entity : entities) {
for( Entry<String, List<String>> columnNameEntry : columnNamesWithKeys.entrySet()) {
if(tableHeader[columnIndex] == null) {
tableHeader[columnIndex] = columnNameEntry.getKey();
}
StringBuilder value = new StringBuilder();
for (String key : columnNameEntry.getValue()) {
if(value.length() > 0) {
value.append(',');
}
Object valueObj = entity.get(key);
if(valueObj == null) {
value.append(' ');
} else {
if(valueObj instanceof Double) {
value.append(String.valueOf(round(((Double) valueObj).doubleValue(), 2, BigDecimal.ROUND_FLOOR)));
} else {
value.append(valueObj);
}
}
}
if (isJansiAvailable()) {
table[rowIndex][columnIndex] = transferEncoding(value.toString());
} else {
table[rowIndex][columnIndex] = value.toString();
}
columnIndex ++;
}
rowIndex ++;
}
printTable(table, spacesBeforeStart);
}
}
}
private static void printTable(String[][] table, String spacesBeforeStart) {
// find the maximum length of a string in each column
int numOfColumns = table[0].length;
int[] lengths = new int[numOfColumns];
for (int i = 0; i < table.length; i++) {
for (int j = 0; j < numOfColumns; j++) {
if (table[i][j] != null) {
lengths[j] = Math.max(table[i][j].length(), lengths[j]);
}
}
}
// generate a format string for each column
String[] formats = new String[numOfColumns];
for (int i = 0; i < lengths.length; i++) {
lengths[i] +=
(i + 1 == numOfColumns) ? 0 : Constants.FORMAT_COLUMN_DISTANCE;
formats[i] =
"%1$-" + lengths[i] + "s"
+ (i + 1 == lengths.length ? "\n" : "");
}
// print out
for (int i = 0; i < table.length; i++) {
System.out.print(spacesBeforeStart);
//print '------'
if (i == 1) {
StringBuilder outputBuffer = new StringBuilder();
for (int l : lengths) {
for (int k = 0; k < l; k++)
outputBuffer.append("-");
}
outputBuffer.append("\n").append(spacesBeforeStart);
System.out.print(outputBuffer.toString());
}
for (int j = 0; j < table[i].length; j++) {
System.out.printf(formats[j], table[i][j]);
}
}
System.out.println();
}
public static void printCmdSuccess(String objectType,
String result) {
System.out.println(objectType + " " + result);
}
public static void printCmdFailure(String objectType,
String opName, String result, String message) {
if (isJansiAvailable() && !isBlank(message)) {
try {
message = transferEncoding(message);
} catch (UnsupportedEncodingException|CliException e) {
logger.warn("failed to transferEncoding: " + e.getMessage());
}
}
if (!isBlank(opName)) {
System.out.println(objectType + " " + opName + " " + result + ": "
+ message);
} else {
System.out.println(objectType + " " + result + ": " + message);
}
}
public static void prettyOutputErrorNode(List<NodeGroupRead> nodegroups)
throws Exception {
List<NodeRead> failedNodes = new ArrayList<NodeRead>();
for (NodeGroupRead nodegroup : nodegroups) {
List<NodeRead> nodes = nodegroup.getInstances();
if (nodes != null) {
for (NodeRead node : nodes) {
if (node.isActionFailed()) {
failedNodes.add(node);
}
}
}
}
if (!failedNodes.isEmpty()) {
System.out.println();
System.out
.println(Constants.FAILED_NODES_MESSAGE + failedNodes.size());
CommandsUtils.printSeperator();
for (NodeRead failedNode : failedNodes) {
System.out.println(" [" + Constants.FORMAT_TABLE_COLUMN_NAME + "] " + failedNode.getName());
System.out.println(" [" + Constants.FORMAT_TABLE_COLUMN_STATUS + "] " + failedNode.getStatus());
System.out.println(" [" + Constants.FORMAT_TABLE_COLUMN_ERROR + "] " + failedNode.getErrMessage());
CommandsUtils.printSeperator();;
}
System.out.println();
}
}
private static void printSeperator() {
StringBuffer seperator =
new StringBuffer().append(Constants.OUTPUT_INDENT);
for (int i = 0; i < Constants.SEPERATOR_LEN; i++) {
seperator.append("-");
}
System.out.println(seperator.toString());
}
/**
* Take the accuracy of double data.
* <p>
* For example: <br>
* A double value = 100.345678; <br>
* The Double ret = round (value, 4, BigDecimal.ROUND_HALF_UP); <br>
* "Ret 100.3457 <br>
*
* @param value
* Double data value. @param scale Precision digits (reserve of
* decimal digits).
* @param roundingMode
* Precision value way.
* @return Precision calculation of data.
*/
private static double round(double value, int scale, int roundingMode) {
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(scale, roundingMode);
double d = bd.doubleValue();
return d;
}
private static ObjectMapper getMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
return mapper;
}
public static Properties readProperties(String propertiesFilePath) {
Properties properties = new Properties();
FileInputStream fis = null;
try {
File file = new File(propertiesFilePath);
if (!file.exists()) {
return null;
}
fis = new FileInputStream(propertiesFilePath);
properties.load(fis);
return properties;
} catch (IOException e) {
System.out.println(e.getMessage());
return null;
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
}
public static void writeProperties(Properties properties,
String propertiesFilePath) {
FileOutputStream fos = null;
try {
Properties prop = null;
File file = new File(propertiesFilePath);
if (file.exists()) {
prop = new Properties();
prop.load(new FileInputStream(propertiesFilePath));
prop.putAll(properties);
} else {
prop = properties;
}
fos = new FileOutputStream(propertiesFilePath);
prop.store(fos, "");
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
System.out.println(e1.getMessage());
}
}
}
}
public static boolean showWarningMsg(final String name,
final String targetObject, final String operateType,
final List<String> warningMsgList, final boolean alwaysAnswerYes, String promptMsg) {
if (warningMsgList != null && !warningMsgList.isEmpty()) {
for (String message : warningMsgList) {
System.out.println(message);
}
if (!isContinue(name, targetObject, operateType,
promptMsg != null ? promptMsg : Constants.PARAM_PROMPT_CONTINUE_MESSAGE, alwaysAnswerYes)) {
return false;
}
}
return true;
}
private static boolean isContinue(final String name,
final String targetObject, final String operateType,
final String promptMsg, final boolean alwaysAnswerYes) {
if (alwaysAnswerYes) {
return true;
}
boolean continueCreate = true;
boolean continueLoop = true;
String readMsg = "";
try {
ConsoleReader reader = new ConsoleReader();
// Set prompt message
reader.setPrompt(promptMsg);
int k = 0;
while (continueLoop) {
if (k >= 3) {
continueCreate = false;
break;
}
// Read user input
readMsg = reader.readLine();
if ("yes".equalsIgnoreCase(readMsg.trim())
|| "y".equalsIgnoreCase(readMsg.trim())) {
continueLoop = false;
} else if ("no".equalsIgnoreCase(readMsg.trim())
|| "n".equalsIgnoreCase(readMsg.trim())) {
continueLoop = false;
continueCreate = false;
} else {
k++;
}
}
} catch (Exception e) {
CommandsUtils.printCmdFailure(targetObject, operateType,
Constants.OUTPUT_OP_RESULT_FAIL, e.getMessage());
continueCreate = false;
}
return continueCreate;
}
public static String getExceptionMessage(Exception e) {
if (e.getCause() == null) {
return e.getMessage();
} else {
return getRootCause(e).getMessage();
}
}
private static Throwable getRootCause(Exception e) {
Throwable cause = e.getCause();
Throwable rootCause = null;
while (cause != null) {
rootCause = cause;
cause = cause.getCause();
}
return rootCause;
}
/*
* Transfer terminal output encoding to system encoding.
* It only take effect on windows OS.
*/
public static String transferEncoding(final String src)
throws UnsupportedEncodingException, CliException {
// Return CMD output code page.
int codePage = Kernel32.GetConsoleOutputCP();
String outputEncoding = "ms" + codePage;
if (!java.nio.charset.Charset.isSupported(outputEncoding)) {
outputEncoding = "cp" + codePage;
if (!java.nio.charset.Charset.isSupported(outputEncoding)) {
String errorMsg =
"Cannot figure out the Java Charset of this code page ("
+ codePage + ")...";
logger.error("CommandsUtils::transferEncoding: " + errorMsg);
throw new CliException(errorMsg);
}
}
return new String(src.getBytes(outputEncoding),
Configuration.getEncoding());
}
public static void gracefulRackTopologyOutput(
Map<String, String> racksTopology, String filename, String delimeter)
throws Exception {
CommonUtil.gracefulRackTopologyOutput(racksTopology, filename, delimeter);
writeEndingMsgToScreen(filename);
}
public static void prettyOutputStrings(List<Object> list, String fileName, String delimeter) throws Exception {
CommonUtil.prettyOutputStrings(list, fileName, delimeter);
writeEndingMsgToScreen(fileName);
}
public static void writeEndingMsgToScreen(String fileName) throws Exception {
if (fileName == null) {
System.out.println();
} else {
File file = new File(fileName);
String filePath = file.getAbsolutePath();
if (isJansiAvailable() && !isBlank(filePath)) {
filePath = transferEncoding(filePath);
}
System.out.println("Exported to file " + filePath);
}
}
public enum PromptType {
USER_NAME, PASSWORD
}
public static String prompt(String msg) throws IOException {
ConsoleReader reader = getConsoleReader();
reader.setPrompt(msg);
return reader.readLine();
}
public static boolean prompt(String msg, PromptType promptType,
Map<String, String> loginInfo) throws Exception {
int k = 0;
String enter = "";
while (k < 3) {
enter = readEnter(msg, promptType);
if (!CommandsUtils.isBlank(enter)) {
if (promptType == PromptType.USER_NAME) {
loginInfo.put(Constants.LOGIN_USERNAME, enter);
} else {
loginInfo.put(Constants.LOGIN_PASSWORD, enter);
}
break;
} else {
StringBuilder warningMsg = new StringBuilder();
if (promptType == PromptType.USER_NAME) {
warningMsg.append(Constants.CONNECT_USER_NAME);
} else {
warningMsg.append(Constants.CONNECT_PASSWORD);
}
warningMsg.append(Constants.CONNECT_CAN_NOT_BE_NULL);
System.out.println(warningMsg.toString());
}
k++;
}
return k < 3;
}
private static String readEnter(String msg, PromptType promptType) throws Exception {
String enter = "";
ConsoleReader reader = getConsoleReader();
reader.setPrompt(msg);
if (promptType == PromptType.USER_NAME) {
enter = reader.readLine();
} else if (promptType == PromptType.PASSWORD) {
enter = reader.readLine(Character.valueOf('*'));
}
return enter;
}
public static ConsoleReader getConsoleReader() throws IOException{
ConsoleReader reader = new ConsoleReader();
reader.setExpandEvents(false);
return reader;
}
}