/***************************************************************************** * 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; } }