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

alluxio.util.CommonUtils Maven / Gradle / Ivy

There is a newer version: 313
Show newest version
/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.util;

import alluxio.Constants;
import alluxio.conf.AlluxioConfiguration;
import alluxio.conf.PropertyKey;
import alluxio.exception.status.AlluxioStatusException;
import alluxio.proto.dataserver.Protocol;
import alluxio.security.group.CachedGroupMapping;
import alluxio.security.group.GroupMappingService;
import alluxio.util.ShellUtils.ExitCodeException;
import alluxio.util.io.PathUtils;
import alluxio.util.network.NetworkAddressUtils;
import alluxio.util.proto.ProtoUtils;
import alluxio.wire.WorkerNetAddress;

import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.io.Closer;
import com.google.protobuf.ByteString;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.netty.channel.Channel;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;

/**
 * Common utilities shared by all components in Alluxio.
 */
@ThreadSafe
public final class CommonUtils {
  private static final Logger LOG = LoggerFactory.getLogger(CommonUtils.class);

  private static final String ALPHANUM =
      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  private static final Random RANDOM = new Random();

  private static final int JAVA_MAJOR_VERSION =
      parseMajorVersion(System.getProperty("java.version"));

  /**
   * Convenience method for calling {@link #createProgressThread(long, PrintStream)} with an
   * interval of 2 seconds.
   *
   * @param stream the print stream to write to
   * @return the thread
   */
  public static Thread createProgressThread(PrintStream stream) {
    return createProgressThread(2L * Constants.SECOND_MS, stream);
  }

  /**
   * Creates a thread which will write "." to the given print stream at the given interval. The
   * created thread is not started by this method. The created thread will be a daemon thread
   * and will halt when interrupted.
   *
   * @param intervalMs the time interval in milliseconds between writes
   * @param stream the print stream to write to
   * @return the thread
   */
  public static Thread createProgressThread(final long intervalMs, final PrintStream stream) {
    Thread t = new Thread(() -> {
      while (true) {
        CommonUtils.sleepMs(intervalMs);
        if (Thread.interrupted()) {
          return;
        }
        stream.print(".");
      }
    });
    t.setDaemon(true);
    return t;
  }

  /**
   * @return current time in milliseconds
   */
  public static long getCurrentMs() {
    return Instant.now().toEpochMilli();
  }

  /**
   * @return the current jvm major version, 8 for java 1.8 or 11 for java 11
   */
  public static int getJavaVersion() {
    return JAVA_MAJOR_VERSION;
  }

  /**
   * @param tmpDirs the list of possible temporary directories to pick from
   * @return a path to a temporary directory based on the user configuration
   */
  public static String getTmpDir(List tmpDirs) {
    Preconditions.checkState(!tmpDirs.isEmpty(), "No temporary directories available");
    if (tmpDirs.size() == 1) {
      return tmpDirs.get(0);
    }
    // Use existing random instead of ThreadLocal because contention is not expected to be high.
    return tmpDirs.get(RANDOM.nextInt(tmpDirs.size()));
  }

  /**
   * @param storageDir the root of a storage directory in tiered storage
   * @param conf Alluxio's current configuration
   *
   * @return the worker data folder path after each storage directory, the final path will be like
   * "/mnt/ramdisk/alluxioworker" for storage dir "/mnt/ramdisk" by appending
   * {@link PropertyKey#WORKER_DATA_FOLDER}.
   */
  public static String getWorkerDataDirectory(String storageDir, AlluxioConfiguration conf) {
    return PathUtils.concatPath(
        storageDir.trim(), conf.get(PropertyKey.WORKER_DATA_FOLDER));
  }

  /**
   * Converts a list of objects to a string.
   *
   * @param list list of objects
   * @param  type of the objects
   * @return space-separated concatenation of the string representation returned by Object#toString
   *         of the individual objects
   */
  public static  String listToString(List list) {
    StringBuilder sb = new StringBuilder();
    for (T s : list) {
      if (sb.length() != 0) {
        sb.append(" ");
      }
      sb.append(s);
    }
    return sb.toString();
  }

  /**
   * Converts varargs of objects to a string.
   *
   * @param separator separator string
   * @param args variable arguments
   * @param  type of the objects
   * @return concatenation of the string representation returned by Object#toString
   *         of the individual objects
   */
  public static  String argsToString(String separator, T... args) {
    StringBuilder sb = new StringBuilder();
    for (T s : args) {
      if (sb.length() != 0) {
        sb.append(separator);
      }
      sb.append(s);
    }
    return sb.toString();
  }

  /**
   * Parses {@code ArrayList} into {@code String[]}.
   *
   * @param src is the ArrayList of strings
   * @return an array of strings
   */
  public static String[] toStringArray(ArrayList src) {
    String[] ret = new String[src.size()];
    return src.toArray(ret);
  }

  /**
   * Generates a random alphanumeric string of the given length.
   *
   * @param length the length
   * @return a random string
   */
  public static String randomAlphaNumString(int length) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < length; i++) {
      sb.append(ALPHANUM.charAt(RANDOM.nextInt(ALPHANUM.length())));
    }
    return sb.toString();
  }

  /**
   * Generates a random byte array of the given length.
   *
   * @param length the length
   * @return a random byte array
   */
  public static byte[] randomBytes(int length) {
    byte[] result = new byte[length];
    RANDOM.nextBytes(result);
    return result;
  }

  /**
   * Sleeps for the given number of milliseconds.
   *
   * @param timeMs sleep duration in milliseconds
   */
  public static void sleepMs(long timeMs) {
    // TODO(adit): remove this wrapper
    SleepUtils.sleepMs(timeMs);
  }

  /**
   * Sleeps for the given number of milliseconds, reporting interruptions using the given logger.
   *
   * Unlike Thread.sleep(), this method responds to interrupts by setting the thread interrupt
   * status. This means that callers must check the interrupt status if they need to handle
   * interrupts.
   *
   * @param logger logger for reporting interruptions; no reporting is done if the logger is null
   * @param timeMs sleep duration in milliseconds
   */
  public static void sleepMs(Logger logger, long timeMs) {
    // TODO(adit): remove this wrapper
    SleepUtils.sleepMs(logger, timeMs);
  }

  /**
   * Common empty loop utility that serves the purpose of warming up the JVM before performance
   * microbenchmarks.
   */
  public static void warmUpLoop() {
    for (int k = 0; k < 10000000; k++) {}
  }

  /**
   * Creates new instance of a class by calling a constructor that receives ctorClassArgs arguments.
   *
   * @param  type of the object
   * @param cls the class to create
   * @param ctorClassArgs parameters type list of the constructor to initiate, if null default
   *        constructor will be called
   * @param ctorArgs the arguments to pass the constructor
   * @return new class object
   * @throws RuntimeException if the class cannot be instantiated
   */
  public static  T createNewClassInstance(Class cls, Class[] ctorClassArgs,
      Object[] ctorArgs) {
    try {
      if (ctorClassArgs == null) {
        return cls.newInstance();
      }
      Constructor ctor = cls.getConstructor(ctorClassArgs);
      return ctor.newInstance(ctorArgs);
    } catch (InvocationTargetException e) {
      throw new RuntimeException(e.getCause());
    } catch (ReflectiveOperationException e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Gets the current user's group list from Unix by running the command 'groups' NOTE. For
   * non-existing user it will return EMPTY list. This method may return duplicate groups.
   *
   * @param user user name
   * @return the groups list that the {@code user} belongs to. The primary group is returned first
   */
  public static List getUnixGroups(String user) throws IOException {
    String result;
    List groups = new ArrayList<>();
    try {
      result = ShellUtils.execCommand(ShellUtils.getGroupsForUserCommand(user));
    } catch (ExitCodeException e) {
      // if we didn't get the group - just return empty list
      LOG.warn("got exception trying to get groups for user {}: {}", user, e.toString());
      return groups;
    }

    StringTokenizer tokenizer = new StringTokenizer(result, ShellUtils.TOKEN_SEPARATOR_REGEX);
    while (tokenizer.hasMoreTokens()) {
      groups.add(tokenizer.nextToken());
    }
    return groups;
  }

  /**
   * Waits for a condition to be satisfied.
   *
   * @param description a description of what causes condition to evaluate to true
   * @param condition the condition to wait on
   * @throws TimeoutException if the function times out while waiting for the condition to be true
   */
  public static void waitFor(String description, Supplier condition)
      throws InterruptedException, TimeoutException {
    waitFor(description, condition, WaitForOptions.defaults());
  }

  /**
   * Waits for a condition to be satisfied.
   *
   * @param description a description of what causes condition to evaluate to true
   * @param condition the condition to wait on
   * @param options the options to use
   * @throws TimeoutException if the function times out while waiting for the condition to be true
   */
  public static void waitFor(String description, Supplier condition,
      WaitForOptions options) throws InterruptedException, TimeoutException {
    waitForResult(description, condition, (b) -> b, options);
  }

  /**
   * Waits for the object to meet a certain condition.
   *
   * @param description a description of what causes condition to be met
   * @param objectSupplier the object to check the condition for
   * @param condition the condition to wait on
   * @param  type of the object
   * @return the object
   * @throws TimeoutException if the function times out while waiting for the condition to be true
   * @throws InterruptedException if the thread was interrupted
   */
  public static  T waitForResult(String description, Supplier objectSupplier,
                                    Function condition)
      throws TimeoutException, InterruptedException {
    return waitForResult(description, objectSupplier, condition, WaitForOptions.defaults());
  }

  /**
   * Waits for the object to meet a certain condition.
   *
   * @param description a description of what causes condition to be met
   * @param objectSupplier the object to check the condition for
   * @param condition the condition to wait on
   * @param options the options to use
   * @param  type of the object
   * @return the object
   * @throws TimeoutException if the function times out while waiting for the condition to be true
   * @throws InterruptedException if the thread was interrupted
   */
  public static  T waitForResult(String description, Supplier objectSupplier,
                                    Function condition, WaitForOptions options)
      throws TimeoutException, InterruptedException {
    T value;
    long start = getCurrentMs();
    int interval = options.getInterval();
    int timeout = options.getTimeoutMs();
    while (condition.apply(value = objectSupplier.get()) != true) {
      if (timeout != WaitForOptions.NEVER && getCurrentMs() - start > timeout) {
        throw new TimeoutException("Timed out waiting for " + description + " options: " + options
            + " last value: " + ObjectUtils.toString(value));
      }
      Thread.sleep(interval);
    }
    return value;
  }

  /**
   * Gets the primary group name of a user.
   *
   * @param userName Alluxio user name
   * @param conf Alluxio configuration
   * @return primary group name
   */
  public static String getPrimaryGroupName(String userName, AlluxioConfiguration conf)
      throws IOException {
    List groups = getGroups(userName, conf);
    return (groups != null && groups.size() > 0) ? groups.get(0) : "";
  }

  /**
   * Using {@link CachedGroupMapping} to get the group list of a user.
   *
   * @param userName Alluxio user name
   * @param conf Alluxio configuration
   * @return the group list of the user
   */
  public static List getGroups(String userName, AlluxioConfiguration conf)
      throws IOException {
    GroupMappingService groupMappingService = GroupMappingService.Factory.get(conf);
    return groupMappingService.getGroups(userName);
  }

  /**
   * Strips the suffix if it exists. This method will leave keys without a suffix unaltered.
   *
   * @param key the key to strip the suffix from
   * @param suffix suffix to remove
   * @return the key with the suffix removed, or the key unaltered if the suffix is not present
   */
  public static String stripSuffixIfPresent(final String key, final String suffix) {
    if (key.endsWith(suffix)) {
      return key.substring(0, key.length() - suffix.length());
    }
    return key;
  }

  /**
   * Strips the prefix from the key if it is present. For example, for input key
   * ufs://my-bucket-name/my-key/file and prefix ufs://my-bucket-name/, the output would be
   * my-key/file. This method will leave keys without a prefix unaltered, ie. my-key/file
   * returns my-key/file.
   *
   * @param key the key to strip
   * @param prefix prefix to remove
   * @return the key without the prefix
   */
  public static String stripPrefixIfPresent(final String key, final String prefix) {
    if (key.startsWith(prefix)) {
      return key.substring(prefix.length());
    }
    return key;
  }

  /**
   * Strips the leading and trailing quotes from the given string.
   * E.g. return 'alluxio' for input '"alluxio"'.
   *
   * @param str The string to strip
   * @return The string without the leading and trailing quotes
   */
  public static String stripLeadingAndTrailingQuotes(String str) {
    int length = str.length();
    if (length > 1 && str.startsWith("\"") && str.endsWith("\"")) {
      str = str.substring(1, length - 1);
    }
    return str;
  }

  /**
   * Gets the value with a given key from a static key/value mapping in string format. E.g. with
   * mapping "id1=user1;id2=user2", it returns "user1" with key "id1". It returns null if the given
   * key does not exist in the mapping.
   *
   * @param mapping the "key=value" mapping in string format separated by ";"
   * @param key the key to query
   * @return the mapped value if the key exists, otherwise returns null
   */
  @Nullable
  public static String getValueFromStaticMapping(String mapping, String key) {
    Map m = Splitter.on(";")
        .omitEmptyStrings()
        .trimResults()
        .withKeyValueSeparator("=")
        .split(mapping);
    return m.get(key);
  }

  /**
   * Gets the root cause of an exception.
   * It stops at encountering gRPC's StatusRuntimeException.
   *
   * @param e the exception
   * @return the root cause
   */
  public static Throwable getRootCause(Throwable e) {
    while (e.getCause() != null && !(e.getCause() instanceof StatusRuntimeException)) {
      e = e.getCause();
    }
    return e;
  }

  /**
   * Casts a {@link Throwable} to an {@link IOException}.
   *
   * @param e the throwable
   * @return the IO exception
   */
  public static IOException castToIOException(Throwable e) {
    if (e instanceof IOException) {
      return (IOException) e;
    } else {
      return new IOException(e);
    }
  }

  /**
   * Returns an iterator that iterates on a single element.
   *
   * @param element the element
   * @param  the type of the element
   * @return the iterator
   */
  public static  Iterator singleElementIterator(final T element) {
    return new Iterator() {
      private boolean mHasNext = true;

      @Override
      public boolean hasNext() {
        return mHasNext;
      }

      @Override
      public T next() {
        if (!hasNext()) {
          throw new NoSuchElementException();
        }
        mHasNext = false;
        return element;
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException("remove is not supported.");
      }
    };
  }

  /**
   * Executes the given callables, waiting for them to complete (or time out). If a callable throws
   * an exception, that exception will be re-thrown from this method.
   *
   * @param callables the callables to execute
   * @param timeoutMs time to wait for the callables to complete, in milliseconds
   * @param  the return type of the callables
   * @throws TimeoutException if the callables don't complete before the timeout
   * @throws ExecutionException if any of the callables throws an exception
   */
  public static  void invokeAll(List> callables, long timeoutMs)
      throws TimeoutException, ExecutionException {
    ExecutorService service = Executors.newCachedThreadPool();
    try {
      invokeAll(service, callables, timeoutMs);
    } finally {
      service.shutdownNow();
    }
  }

  /**
   * Executes the given callables, waiting for them to complete (or time out). If a callable throws
   * an exception, that exception will be re-thrown from this method. If the current thread is
   * interrupted, an Exception will be thrown.
   *
   * @param service the service to execute the callables
   * @param callables the callables to execute
   * @param timeoutMs time to wait for the callables to complete, in milliseconds
   * @param  the return type of the callables
   * @throws TimeoutException if the callables don't complete before the timeout
   * @throws ExecutionException if any of the callables throws an exception
   */
  public static  void invokeAll(ExecutorService service, List> callables,
      long timeoutMs) throws TimeoutException, ExecutionException {
    long endMs = getCurrentMs() + timeoutMs;
    List> pending = new ArrayList<>();
    for (Callable c : callables) {
      pending.add(service.submit(c));
    }
    // Poll the tasks to exit early in case of failure.
    while (!pending.isEmpty()) {
      if (Thread.interrupted()) {
        // stop waiting if interrupted
        Thread.currentThread().interrupt();
        throw new RuntimeException("Thread was interrupted while waiting for invokeAll.");
      }
      Iterator> it = pending.iterator();
      while (it.hasNext()) {
        Future future = it.next();
        if (future.isDone()) {
          // Check whether the callable threw an exception.
          try {
            future.get();
          } catch (InterruptedException e) {
            // This should never happen since we already checked isDone().
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
          }
          it.remove();
        }
      }
      if (pending.isEmpty()) {
        break;
      }
      long remainingMs = endMs - getCurrentMs();
      if (remainingMs <= 0) {
        // Cancel the pending futures
        for (Future future : pending) {
          future.cancel(true);
        }
        throw new TimeoutException(
            String.format("Timed out after %dms", timeoutMs - remainingMs));
      }
      CommonUtils.sleepMs(Math.min(remainingMs, 50));
    }
  }

  /**
   * Closes the Closer and re-throws the Throwable. Any exceptions thrown while closing the Closer
   * will be added as suppressed exceptions to the Throwable. This method always throws the given
   * Throwable, wrapping it in a RuntimeException if it's a non-IOException checked exception.
   *
   * Note: This method will wrap non-IOExceptions in RuntimeException. Do not use this method in
   * methods which throw non-IOExceptions.
   *
   * 
   * Closer closer = new Closer();
   * try {
   *   Closeable c = closer.register(new Closeable());
   * } catch (Throwable t) {
   *   throw closeAndRethrow(closer, t);
   * }
   * 
* * @param closer the Closer to close * @param t the Throwable to re-throw * @return this method never returns */ public static RuntimeException closeAndRethrow(Closer closer, Throwable t) throws IOException { try { throw closer.rethrow(t); } finally { closer.close(); } } /** * Similar to {@link CommonUtils#closeAndRethrow} but always return a RuntimeException. * * @param closer the Closer to close * @param t the Throwable to re-throw * @return this method never returns */ public static RuntimeException closeAndRethrowRuntimeException(Closer closer, Throwable t) { try { throw closer.rethrow(t instanceof IOException ? new RuntimeException(t) : t); } catch (IOException e) { // we shall never reach here as no IOException enters rethrow } finally { try { closer.close(); } catch (IOException e) { // we shall never reach here as close catches all throwable } } return new IllegalStateException("this method shall never return"); } /** Alluxio process types. */ public enum ProcessType { JOB_MASTER, JOB_WORKER, CLIENT, MASTER, PLUGIN, PROXY, WORKER; } /** * Represents the type of Alluxio process running in this JVM. * * NOTE: This will only be set by main methods of Alluxio processes. It will not be set properly * for tests. Avoid using this field if at all possible. */ public static final AtomicReference PROCESS_TYPE = new AtomicReference<>(ProcessType.CLIENT); /** * Unwraps a {@link alluxio.proto.dataserver.Protocol.Response}. * * @param response the response */ public static void unwrapResponse(Protocol.Response response) throws AlluxioStatusException { Status status = ProtoUtils.fromProto(response.getStatus()); if (status != Status.OK) { throw AlluxioStatusException.from(status.withDescription(response.getMessage())); } } /** * Unwraps a {@link alluxio.proto.dataserver.Protocol.Response} associated with a channel. * * @param response the response * @param channel the channel that receives this response */ public static void unwrapResponseFrom(Protocol.Response response, Channel channel) throws AlluxioStatusException { Status status = ProtoUtils.fromProto(response.getStatus()); if (status != Status.OK) { throw AlluxioStatusException.from(status.withDescription( String.format("Channel to %s: %s", channel.remoteAddress(), response.getMessage()))); } } /** * @param address the Alluxio worker network address * @param conf Alluxio configuration * @return true if the worker is local */ public static boolean isLocalHost(WorkerNetAddress address, AlluxioConfiguration conf) { return address.getHost().equals(NetworkAddressUtils.getClientHostName(conf)); } /** * Closes the netty channel from outside the netty I/O thread. * NOTE: Be careful when holding any lock that can be acquired in the netty I/O thread when * calling this function to avoid having deadlocks. * * @param channel the netty channel */ public static void closeChannel(final Channel channel) { if (channel.isOpen()) { try { channel.eventLoop().submit(new Runnable() { @Override public void run() { channel.close(); } }).sync(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } } /** * Converts a millisecond number to a formatted date String. * * @param millis a long millisecond number * @param dateFormatPattern the date format to follow when converting. i.e. mm-dd-yyyy * @return formatted date String */ public static String convertMsToDate(long millis, String dateFormatPattern) { DateFormat dateFormat = new SimpleDateFormat(dateFormatPattern); return dateFormat.format(new Date(millis)); } /** * Converts milliseconds to clock time. * * @param millis milliseconds * @return input encoded as clock time */ public static String convertMsToClockTime(long millis) { Preconditions.checkArgument(millis >= 0, "Negative values %s are not supported to convert to clock time.", millis); long days = millis / Constants.DAY_MS; long hours = (millis % Constants.DAY_MS) / Constants.HOUR_MS; long mins = (millis % Constants.HOUR_MS) / Constants.MINUTE_MS; long secs = (millis % Constants.MINUTE_MS) / Constants.SECOND_MS; return String.format("%d day(s), %d hour(s), %d minute(s), and %d second(s)", days, hours, mins, secs); } /** * @param input the input map * @return a map using protobuf {@link ByteString} for values instead of {@code byte[]} */ public static Map convertToByteString(Map input) { if (input == null) { return Collections.emptyMap(); } Map output = new HashMap<>(input.size()); input.forEach((k, v) -> output.put(k, ByteString.copyFrom(v))); return output; } /** * @param input the input map * @return a map using {@code byte[]} for values instead of protobuf {@link ByteString} */ public static Map convertFromByteString(Map input) { if (input == null) { return Collections.emptyMap(); } Map output = new HashMap<>(input.size()); input.forEach((k, v) -> output.put(k, v.toByteArray())); return output; } /** * Memoize implementation for java.util.function.supplier. * * @param original the original supplier * @param the object type * @return the supplier with memorization */ public static Supplier memoize(Supplier original) { return new Supplier() { Supplier mDelegate = this::firstTime; boolean mInitialized; @Override public T get() { return mDelegate.get(); } private synchronized T firstTime() { if (!mInitialized) { T value = original.get(); mDelegate = () -> value; mInitialized = true; } return mDelegate.get(); } }; } /** * Partitions a list into numLists many lists each with around list.size() / numLists elements. * * @param list the list to partition * @param numLists number of lists to return * @param the object type * @return partitioned list */ public static List> partition(List list, int numLists) { ArrayList> result = new ArrayList<>(numLists); for (int i = 0; i < numLists; i++) { result.add(new ArrayList<>(list.size() / numLists + 1)); } for (int i = 0; i < list.size(); i++) { result.get(i % numLists).add(list.get(i)); } return result; } /** * Validates whether a network address is reachable. * * @param hostname host name of the network address * @param port port of the network address * @param timeoutMs duration to attempt connection before returning false * @return whether the network address is reachable */ public static boolean isAddressReachable(String hostname, int port, int timeoutMs) { try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress(hostname, port), timeoutMs); return true; } catch (IOException e) { return false; } } /** * Recursively lists a local dir and all its subdirs and return all the files. * * @param dir the directory * @return a list of all the files * */ public static List recursiveListLocalDir(File dir) { File[] files = dir.listFiles(); // File#listFiles can return null when the path is invalid if (files == null) { return Collections.emptyList(); } List result = new ArrayList<>(files.length); for (File f : files) { if (f.isDirectory()) { result.addAll(recursiveListLocalDir(f)); continue; } result.add(f); } return result; } /** * @param version the version string of the JVMgi * @return the major version of the current JVM, 8 for 1.8, 11 for java 11 * see https://www.oracle.com/java/technologies/javase/versioning-naming.html for reference */ public static int parseMajorVersion(String version) { if (version.startsWith("1.")) { version = version.substring(2, 3); } else { int dot = version.indexOf("."); if (dot != -1) { version = version.substring(0, dot); } } return Integer.parseInt(version); } /** * @param obj a Java object * @return if this object is an collection */ public static boolean isCollection(Object obj) { return obj instanceof Collection || obj instanceof Map; } /** * @param obj a Java object * @return a string to summarize the object if this is a collection or a map */ public static String summarizeCollection(Object obj) { if (obj instanceof Collection) { return String.format( "%s{%d entries}", obj.getClass().getSimpleName(), ((Collection) obj).size()); } else if (obj instanceof Map) { return String.format("Map{%d entries}", ((Map) obj).size()); } return Objects.toString(obj); } /** * @param e error * @return whether it's fatal error */ public static boolean isFatalError(Throwable e) { // StackOverflowError ok even though it is a VirtualMachineError if (e instanceof StackOverflowError) { return false; } // VirtualMachineError includes OutOfMemoryError and other fatal errors return e instanceof VirtualMachineError || e instanceof LinkageError; } private CommonUtils() {} // prevent instantiation }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy