/** * VMware Continuent Tungsten Replicator * Copyright (C) 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. * * Initial developer(s): Edward Archibald * Contributor(s): */ package com.continuent.tungsten.common.utils; import java.io.BufferedReader; import java.io.IOException; import java.io.Serializable; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.Vector; import jline.ArgumentCompletor; import jline.Completor; import jline.ConsoleReader; import jline.MultiCompletor; import jline.NullCompletor; import jline.SimpleCompletor; import com.continuent.tungsten.common.cluster.resource.DataSource; import com.continuent.tungsten.common.cluster.resource.DataSourceRole; import com.continuent.tungsten.common.cluster.resource.Replicator; import com.continuent.tungsten.common.cluster.resource.ResourceState; import com.continuent.tungsten.common.cluster.resource.notification.ReplicatorNotification; import com.continuent.tungsten.common.config.TungstenProperties; import com.continuent.tungsten.common.exception.CLException; public class CLUtils implements Serializable { /** * */ private static final long serialVersionUID = 1L; private static final String COMMAND_COMMIT = "commit"; private static final String COMMAND_QUIT = "quit"; private static final String COMMAND_ROLLBACK = "rollback"; private static final String COMMAND_LIST = "ls"; private static final String NEWLINE = "\n"; private static Vector<String> captureBuffer = new Vector<String>(); private static CLLogLevel logLevel = CLLogLevel.normal; public static void clearCapture() { captureBuffer.clear(); } public static void getCapture(Vector<String> transferBuffer) { transferBuffer.addAll(captureBuffer); clearCapture(); } public static CLLogLevel getLogLevel() { return logLevel; } public static void setLogLevel(CLLogLevel logLevel) { CLUtils.logLevel = logLevel; } public static String setLogLevel(String level) throws CLException { try { logLevel = CLLogLevel.valueOf(level); return logLevel.toString(); } catch (IllegalArgumentException i) { throw new CLException(String.format( "'%s' is not a valid value for the log level", level)); } } private static SimpleDateFormat dateFormat = new SimpleDateFormat( "[yyyy/MM/dd hh:mm:ss a z]"); public static String[] getInputTokens(ConsoleReader cr, String prompt, BufferedReader in) throws IOException { String inbuf = null; if (cr != null) { inbuf = cr.readLine(prompt); } else { System.out.print(prompt); inbuf = in.readLine(); } if (inbuf == null) { CLUtils.println("\nExiting..."); System.exit(0); } Vector<String> noBlanks = new Vector<String>(); for (String token : inbuf.split("\\b")) { if (!token.trim().equals("")) noBlanks.add(token); } if (noBlanks.size() > 0) { return noBlanks.toArray(new String[noBlanks.size()]); } return null; } /** * Does a generic format of a TungstenProperties instance. * * @param name - a name to be associated with the properties * @param props - the TungstenProperties instance * @param header - an optional header that can be pre-pended to each * property * @return a String with the aggregate formatted properties. */ public static String formatPropertiesOld(String name, TungstenProperties props, String header, boolean wasModified) { String indent = " "; StringBuilder builder = new StringBuilder(); builder.append(header); builder.append(String.format("%s%s\n", name, modifiedSign(wasModified))); builder.append("{\n"); Map<String, String> propMap = props.hashMap(); for (String key : propMap.keySet()) { builder.append(String.format("%s%s = %s\n", indent, key, propMap.get(key))); } builder.append(String.format("}")); return builder.toString(); } public static String formatProperties(String name, TungstenProperties props, String header, boolean wasModified) { Map<String, String> propMap = props.hashMap(); return formatMap(name, propMap, "", header, wasModified); } /** * Does a generic format of a TungstenProperties instance. * * @param name - a name to be associated with the properties * @param props - the TungstenProperties instance * @param header - an optional header that can be pre-pended to each * property * @return a String with the aggregate formatted properties. */ public static String formatMapOld(String name, Map<String, String> props, String header, boolean wasModified) { String indent = " "; StringBuilder builder = new StringBuilder(); builder.append(header); builder.append(String.format("%s%s\n", name, modifiedSign(wasModified))); builder.append("{\n"); for (String key : props.keySet()) { builder.append(String.format("%s%s = %s\n", indent, key, props.get(key))); } builder.append(String.format("}")); return builder.toString(); } public static String formatMap(String name, Map<String, String> props, String header, boolean wasModified) { return formatMap(name, props, "", header, wasModified); } public static String formatMap(String name, Map<String, String> props, String indent, String header, boolean wasModified) { StringBuilder builder = new StringBuilder(); TreeMap<String, String> sorted = new TreeMap<String, String>(); sorted.putAll(props); for (String key : sorted.keySet()) { Object value = props.get(key); value = (value == null ? "" : value); builder.append(String.format("%s%30s: %s\n", indent, key, value.toString())); } Vector<String[]> results = new Vector<String[]>(); results.add(new String[]{builder.toString()}); return ResultFormatter.formatResults(name, null, results, ResultFormatter.DEFAULT_WIDTH, true, true); } public static String formatProperties(String name, TungstenProperties props, String header) { return formatProperties(name, props, header, false); } /** * @param header * @param wasModified * @param printDetails * @param includeStatistics * @param useRelativeLatency * @return a formatted string representing a datasource */ public static String formatDsMap(Map<String, TungstenProperties> dsMap, String header, boolean wasModified, boolean printDetails, boolean includeStatistics, boolean useRelativeLatency) { StringBuilder builder = new StringBuilder(); for (TungstenProperties dsProps : dsMap.values()) { builder.append( formatStatus(dsProps, null, null, null, true, header, wasModified, printDetails, includeStatistics, false, false, useRelativeLatency)).append(NEWLINE); } return builder.toString(); } /** * @param dsProps * @param header * @param wasModified * @param printDetails * @param includeStatistics * @param useRelativeLatency * @return a formatted string representing a datasource */ public static String formatDsProps(TungstenProperties dsProps, String header, boolean wasModified, boolean printDetails, boolean includeStatistics, boolean useRelativeLatency) { return formatStatus(dsProps, null, null, null, true, header, wasModified, printDetails, includeStatistics, true, false, useRelativeLatency); } /** * @param dsProps - datasource properties to format * @param replProps - formatted replicator status for the datasource * @param header - header to be inserted on each line * @param wasModified - indicates whether or not the datasource has been * modified * @param printDetails - print details * @param includeStatistics - include statistics * @param useRelativeLatency * @return a formatted string representing a datasource/replicator status */ public static String formatStatus(TungstenProperties dsProps, TungstenProperties replProps, String header, boolean wasModified, boolean printDetails, boolean includeStatistics, boolean useRelativeLatency) { return formatStatus(dsProps, replProps, null, null, true, header, wasModified, printDetails, includeStatistics, false, false, useRelativeLatency); } /** * Format manager status * * @param isRawFormat * @param useRelativeLatency */ public static String formatStatus(TungstenProperties dsProps, TungstenProperties replProps, TungstenProperties dbProps, TungstenProperties routerUsage, boolean managerIsOnline, String header, boolean wasModified, boolean printDetails, boolean includeStatistics, boolean isRawFormat, boolean useRelativeLatency) { return formatStatus(dsProps, replProps, dbProps, routerUsage, managerIsOnline, header, wasModified, printDetails, includeStatistics, true, isRawFormat, useRelativeLatency); } public static String formatRouterStatus(TungstenProperties dsProps, boolean printDetails, boolean useRelativeLatency) { String activeConnections = dsProps .getString(DataSource.ACTIVE_CONNECTION_COUNT); String connectionsCreated = dsProps .getString(DataSource.CONNECTIONS_CREATED_COUNT); String latencyDisplay = ""; if (dsProps.getString("role").equals("slave") || dsProps.getString("role").equals("relay")) { String relativeLatencyInfo = useRelativeLatency ? String.format( ", relative=%5.3f", dsProps.getDouble( DataSource.RELATIVE_LATENCY, "-1.0", false)) : ""; latencyDisplay = String.format(", latency=%5.3f%s", dsProps.getDouble(DataSource.APPLIED_LATENCY), relativeLatencyInfo); } return String.format("%s(%s:%s, created=%s, active=%s%s)", dsProps.getString("name"), dsProps.getString("role"), dsProps.getString("state"), connectionsCreated, activeConnections, latencyDisplay); } /** * @param dsProps - datasource properties to format * @param replProps - formatted replicator status for the datasource * @param dbProps - properties that represent the database server state * @param managerIsOnline * @param header - header to be inserted on each line * @param wasModified - indicates whether or not the datasource has been * modified * @param printDetails - print details * @param includeStatistics - whether or not to include statistics * @param includeComponents * @param isRawFormat * @param userRelativeLatency * @return a formatted string representing a datasource/replicator status */ public static String formatStatus(TungstenProperties dsProps, TungstenProperties replProps, TungstenProperties dbProps, boolean managerIsOnline, String header, boolean wasModified, boolean printDetails, boolean includeStatistics, boolean includeComponents, boolean isRawFormat, boolean userRelativeLatency) { return formatStatus(dsProps, replProps, dbProps, null, managerIsOnline, header, wasModified, printDetails, includeStatistics, includeComponents, isRawFormat, userRelativeLatency); } /** * @param dsProps - datasource properties to format * @param replProps - formatted replicator status for the datasource * @param dbProps - properties that represent the database server state * @param routerUsage * @param managerIsOnline * @param header - header to be inserted on each line * @param wasModified - indicates whether or not the datasource has been * modified * @param printDetails - print details * @param includeStatistics - whether or not to include statistics * @param includeComponents * @param isRawFormat If true, eliminates 'pretty' formatting. * @param useRelativeLatency * @return a formatted string representing a datasource/replicator status */ public static String formatStatus(TungstenProperties dsProps, TungstenProperties replProps, TungstenProperties dbProps, TungstenProperties routerUsage, boolean managerIsOnline, String header, boolean wasModified, boolean printDetails, boolean includeStatistics, boolean includeComponents, boolean isRawFormat, boolean useRelativeLatency) { String indent = " "; StringBuilder builder = new StringBuilder(); builder.append(header); String progressInformation = ""; String additionalInfo = ""; String replicator_useSSLConnection = ""; // true if Replicator uses SSL int indentToUse = dsProps.getString(DataSource.NAME).length() + 1; /* * Witness have only a header, so take care of them here and return. */ if (dsProps.getString(DataSource.ROLE).equals( DataSourceRole.witness.toString())) { String dsHeader = String.format("%s(%s:%s)", dsProps .getString(DataSource.NAME), dsProps .getString(DataSource.ROLE), managerIsOnline ? dsProps.getString(DataSource.STATE) : "FAILED"); if (!isRawFormat) { builder.append( ResultFormatter.makeSeparator( ResultFormatter.DEFAULT_WIDTH, 1, true)) .append(NEWLINE); builder.append(ResultFormatter.makeRow( (new String[]{dsHeader}), ResultFormatter.DEFAULT_WIDTH, indentToUse, true, true)); builder.append( ResultFormatter.makeSeparator( ResultFormatter.DEFAULT_WIDTH, 1, true)) .append(NEWLINE); builder.append(ResultFormatter.makeRow( new String[]{indent + String.format("MANAGER(state=%s)", managerIsOnline ? "ONLINE" : "STOPPED")}, ResultFormatter.DEFAULT_WIDTH, 0, false, true)); builder.append( ResultFormatter.makeSeparator( ResultFormatter.DEFAULT_WIDTH, 1, true)) .append(NEWLINE); } else { builder.append(dsHeader + "\n" + String.format("MANAGER(state=%s)", managerIsOnline ? "ONLINE" : "STOPPED")); } builder.append(NEWLINE); return builder.toString(); } // --- Replicator properties --- if (replProps != null) { progressInformation = String.format("progress=%s", replProps.getString(Replicator.APPLIED_LAST_SEQNO)); String relativeLatencyInfo = useRelativeLatency ? String.format( ", relative=%5.3f", replProps.getDouble( Replicator.RELATIVE_LATENCY, "-1.0", false)) : ""; if (dsProps.getString(Replicator.ROLE).equals("master")) { additionalInfo = String.format(", %s, THL latency=%5.3f%s", progressInformation, replProps.getDouble( Replicator.APPLIED_LATENCY, "-1.0", false), relativeLatencyInfo); } else { additionalInfo = String.format(", %s, latency=%5.3f%s", progressInformation, replProps.getDouble( Replicator.APPLIED_LATENCY, "-1.0", false), relativeLatencyInfo); } // Retrieve useSSLConnection value Boolean _replicator_useSSLConnection = replProps .getBoolean(Replicator.USE_SSL_CONNECTION); if (_replicator_useSSLConnection != null && _replicator_useSSLConnection) replicator_useSSLConnection = MessageFormat.format("[{0}]", "SSL"); } // --- DataSource properties --- String vipInfo = null; if (dsProps.getBoolean(DataSource.VIPISBOUND) && dsProps.getString(DataSource.STATE).equals( ResourceState.ONLINE.toString())) { vipInfo = String.format("VIP=(%s:%s)", dsProps.getString(DataSource.VIPINTERFACE), dsProps.getString(DataSource.VIPADDRESS)); } String state = dsProps.get(DataSource.STATE); String failureInfo = ""; if (state.equals(ResourceState.FAILED.toString())) { failureInfo = String.format("(%s)", dsProps.getString(DataSource.LASTERROR)); } else if (state.equals(ResourceState.SHUNNED.toString())) { String lastError = dsProps.getString(DataSource.LASTERROR); String shunReason = dsProps.getString(DataSource.LASTSHUNREASON); if (!shunReason.equals("") && !shunReason.equals("NONE")) { if (lastError != null && (!lastError.equals("") && !lastError.equals("--"))) failureInfo = String.format("(%s AFTER %s)", shunReason, lastError); else failureInfo = String.format("(%s)", shunReason); } else { failureInfo = String.format("(%s)", lastError); } } // / --- Build String for status --- String connectionStats = ""; boolean isComposite = dsProps.getBoolean(DataSource.ISCOMPOSITE, "false", false); String fullState = String .format("%s%s", dsProps.getString(DataSource.STATE), (dsProps.getInt(DataSource.PRECEDENCE) == -1 ? ":ARCHIVE " : "")); String dsHeader = String.format("%s%s(%s:%s%s%s) %s", dsProps .getString("name"), modifiedSign(wasModified), String.format( "%s%s", (isComposite ? "composite " : ""), dsProps.getString(DataSource.ROLE)), fullState, failureInfo, additionalInfo, connectionStats); String alertMessage = dsProps.getString(DataSource.ALERT_MESSAGE, "", false); alertMessage = (alertMessage.length() > 0 ? String.format( "\nREASON[%s]", alertMessage) : alertMessage); String dsAlert = String.format("STATUS [%s] %s%s%s", dsProps .getString(DataSource.ALERT_STATUS), dateFormat .format((new Date(dsProps.getLong(DataSource.ALERT_TIME)))), replicator_useSSLConnection, alertMessage); if (!printDetails) { if (!isRawFormat) { builder.append( ResultFormatter.makeSeparator( ResultFormatter.DEFAULT_WIDTH, 1, true)) .append(NEWLINE); builder.append(ResultFormatter.makeRow( (new String[]{dsHeader}), ResultFormatter.DEFAULT_WIDTH, indentToUse, true, true)); if (vipInfo != null) { builder.append(ResultFormatter.makeRow( (new String[]{vipInfo}), ResultFormatter.DEFAULT_WIDTH, indentToUse, true, true)); } builder.append(ResultFormatter.makeRow((new String[]{dsAlert}), ResultFormatter.DEFAULT_WIDTH, indentToUse, true, true)); builder.append( ResultFormatter.makeSeparator( ResultFormatter.DEFAULT_WIDTH, 1, true)) .append(NEWLINE); } else { builder.append(dsHeader).append(NEWLINE); if (vipInfo != null) { builder.append(vipInfo).append(NEWLINE); } builder.append(dsAlert).append(NEWLINE); } if (!includeComponents) { return builder.toString(); } if (!isComposite) { String managerStatus = String.format("MANAGER(state=%s)", managerIsOnline ? "ONLINE" : "STOPPED"); String replicatorStatus = formatReplicatorProps(replProps, managerIsOnline, header, printDetails); String dbState = (dbProps != null ? dbProps.getString("state") : "UNKNOWN"); String dbHeader = String.format("DATASERVER(state=%s)\n", dbState); String usage = null; if (routerUsage != null) { usage = String .format("CONNECTIONS(created=%s, active=%s)", routerUsage .getString(DataSource.CONNECTIONS_CREATED_COUNT), routerUsage .getString(DataSource.ACTIVE_CONNECTION_COUNT)); } if (!isRawFormat) { builder.append(ResultFormatter.makeRow(new String[]{indent + managerStatus}, ResultFormatter.DEFAULT_WIDTH, 0, false, true)); builder.append(ResultFormatter.makeRow(new String[]{indent + replicatorStatus}, ResultFormatter.DEFAULT_WIDTH, 0, false, true)); builder.append(ResultFormatter.makeRow((new String[]{indent + dbHeader}), ResultFormatter.DEFAULT_WIDTH, indentToUse, true, true)); if (routerUsage != null) { builder.append(ResultFormatter.makeRow( (new String[]{indent + usage}), ResultFormatter.DEFAULT_WIDTH, indentToUse, true, true)); } builder.append( ResultFormatter.makeSeparator( ResultFormatter.DEFAULT_WIDTH, 1, true)) .append(NEWLINE); } else { builder.append(managerStatus).append(NEWLINE); builder.append(replicatorStatus).append(NEWLINE); builder.append(dbHeader).append(NEWLINE); if (usage != null) { builder.append(usage).append(NEWLINE); } } } builder.append(NEWLINE); return builder.toString(); } // DETAILS: builder.append(formatMap(dsHeader, dsProps.map(), "", "", false)); if (!isComposite) { if (replProps != null) { String replHeader = null; if (replProps.getString(Replicator.STATE).equals( ResourceState.STOPPED.toString())) { replHeader = String.format("%s:REPLICATOR(state=STOPPED)", replProps.getString("host")); } else { replHeader = String.format( "%s:REPLICATOR(role=%s, state=%s)", replProps.getString("host"), replProps.getString("role"), replProps.getString("state")); } builder.append(formatMap(replHeader, replProps.map(), "", " ", false)); } if (dbProps != null) { String dbHeader = String.format("%s:DATASERVER(state=%s)", dsProps.get("host"), dbProps.getString("state")); builder.append(formatMap(dbHeader, dbProps.map(), "", " ", false)); } } builder.append(NEWLINE); return builder.toString(); } public static String formatHeaderRow(String header) { StringBuilder builder = new StringBuilder(); builder.append( ResultFormatter.makeSeparator(ResultFormatter.DEFAULT_WIDTH, 1, true)).append(NEWLINE); builder.append(ResultFormatter.makeRow((new String[]{header}), ResultFormatter.DEFAULT_WIDTH, 0, true, true)); builder.append( ResultFormatter.makeSeparator(ResultFormatter.DEFAULT_WIDTH, 1, true)).append(NEWLINE); return builder.toString(); } public static String formatReplicatorProps(TungstenProperties replProps, boolean managerIsOnline, String header, boolean printDetails) { if (replProps == null) { if (managerIsOnline) return "REPLICATOR(state=STOPPED)"; else return "REPLICATOR(state=STATUS NOT AVAILABLE)"; } else if (replProps.getString(Replicator.STATE).equals( ResourceState.STOPPED.toString())) { return "REPLICATOR(state=STOPPED)"; } String indent = "\t"; StringBuilder builder = new StringBuilder(); String role = replProps.getString(Replicator.ROLE); String masterReplicator = ""; if (role.equals("slave") || role.equals("relay")) { final String prefix = "thl://"; String masterUri = replProps .getString(Replicator.MASTER_CONNECT_URI); if (masterUri != null) { // don't display the port int lastIdx = masterUri.indexOf(":", prefix.length()); // if we don't have a ':' at the end, maybe a '/'? if (lastIdx == -1) { lastIdx = masterUri.indexOf("/", prefix.length()); } // If we don't have either, we just go to the end of the string if (lastIdx == -1) { lastIdx = masterUri.length(); } masterReplicator = ", master=" + masterUri.substring(masterUri.indexOf("//") + 2, lastIdx); } } builder.append(String.format("REPLICATOR(role=%s%s, state=%s)", replProps.getString("role"), masterReplicator, ReplicatorNotification.replicatorStateToResourceState(replProps .getString("state")))); TreeMap<String, String> sortedMap = new TreeMap<String, String>(); sortedMap.putAll(replProps.map()); if (!printDetails) { return builder.toString(); } builder.append("\n").append(header); builder.append("{").append("\n"); builder.append(header); builder.append( String.format("%shost = %s", indent, replProps.getString("host"))).append("\n"); builder.append(header); builder.append( String.format("%sminSeqNo = %s", indent, replProps.getString("minSeqNo"))).append("\n"); builder.append(header); builder.append( String.format("%smaxSeqNo = %s", indent, replProps.getString("maxSeqNo"))).append("\n"); builder.append(header); builder.append( String.format("%smasterUri = %s", indent, replProps.getString("masterUri"))).append("\n"); builder.append(header); builder.append( String.format("%suptimeSeconds = %s", indent, replProps.getString("uptimeSeconds"))).append("\n"); builder.append(header); builder.append( String.format("%spendingExceptionMessage = %s", indent, replProps.getString("pendingExceptionMessage"))) .append("\n"); builder.append(header); builder.append( String.format("%spendingErrorCode = %s", indent, replProps.getString("pendingErrorCode"))).append("\n"); builder.append(header); builder.append( String.format("%spendingError = %s", indent, replProps.getString("pendingError"))).append("\n"); builder.append(header); builder.append(String.format("}")); return builder.toString(); } public static String formatAllReplicatorProps(TungstenProperties replProps, String header, boolean printDetails) { if (replProps == null) { return "REPLICATOR(state=STATUS NOT AVAILABLE)"; } else if (replProps.getString(Replicator.STATE).equals( ResourceState.STOPPED.toString())) { return "REPLICATOR(state=STOPPED)"; } String indent = "\t"; StringBuilder builder = new StringBuilder(); builder.append(String.format("REPLICATOR(role=%s, state=%s)", replProps.getString("role"), replProps.getString("state"))); TreeMap<String, String> sortedProps = new TreeMap<String, String>(); sortedProps.putAll(replProps.map()); if (!printDetails) { return builder.toString(); } builder.append("\n").append(header); builder.append("{").append("\n"); for (String key : sortedProps.keySet()) { builder.append(header); builder.append( String.format("%s%s = %s", indent, key, sortedProps.get(key))).append("\n"); } builder.append(header); builder.append(String.format("}")); return builder.toString(); } static public String modifiedSign(boolean wasModified) { return ((wasModified ? "*" : "")); } static public void formatStatistics(TungstenProperties dsProps, StringBuilder builder, String header, String indent) { builder.append( String.format("%sactiveConnectionCount = %s", indent, dsProps.getObject("activeConnectionCount"))).append( "\n"); builder.append(header); builder.append( String.format("%sconnectionsCreatedCount = %s", indent, dsProps.getObject("connectionsCreatedCount")) .toString()).append("\n"); builder.append(header); builder.append( String.format("%sstatementsCreatedCount = %s", indent, dsProps.getObject("statementsCreatedCount")).toString()) .append("\n"); builder.append(header); builder.append( String.format("%spreparedStatementsCreatedCount = %s", indent, dsProps.getObject("preparedStatementsCreatedCount")) .toString()).append("\n"); builder.append(header); builder.append( String.format("%scallableStatementsCreatedCount = %s", indent, dsProps.getObject("callableStatementsCreatedCount")) .toString()).append("\n"); builder.append(header); } /** * Temporary utility method to keep current println behavior. * * @param msg */ static public void println(String msg) { println(msg, CLLogLevel.normal); } // Print a message to stdout. static public void println(String msg, CLLogLevel level) { if (msg == null) return; if (level.getLevel() > logLevel.getLevel()) { return; } if (msg.length() > 0) { if (level.getLevel() >= CLLogLevel.detailed.getLevel()) { msg = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss ") .format(new Date()) + msg; } if (level.getLevel() >= CLLogLevel.debug.getLevel()) { msg = String.format("%s: %s", level.toString().toUpperCase(), msg); } System.out.println(msg); } } // Print a formatted message to stdout. static public void printf(String fmt, Object... args) { System.out.printf(fmt, args); } // Print a message to stdout without a newline static public void print(String msg) { if (msg.length() > 0) System.out.print(msg); } // Print an error. static public void error(String msg, Throwable t) { println("ERROR: " + msg); if (t != null) t.printStackTrace(); } // Abort following a fatal error. static public void fatal(String msg, Throwable t) { println(msg); if (t != null) t.printStackTrace(); System.exit(1); } public static void printDataService( Map<String, TungstenProperties> dataSourceProps, String[] args, boolean useRelativeLatency) { boolean printDetail = false; if (dataSourceProps == null) { System.out.println("CLUSTER UNAVAILABLE\n"); } if (args.length > 1 && args[1].equals("-l")) { printDetail = true; } for (String dsName : dataSourceProps.keySet()) { if (args.length >= 3) { if (!dsName.equals(args[2])) continue; } printDataSource(dataSourceProps.get(dsName), "", printDetail, useRelativeLatency); } } public static void printDataSource(TungstenProperties dsProperties, String header, boolean printDetails, boolean useRelativeLatency) { println(formatStatus(dsProperties, null, null, null, true, header, false, printDetails, printDetails, false, false, useRelativeLatency)); } public static String printArgs(String args[]) { return printArgs(args, 0); } public static String printArgs(String args[], int startElement) { StringBuffer buf = new StringBuffer(); for (int i = startElement; i < args.length; i++) { if (buf.length() > 0) { buf.append(" "); } buf.append(args[i]); } return buf.toString(); } public static TungstenProperties editProperties(TungstenProperties props, boolean isNew, BufferedReader in) throws IOException { boolean wasModified = false; ConsoleReader newDSReader = new ConsoleReader(); List<Completor> comps = new LinkedList<Completor>(); // Add a choice for each property setting. // Complete with currently set value so user can edit it easily for (String key : props.hashMap().keySet()) { List<Completor> completor = new LinkedList<Completor>(); completor.add(new SimpleCompletor(key)); completor.add(new SimpleCompletor(props.getString(key))); completor.add(new NullCompletor()); comps.add(new ArgumentCompletor(completor)); } // Add commit and rollback keywords comps.add(new SimpleCompletor(new String[]{COMMAND_COMMIT, COMMAND_ROLLBACK})); newDSReader.addCompletor(new MultiCompletor(comps)); String[] args = null; while ((args = getInputTokens( newDSReader, String.format("edit %s> ", props.getString("name"), CLUtils.modifiedSign(wasModified || isNew)), in)) != null) { if (COMMAND_QUIT.equals(args[0])) { if (wasModified) { CLUtils.println("Please either commit or rollback changes before quitting"); continue; } break; } else if (COMMAND_COMMIT.equals(args[0])) { break; } else if (COMMAND_ROLLBACK.equals(args[0])) { props = null; break; } // list current properties else if (COMMAND_LIST.equals(args[0])) { // for (String key : dsProps.keySet()) println(formatProperties(props.getString("name"), props, "", wasModified)); } // not enough args or key not present in the predefined settings else if (args.length != 2 || props.getString(args[0]) == null) { CLUtils.println("Usage: <attribute> <new value> (example: \"role master\")"); CLUtils.println(" or use 'rollback' or 'commit' to complete your work"); } // set a property else { props.setString(args[0], args[1]); wasModified = true; CLUtils.println(CLUtils.formatProperties( props.getString("name"), props, "", wasModified)); } } if (!wasModified && !isNew) return null; return props; } public static String[] appendArg(String args[], String newArg) { ArrayList<String> newArgs = new ArrayList<String>(); for (String arg : args) newArgs.add(arg); newArgs.add(newArg); return newArgs.toArray(new String[newArgs.size()]); } public static String[] prependArg(String args[], String newArg) { ArrayList<String> newArgs = new ArrayList<String>(); newArgs.add(newArg); for (String arg : args) newArgs.add(arg); return newArgs.toArray(new String[newArgs.size()]); } public static String listToString(List<Object> list) { StringBuilder builder = new StringBuilder(); for (Object obj : list) { builder.append(String.format("%s\n", obj.toString())); } return builder.toString(); } /** * This method will format any iterable class into a comma-separated list. * * @param iterable An iterable value. * @return formatted string */ public static String iterableToCommaSeparatedList(Iterable<?> iterable) { StringBuilder builder = new StringBuilder(); boolean first = true; for (Object obj : iterable) { if (first) { builder.append(String.format("%s", obj.toString())); first = false; } else { builder.append(String.format(", %s", obj.toString())); } } return builder.toString(); } /** * This method will format any iterable class into a simple newline * delimited list. * * @param iterable * @return formatted string */ public static String iterableToString(Iterable<?> iterable) { StringBuilder builder = new StringBuilder(); for (Object obj : iterable) { builder.append(String.format("%s\n", obj.toString())); } return builder.toString(); } public static String stringListToString(List<String> list) { StringBuilder builder = new StringBuilder(); for (String obj : list) { builder.append(String.format("%s\n", obj.toString())); } return builder.toString(); } public static String stringCollectionToString(Collection<String> list) { StringBuilder builder = new StringBuilder(); for (String obj : list) { builder.append(String.format("%s\n", obj.toString())); } return builder.toString(); } public static String collectionToString(Collection<Object> collection) { StringBuilder builder = new StringBuilder(); for (Object obj : collection) { builder.append(String.format("%s\n", obj.toString())); } return builder.toString(); } }