All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.voltdb.utils.MiscUtils Maven / Gradle / Ivy

There is a newer version: 10.1.1
Show newest version
/* This file is part of VoltDB.
 * Copyright (C) 2008-2020 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see .
 */

package org.voltdb.utils;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Deque;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json_voltpatches.JSONArray;
import org.json_voltpatches.JSONObject;
import org.voltcore.logging.VoltLogger;
import org.voltcore.utils.CoreUtils;
import org.voltcore.utils.DeferredSerialization;
import org.voltdb.PrivateVoltTableFactory;
import org.voltdb.RealVoltDB;
import org.voltdb.StartAction;
import org.voltdb.StoredProcedureInvocation;
import org.voltdb.TheHashinator;
import org.voltdb.VoltDB;
import org.voltdb.VoltTable;
import org.voltdb.VoltType;
import org.voltdb.client.Client;
import org.voltdb.client.ClientResponse;
import org.voltdb.common.Constants;
import org.voltdb.compiler.deploymentfile.DrRoleType;
import org.voltdb.iv2.TxnEgo;
import org.voltdb.licensetool.LicenseApi;
import org.voltdb.licensetool.LicenseException;

import com.google_voltpatches.common.base.Supplier;
import com.google_voltpatches.common.collect.ArrayListMultimap;
import com.google_voltpatches.common.collect.ListMultimap;
import com.google_voltpatches.common.collect.Lists;
import com.google_voltpatches.common.collect.Maps;
import com.google_voltpatches.common.collect.Multimap;
import com.google_voltpatches.common.collect.Multimaps;
import com.google_voltpatches.common.net.HostAndPort;

public class MiscUtils {
    private static final VoltLogger hostLog = new VoltLogger("HOST");
    private static final VoltLogger consoleLog = new VoltLogger("CONSOLE");

    private static final boolean assertsEnabled;

    static {
        boolean assertCaught = false;
        assert(assertCaught = true);
        assertsEnabled = assertCaught;
    }

    public static boolean areAssertsEnabled() {
        return assertsEnabled;
    }

    /**
     * Simple code to copy a file from one place to another...
     * Java should have this built in... stupid java...
     */
    public static void copyFile(String fromPath, String toPath) throws Exception {
        File inputFile = new File(fromPath);
        File outputFile = new File(toPath);
        com.google_voltpatches.common.io.Files.copy(inputFile, outputFile);
    }

    /**
     * Serialize a file into bytes. Used to serialize catalog and deployment
     * file for UpdateApplicationCatalog on the client.
     * Notice that if the file is larger than 2GB, readAllBytes() will throw a OutOfMemoryError.
     *
     * @param path
     * @return a byte array of the file
     * @throws IOException
     *             If there are errors reading the file
     */
    public static byte[] fileToBytes(File path) throws IOException {
        return Files.readAllBytes(path.toPath());
    }

    /**
     * Instantiate the license api impl based on enterprise/community editions
     * @return a valid API for community and pro editions, or null on error.
     */
    public static LicenseApi createLicenseApi(String pathToLicense) {

        if (MiscUtils.isPro() == false) {
            return new LicenseApi() {
                @Override
                public boolean initializeFromFile(File license) {
                    return true;
                }

                @Override
                public boolean isAnyKindOfTrial() {
                    return false;
                }

                @Override
                public boolean isProTrial() {
                    return false;
                }

                @Override
                public boolean isEnterpriseTrial() {
                    return false;
                }

                @Override
                public int maxHostcount() {
                    return Integer.MAX_VALUE;
                }

                @Override
                public Calendar expires() {
                    Calendar result = Calendar.getInstance();
                    result.add(Calendar.YEAR, 20); // good enough?
                    return result;
                }

                @Override
                public boolean verify() {
                    return true;
                }

                @Override
                public boolean isDrReplicationAllowed() {
                    return false;
                }

                @Override
                public boolean isDrActiveActiveAllowed() {
                    return false;
                }

                @Override
                public boolean isCommandLoggingAllowed() {
                    return false;
                }

                @Override
                public boolean isAWSMarketplace() {
                    return false;
                }

                @Override
                public boolean isEnterprise() {
                    return false;
                }

                @Override
                public boolean isPro() {
                    return false;
                }

                @Override
                public String licensee() {
                    return "VoltDB Community Edition User";
                }

                @Override
                public Calendar issued() {
                    Calendar result = Calendar.getInstance();
                    return result;
                }

                @Override
                public String note() {
                    return "";
                }

                @Override
                public boolean hardExpiration() {
                    return false;
                }

                @Override
                public boolean secondaryInitialization() {
                    return true;
                }

                @Override
                public String getSignature() {
                    return null;
                }

                @Override
                public String getLicenseType() {
                    return "Community Edition";
                }

                @Override
                public boolean isUnrestricted() {
                    return false;
                }

                @Override
                public String getIssuerCompany()
                {
                    return null;
                }

                @Override
                public String getIssuerUrl()
                {
                    return null;
                }

                @Override
                public String getIssuerEmail()
                {
                    return null;
                }

                @Override
                public String getIssuerPhone()
                {
                    return null;
                }

                @Override
                public int getVersion()
                {
                    return 0;
                }

                @Override
                public int getScheme()
                {
                    return 0;
                }
            };
        }

        LicenseApi licenseApi = ProClass
                .load("org.voltdb.licensetool.LicenseApiImpl", "License API", hostLog::fatal)
                .errorHandler(hostLog::fatal).newInstance();
        if (licenseApi == null) {
            return null;
        }

        // verify the license file exists.
        File licenseFile = new File(pathToLicense);
        if (licenseFile.exists() == false) {
            return null;
        }

        // Initialize the API. This parses the file but does NOT verify signatures.
        if (licenseApi.initializeFromFile(licenseFile) == false) {
            hostLog.fatal("Unable to load license file: could not parse license.");
            return null;
        }

        // Perform signature verification - detect modified files
        try
        {
            if (licenseApi.verify() == false) {
                hostLog.fatal("Unable to load license file: could not verify license signature.");
                return null;
            }
        }
        catch (LicenseException lex)
        {
            hostLog.fatal(lex.getMessage());
            return null;
        }

        return licenseApi;
    }

    public static String[] buildDefaultLicenseDirs(File voltdbroot) {
        // First starts from voltdbroot directory
        File licenseF = new VoltFile(voltdbroot, Constants.LICENSE_FILE_NAME);
        String vdbrt = licenseF.getAbsolutePath();
        // Then search current directory
        String crt = System.getProperty("user.dir") + File.separator + Constants.LICENSE_FILE_NAME;
        // Then search jar file directory
        String jar = null;
        try {
            String jarLoc = VoltDB.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
            // Strip of file name
            int lastSlashOff = jarLoc.lastIndexOf(File.separator);
            if (lastSlashOff == -1) {
                // Jar is at root directory
                jar = File.separator + Constants.LICENSE_FILE_NAME;
            }
            else {
                jar = jarLoc.substring(0, lastSlashOff+1) + Constants.LICENSE_FILE_NAME;
            }
        } catch (URISyntaxException dontcare) {}
        // Last search user home directory
        String home = System.getProperty("user.home") + File.separator + Constants.LICENSE_FILE_NAME;

        return new String[] {vdbrt, crt, jar, home};
    }

    /**
     * Validate the signature and business logic enforcement for a license.
     * @return true if the licensing constraints are met
     */
    public static boolean validateLicense(LicenseApi licenseApi, int numberOfNodes, DrRoleType replicationRole,
            StartAction startAction)
    {
        // Delay the handling of an invalid license file until here so
        // that the leader can terminate the full cluster.
        if (licenseApi == null) {
            hostLog.fatal("VoltDB license is not valid.");
            return false;
        }

        // do some extra initialization here
        if (!licenseApi.secondaryInitialization()) {
            return false;
        }

        Calendar now = GregorianCalendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("MMM d, yyyy");
        String expiresStr = sdf.format(licenseApi.expires().getTime());
        boolean valid = true;

        // make it really expire tomorrow to deal with timezone whiners
        Calendar yesterday = GregorianCalendar.getInstance();
        yesterday.add(Calendar.DATE, -1);

        if (yesterday.after(licenseApi.expires())) {
            if (licenseApi.hardExpiration()) {
                if (licenseApi.isAnyKindOfTrial()) {
                    hostLog.fatal("VoltDB trial license expired on " + expiresStr + ".");
                }
                else {
                    hostLog.fatal("VoltDB license expired on " + expiresStr + ".");
                }
                hostLog.fatal("Please contact [email protected] to request a new license.");
                return false;
            }
            else {
                // Expired commercial licenses are allowed but generate log messages.
                hostLog.error("Warning, VoltDB commercial license expired on " + expiresStr + ".");
                valid = false;
            }
        }

        // enforce DR replication constraint
        if (licenseApi.isDrReplicationAllowed() == false) {
            if (replicationRole != DrRoleType.NONE) {
                hostLog.fatal("Warning, VoltDB license does not allow use of DR replication.");
                return false;
            }
        } else if (licenseApi.isDrActiveActiveAllowed() == false) {
            if (replicationRole == DrRoleType.XDCR) {
                hostLog.fatal("Warning, VoltDB license does not allow use of XDCR.");
                return false;
            }
        }

        // check node count
        if (licenseApi.maxHostcount() < numberOfNodes) {
            hostLog.fatal("Attempting to " + (startAction.doesJoin() ? "join" : "start") + " with too many nodes ("
                    + numberOfNodes + "). " + "Current license only supports " + licenseApi.maxHostcount()
                    + ". Please contact VoltDB at [email protected].");
            return false;
        }

        // If this is a commercial license, and there is less than or equal to 30 days until expiration,
        // issue a "days remaining" warning message.
        long diff = licenseApi.expires().getTimeInMillis() - now.getTimeInMillis();
        // The original license is only a whole data (no minutes/millis).
        // There should thus be no issue with daylight savings time,
        // but just in case, if the diff is a negative number, round up to zero.
        if (diff < 0) {
            diff = 0;
        }
        long diffDays = diff / (24 * 60 * 60 * 1000);

        // print out trial success message
        if (licenseApi.isAnyKindOfTrial()) {
            consoleLog.info("Starting VoltDB with trial license. License expires on " + expiresStr + " (" + diffDays + " days remaining).");
            return true;
        }

        if (licenseApi.isAWSMarketplace()) {
            return true;
        }

        // print out a warning within a month for other licenses
        if ((diff > 0) && (diff <= 30))
        {
            String msg = "Warning: VoltDB license expires in " + diffDays + " day(s).";
            consoleLog.info(msg);
        }

        // this gets printed even if there are non-fatal problems, so it
        // injects the word "invalid" to make it clear this is the case
        String msg = String.format("Starting VoltDB with %scommercial license. " +
                                   "License for %d nodes expires on %s.",
                                   (valid ? "" : "invalid "),
                                   licenseApi.maxHostcount(),
                                   expiresStr);
        consoleLog.info(msg);

        return true;
    }

    /**
     * Compare the new and current license, see if the difference is allowed to be updated in live database.
     * 

Currently only following types of change is allowed in live cluster: *

  • expiration date, and
  • *
  • max host count
  • *

    *

    Ignore differences like license version/scheme, issuer information and licensee name.

    * @param newLicense * @param currentLicense * @return error message if change is disallowed, null string if change is allowed. */ public static String isLicenseChangeAllowed(LicenseApi newLicense, LicenseApi currentLicense) { if ( !newLicense.getLicenseType().equalsIgnoreCase(currentLicense.getLicenseType()) ) { return "Change license type from " + currentLicense.getLicenseType() + " to " + newLicense.getLicenseType() + " is disallowed. " + "A maintenance window is needed to do that change."; } // Commandlogging is always allowed in enterprise/trail/pro license, check for extra caution if (newLicense.isCommandLoggingAllowed() != currentLicense.isCommandLoggingAllowed()) { return (newLicense.isCommandLoggingAllowed() ? "add" : "remove") + " feature command logging is disallowed. " + "A maintenance window is needed to do that change."; } if ( newLicense.isDrActiveActiveAllowed() != currentLicense.isDrActiveActiveAllowed()) { return (newLicense.isDrActiveActiveAllowed() ? "add" : "remove") + " feature XDCR is disallowed. " + "A maintenance window is needed to do that change."; } if (newLicense.isDrReplicationAllowed() != currentLicense.isDrReplicationAllowed() ) { return (newLicense.isDrReplicationAllowed() ? "add" : "remove") + " feature DR is disallowed. " + "A maintenance window is needed to do that change."; } if ( newLicense.hardExpiration() != currentLicense.hardExpiration() ) { return "Can not change license from " + (currentLicense.hardExpiration() ? "hard expiration" : "soft expiration") + " to " + (newLicense.hardExpiration() ? "hard expiration" : "soft expiration"); } if ( newLicense.isUnrestricted() != currentLicense.isUnrestricted()) { return "Can not change license from " + (currentLicense.isUnrestricted() ? "unrestricted" : "restricted") + " to " + (newLicense.isUnrestricted() ? "unrestricted" : "restricted"); } int clusterSize = ((RealVoltDB)VoltDB.instance()).getHostCount(); if ( newLicense.maxHostcount() < clusterSize) { return String.format("Can not update a license with the max host count [%d] lower than current cluster size [%d].", newLicense.maxHostcount(), clusterSize); } return null; } public static boolean isCommunity(LicenseApi api) { return !api.isEnterprise() && !api.isPro() && !api.isAnyKindOfTrial() && !api.isAWSMarketplace(); } /** * Check that RevisionStrings are properly formatted. * @param fullBuildString * @return build revision # (SVN), build hash (git) or null */ public static String parseRevisionString(String fullBuildString) { String build = ""; // Test for SVN revision string - example: https://svn.voltdb.com/eng/trunk?revision=2352 String[] splitted = fullBuildString.split("=", 2); if (splitted.length == 2) { build = splitted[1].trim(); if (build.length() == 0) { return null; } return build; } // Test for git build string - example: 2.0 voltdb-2.0-70-gb39f43e-dirty Pattern p = Pattern.compile("-(\\d*-\\w{8}(?:-.*)?)"); Matcher m = p.matcher(fullBuildString); if (! m.find()) { return null; } build = m.group(1).trim(); if (build.length() == 0) { return null; } return build; } /** * Parse a version string in the form of x.y.z. It doesn't require that * there are exactly three parts in the version. Each part must be separated * by a dot. * * @param versionString * @return an array of each part as integer. */ public static Object[] parseVersionString(String versionString) { if (versionString == null) { return null; } // check for whitespace if (versionString.matches("\\s")) { return null; } // split on the dots String[] split = versionString.split("\\."); if (split.length == 0) { return null; } Object[] v = new Object[split.length]; int i = 0; for (String s : split) { try { v[i] = Integer.parseInt(s); } catch (NumberFormatException e) { v[i] = s; } i++; } // check for a numeric beginning if (v[0] instanceof Integer) { return v; } else { return null; } } /** * Compare two versions. Version should be represented as an array of * integers. * * @param left * @param right * @return -1 if left is smaller than right, 0 if they are equal, 1 if left * is greater than right. */ public static int compareVersions(Object[] left, Object[] right) { if (left == null || right == null) { throw new IllegalArgumentException("Invalid versions"); } for (int i = 0; i < left.length; i++) { // right is shorter than left and share the same prefix => left must be larger if (right.length == i) { return 1; } if (left[i] instanceof Integer) { if (right[i] instanceof Integer) { // compare two numbers if (((Integer) left[i]) > ((Integer) right[i])) { return 1; } else if (((Integer) left[i]) < ((Integer) right[i])) { return -1; } else { continue; } } else { // numbers always greater than alphanumeric tags return 1; } } else if (right[i] instanceof Integer) { // alphanumeric tags always less than numbers return -1; } else { // compare two alphanumeric tags lexicographically int cmp = ((String) left[i]).compareTo((String) right[i]); if (cmp != 0) { return cmp; } else { // two alphanumeric tags are the same... so keep comparing continue; } } } // left is shorter than right and share the same prefix, must be less if (left.length < right.length) { return -1; } // samesies return 0; } public static String formatHostMetadataFromJSON(String json) { try { JSONObject obj = new JSONObject(json); StringBuilder sb = new StringBuilder(); JSONArray interfaces = (JSONArray) obj.get("interfaces"); for (int ii = 0; ii < interfaces.length(); ii++) { sb.append(interfaces.getString(ii)); if (ii + 1 < interfaces.length()) { sb.append(" "); } } sb.append(" "); sb.append(obj.getString("clientPort")).append(','); sb.append(obj.getString("adminPort")).append(','); sb.append(obj.getString("httpPort")); return sb.toString(); } catch (Exception e) { hostLog.warn("Unable to format host metadata " + json, e); } return ""; } // cache whether we're running pro code private static Boolean m_isPro = null; // check if we're running pro code public static boolean isPro() { if (m_isPro == null) { //Allow running pro kit as community. if (!Boolean.parseBoolean(System.getProperty("community", "false"))) { m_isPro = ProClass.load("org.voltdb.CommandLogImpl", "Command logging", ProClass.HANDLER_IGNORE) .hasProClass(); } else { m_isPro = false; } } return m_isPro.booleanValue(); } /** * @param server String containing a hostname/ip, or a hostname/ip:port. * @param defaultPort If a port isn't specified, use this one. * @return hostname or textual ip representation. */ public static String getHostnameFromHostnameColonPort(String server) { return HostAndPort.fromString(server).getHostText(); } /** * @param server String containing a hostname/ip, or a hostname/ip:port. * @param defaultPort If a port isn't specified, use this one. * @return port number. */ public static int getPortFromHostnameColonPort(String server, int defaultPort) { return HostAndPort.fromString(server).getPortOrDefault(defaultPort); } /** * @param server String containing a hostname/ip, or a hostname/ip:port. * @param defaultPort If a port isn't specified, use this one. * @return HostAndPort number. */ public static HostAndPort getHostAndPortFromHostnameColonPort(String server, int defaultPort) { return HostAndPort.fromString(server).withDefaultPort(defaultPort); } /** * @param server String containing a hostname/ip, or a hostname/ip:port. * @param defaultPort If a port isn't specified, use this one. * @return String in hostname/ip:port format. */ public static String getHostnameColonPortString(String server, int defaultPort) { return HostAndPort.fromString(server).withDefaultPort(defaultPort).toString(); } /** * I heart commutativity * @param buffer ByteBuffer assumed position is at end of data * @return the cheesy checksum of this VoltTable */ public static final long cheesyBufferCheckSum(ByteBuffer buffer) { final int mypos = buffer.position(); buffer.position(0); long checksum = 0; if (buffer.hasArray()) { final byte bytes[] = buffer.array(); final int end = buffer.arrayOffset() + mypos; for (int ii = buffer.arrayOffset(); ii < end; ii++) { checksum += bytes[ii]; } } else { for (int ii = 0; ii < mypos; ii++) { checksum += buffer.get(); } } buffer.position(mypos); return checksum; } public static String getCompactStringTimestamp(long timestamp) { SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss"); Date tsDate = new Date(timestamp); return sdf.format(tsDate); } public static synchronized boolean isBindable(int port) { try { ServerSocket ss = new ServerSocket(port); ss.close(); ss = null; return true; } catch (BindException be) { return false; } catch (IOException e) { throw new RuntimeException(e); } } /** * Concatenate an list of arrays of typed-objects * @param empty An empty array of the right type used for cloning * @param arrayList A list of arrays to concatenate. * @return The concatenated mega-array. */ public static T[] concatAll(final T[] empty, Iterable arrayList) { assert(empty.length == 0); if (arrayList.iterator().hasNext() == false) { return empty; } int len = 0; for (T[] subArray : arrayList) { len += subArray.length; } int pos = 0; T[] result = Arrays.copyOf(empty, len); for (T[] subArray : arrayList) { System.arraycopy(subArray, 0, result, pos, subArray.length); pos += subArray.length; } return result; } public static void deleteRecursively( File file) { if (file == null || !file.exists() || !file.canRead() || !file.canWrite()) { return; } if (file.isDirectory() && file.canExecute()) { for (File f: file.listFiles()) { deleteRecursively(f); } } file.delete(); } /** * Get the resident set size, in mb, for the voltdb server on the other end of the client. * If the client is connected to multiple servers, return the max individual rss across * the cluster. */ public static long getMBRss(Client client) { assert(client != null); long rssMax = 0; try { ClientResponse r = client.callProcedure("@Statistics", "MEMORY", 0); VoltTable stats = r.getResults()[0]; stats.resetRowPosition(); while (stats.advanceRow()) { long rss = stats.getLong("RSS") / 1024; if (rss > rssMax) { rssMax = rss; } } return rssMax; } catch (Exception e) { e.printStackTrace(); System.exit(-1); return 0; } } /** * Zip the two lists up into a multimap * @return null if one of the lists is empty */ public static Multimap zipToMap(List keys, List values) { if (keys.isEmpty() || values.isEmpty()) { return null; } Iterator keyIter = keys.iterator(); Iterator valueIter = values.iterator(); ArrayListMultimap result = ArrayListMultimap.create(); while (keyIter.hasNext() && valueIter.hasNext()) { result.put(keyIter.next(), valueIter.next()); } // In case there are more values than keys, assign the rest of the // values to the first key K firstKey = keys.get(0); while (valueIter.hasNext()) { result.put(firstKey, valueIter.next()); } return result; } /** * Aggregates the elements from each of the given deque. It takes one * element from the head of each deque in each loop and put them into a * single list. This method modifies the deques in-place. * @param stuff * @return */ public static List zip(Collection> stuff) { final List result = Lists.newArrayList(); // merge the results Iterator> iter = stuff.iterator(); while (iter.hasNext()) { final K next = iter.next().poll(); if (next != null) { result.add(next); } else { iter.remove(); } if (!iter.hasNext()) { iter = stuff.iterator(); } } return result; } /** * Create an ArrayListMultimap that uses TreeMap as the container map, so order is preserved. */ public static , V> ListMultimap sortedArrayListMultimap() { Map> map = Maps.newTreeMap(); return Multimaps.newListMultimap(map, new Supplier>() { @Override public List get() { return Lists.newArrayList(); } }); } /** * Serialize and then deserialize an invocation so that it has serializedParams set for command logging if the * invocation is sent to a local site. * @return The round-tripped version of the invocation * @throws IOException */ public static StoredProcedureInvocation roundTripForCL(StoredProcedureInvocation invocation) throws IOException { if (invocation.getSerializedParams() != null) { return invocation; } ByteBuffer buf = ByteBuffer.allocate(invocation.getSerializedSize()); invocation.flattenToBuffer(buf); buf.flip(); StoredProcedureInvocation rti = new StoredProcedureInvocation(); rti.initFromBuffer(buf); return rti; } /** * Utility class to convert and hold a human-friendly time value and unit * string. For now it only deals with hours, minutes or seconds and their * fractions. * TODO: Parameterize conversion to optionally support other units, e.g. ms. */ public static class HumanTime { /// The scaled time value. public final double value; /// The scale unit name ("hour", "minute", or "second"). public final String unit; /** * Private constructor. Use static methods to construct. * @param value the scaled time value. * @param unit the unit name (unchecked) */ private HumanTime(double value, String unit) { this.value = value; this.unit = unit; } /** * Scale a nanoseconds number for human consumption. * @param nanos time in nanoseconds. */ public static HumanTime scale(double nanos) { // Start with hours and adjust down until it's >1. Stop at seconds. double value = nanos / 1000000000 / 3600; String unit; if (value >= 1) { unit = "hour"; } else{ value *= 60.0; if (value >= 1) { unit = "minute"; } else { value *= 60.0; unit = "second"; } } return new HumanTime(value, unit); } /** * Format a string for human consumption based on raw nanoseconds. * @param nanos time in nanoseconds. * @return formatted string. */ public static String formatTime(double nanos) { HumanTime tu = scale(nanos); return String.format("%.2f %ss", tu.value, tu.unit); } /** * Format a rate string, for example /second based on an input value * and duration in nanoseconds. Specify the itemUnit value if you would * like to insert a character or word (add your own leading space), * e.g. "%" or " Megawatts", between the rate and the slash ('/'). * @param value arbitrary value for rate calculation. * @param nanos time in nanoseconds. * @param itemUnit unit name for value * @return formatted string. */ public static String formatRate(double value, double nanos, String itemUnit) { // Multiply by 60 so that a seconds duration becomes a per minute rate, and so on.. HumanTime tu = scale((nanos * 60) / value); return String.format("%.2f%s/%s", 60 / tu.value, itemUnit, tu.unit); } /** * Format a rate string, for example /second based on an input value * and duration in nanoseconds. * @param value arbitrary value for rate calculation. * @param nanos time in nanoseconds. * @return formatted string. */ public static String formatRate(double value, double nanos) { return formatRate(value, nanos, ""); } } public static String formatUptime(long uptimeInMs) { long remainingMs = uptimeInMs; long days = TimeUnit.MILLISECONDS.toDays(remainingMs); remainingMs -= TimeUnit.DAYS.toMillis(days); long hours = TimeUnit.MILLISECONDS.toHours(remainingMs); remainingMs -= TimeUnit.HOURS.toMillis(hours); long minutes = TimeUnit.MILLISECONDS.toMinutes(remainingMs); remainingMs -= TimeUnit.MINUTES.toMillis(minutes); long seconds = TimeUnit.MILLISECONDS.toSeconds(remainingMs); remainingMs -= TimeUnit.SECONDS.toMillis(seconds); return String.format("%d days %02d:%02d:%02d.%03d", days, hours, minutes, seconds, remainingMs); } /** * Delays retrieval until first use, but holds onto a boolean value to * minimize overhead. The delayed retrieval allows tests to set properties * dynamically and have them obeyed. */ public static class BooleanSystemProperty { private final String key; private Boolean value = null; private final boolean defaultValue; /** * Construct system property retriever with default value of false * @param key key name */ public BooleanSystemProperty(String key) { this(key, false); } /** * Construct system property retriever with default value provided by caller * @param key key name */ public BooleanSystemProperty(String key, boolean defaultValue) { this.key = key; this.defaultValue = defaultValue; } /** * Retrieves once and caches boolean value. Uses default if not available. * @return true if value or default is true ("true" or "yes" string) */ public boolean isTrue() { if (this.value == null) { // First time - retrieve and convert the value or use the default value. String stringValue = System.getProperty(this.key); if (stringValue != null) { this.value = (stringValue.equalsIgnoreCase("true") || stringValue.equalsIgnoreCase("yes")); } else { this.value = this.defaultValue; } } assert this.value != null; return this.value; } } public static String hsIdTxnIdToString(long hsId, long txnId) { final StringBuilder sb = new StringBuilder(); CoreUtils.hsIdToString(hsId, sb); sb.append(" "); TxnEgo.txnIdToString(txnId, sb); return sb.toString(); } public static String hsIdPairTxnIdToString(final long srcHsId, final long destHsId, final long txnId, final long uniqID) { final StringBuilder sb = new StringBuilder(32); CoreUtils.hsIdToString(srcHsId, sb); sb.append("->"); CoreUtils.hsIdToString(destHsId, sb); sb.append(" "); TxnEgo.txnIdToString(txnId, sb); sb.append(" ").append(uniqID); return sb.toString(); } /** * Get VARBINARY partition keys for the current topology. * @return A map from partition IDs to partition keys, null if failed to get the keys. */ public static Map getBinaryPartitionKeys() { return getBinaryPartitionKeys(null); } /** * Get VARBINARY partition keys for the specified topology. * @return A map from partition IDs to partition keys, null if failed to get the keys. */ public static Map getBinaryPartitionKeys(TheHashinator hashinator) { Map partitionMap = new HashMap<>(); VoltTable partitionKeys = null; if (hashinator == null) { partitionKeys = TheHashinator.getPartitionKeys(VoltType.VARBINARY); } else { partitionKeys = TheHashinator.getPartitionKeys(hashinator, VoltType.VARBINARY); } if (partitionKeys == null) { return null; } else { // This is a shared resource so make a copy of the table to protect the cache copy in TheHashinator ByteBuffer buf = ByteBuffer.allocate(partitionKeys.getSerializedSize()); partitionKeys.flattenToBuffer(buf); buf.flip(); VoltTable keyCopy = PrivateVoltTableFactory.createVoltTableFromSharedBuffer(buf); while (keyCopy.advanceRow()) { partitionMap.put((int) keyCopy.getLong(0), keyCopy.getVarbinary(1)); } } return partitionMap; } /** * Get username and password from credentials file. * @return a Properties variable which contains username and password. */ public static Properties readPropertiesFromCredentials(String credentials) { Properties props = new Properties(); File propFD = new File(credentials); if (!propFD.exists() || !propFD.isFile() || !propFD.canRead()) { throw new IllegalArgumentException("Credentials file " + credentials + " is not a read accessible file"); } else { FileReader fr = null; try { fr = new FileReader(credentials); props.load(fr); } catch (IOException e) { throw new IllegalArgumentException("Credential file not found or permission denied."); } } return props; } /** * Serialize the deferred serializer data into byte buffer * @param mbuf ByteBuffer the buffer is written to * @param ds DeferredSerialization data writes to the byte buffer * @return size of data * @throws IOException */ public static int writeDeferredSerialization(ByteBuffer mbuf, DeferredSerialization ds) throws IOException { int written = 0; try { final int objStartPosition = mbuf.position(); ds.serialize(mbuf); written = mbuf.position() - objStartPosition; } finally { ds.cancel(); } return written; } /** * Log (to the fatal logger) the list of ports in use. * Uses "lsof -i" internally. * * @param log VoltLogger used to print output or warnings. */ public static synchronized void printPortsInUse(VoltLogger log) { try { /* * Don't do DNS resolution, don't use names for port numbers */ ProcessBuilder pb = new ProcessBuilder("lsof", "-i", "-n", "-P"); pb.redirectErrorStream(true); Process p = pb.start(); java.io.InputStreamReader reader = new java.io.InputStreamReader(p.getInputStream()); java.io.BufferedReader br = new java.io.BufferedReader(reader); String str = br.readLine(); log.fatal("Logging ports that are bound for listening, " + "this doesn't include ports bound by outgoing connections " + "which can also cause a failure to bind"); log.fatal("The PID of this process is " + CLibrary.getpid()); if (str != null) { log.fatal(str); } while((str = br.readLine()) != null) { if (str.contains("LISTEN")) { log.fatal(str); } } } catch (Exception e) { log.fatal("Unable to list ports in use at this time."); } } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy