org.conqat.lib.commons.concurrent.FutureWithException Maven / Gradle / Ivy
Show all versions of teamscale-lib-commons Show documentation
package org.conqat.lib.commons.concurrent;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.conqat.lib.commons.function.FunctionWithException;
/**
* Extension of the general {@link Future}, that is ready to handle Exceptions of type {@code E}.
*/
public class FutureWithException implements Future {
private final Future delegate;
private final Class expectedException;
public FutureWithException(Future delegate, Class expectedException) {
this.delegate = delegate;
this.expectedException = expectedException;
}
/**
* Creates a resolved {@link FutureWithException} with the provided {@code resolved} value
*/
public static FutureWithException resolved(T resolved) {
@SuppressWarnings("unchecked")
FutureWithException result = (FutureWithException) new FutureWithException<>(
CompletableFuture.completedFuture(resolved), Exception.class);
return result;
}
/**
* Creates a failed {@link FutureWithException} with the provided {@code exception} value
*/
public static FutureWithException failed(E exception) {
CompletableFuture future = new CompletableFuture<>();
future.completeExceptionally(exception);
@SuppressWarnings("unchecked")
Class exceptionClass = (Class) exception.getClass();
return new FutureWithException<>(future, exceptionClass);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return delegate.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return delegate.isCancelled();
}
@Override
public boolean isDone() {
return delegate.isDone();
}
@Override
public T get() throws InterruptedException, ExecutionException {
return delegate.get();
}
@Override
public T get(long timeout, @NonNull TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return delegate.get(timeout, unit);
}
/**
* Waits if necessary for the computation to complete, and then retrieves its result.
*
* If the underlying computation threw an Exception of type {@code E}, it is rethrown. Any other
* exception is wrapped in a {@link RuntimeException}.
*/
public T getWithException() throws InterruptedException, E {
try {
return get();
} catch (ExecutionException e) {
return handleExecutionException(e);
}
}
/**
* Waits if necessary for at most the given time for the computation to complete, and then retrieves
* its result, if available.
*
* If the underlying computation threw an Exception of type {@code E}, it is rethrown. Any other
* exception is wrapped in a {@link RuntimeException}.
*/
public T getWithException(long timeout, @NonNull TimeUnit unit) throws InterruptedException, E, TimeoutException {
try {
return get(timeout, unit);
} catch (ExecutionException e) {
return handleExecutionException(e);
}
}
private T handleExecutionException(ExecutionException e) throws E {
Throwable cause = e.getCause();
if (expectedException.isInstance(cause)) {
throw expectedException.cast(cause);
} else {
throw new RuntimeException("Unexpected Exception thrown", cause);
}
}
/**
* Returns a new {@link FutureWithException}, that applies the {@code transformer} on the result
* value.
*/
public FutureWithException then(FunctionWithException transformer) {
return new FutureWithException<>(new Future() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return FutureWithException.this.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return FutureWithException.this.isCancelled();
}
@Override
public boolean isDone() {
return FutureWithException.this.isDone();
}
@Override
public S get() throws InterruptedException, ExecutionException {
try {
return transformer.apply(FutureWithException.this.get());
} catch (InterruptedException | ExecutionException | RuntimeException e) {
throw e;
} catch (Exception e) {
throw new ExecutionException(e);
}
}
@Override
public S get(long timeout, @NonNull TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
try {
return transformer.apply(FutureWithException.this.get(timeout, unit));
} catch (InterruptedException | ExecutionException | TimeoutException | RuntimeException e) {
throw e;
} catch (Exception e) {
throw new ExecutionException(e);
}
}
}, expectedException);
}
}