/*
* Copyright 2015-present Facebook, Inc.
*
* 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 com.facebook.buck.util;
import com.google.common.base.Preconditions;
import java.io.InterruptedIOException;
import java.net.SocketTimeoutException;
import java.nio.channels.ClosedByInterruptException;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
public class MoreThrowables {
private MoreThrowables() {}
private static InterruptedException asInterruptedException(Throwable throwable) {
InterruptedException e = new InterruptedException();
e.initCause(throwable);
return e;
}
/** Propagates an {@link InterruptedException} masquerading as another {@code Throwable}. */
public static void propagateIfInterrupt(Throwable thrown) throws InterruptedException {
// If it's already an `InterruptedException`, just rethrow it.
if (thrown instanceof InterruptedException) {
throw (InterruptedException) thrown;
}
// Thrown when a thread is interrupted while blocked on I/O. So propagate this as
// an `InterruptedException`.
if (thrown instanceof ClosedByInterruptException) {
throw asInterruptedException(thrown);
}
// `InterruptedIOException` can also be thrown when a thread is interrupted while blocked
// by I/O, so propagate this -- unless it's a `SocketTimeoutException` which is thrown when
// when a the timeout set on a socket is triggered.
if (thrown instanceof InterruptedIOException && !(thrown instanceof SocketTimeoutException)) {
throw asInterruptedException(thrown);
}
}
/** If throwable has a non-empty cause, returns throwable at the bottom of the stack. */
public static Throwable getInitialCause(Throwable throwable) {
if (throwable.getCause() != null) {
Set<Throwable> seen = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
seen.add(throwable);
return getInitialCause(throwable, seen);
}
return throwable;
}
private static Throwable getInitialCause(Throwable throwable, Set<Throwable> seen) {
Throwable cause = throwable.getCause();
if ((cause == null) || seen.contains(cause)) {
return throwable;
}
seen.add(throwable);
return getInitialCause(cause, seen);
}
/**
* Returns string representing class, method, filename and line number that throwable was thrown
* from
*/
public static String getThrowableOrigin(Throwable throwable) {
StackTraceElement[] stack = throwable.getStackTrace();
Preconditions.checkState(stack.length > 0);
StackTraceElement element = stack[0];
return element.toString();
}
}