code: public static <T extends Throwable> Throwable peel(final Throwable t, Class<T> allowedType,
                                                         String message, RuntimeExceptionFactory runtimeExceptionFactory) {
          if (t instanceof RuntimeException) {
              return t;
          }
  
          if (t instanceof ExecutionException || t instanceof InvocationTargetException) {
              final Throwable cause = t.getCause();
              if (cause != null) {
                  return peel(cause, allowedType, message, runtimeExceptionFactory);
              } else {
                  return runtimeExceptionFactory.create(t, message);
              }
          }
  
          if (allowedType != null && allowedType.isAssignableFrom(t.getClass())) {
              return t;
          }
  
          return runtimeExceptionFactory.create(t, message);
      }
docs: Processes {@code Throwable t} so that the returned {@code Throwable}'s type matches {@code allowedType} or
  {@code RuntimeException}. Processing may include unwrapping {@code t}'s cause hierarchy, wrapping it in a
  {@code RuntimeException} created by using runtimeExceptionFactory or just returning the same instance {@code t}
  if it is already an instance of {@code RuntimeException}.
  
  @param t                       {@code Throwable} to be peeled
  @param allowedType             the type expected to be returned; when {@code null}, this method returns instances
  of {@code RuntimeException}
  @param message                 if not {@code null}, used as the message in {@code RuntimeException} that
  may wrap the peeled {@code Throwable}
  @param runtimeExceptionFactory wraps the peeled code using this runtimeExceptionFactory
  @param <T>                     expected type of {@code Throwable}
  @return the peeled {@code Throwable}