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

com.pivotal.gemfirexd.internal.shared.common.SharedUtils Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2015 Pivotal Software, 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. See accompanying
 * LICENSE file.
 */

package com.pivotal.gemfirexd.internal.shared.common;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;
import java.util.Properties;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Some common utilities shared by client and server code. Only has static
 * methods so no instance allowed.
 * 
 * @author swale
 */
public abstract class SharedUtils {

  /** no instance allowed */
  private SharedUtils() {
  }

  /** the number of bytes required for representation of SQL Timestamp */
  public static final int TIMESTAMP_RLENGTH = 26;

  /**
   * the value for VMKind that indicates that this VM is a GemFireXD
   * datastore
   */
  public static final String VM_DATASTORE = "datastore";

  /**
   * the value for VMKind that indicates that this VM is a GemFireXD
   * accessor that holds no data
   */
  public static final String VM_ACCESSOR = "accessor";

  /**
   * the value for VMKind that indicates that this VM is a GemFireXD
   * stand-alone locator started using GfxdDistributionLocator
   */
  public static final String VM_LOCATOR = "locator";

  /**
   * the value for VMKind that indicates that this VM is a GemFireXD
   * jmx agent started using AdminDistributedSystem
   */
  public static final String VM_AGENT = "agent";
  
  /**
   * the value for VMKind that indicates that this VM is a GemFireXD
   * admin started using GfxdSystemAdmin
   */
  public static final String VM_ADMIN = "admin";
  
  private static final Pattern csvPattern = Pattern
      .compile("[\\s]*(,[\\s]*)|$");

  /**
   * The default number of millis to sleep before retry during failover.
   */
  public static final int HA_RETRY_SLEEP_MILLIS = 10;

  public static final String GFXD_PRODUCT_NAME = "SnappyData";

  public static final String GFXD_VERSION_PROPERTIES =
      "com/pivotal/gemfirexd/internal/GemFireXDVersion.properties";

  /**
   * Adapted from derby client DateTime#parseTimestampString.
   * 
   * Parse a String of the form yyyy-mm-dd-hh.mm.ss.ffffff and
   * store the various fields into the received Calendar object.
   * 
   * @param timestamp
   *          Timestamp value to parse, as a String.
   * @param cal
   *          Calendar into which to store the parsed fields. Should not be
   *          null.
   * 
   * @return The microseconds field as parsed from the timestamp string. This
   *         cannot be set in the Calendar object but we still want to preserve
   *         the value, in case the caller needs it (for example, to create a
   *         java.sql.Timestamp with microsecond precision).
   */
  public static int parseTimestampString(String timestamp, Calendar cal) {
    final int zeroBase = '0';
    cal.set(Calendar.YEAR, 1000 * ((timestamp.charAt(0)) - zeroBase) + 100
        * ((timestamp.charAt(1)) - zeroBase) + 10
        * ((timestamp.charAt(2)) - zeroBase)
        + ((timestamp.charAt(3)) - zeroBase));

    cal.set(Calendar.MONTH, 10 * ((timestamp.charAt(5)) - zeroBase)
        + ((timestamp.charAt(6)) - zeroBase) - 1);

    cal.set(Calendar.DAY_OF_MONTH, 10 * ((timestamp.charAt(8)) - zeroBase)
        + ((timestamp.charAt(9)) - zeroBase));

    // should be 24-hour below
    cal.set(Calendar.HOUR_OF_DAY,
    /* (original code) cal.set(Calendar.HOUR, */
    10 * ((timestamp.charAt(11)) - zeroBase)
        + ((timestamp.charAt(12)) - zeroBase));

    cal.set(Calendar.MINUTE, 10 * ((timestamp.charAt(14)) - zeroBase)
        + ((timestamp.charAt(15)) - zeroBase));

    cal.set(Calendar.SECOND, 10 * ((timestamp.charAt(17)) - zeroBase)
        + ((timestamp.charAt(18)) - zeroBase));

    int micros = 100000 * ((timestamp.charAt(20)) - zeroBase) + 10000
        * ((timestamp.charAt(21)) - zeroBase) + 1000
        * ((timestamp.charAt(22)) - zeroBase) + 100
        * ((timestamp.charAt(23)) - zeroBase) + 10
        * ((timestamp.charAt(24)) - zeroBase)
        + ((timestamp.charAt(25)) - zeroBase);

    /* The "ffffff" that we parsed is microseconds.  In order to
     * capture that information inside of the MILLISECOND field
     * we have to divide by 1000.
     */
    cal.set(Calendar.MILLISECOND, micros / 1000);
    return micros;
  }

  /**
   * java.sql.Timestamp is converted to a character representation which is in
   * DERBY string representation of a timestamp:
   * yyyy-mm-dd-hh.mm.ss.ffffff. and then converted to bytes
   * 
   * @param timestamp
   *          timestamp value
   * @param cal
   *          the {@link java.util.Calendar} object that represents the
   *          timezone/locale of this timestamp
   * @return the byte array representing the given {@link java.sql.Timestamp}
   */
  public static byte[] timestampToTimestampChars(java.sql.Timestamp timestamp,
      java.util.Calendar cal) {
    final byte[] bytes = new byte[TIMESTAMP_RLENGTH];
    final int len = timestampToTimestampChars(bytes, 0, timestamp, cal, true);
    assert len == TIMESTAMP_RLENGTH;
    return bytes;
  }

  /**
   * java.sql.Timestamp is converted to a character representation which is in
   * DERBY string representation of a timestamp:
   * yyyy-mm-dd-hh.mm.ss.ffffff. and then converted to bytes using
   * ASCII encoding
   * 
   * @param buffer
   *          bytes in ASCII encoding of the timestamp
   * @param offset
   *          write into the buffer from this offset
   * @param timestamp
   *          timestamp value
   * @param cal
   *          the {@link java.util.Calendar} object that represents the
   *          timezone/locale of this timestamp
   * @return {@link #TIMESTAMP_RLENGTH}. This is the fixed length in bytes,
   *         taken to represent the timestamp value or -year if year exceeds
   *         9999
   */
  public static int timestampToTimestampChars(byte[] buffer, int offset,
      java.sql.Timestamp timestamp, java.util.Calendar cal) {
    return timestampToTimestampChars(buffer, offset, timestamp, cal, false);
  }

  /**
   * Adapted from derby client DateTime#timestampToTimestampBytes.
   * 
   * Given year, month, day, hour, minute, second, microsecond/nanos is
   * converted to a character representation which is in DERBY string
   * representation of a timestamp: yyyy-mm-dd hh:mm:ss.ffffff or
   * yyyy-mm-dd hh:mm:ss.fffffffff.
   */
  public static int dateTimeToString(final char[] buffer, int offset,
      int year, final int month, final int day, final int hour,
      final int minute, final int second, final int microsecond,
      int nanos, final boolean clientFormat) {
    final int zeroBase = '0';
    final char dateTimeSep;
    final char timeSep;
    if (clientFormat) {
      dateTimeSep = '-';
      timeSep = '.';
    }
    else {
      dateTimeSep = ' ';
      timeSep = ':';
    }
    if (month >= 0) {
      if (year > 9999) {
        year = 9999;
      }
      buffer[offset++] = (char)(year / 1000 + zeroBase);
      buffer[offset++] = (char)((year % 1000) / 100 + zeroBase);
      buffer[offset++] = (char)((year % 100) / 10 + zeroBase);
      buffer[offset++] = (char)(year % 10 + +zeroBase);
      buffer[offset++] = (char)'-';
      buffer[offset++] = (char)(month / 10 + zeroBase);
      buffer[offset++] = (char)(month % 10 + zeroBase);
      buffer[offset++] = (char)'-';
      buffer[offset++] = (char)(day / 10 + zeroBase);
      buffer[offset++] = (char)(day % 10 + zeroBase);
      if (hour >= 0) {
        buffer[offset++] = dateTimeSep;
      }
    }
    if (hour >= 0) {
      buffer[offset++] = (char)(hour / 10 + zeroBase);
      buffer[offset++] = (char)(hour % 10 + zeroBase);
      buffer[offset++] = timeSep;
      buffer[offset++] = (char)(minute / 10 + zeroBase);
      buffer[offset++] = (char)(minute % 10 + zeroBase);
      buffer[offset++] = timeSep;
      buffer[offset++] = (char)(second / 10 + zeroBase);
      buffer[offset++] = (char)(second % 10 + zeroBase);
    }
    if (nanos < 0) {
      if (microsecond >= 0) {
        buffer[offset++] = (char)'.';
        buffer[offset++] = (char)(microsecond / 100000 + zeroBase);
        buffer[offset++] = (char)((microsecond % 100000) / 10000 + zeroBase);
        buffer[offset++] = (char)((microsecond % 10000) / 1000 + zeroBase);
        buffer[offset++] = (char)((microsecond % 1000) / 100 + zeroBase);
        buffer[offset++] = (char)((microsecond % 100) / 10 + zeroBase);
        buffer[offset] = (char)(microsecond % 10 + zeroBase);
      }
    }
    else {
      buffer[offset++] = (char)'.';
      buffer[offset++] = (char)(nanos / 100000000 + zeroBase);
      /* originally
      buffer[offset++] = (char)((nanos % 100000000) / 10000000 + zeroBase);
      buffer[offset++] = (char)((nanos % 10000000) / 1000000 + zeroBase);
      buffer[offset++] = (char)((nanos % 1000000) / 100000 + zeroBase);
      buffer[offset++] = (char)((nanos % 100000) / 10000 + zeroBase);
      buffer[offset++] = (char)((nanos % 10000) / 1000 + zeroBase);
      buffer[offset++] = (char)((nanos % 1000) / 100 + zeroBase);
      buffer[offset++] = (char)((nanos % 100) / 10 + zeroBase);
      buffer[offset] = (char)(nanos % 10 + zeroBase);
      */
      if (nanos > 0) {
        int startFactor = 100000000, startDivisor = 10000000;
        // remove trailing zeros for server-side formatting
        if (!clientFormat) {
          while ((nanos % 10) == 0 && startDivisor > 0) {
            nanos /= 10;
            startFactor /= 10;
            startDivisor /= 10;
          }
        }
        while (startDivisor > 0) {
          buffer[offset++] = (char)(((nanos % startFactor) / startDivisor)
              + zeroBase);
          startFactor /= 10;
          startDivisor /= 10;
        }
      }
    }
    return offset;
  }

  /**
   * Adapted from derby client DateTime#timestampToTimestampBytes.
   * 
   * Given year, month, day, hour, minute, second, microsecond/nanos is
   * converted to a character representation which is in DERBY string
   * representation of a timestamp: yyyy-mm-dd-hh.mm.ss.ffffff or
   * yyyy-mm-dd-hh.mm.ss.fffffffff and then converted to bytes
   * using ASCII encoding.
   */
  public static int dateTimeToChars(final byte[] buffer, int offset,
      int year, final int month, final int day, final int hour,
      final int minute, final int second, final int microsecond,
      final int nanos, final boolean clientFormat) {
    final int zeroBase = '0';
    final byte dateTimeSep;
    final byte timeSep;
    if (clientFormat) {
      dateTimeSep = (byte)'-';
      timeSep = (byte)'.';
    }
    else {
      dateTimeSep = (byte)' ';
      timeSep = (byte)':';
    }
    if (month >= 0) {
      if (year > 9999) {
        year = 9999;
      }
      buffer[offset++] = (byte)(year / 1000 + zeroBase);
      buffer[offset++] = (byte)((year % 1000) / 100 + zeroBase);
      buffer[offset++] = (byte)((year % 100) / 10 + zeroBase);
      buffer[offset++] = (byte)(year % 10 + +zeroBase);
      buffer[offset++] = (byte)'-';
      buffer[offset++] = (byte)(month / 10 + zeroBase);
      buffer[offset++] = (byte)(month % 10 + zeroBase);
      buffer[offset++] = (byte)'-';
      buffer[offset++] = (byte)(day / 10 + zeroBase);
      buffer[offset++] = (byte)(day % 10 + zeroBase);
      if (hour >= 0) {
        buffer[offset++] = dateTimeSep;
      }
    }
    if (hour >= 0) {
      buffer[offset++] = (byte)(hour / 10 + zeroBase);
      buffer[offset++] = (byte)(hour % 10 + zeroBase);
      buffer[offset++] = timeSep;
      buffer[offset++] = (byte)(minute / 10 + zeroBase);
      buffer[offset++] = (byte)(minute % 10 + zeroBase);
      buffer[offset++] = timeSep;
      buffer[offset++] = (byte)(second / 10 + zeroBase);
      buffer[offset++] = (byte)(second % 10 + zeroBase);
    }
    if (nanos < 0) {
      if (microsecond >= 0) {
        buffer[offset++] = (byte)'.';
        buffer[offset++] = (byte)(microsecond / 100000 + zeroBase);
        buffer[offset++] = (byte)((microsecond % 100000) / 10000 + zeroBase);
        buffer[offset++] = (byte)((microsecond % 10000) / 1000 + zeroBase);
        buffer[offset++] = (byte)((microsecond % 1000) / 100 + zeroBase);
        buffer[offset++] = (byte)((microsecond % 100) / 10 + zeroBase);
        buffer[offset] = (byte)(microsecond % 10 + zeroBase);
      }
    }
    else {
      buffer[offset++] = (byte)'.';
      buffer[offset++] = (byte)(nanos / 100000000 + zeroBase);
      buffer[offset++] = (byte)((nanos % 100000000) / 10000000 + zeroBase);
      buffer[offset++] = (byte)((nanos % 10000000) / 1000000 + zeroBase);
      buffer[offset++] = (byte)((nanos % 1000000) / 100000 + zeroBase);
      buffer[offset++] = (byte)((nanos % 100000) / 10000 + zeroBase);
      buffer[offset++] = (byte)((nanos % 10000) / 1000 + zeroBase);
      buffer[offset++] = (byte)((nanos % 1000) / 100 + zeroBase);
      buffer[offset++] = (byte)((nanos % 100) / 10 + zeroBase);
      buffer[offset] = (byte)(nanos % 10 + zeroBase);
    }
    return offset;
  }

  /**
   * Adapted from derby client DateTime#timestampToTimestampBytes.
   * 
   * java.sql.Timestamp is converted to a character representation which is in
   * DERBY string representation of a timestamp:
   * yyyy-mm-dd-hh.mm.ss.ffffff. and then converted to bytes using
   * ASCII encoding
   * 
   * @param buffer
   *          bytes in ASCII encoding of the timestamp
   * @param offset
   *          write into the buffer from this offset
   * @param timestamp
   *          timestamp value
   * @param cal
   *          the {@link java.util.Calendar} object that represents the
   *          timezone/locale of this timestamp
   * @param truncateYear
   *          true to truncate year to 9999 if greater than 9999 else return
   *          -year
   * @return {@link #TIMESTAMP_RLENGTH}. This is the fixed length in bytes,
   *         taken to represent the timestamp value or -year if year exceeds
   *         9999 when truncateYear is true
   */
  private static int timestampToTimestampChars(byte[] buffer, int offset,
      java.sql.Timestamp timestamp, java.util.Calendar cal, boolean truncateYear) {
    cal.setTime(timestamp);
    int year = cal.get(Calendar.YEAR);
    if (year > 9999) {
      if (truncateYear) {
        year = 9999;
      }
      else {
        return -year;
      }
    }
    final int month = cal.get(Calendar.MONTH) + 1;
    final int day = cal.get(Calendar.DAY_OF_MONTH);
    final int hour = cal.get(Calendar.HOUR_OF_DAY);
    final int minute = cal.get(Calendar.MINUTE);
    final int second = cal.get(Calendar.SECOND);
    final int microsecond = timestamp.getNanos() / 1000;

    dateTimeToChars(buffer, offset, year, month, day, hour, minute, second,
        microsecond, -1, true);

    return TIMESTAMP_RLENGTH;
  }

  // START: Utilities below are copied from StringUtil

  /**
   * Convert string to uppercase Always use the java.util.ENGLISH locale
   * Copied from derby's StringUtil#SQLToUpperCase.
   * 
   * @param s
   *          string to uppercase
   * @return uppercased string
   */
  public static String SQLToUpperCase(String s) {
    return s.toUpperCase(Locale.ENGLISH);
  }

  // END methods from StringUtil

  /**
   * Open an input stream to read a URL or a file. URL is attempted first, if
   * the string does not conform to a URL then an attempt to open it as a
   * regular file is tried. 
* Attempting the file first can throw a security execption when a valid URL * is passed in. The security exception is due to not have the correct * permissions to access the bogus file path. To avoid this the order was * reversed to attempt the URL first and only attempt a file open if creating * the URL throws a MalformedURLException. * * Copied from JarUtil. */ public static InputStream openURL(final String externalPath) throws IOException { return openURL(null, externalPath); } public static InputStream openURL(final String scriptPath, final String externalPath) throws IOException { try { return AccessController.doPrivileged( new PrivilegedExceptionAction() { public InputStream run() throws IOException { try { return new URL(externalPath).openStream(); } catch (MalformedURLException mfurle) { try { return new FileInputStream(externalPath); } catch (FileNotFoundException fnfe) { return new FileInputStream(new File(scriptPath, externalPath)); } } } }); } catch (PrivilegedActionException e) { if (e.getException() instanceof IOException) { throw (IOException)e.getException(); } else { throw new IOException(e.getException()); } } } /** * Visitor for each string in a comma-separated string. * * @author swale */ public static interface CSVVisitor { /** * Visit one string of the list provided as a comma-separated string. */ public void visit(String str, A action, C context); } /** convert comma separated string to SortedSet */ public static SortedSet toSortedSet(final String csv, final boolean caseSensitive) { final TreeSet strings = new TreeSet(); splitCSV(csv, stringAggregator, strings, Boolean.valueOf(caseSensitive)); return strings; } public static final CSVVisitor, Boolean> stringAggregator = new CSVVisitor, Boolean>() { public void visit(String str, Collection strings, Boolean caseSensitive) { if (caseSensitive.booleanValue()) { strings.add(str); } else { strings.add(SQLToUpperCase(str)); } } }; /** invoke a given callback for each string in a comma separated string */ public static void splitCSV(final String csv, final CSVVisitor visitor, A action, C context) { final int csvLen; if (csv != null && (csvLen = csv.length()) > 0) { int start = 0; // skip leading spaces, if any while (start < csvLen && Character.isWhitespace(csv.charAt(start))) { ++start; } if (start < csvLen) { Matcher mt = csvPattern.matcher(csv); int end; while (mt.find()) { end = mt.start(); // skip trailing blanks, if any while (end > 0 && Character.isWhitespace(csv.charAt(end - 1))) { --end; } final String str = csv.substring(start, end); if (str.length() > 0) { visitor.visit(str, action, context); } start = mt.end(); } } } } /** convert a given collection to a comma separated string */ public static String toCSV(final Collection collection) { final StringBuilder csv = new StringBuilder(); if (collection != null) { boolean firstObj = true; final Iterator iter = collection.iterator(); while (iter.hasNext()) { if (!firstObj) { csv.append(','); } else { firstObj = false; } csv.append(iter.next()); } } return csv.toString(); } /** * Split a given host[port] string into host and port parts. */ public static String getHostPort(final String hostPort, final int[] port) { final int len = hostPort.length(); int spos; try { if ((hostPort.charAt(len - 1)) == ']' && (spos = hostPort.indexOf('[')) >= 0) { port[0] = Integer.parseInt(hostPort.substring(spos + 1, len - 1)); return hostPort.substring(0, spos); } else if ((spos = hostPort.indexOf(':')) >= 0) { port[0] = Integer.parseInt(hostPort.substring(spos + 1)); return hostPort.substring(0, spos); } } catch (NumberFormatException nfe) { throw new NumberFormatException( "failed to parse integer port in given host[port] string '" + hostPort + "': " + nfe.getLocalizedMessage()); } throw new NumberFormatException("failed to split given host[port] string: " + hostPort); } /** * Stripped from InternalDriver.getAttributes */ public static Properties getAttributes(String url, Properties info) throws SQLException { // We use FormatableProperties here to take advantage // of the clearDefaults, method. Properties finfo = new Properties(info); info = null; // ensure we don't use this reference directly again. StringTokenizer st = new StringTokenizer(url, ";"); st.nextToken(); // skip the first part of the url while (st.hasMoreTokens()) { String v = st.nextToken(); int eqPos = v.indexOf('='); if (eqPos == -1) { throw new SQLException("The URL '" + url + "' is not properly formed.", "XJ028", 40000); } // if (eqPos != v.lastIndexOf('=')) // throw Util.malformedURL(url); finfo .put((v.substring(0, eqPos)).trim(), (v.substring(eqPos + 1)).trim()); } return finfo; } /** * Print a stacktrace from the throwable to given writer. */ public static void printStackTrace(Throwable t, PrintWriter pw, boolean onlyStack) { int level = 0, commonTraceIndex = 0, commonTraceCauseIndex = 0; int framesInCommon = 0; StackTraceElement[] trace; StackTraceElement[] causedTrace = null; while (t != null) { trace = t.getStackTrace(); if (level > 0) { commonTraceIndex = trace.length - 1; commonTraceCauseIndex = causedTrace.length - 1; while (commonTraceIndex >= 0 && commonTraceCauseIndex >= 0 && trace[commonTraceIndex] .equals(causedTrace[commonTraceCauseIndex])) { --commonTraceIndex; --commonTraceCauseIndex; } framesInCommon = trace.length - 1 - commonTraceIndex; pw.println("============= begin nested exception, level (" + level + ") ==========="); } if (onlyStack) { pw.println(t.getClass()); } else { pw.println(t.toString()); } for (StackTraceElement traceElement : trace) { pw.print("\tat "); pw.println(traceElement); if (level > 0 && commonTraceIndex-- == 0) { break; } } if (framesInCommon > 0) { pw.println("\t... " + framesInCommon + " more"); } if (t instanceof java.sql.SQLException) { Throwable next = ((java.sql.SQLException)t).getNextException(); t = (next == null) ? t.getCause() : next; } else { t = t.getCause(); } if (level > 0) { pw.println("============= end nested exception, level (" + level + ") ==========="); } ++level; causedTrace = trace; } } public static StopWatch newTimer(long startTime) { return startTime > 0 ? new StopWatch(startTime) : null; } /** * Quote a string so that it can be used as an identifier or a string literal * in SQL statements. Identifiers are surrounded by double quotes and string * literals are surrounded by single quotes. If the string contains quote * characters, they are escaped. * * @param source * the string to quote * @param quote * the character to quote the string with (' or ") * @return a string quoted with the specified quote character */ public static String quoteString(String source, char quote) { // Normally, the quoted string is two characters longer than the source // string (because of start quote and end quote). StringBuilder quoted = new StringBuilder(source.length() + 2); quoted.append(quote); for (int i = 0; i < source.length(); i++) { char c = source.charAt(i); // if the character is a quote, escape it with an extra quote if (c == quote) quoted.append(quote); quoted.append(c); } quoted.append(quote); return quoted.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy