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

org.spf4j.base.Throwables Maven / Gradle / Ivy

Go to download

A continuously growing collection of utilities to measure performance, get better diagnostics, improve performance, or do things more reliably, faster that other open source libraries...

The newest version!
/*
 * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Additionally licensed with:
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.spf4j.base;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import gnu.trove.set.hash.THashSet;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.net.URL;
import java.sql.SQLRecoverableException;
import java.sql.SQLTransientException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.spf4j.base.avro.AThrowables;
import org.spf4j.base.avro.RemoteException;
import org.spf4j.ds.IdentityHashSet;

/**
 * utility class for throwables.
 *
 * @author zoly
 */
@ParametersAreNonnullByDefault
@SuppressFBWarnings("FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY")
// Circular dependency with AThrowables, both static utility classes... should be fine...
public final class Throwables {

  /**
   * Caption for labeling suppressed exception stack traces
   */
  public static final String SUPPRESSED_CAPTION = "Suppressed: ";
  /**
   * Caption for labeling causative exception stack traces
   */
  public static final String CAUSE_CAPTION = "Caused by: ";


  private static final int MAX_SUPPRESS_CHAIN
          = Integer.getInteger("spf4j.throwables.defaultMaxSuppressChain", 100);

  private static final PackageDetail DEFAULT_PACKAGE_DETAIL
          = PackageDetail.valueOf(System.getProperty("spf4j.throwables.defaultStackTracePackageDetail", "SHORT"));


  private static final boolean DEFAULT_TRACE_ELEMENT_ABBREVIATION
          = Boolean.parseBoolean(System.getProperty("spf4j.throwables.defaultStackTraceAbbreviation", "true"));


  private static volatile Predicate nonRecoverableClassificationPredicate = new Predicate() {
    @Override
    @SuppressFBWarnings("ITC_INHERITANCE_TYPE_CHECKING")
    public boolean test(final Throwable t) {

      if (t instanceof Error && !(t instanceof StackOverflowError)
              && !(t instanceof AssertionError)
              && !(t.getClass().getName().endsWith("TokenMgrError"))) {
        return true;
      }
      if (t instanceof IOException) {
        String message = t.getMessage();
        if (message != null && message.contains("Too many open files")) {
          return true;
        }
      }
      return false;
    }
  };


  private static volatile Function isRetryablePredicate =
          new Function() {
    /**
     * A default predicate that will answer if a exception is retry-able or not.
     * @param t
     * @return true if a exception is retry-able, false if it is not, null if this is not known.
     */
    @Override
    @SuppressFBWarnings({"ITC_INHERITANCE_TYPE_CHECKING", "NP_BOOLEAN_RETURN_NULL" })
    @Nullable
    public Boolean apply(final Throwable t) {
      // non recoverables are not retryable.
      if (Throwables.containsNonRecoverable(t)) {
        return Boolean.FALSE;
      }
      // check causal chaing
      Throwable e = Throwables.firstCause(t,
              ex -> {
                String exClassName = ex.getClass().getName();
                return (ex instanceof SQLTransientException
              || ex instanceof SQLRecoverableException
              || (ex instanceof IOException && !exClassName.contains("Json"))
              || ex instanceof TimeoutException
              || ex instanceof UncheckedTimeoutException
              || (exClassName.contains("Transient")
                        && !exClassName.contains("NonTransient")));
                        });
      return e != null ? Boolean.TRUE : null;
    }
  };


  private Throwables() {
  }

  /**
   * figure out if a Exception is retry-able or not.
   * If while executing a operation a exception is returned, that exception is retryable if retrying the operation
   * can potentially succeed.
   * @param value
   * @return true/false is retry-able or not, null when this is not clear and can be context dependent.
   */
  @CheckReturnValue
  public static boolean isRetryable(final Throwable value) {
    Boolean result = isRetryablePredicate.apply(value);
    return result == null ? false : result;
  }

  @Nonnull
  @CheckReturnValue
  public static Function getIsRetryablePredicate() {
    return isRetryablePredicate;
  }

  public static void setIsRetryablePredicate(final Function isRetryablePredicate) {
    Throwables.isRetryablePredicate = isRetryablePredicate;
  }


  public static int getNrSuppressedExceptions(final Throwable t) {
    return UnsafeThrowable.getSuppressedNoCopy(t).size();
  }

  public static int getNrRecursiveSuppressedExceptions(final Throwable t) {
    final List suppressedExceptions = UnsafeThrowable.getSuppressedNoCopy(t);
    int count = 0;
    for (Throwable se : suppressedExceptions) {
      count += 1 + getNrRecursiveSuppressedExceptions(se);
    }
    return count;
  }

  @Nullable
  public static Throwable removeOldestSuppressedRecursive(final Throwable t) {
      final List suppressedExceptions = UnsafeThrowable.getSuppressedNoCopy(t);
      if (suppressedExceptions.isEmpty()) {
        return null;
      } else {
        Throwable ex = suppressedExceptions.get(0);
        if (getNrSuppressedExceptions(ex) > 0) {
          return removeOldestSuppressedRecursive(ex);
        } else {
          return suppressedExceptions.remove(0);
        }
      }
  }

  @Nullable
  public static Throwable removeOldestSuppressed(final Throwable t) {
    final List suppressedExceptions = UnsafeThrowable.getSuppressedNoCopy(t);
    if (suppressedExceptions.isEmpty()) {
      return null;
    } else {
      return suppressedExceptions.remove(0);
    }
  }


  public static final class TrimmedException extends Exception {

    public TrimmedException(final String message) {
      super(message);
    }

    @Override
    public synchronized Throwable fillInStackTrace() {
      return this;
    }
  }


  /**
   * Functionality will call Throwable.addSuppressed, 2 extra things happen:
   *
   * 1) limit to nr of exceptions suppressed.
   * 2) if exception is already suppressed, will not add it.
   * 3) will return a clone of exception t.
   *
   * @param 
   * @param t
   * @param suppressed
   * @returna clone of exception t with suppressed exception suppressed;
   * @deprecated use suppressLimited instead.
   *
   */
  @CheckReturnValue
  @Deprecated
  public static  T suppress(@Nonnull final T t, @Nonnull final Throwable suppressed) {
    return suppress(t, suppressed, MAX_SUPPRESS_CHAIN);
  }

  /**
   * Functionality will call Throwable.addSuppressed, 2 extra things happen:
   *
   * 1) limit to nr of exceptions suppressed.
   * 2) if exception is already suppressed, will not add it.
   *
   * @param t
   * @param suppressed
   */
  public static void suppressLimited(@Nonnull final Throwable t, @Nonnull final Throwable suppressed) {
    suppressLimited(t, suppressed, MAX_SUPPRESS_CHAIN);
  }

  @SuppressFBWarnings("NOS_NON_OWNED_SYNCHRONIZATION")
  public static void suppressLimited(@Nonnull final Throwable t, @Nonnull final Throwable suppressed,
          final int maxSuppressed) {
    if (contains(t, suppressed)) { //protect against circular references.
      Logger.getLogger(Throwables.class.getName()).log(Level.INFO,
              "Circular suppression attempted", new RuntimeException(suppressed));
      return;
    }
    synchronized (t) {
      t.addSuppressed(suppressed);
      while (getNrRecursiveSuppressedExceptions(t) > maxSuppressed) {
        if (removeOldestSuppressedRecursive(t) == null) {
          throw new IllegalArgumentException("Impossible state for " + t);
        }
      }
    }
  }


  @CheckReturnValue
  public static  T suppress(@Nonnull final T t, @Nonnull final Throwable suppressed,
          final int maxSuppressed) {
    T clone;
    try {
      clone = Objects.clone(t);
    } catch (RuntimeException ex) {
      t.addSuppressed(ex);
      clone = t;
    }
    if (contains(t, suppressed)) {
      return clone;
    }
    clone.addSuppressed(suppressed);
    while (getNrRecursiveSuppressedExceptions(clone) > maxSuppressed) {
      if (removeOldestSuppressedRecursive(clone) == null) {
        throw new IllegalArgumentException("Impossible state for " + clone);
      }
    }
    return clone;
  }

  /**
   * Utility to get suppressed exceptions.
   *
   * In java 1.7 it will return t.getSuppressed()
   * + in case it is Iterable any other linked exceptions (see
   * SQLException)
   *
   * java 1.6 behavior is deprecated.
   *
   * @param t
   * @return
   */
  public static Throwable[] getSuppressed(final Throwable t) {
    if (t instanceof Iterable) {
      // see SQLException
      Throwable[] osuppressed = t.getSuppressed();
      List suppressed = new ArrayList<>(osuppressed.length);
      Set ignore = new IdentityHashSet<>();
      ignore.addAll(java.util.Arrays.asList(osuppressed));
      suppressed.addAll(ignore);
      ignore.addAll(com.google.common.base.Throwables.getCausalChain(t));
      Iterator it = ((Iterable) t).iterator();
      while (it.hasNext()) {
        Object next = it.next();
        if (next instanceof Throwable) {
          if (ignore.contains(next)) {
            continue;
          }
          suppressed.add((Throwable) next);
          ignore.addAll(com.google.common.base.Throwables.getCausalChain((Throwable) next));
        } else {
          break;
        }
      }
      return suppressed.toArray(new Throwable[suppressed.size()]);
    } else {
      return t.getSuppressed();
    }

  }

  public static void writeTo(final StackTraceElement element, @Nullable final StackTraceElement previous,
          final Appendable to, final PackageDetail detail,
          final boolean abbreviatedTraceElement)
          throws IOException {
    String currClassName = element.getClassName();
    String prevClassName = previous == null ? null : previous.getClassName();
    if (abbreviatedTraceElement) {
      if (currClassName.equals(prevClassName)) {
        to.append('^');
      } else {
        writeAbreviatedClassName(currClassName, to);
      }
    } else {
      to.append(currClassName);
    }
    to.append('.');
    to.append(element.getMethodName());
    String currFileName = element.getFileName();
    String fileName = currFileName;
    if (abbreviatedTraceElement && java.util.Objects.equals(currFileName,
            previous == null ? null : previous.getFileName())) {
      fileName = "^";
    }
    final int lineNumber = element.getLineNumber();
    if (element.isNativeMethod()) {
      to.append("(Native Method)");
    } else if (fileName == null) {
      to.append("(Unknown Source)");
    } else if (lineNumber >= 0) {
      to.append('(').append(fileName).append(':')
              .append(Integer.toString(lineNumber)).append(')');
    } else {
      to.append('(').append(fileName).append(')');
    }
    if (detail == PackageDetail.NONE) {
      return;
    }
    if (abbreviatedTraceElement && currClassName.equals(prevClassName)) {
      to.append("[^]");
      return;
    }
    PackageInfo pInfo = PackageInfo.getPackageInfo(currClassName);
    if (abbreviatedTraceElement && prevClassName != null && pInfo.equals(PackageInfo.getPackageInfo(prevClassName))) {
      to.append("[^]");
      return;
    }
    URL pInfoUrl = pInfo.getUrl();
    if (pInfoUrl != null || pInfo.hasVersion()) {
      to.append('[');
      if (pInfoUrl != null) {
        if (detail == PackageDetail.SHORT) {
          String url = pInfoUrl.toString();
          int lastIndexOf = url.lastIndexOf('/');
          if (lastIndexOf >= 0) {
            int lpos = url.length() - 1;
            if (lastIndexOf == lpos) {
              int prevSlPos = url.lastIndexOf('/', lpos - 1);
              if (prevSlPos < 0) {
                to.append(url);
              } else {
                to.append(url, prevSlPos + 1, url.length());
              }
            } else {
              to.append(url, lastIndexOf + 1, url.length());
            }
          } else {
            to.append(url);
          }
        } else {
          to.append(pInfoUrl.toString());
        }
      } else {
        to.append("na");
      }
      if (pInfo.hasVersion()) {
        to.append(':');
        to.append(pInfo.getVersion());
      }
      to.append(']');
    }
  }

  /**
   * enum describing the PackageDetail level to be logged in the stack trace.
   */
  public enum PackageDetail {
    /**
     * No jar info or version info.
     */
    NONE,
    /**
     * jar file name + manifest version.
     */
    SHORT,
    /**
     * complete jar path + manifest version.
     */
    LONG

  }

  public static String toString(final Throwable t) {
    return toString(t, DEFAULT_PACKAGE_DETAIL);
  }

  public static String toString(final Throwable t, final PackageDetail detail) {
    return toString(t, detail, DEFAULT_TRACE_ELEMENT_ABBREVIATION);
  }

  public static String toString(final Throwable t, final PackageDetail detail, final boolean abbreviatedTraceElement) {
    StringBuilder sb = toStringBuilder(t, detail, abbreviatedTraceElement);
    return sb.toString();
  }

  public static StringBuilder toStringBuilder(final Throwable t, final PackageDetail detail) {
    return toStringBuilder(t, detail, DEFAULT_TRACE_ELEMENT_ABBREVIATION);
  }

  public static StringBuilder toStringBuilder(final Throwable t, final PackageDetail detail,
          final boolean abbreviatedTraceElement) {
    StringBuilder sb = new StringBuilder(1024);
    try {
      writeTo(t, sb, detail, abbreviatedTraceElement);
    } catch (IOException ex) {
      throw new RuntimeException(ex);
    }
    return sb;
  }

  public static void writeTo(@Nonnull final Throwable t, @Nonnull final PrintStream to,
          @Nonnull final PackageDetail detail) {
    writeTo(t, to, detail, DEFAULT_TRACE_ELEMENT_ABBREVIATION);
  }

  @SuppressFBWarnings({"OCP_OVERLY_CONCRETE_PARAMETER"}) // on purpose :-)
  public static void writeTo(@Nonnull final Throwable t, @Nonnull final PrintStream to,
          @Nonnull final PackageDetail detail, final boolean abbreviatedTraceElement) {
    StringBuilder sb = new StringBuilder(1024);
    try {
      writeTo(t, sb, detail, abbreviatedTraceElement);
      to.append(sb);
    } catch (IOException ex) {
      throw new UncheckedIOException(ex);
    }
  }

  public static void writeTo(final Throwable t, final Appendable to, final PackageDetail detail) throws IOException {
    writeTo(t, to, detail, DEFAULT_TRACE_ELEMENT_ABBREVIATION);
  }

  public static void writeTo(final Throwable t, final Appendable to, final PackageDetail detail,
          final String prefix) throws IOException {
    writeTo(t, to, detail, DEFAULT_TRACE_ELEMENT_ABBREVIATION, prefix);
  }

  public static void writeTo(final Throwable t, final Appendable to, final PackageDetail detail,
          final boolean abbreviatedTraceElement) throws IOException {
    writeTo(t, to, detail, abbreviatedTraceElement, "");
  }

  public static void writeTo(final Throwable t, final Appendable to, final PackageDetail detail,
          final boolean abbreviatedTraceElement, final String prefix) throws IOException {
    if (t instanceof RemoteException) {
      AThrowables.writeTo((RemoteException) t, to, detail, abbreviatedTraceElement, prefix);
      return;
    }
    to.append(prefix);
    writeMessageString(to, t);
    to.append('\n');
    writeThrowableDetail(t, to, detail, abbreviatedTraceElement, prefix);
  }

  public static void writeThrowableDetail(final Throwable t, final Appendable to, final PackageDetail detail,
          final boolean abbreviatedTraceElement, final String prefix) throws IOException {
    StackTraceElement[] trace = t.getStackTrace();
    writeTo(trace, to, detail, abbreviatedTraceElement, prefix);
    Throwable[] suppressed = getSuppressed(t);
    Throwable ourCause = t.getCause();
    if (ourCause == null && suppressed.length == 0) {
      return;
    }
    Set dejaVu = new IdentityHashSet();
    dejaVu.add(t);
    // Print suppressed exceptions, if any
    for (Throwable se : suppressed) {
      printEnclosedStackTrace(se, to, trace, SUPPRESSED_CAPTION, prefix + "\t",
              dejaVu, detail, abbreviatedTraceElement);
    }
    // Print cause, if any
    if (ourCause != null) {
      printEnclosedStackTrace(ourCause, to, trace, CAUSE_CAPTION, prefix, dejaVu, detail, abbreviatedTraceElement);
    }
  }

  public static void writeMessageString(final Appendable to, final Throwable t) throws IOException {
    to.append(t.getClass().getName());
    String message = t.getMessage();
    if (message != null) {
      to.append(':').append(message);
    }
  }

  public static void writeTo(final StackTraceElement[] trace, final Appendable to, final PackageDetail detail,
          final boolean abbreviatedTraceElement)
          throws IOException {
    writeTo(trace, to, detail, abbreviatedTraceElement, "");
  }

  public static void writeTo(final StackTraceElement[] trace, final Appendable to, final PackageDetail detail,
          final boolean abbreviatedTraceElement, final String prefix)
          throws IOException {
    StackTraceElement prevElem = null;
    for (StackTraceElement traceElement : trace) {
      to.append(prefix);
      to.append("\tat ");
      writeTo(traceElement, prevElem, to, detail, abbreviatedTraceElement);
      to.append('\n');
      prevElem = traceElement;
    }
  }

  public static int commonFrames(final StackTraceElement[] trace, final StackTraceElement[] enclosingTrace) {
    int from = trace.length - 1;
    int m = from;
    int n = enclosingTrace.length - 1;
    while (m >= 0 && n >= 0 && trace[m].equals(enclosingTrace[n])) {
      m--;
      n--;
    }
    return from - m;
  }

  private static void printEnclosedStackTrace(final Throwable t, final Appendable s,
          final StackTraceElement[] enclosingTrace,
          final String caption,
          final String prefix,
          final Set dejaVu,
          final PackageDetail detail,
          final boolean abbreviatedTraceElement) throws IOException {
    if (dejaVu.contains(t)) {
      s.append("\t[CIRCULAR REFERENCE:");
      writeMessageString(s, t);
      s.append("]\n");
    } else {
      dejaVu.add(t);
      // Compute number of frames in common between this and enclosing trace
      StackTraceElement[] trace = t.getStackTrace();
      int framesInCommon = commonFrames(trace, enclosingTrace);
      int m = trace.length - framesInCommon;
      // Print our stack trace
      s.append(prefix).append(caption);
      writeMessageString(s, t);
      s.append('\n');
      StackTraceElement prev = null;
      for (int i = 0; i < m; i++) {
        s.append(prefix).append("\tat ");
        StackTraceElement ste = trace[i];
        writeTo(ste, prev, s, detail, abbreviatedTraceElement);
        s.append('\n');
        prev = ste;
      }
      if (framesInCommon != 0) {
        s.append(prefix).append("\t... ").append(Integer.toString(framesInCommon)).append(" more");
        s.append('\n');
      }

      // Print suppressed exceptions, if any
      for (Throwable se : getSuppressed(t)) {
        printEnclosedStackTrace(se, s, trace, SUPPRESSED_CAPTION, prefix + '\t',
                dejaVu, detail, abbreviatedTraceElement);
      }

      // Print cause, if any
      Throwable ourCause = t.getCause();
      if (ourCause != null) {
        printEnclosedStackTrace(ourCause, s, trace, CAUSE_CAPTION, prefix,
                dejaVu, detail, abbreviatedTraceElement);
      }
    }
  }

  /**
   * Is this Throwable a JVM non-recoverable exception. (Oom, VMError, etc...)
   * @param t
   * @return
   */
  public static boolean isNonRecoverable(@Nonnull final Throwable t) {
    return nonRecoverableClassificationPredicate.test(t);
  }

  /**
   * Does this Throwable contain a JVM non-recoverable exception. (Oom, VMError, etc...)
   * @param t
   * @return
   */
  public static boolean containsNonRecoverable(@Nonnull final Throwable t) {
    return contains(t, nonRecoverableClassificationPredicate);
  }

  /**
   * checks in the throwable + children (both causal and suppressed) contain a throwable that
   * respects the Predicate.
   * @param t the throwable
   * @param predicate the predicate
   * @return true if a Throwable matching the predicate is found.
   */
  public static boolean contains(@Nonnull final Throwable t, final Predicate predicate) {
    return first(t, predicate) != null;
  }


  /**
   * checks in the throwable + children (both causal and suppressed) contain a throwable that
   * respects the Predicate.
   * @param t the throwable
   * @param predicate the predicate
   * @return true if a Throwable matching the predicate is found.
   */
  public static boolean contains(@Nonnull final Throwable t, @Nonnull final Throwable toLookFor) {
    return first(t, (x) -> x == toLookFor) != null;
  }

  /**
   * return first Exception in the causal chain Assignable to clasz.
   * @param 
   * @param t
   * @param clasz
   * @return
   */
  @Nullable
  @CheckReturnValue
  public static  T first(@Nonnull final Throwable t, final Class clasz) {
    return (T) first(t, (Throwable th) -> clasz.isAssignableFrom(th.getClass()));
  }

  /**
   * Returns the first Throwable that matches the predicate in the causal and suppressed chain,
   * the suppressed chain includes the supression mechanism included in SQLException.
   * @param t the Throwable
   * @param predicate the Predicate
   * @return the Throwable the first matches the predicate or null is none matches.
   */
  @Nullable
  @CheckReturnValue
  public static Throwable first(@Nonnull final Throwable t, final Predicate predicate) {
    if (predicate.test(t)) { //shortcut
      return t;
    }
    ArrayDeque toScan =  new ArrayDeque<>();
    Throwable cause = t.getCause();
    if (cause != null) {
      toScan.addFirst(cause);
    }
    for (Throwable supp : getSuppressed(t)) {
      toScan.addLast(supp);
    }
    Throwable th;
    THashSet seen = new IdentityHashSet<>();
    while ((th = toScan.pollFirst()) != null) {
      if (seen.contains(th)) {
        continue;
      }
      if (predicate.test(th)) {
        return th;
      } else {
        cause = th.getCause();
        if (cause != null) {
          toScan.addFirst(cause);
        }
        for (Throwable supp : getSuppressed(th)) {
          toScan.addLast(supp);
        }
      }
      seen.add(th);
    }
    return null;
  }


  /**
   * Returns first Throwable in the causality chain that is matching the provided predicate.
   * @param throwable the Throwable to go through.
   * @param predicate the predicate to apply
   * @return the first Throwable from the chain that the predicate matches.
   */

  @Nullable
  @CheckReturnValue
  public static Throwable firstCause(@Nonnull final Throwable throwable, final Predicate predicate) {
    Throwable t = throwable;
    do {
      if (predicate.test(t)) {
        return t;
      }
      t = t.getCause();
    } while (t != null);
    return null;
  }


  public static Predicate getNonRecoverablePredicate() {
    return nonRecoverableClassificationPredicate;
  }

  /**
   * Overwrite the default non-recoverable predicate.
   * @param predicate
   */
  public static void setNonRecoverablePredicate(final Predicate predicate) {
    Throwables.nonRecoverableClassificationPredicate = predicate;
  }

  public static void writeAbreviatedClassName(final String className, final Appendable writeTo) throws IOException {
    int ldIdx = className.lastIndexOf('.');
    if (ldIdx < 0) {
      writeTo.append(className);
      return;
    }
    boolean isPreviousDot = true;
    for (int i = 0; i < ldIdx; i++) {
      char c = className.charAt(i);
      boolean isCurrentCharDot = c == '.';
      if (isPreviousDot || isCurrentCharDot) {
        writeTo.append(c);
      }
      isPreviousDot = isCurrentCharDot;
    }
    writeTo.append(className, ldIdx, className.length());
  }

  @SuppressFBWarnings("ITC_INHERITANCE_TYPE_CHECKING")
  public static void throwException(final Exception ex) throws IOException, InterruptedException,
          ExecutionException, TimeoutException {
    if (ex instanceof IOException) {
      throw (IOException) ex;
    } else if (ex instanceof InterruptedException) {
      throw (InterruptedException) ex;
    } else if (ex instanceof ExecutionException) {
      throw (ExecutionException) ex;
    } else if (ex instanceof TimeoutException) {
      throw (TimeoutException) ex;
    } else {
      throw new ExecutionException(ex);
    }
  }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy