UPDATE: The latest version of Intellij IDEA implements exactly what I'm looking for. The question is how to implement this outside of the IDE (so I can to dump async stack traces to log files), ideally without the use of an instrumenting agent.
Ever since I converted my application from a synchronous to asynchronous model I am having problems debugging failures.
When I use synchronous APIs, I always find my classes in exception stacktraces so I know where to begin looking if something goes wrong. With asynchronous APIs, I am getting stacktraces that do not reference my classes nor indicate what request triggered the failure.
I'll give you a concrete example, but I'm interested in a general solution to this kind of problem.
Concrete example
I make an HTTP request using Jersey:
new Client().target("http://test.com/").request().rx().get(JsonNode.class);
where rx() indicates that the request should take place asynchronously, returning a CompletionStage<JsonNode> instead of a JsonNode directly. If this call fails, I get this stacktrace:
javax.ws.rs.ForbiddenException: HTTP 403 Authentication Failed
at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1083)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:883)
at org.glassfish.jersey.client.JerseyInvocation.lambda$invoke$1(JerseyInvocation.java:767)
at org.glassfish.jersey.internal.Errors.process(Errors.java:316)
at org.glassfish.jersey.internal.Errors.process(Errors.java:298)
at org.glassfish.jersey.internal.Errors.process(Errors.java:229)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:414)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:765)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:456)
at org.glassfish.jersey.client.JerseyCompletionStageRxInvoker.lambda$method$1(JerseyCompletionStageRxInvoker.java:70)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
Notice:
The stacktrace does not reference user code.
The exception message does not contain contextual information about the HTTP request that triggered the error (HTTP method, URI, etc).
As a result, I have no way of tracking the exception back to its source.
Why this is happening
If you dig under the hood, you will discover that Jersey is invoking:
CompletableFuture.supplyAsync(() -> getSyncInvoker().method(name, entity, responseType))
for rx() invocations. Because the supplier is constructed by Jersey, there is no reference back to user code.
What I've tried
I tried filing a bug report against Jetty for an unrelated async example, and was subsequently turned down on security grounds.
Instead, I've been adding contextual information as follows:
makeHttpRequest().exceptionally(e ->
{
throw new RuntimeException(e);
});
Meaning, I am manually adding exceptionally() after every single HTTP request in my code. Any exceptions thrown by Jersey are wrapped in a secondary exception that references my code. The resulting stacktrace looks like this:
java.lang.RuntimeException: javax.ws.rs.ForbiddenException: HTTP 403 Authentication Failed
at my.user.code.Testcase.lambda$null$1(Testcase.java:25)
at java.util.concurrent.CompletableFuture.uniExceptionally(CompletableFuture.java:870)
... 6 common frames omitted
Caused by: javax.ws.rs.ForbiddenException: HTTP 403 Authentication Failed
at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1083)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:883)
at org.glassfish.jersey.client.JerseyInvocation.lambda$invoke$1(JerseyInvocation.java:767)
at org.glassfish.jersey.internal.Errors.process(Errors.java:316)
at org.glassfish.jersey.internal.Errors.process(Errors.java:298)
at org.glassfish.jersey.internal.Errors.process(Errors.java:229)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:414)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:765)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:456)
at org.glassfish.jersey.client.JerseyCompletionStageRxInvoker.lambda$method$1(JerseyCompletionStageRxInvoker.java:70)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
... 3 common frames omitted
I don't like this approach because it is error prone and decreases the readability of the code. If I mistakenly omit this for some HTTP request I will end up with a vague stacktrace and spend a lot time tracking it down.
Further, if I want to hide this trick behind a utility class then I have to instantiate an exception outside of a CompletionStage; otherwise, the utility class will show up in the stacktrace instead of the actual call site. Instantiating an exception outside of a CompletionStage is extremely expensive because this code runs even if no exception is ever thrown by the async call.
My question
Is there a robust, easy-to-maintain approach to add contextual information to asynchronous calls?
Alternatively, is there an efficient approach to track stacktraces back to their source without this contextual information?
Seeing as this question has not received any answers in almost a month, I'm going to post the best solution I've found to date:
DebugCompletableFuture.java:
/**
* A {#link CompletableFuture} that eases debugging.
*
* #param <T> the type of value returned by the future
*/
public final class DebugCompletableFuture<T> extends CompletableFuture<T>
{
private static RunMode RUN_MODE = RunMode.DEBUG;
private static final Set<String> CLASS_PREFIXES_TO_REMOVE = ImmutableSet.of(DebugCompletableFuture.class.getName(),
CompletableFuture.class.getName(), ThreadPoolExecutor.class.getName());
private static final Set<Class<? extends Throwable>> EXCEPTIONS_TO_UNWRAP = ImmutableSet.of(AsynchronousException.class,
CompletionException.class, ExecutionException.class);
private final CompletableFuture<T> delegate;
private final AsynchronousException asyncStacktrace;
/**
* #param delegate the stage to delegate to
* #throws NullPointerException if any of the arguments are null
*/
private DebugCompletableFuture(CompletableFuture<T> delegate)
{
requireThat("delegate", delegate).isNotNull();
this.delegate = delegate;
this.asyncStacktrace = new AsynchronousException();
delegate.whenComplete((value, exception) ->
{
if (exception == null)
{
super.complete(value);
return;
}
exception = Exceptions.unwrap(exception, EXCEPTIONS_TO_UNWRAP);
asyncStacktrace.initCause(exception);
filterStacktrace(asyncStacktrace, element ->
{
String className = element.getClassName();
for (String prefix : CLASS_PREFIXES_TO_REMOVE)
if (className.startsWith(prefix))
return true;
return false;
});
Set<String> newMethods = getMethodsInStacktrace(asyncStacktrace);
if (!newMethods.isEmpty())
{
Set<String> oldMethods = getMethodsInStacktrace(exception);
newMethods.removeAll(oldMethods);
if (!newMethods.isEmpty())
{
// The async stacktrace introduces something new
super.completeExceptionally(asyncStacktrace);
return;
}
}
super.completeExceptionally(exception);
});
}
/**
* #param exception an exception
* #return the methods referenced by the stacktrace
* #throws NullPointerException if {#code exception} is null
*/
private Set<String> getMethodsInStacktrace(Throwable exception)
{
requireThat("exception", exception).isNotNull();
Set<String> result = new HashSet<>();
for (StackTraceElement element : exception.getStackTrace())
result.add(element.getClassName() + "." + element.getMethodName());
for (Throwable suppressed : exception.getSuppressed())
result.addAll(getMethodsInStacktrace(suppressed));
return result;
}
/**
* #param <T2> the type returned by the delegate
* #param delegate the stage to delegate to
* #return if {#code RUN_MODE == DEBUG} returns an instance that wraps {#code delegate}; otherwise, returns {#code delegate}
* unchanged
* #throws NullPointerException if any of the arguments are null
*/
public static <T2> CompletableFuture<T2> wrap(CompletableFuture<T2> delegate)
{
if (RUN_MODE != RunMode.DEBUG)
return delegate;
return new DebugCompletableFuture<>(delegate);
}
/**
* Removes stack trace elements that match a filter. The exception and its descendants are processed recursively.
* <p>
* This method can be used to remove lines that hold little value for the end user (such as the implementation of utility functions).
*
* #param exception the exception to process
* #param elementFilter returns true if the current stack trace element should be removed
*/
private void filterStacktrace(Throwable exception, Predicate<StackTraceElement> elementFilter)
{
Throwable cause = exception.getCause();
if (cause != null)
filterStacktrace(cause, elementFilter);
for (Throwable suppressed : exception.getSuppressed())
filterStacktrace(suppressed, elementFilter);
StackTraceElement[] elements = exception.getStackTrace();
List<StackTraceElement> keep = new ArrayList<>(elements.length);
for (StackTraceElement element : elements)
{
if (!elementFilter.test(element))
keep.add(element);
}
exception.setStackTrace(keep.toArray(new StackTraceElement[0]));
}
#Override
public <U> CompletableFuture<U> thenApply(Function<? super T, ? extends U> fn)
{
return wrap(super.thenApply(fn));
}
#Override
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T, ? extends U> fn)
{
return wrap(super.thenApplyAsync(fn));
}
#Override
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T, ? extends U> fn, Executor executor)
{
return wrap(super.thenApplyAsync(fn, executor));
}
#Override
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
{
return wrap(super.thenAccept(action));
}
#Override
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
{
return wrap(super.thenAcceptAsync(action));
}
#Override
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
{
return wrap(super.thenAcceptAsync(action, executor));
}
#Override
public CompletableFuture<Void> thenRun(Runnable action)
{
return wrap(super.thenRun(action));
}
#Override
public CompletableFuture<Void> thenRunAsync(Runnable action)
{
return wrap(super.thenRunAsync(action));
}
#Override
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)
{
return wrap(super.thenRunAsync(action, executor));
}
#Override
public <U, V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,
BiFunction<? super T, ? super U, ? extends V> fn)
{
return wrap(super.thenCombine(other, fn));
}
#Override
public <U, V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T, ? super U, ? extends V> fn)
{
return wrap(super.thenCombineAsync(other, fn));
}
#Override
public <U, V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T, ? super U, ? extends V> fn,
Executor executor)
{
return wrap(super.thenCombineAsync(other, fn, executor));
}
#Override
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action)
{
return wrap(super.thenAcceptBoth(other, action));
}
#Override
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action)
{
return wrap(super.thenAcceptBothAsync(other, action));
}
#Override
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action,
Executor executor)
{
return wrap(super.thenAcceptBothAsync(other, action, executor));
}
#Override
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
{
return wrap(super.runAfterBoth(other, action));
}
#Override
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action)
{
return wrap(super.runAfterBothAsync(other, action));
}
#Override
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor)
{
return wrap(super.runAfterBothAsync(other, action, executor));
}
#Override
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
{
return wrap(super.applyToEither(other, fn));
}
#Override
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)
{
return wrap(super.applyToEitherAsync(other, fn));
}
#Override
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,
Executor executor)
{
return wrap(super.applyToEitherAsync(other, fn, executor));
}
#Override
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
{
return wrap(super.acceptEither(other, action));
}
#Override
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
{
return wrap(super.acceptEitherAsync(other, action));
}
#Override
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,
Executor executor)
{
return wrap(super.acceptEitherAsync(other, action, executor));
}
#Override
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
{
return wrap(super.runAfterEither(other, action));
}
#Override
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action)
{
return wrap(super.runAfterEitherAsync(other, action));
}
#Override
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor)
{
return wrap(super.runAfterEitherAsync(other, action, executor));
}
#Override
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
{
return wrap(super.thenCompose(fn));
}
#Override
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn)
{
return wrap(super.thenComposeAsync(fn));
}
#Override
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,
Executor executor)
{
return wrap(super.thenComposeAsync(fn, executor));
}
#Override
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
{
return wrap(super.exceptionally(fn));
}
#Override
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
{
return wrap(super.whenComplete(action));
}
#Override
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
{
return wrap(super.whenCompleteAsync(action));
}
#Override
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action,
Executor executor)
{
return wrap(super.whenCompleteAsync(action, executor));
}
#Override
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
{
return wrap(super.handle(fn));
}
#Override
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
{
return wrap(super.handleAsync(fn));
}
#Override
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,
Executor executor)
{
return wrap(super.handleAsync(fn, executor));
}
#Override
public boolean complete(T value)
{
return delegate.complete(value);
}
#Override
public boolean completeExceptionally(Throwable ex)
{
return delegate.completeExceptionally(ex);
}
}
RunMode.java:
/**
* Operational modes.
*/
public enum RunMode
{
/**
* Optimized for debugging problems (extra runtime checks, logging of the program state).
*/
DEBUG,
/**
* Optimized for maximum performance.
*/
RELEASE
}
AsynchronousException.java
/**
* Thrown when an asynchronous operation fails. The stacktrace indicates who triggered the operation.
*/
public final class AsynchronousException extends RuntimeException
{
private static final long serialVersionUID = 0L;
public AsynchronousException()
{
}
}
Usage:
DebugCompletableFuture.wrap(CompletableFuture.supplyAsync(this::expensiveOperation));
Upside: you'll get relatively clean asynchronous stack traces.
Downside: Constructing a new AsynchronousException every time a future is created is extremely expensive. Specifically, if you're generating a lot of futures, this generates a lot of garbage on the heap and the GC overhead becomes noticeable.
I am still hopeful that someone will come up with a better-performing approach.
This is probably due to the JVM update when it finds that the stack is exhausted of emitting the same log so it starts to omit it.
And the solution is using -XX:-OmitStackTraceInFastThrow flag to prevent JVM from optimizing built-in exceptions stack trace.
Related
Moving my post scriptum to the top instead, so people don't waste their time if I've posted this to the wrong site: I couldn't decide on whether this belonged here or on https://codereview.stackexchange.com. If the latter is deemed more appropriate I'm happy to see it moved.
I have a parameterized interface with a method that takes a Consumer and, if a certain condition holds true, invokes it with this as a parameter. Otherwise it returns this (to make it chainable on the call site, as you may already have gathered):
public interface SomeInterface<T> {
boolean someCondition();
default SomeInterface<T> someMethod(
Consumer<SomeInterface<? extends T>> action) {
if (someCondition()) {
action.accept(this);
}
return this;
}
Now I would like to extend this functionality by adding another parameter so I can return additional information to the caller:
public interface SomeInterfaceExtended<T, U> extends SomeInterface<T> {
U getAdditionalInformation();
#Override
SomeInterfaceExtended<T, U> someMethod(
Consumer<SomeInterfaceExtended<? extends T, U>> action) {
if (someCondition()) {
action.accept(this);
}
return this;
}
}
This of course doesn't work since the two methods have the same erasure. I ended up not extending the interface, but rather defining all the same methods and having the "extended" interface's implementors delegate to an instance of the "basic" interface:
public interface SomeInterface<T> {
boolean someCondition();
default SomeInterface<T> someMethod(
Consumer<SomeInterface<? extends T>> action) {
if (someCondition()) {
action.accept(this);
}
return this;
}
...
public interface SomeInterfaceExtended<T, U> {
U getAdditionalInformation();
boolean someCondition();
default SomeInterfaceExtended<T, U> someMethod(
Consumer<SomeInterfaceExtended<? extends T, U>> action) {
if (someCondition()) {
action.accept(this);
}
return this;
}
}
...
public class SomeClass<T> implements SomeInterface<T> {
#Override
public boolean someCondition() {
// whatever
}
}
...
public class SomeClassExtended<T, U> implements SomeInterfaceExtended<T, U> {
private final SomeClass<T> delegate;
private final U additionalInformation;
public SomeClassExtended(SomeClass<T> delegate, U additionalInformation) {
this.delegate = delegate;
this.additionalInformation = additionalInformation;
}
#Override
public boolean someCondition() {
return delegate.someCondition();
}
}
This all feels a bit clumsy/bloaty. Is there a way I can more efficiently (loc-wise) share the implementation?
I'm trying to create a functional interface that can throw an custom exception, what I've come up with is.
public class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
#FunctionalInterface
public interface ThrowingFunction<T, R> {
R apply(T t) throws MyException;
}
This works great for using the apply function but the problem is I'd also like to use the andThen capabilities of Java's functions. When I try to do something like.
ThrowingFunction<Integer, Integer> times2WithException = (num) -> {
if(num == null) {
throw new MyException("Cannot multiply null by 2");
}
return num * 2;
};
times2WithException.andThen(times2WithException).apply(4);
I get the error
Cannot find symbol: method andThen(ThrowingFunction<Integer, Integer>)
Is there something I should use instead of FunctionalInterface? Or is there another function I need to implement to get it to work with andThen?
Thanks!
Functional interfaces are only allowed to specify one unimplemented function. But you can specify default functions that already have an implementation like this:
#FunctionalInterface
public interface ThrowingFunction<T, R> {
R apply(T t) throws MyException;
default <U> ThrowingFunction<T, U> andThen(ThrowingFunction<R, U> follow) {
Objects.requireNonNull(follow); // Fail fast
return t -> follow.apply(this.apply(t));
}
}
Where are you expecting the andThen method to come from? You haven't defined it anywhere!
#FunctionalInterface
interface ThrowingFunction<T, R> {
R apply(T t) throws MyException;
default <V> ThrowingFunction<T, V> andThen(ThrowingFunction<R, V> after) {
return (T t) -> after.apply(apply(t));
}
}
Here, you can take advantage of default methods in interfaces to create an andThen function.
Problem
I am writing a Result type in Java, and I have found a need for it to have a method that performs an operation which may fail, and then encapulates the value or exception in a new Result object.
I had hoped this would work:
#FunctionalInterface
public interface ThrowingSupplier<R, E extends Throwable>
{
R get() throws E;
}
public class Result<E extends Throwable, V>
{
...
public static <E extends Throwable, V> Result<E, V> of(ThrowingSupplier<V, E> v)
{
try
{
return value(v.get());
}
catch(E e)
{
return error(e);
}
}
...
}
But Java cannot catch an exception defined by a type parameter.
I have also tried using instanceof, but that also cannot be used for generics. Is there any way I can implement this method?
Definitions
This is my result type before the addition of the of method. It's intended to be similar to both Haskell's Either and rust's Result, while also having a meaningful bind operation:
public class Result<E extends Throwable, V>
{
private Either<E, V> value;
private Result(Either<E, V> value)
{
this.value = value;
}
public <T> T match(Function<? super E, ? extends T> ef, Function<? super V, ? extends T> vf)
{
return value.match(ef, vf);
}
public void match(Consumer<? super E> ef, Consumer<? super V> vf)
{
value.match(ef, vf);
}
/**
* Mirror of haskell's Monadic (>>=)
*/
public <T> Result<E, T> bind(Function<? super V, Result<? extends E, ? extends T>> f)
{
return match(
(E e) -> cast(error(e)),
(V v) -> cast(f.apply(v))
);
}
/**
* Mirror of Haskell's Monadic (>>) or Applicative (*>)
*/
public <T> Result<E, T> then(Supplier<Result<? extends E, ? extends T>> f)
{
return bind((__) -> f.get());
}
/**
* Mirror of haskell's Applicative (<*)
*/
public Result<E, V> peek(Function<? super V, Result<? extends E, ?>> f)
{
return bind(v -> f.apply(v).then(() -> value(v)));
}
public <T> Result<E, T> map(Function<? super V, ? extends T> f)
{
return match(
(E e) -> error(e),
(V v) -> value(f.apply(v))
);
}
public static <E extends Throwable, V> Result<E, V> error(E e)
{
return new Result<>(Either.left(e));
}
public static <E extends Throwable, V> Result<E, V> value(V v)
{
return new Result<>(Either.right(v));
}
/**
* If the result is a value, return it.
* If it is an exception, throw it.
*
* #return the contained value
* #throws E the contained exception
*/
public V get() throws E
{
boolean has = match(
e -> false,
v -> true
);
if (has)
{
return value.fromRight(null);
}
else
{
throw value.fromLeft(null);
}
}
/**
* Upcast the Result's type parameters
*/
private static <E extends Throwable, V> Result<E, V> cast(Result<? extends E, ? extends V> r)
{
return r.match(
(E e) -> error(e),
(V v) -> value(v)
);
}
}
And the Either type, designed to closely mirror Haskell's Either:
/**
* A container for a disjunction of two possible types
* By convention, the Left constructor is used to hold an error value and the Right constructor is used to hold a correct value
* #param <L> The left alternative type
* #param <R> The right alternative type
*/
public abstract class Either<L, R>
{
public abstract <T> T match(Function<? super L, ? extends T> lf, Function<? super R, ? extends T> rf);
public abstract void match(Consumer<? super L> lf, Consumer<? super R> rf);
public <A, B> Either<A, B> bimap(Function<? super L, ? extends A> lf, Function<? super R, ? extends B> rf)
{
return match(
(L l) -> left(lf.apply(l)),
(R r) -> right(rf.apply(r))
);
}
public L fromLeft(L left)
{
return match(
(L l) -> l,
(R r) -> left
);
}
public R fromRight(R right)
{
return match(
(L l) -> right,
(R r) -> r
);
}
public static <L, R> Either<L, R> left(L value)
{
return new Left<>(value);
}
public static <L, R> Either<L, R> right(R value)
{
return new Right<>(value);
}
private static <L, R> Either<L, R> cast(Either<? extends L, ? extends R> either)
{
return either.match(
(L l) -> left(l),
(R r) -> right(r)
);
}
static class Left<L, R> extends Either<L, R>
{
final L value;
Left(L value)
{
this.value = value;
}
#Override
public <T> T match(Function<? super L, ? extends T> lf, Function<? super R, ? extends T> rf)
{
return lf.apply(value);
}
#Override
public void match(Consumer<? super L> lf, Consumer<? super R> rf)
{
lf.accept(value);
}
}
static class Right<L, R> extends Either<L, R>
{
final R value;
Right(R value)
{
this.value = value;
}
#Override
public <T> T match(Function<? super L, ? extends T> lf, Function<? super R, ? extends T> rf)
{
return rf.apply(value);
}
#Override
public void match(Consumer<? super L> lf, Consumer<? super R> rf)
{
rf.accept(value);
}
}
}
Example Usage
The main use of this is to convert exception-throwing operations into monadic ones. This allows for (checked) exception-throwing methods to be used in streams and other functional contexts, and also allows for pattern matching and binding on the return type.
private static void writeFiles(List<String> filenames, String content)
{
filenames.stream()
.map(
(String s) -> Result.of(
() -> new FileWriter(s) //Open file for writing
).peek(
(FileWriter f) -> Result.of(
() -> f.write(content) //Write file contents
)
).peek(
(FileWriter f) -> Result.of(
() -> f.close()) //Close file
)
).forEach(
r -> r.match(
(IOException e) -> System.out.println("exception writing to file: " + e), //Log exception
(FileWriter f) -> System.out.println("successfully written to file '" + f + "'") //Log success
)
);
}
Just use the optimistic assumption that the interface fulfills the contract, as ordinary Java code will always do (enforced by the compiler). If someone bypasses this exception-checking, it’s not your responsibility to fix that:
public static <E extends Exception, V> Result<E, V> of(ThrowingSupplier<V, E> v) {
try {
return value(v.get());
}
catch(RuntimeException|Error x) {
throw x; // unchecked throwables
}
catch(Exception ex) {
#SuppressWarnings("unchecked") E e = (E)ex;
return error(e);
}
}
Note that even the Java programming language agrees that it is okay to proceed with this assumption, e.g.
public static <E extends Exception, V> Result<E, V> of(ThrowingSupplier<V, E> v) throws E {
try {
return value(v.get());
}
catch(RuntimeException|Error x) {
throw x; // unchecked throwables
}
catch(Exception ex) {
throw ex; // can only be E
}
}
is valid Java code, as under normal circumstances, the get method can only throw E or unchecked throwables, so it is valid to rethrow ex here, when throws E has been declared. We only have to circumvent a deficiency of the Java language when we want to construct a Result parameterized with E.
You need access to the class of the exception and then use some generics in the catch block.
One simple way is to pass the Class<E> class to the Result.of method:
public static <E extends Throwable, V> Result<E, V> of(
ThrowingSupplier<V, E> v,
Class<E> errorType) {
try {
return value(v.get());
} catch(Throwable e) {
if (errorType.isInstance(e)) {
return error(errorType.cast(e));
}
throw new RuntimeException(e); // rethrow as runtime?
}
}
Usage:
Result.of(() -> new FileWriter(s), IOException.class)
Class.isInstance is the dynamic equivalent of the instanceof static operator, while Class.cast is the same as statically casting: (E) e, except that we don't get a warning from the compiler.
EDIT: You need to think what to do when the catched Throwable is not of the type of the exception you are expecting. I've wrapped it in a RuntimeException and have rethrown it. This allows to keep using a fluent style for your monad, but is not transparent any more, as now any exception is wrapped in an unchecked exception. Maybe you could add a 3rd argument to Result.of to handle this specific case...
Update: this seems not to work at all. I'm keeping it here for now because I've linked to is elsewhere, and because it uses a method provided in other accepted answers, which I would like to continue to investigate.
Using Federico's answer and the answer linked in the comment, I have deduced a solution with the same method signature as the original problem, and I have created a class which encapsulates this functionality for future use.
The Result implementation:
public class Result<E extends Exception, V>
{
...
public static <E extends Exception, V> Result<E, V> of(ThrowingSupplier<V, E> v)
{
try
{
return value(v.get());
}
catch(Exception e)
{
Class<E> errType = Reflector.getType();
if (errType.isInstance(e))
{
return error(errType.cast(e));
}
else
{
throw (RuntimeException) e;
}
}
}
...
}
And the Reflector:
import java.lang.reflect.ParameterizedType;
/**
* This class only exists to provide a generic superclass to {#link Reflector}
* #param <E> The type for the subclass to inspect
*/
abstract class Reflected<E>
{ }
/**
* This class provides the ability to obtain information about its generic type parameter.
* #param <E> The type to inspect
* #author
*/
#Deprecated
public class Reflector<E> extends Reflected<E>
{
/**
* Returns the class corresponding to the type {#code <E>}.
* #param <E> The type to inspect
* #return The class corresponding to the type {#code <E>}
*/
public static <E> Class<E> getType()
{
return new Reflector<E>().getParameterType();
}
private Reflector() {}
private Class<E> getParameterType()
{
final ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
return (Class<E>) type.getActualTypeArguments()[0];
}
}
I want to create a #FunctionalInterface in Java which accepts both Streams or Optional types as a parameter. I tried to do this, but since they don't share a common interface it seems impossible to achieve. I also tried using a common wrapper class which invokes the #FunctionalInterface but since I need the type parameters at runtime it seems this isn't possible.
Minimum example:
#FunctionalInterface
public interface AcceptingInterface<S, T> {
T accept(S s);
}
public class Test<S, T> {
private final AcceptingInterface<S, T> funcInterface;
private final Class<S> source;
private final Class<T> target;
public Test(AcceptingInterface<S, T> a, Class<S> s, Class<T> t) {
this.funcInterface = a;
this.source = s;
this.target = t;
}
public T invoke(S s) {
return s == null ? null : this.funcInterface.accept(s);
}
public Class<S> getSource() {
return source;
}
public Class<T> getTarget() {
return target;
}
}
Maybe my approach is wrong... I would love to receive feedback and/or a solution to this problem.
I assume you want to treat an Optional as a Stream of 0-1 elements, in which case you can add a default method that translates from Optional to Stream, thusly:
#FunctionalInterface
public interface AcceptingInterface<V, T> {
T accept(Stream<? extends V> s);
default T accept(Optional<? extends V> opt){
return accept(opt.map(Stream::of).orElseGet(Stream::empty));
}
}
Java 9 is supposed to add an Optional.stream() method.
Exceptions, especially checked ones, can severely interrupt the flow of program logic when the FP idiom is used in Java 8. Here is an arbitrary example:
String s1 = "oeu", s2 = "2";
Stream.of(s1, s2).forEach(s ->
System.out.println(Optional.of(s).map(Integer::parseInt).get()));
The above code breaks when there's an exception for an unparseable string. But say I just want to replace that with a default value, much like I can with Optional:
Stream.of(s1, s2).forEach(s ->
System.out.println(Optional.of(s)
.map(Integer::parseInt)
.orElse(-1)));
Of course, this still fails because Optional only handles nulls. I would like something as follows:
Stream.of(s1, s2).forEach(s ->
System.out.println(
Exceptional.of(s)
.map(Integer::parseInt)
.handle(NumberFormatException.class, swallow())
.orElse(-1)));
Note: this is a self-answered question.
Presented below is the full code of the Exceptional class. It has a quite large API which is a pure extension of the Optional API so it can be a drop-in replacement for it in any existing code—except that it isn't a subtype of the final Optional class. The class can be seen as being in the same relationship with the Try monad as Optional is with the Maybe monad: it draws inspiration from it, but is adapted to the Java idiom (such as actually throwing exceptions, even from non-terminal operations).
These are some key guidelines followed by the class:
as opposed to the monadic approach, doesn't ignore Java's exception mechanism;
instead it relieves the impedance mismatch between exceptions and higher-order functions;
exception handling not statically typesafe (due to sneaky throwing), but always safe at runtime (never swallows an exception except on explicit request).
The class tries to cover all the typical ways to handle an exception:
recover with some handling code which provides a substitute value;
flatRecover which, analogous to flatMap, allows to return a new Exceptional instance which will be unwrapped and the state of the current instance suitably updated;
propagate an exception, throwing it from the Exceptional expression and making the propagate call declare this exception type;
propagate it after wrapping into another exception (translate it);
handle it, resulting in an empty Exceptional;
as a special case of handling, swallow it with an empty handler block.
The propagate approach allows one to selectively pick which checked exceptions he wants to expose from his code. Exceptions which remain unhandled at the time a terminal operation is called (like get) will be sneakily thrown without declaration. This is often considered as an advanced and dangerous approach, but is nevertheless often employed as a way to somewhat alleviate the nuisance of checked exceptions in combination with lambda shapes which do not declare them. The Exceptional class hopes to offer a cleaner and more selective alternative to sneaky throw.
/*
* Copyright (c) 2015, Marko Topolnik. All Rights Reserved.
*
* 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.
*/
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public final class Exceptional<T>
{
private final T value;
private final Throwable exception;
private Exceptional(T value, Throwable exc) {
this.value = value;
this.exception = exc;
}
public static <T> Exceptional<T> empty() {
return new Exceptional<>(null, null);
}
public static <T> Exceptional<T> ofNullable(T value) {
return value != null ? of(value) : empty();
}
public static <T> Exceptional<T> of(T value) {
return new Exceptional<>(Objects.requireNonNull(value), null);
}
public static <T> Exceptional<T> ofNullableException(Throwable exception) {
return exception != null? new Exceptional<>(null, exception) : empty();
}
public static <T> Exceptional<T> ofException(Throwable exception) {
return new Exceptional<>(null, Objects.requireNonNull(exception));
}
public static <T> Exceptional<T> from(TrySupplier<T> supplier) {
try {
return ofNullable(supplier.tryGet());
} catch (Throwable t) {
return new Exceptional<>(null, t);
}
}
public static Exceptional<Void> fromVoid(TryRunnable task) {
try {
task.run();
return new Exceptional<>(null, null);
} catch (Throwable t) {
return new Exceptional<>(null, t);
}
}
public static <E extends Throwable> Consumer<? super E> swallow() {
return e -> {};
}
public T get() {
if (value != null) return value;
if (exception != null) sneakyThrow(exception);
throw new NoSuchElementException("No value present");
}
public T orElse(T other) {
if (value != null) return value;
if (exception != null) sneakyThrow(exception);
return other;
}
public T orElseGet(Supplier<? extends T> other) {
if (value != null) return value;
if (exception != null) sneakyThrow(exception);
return other.get();
}
public Stream<T> stream() {
return value == null ? Stream.empty() : Stream.of(value);
}
public<U> Exceptional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (value == null) return new Exceptional<>(null, exception);
final U u;
try {
u = mapper.apply(value);
} catch (Throwable exc) {
return new Exceptional<>(null, exc);
}
return ofNullable(u);
}
public<U> Exceptional<U> flatMap(Function<? super T, Exceptional<U>> mapper) {
Objects.requireNonNull(mapper);
return value != null ? Objects.requireNonNull(mapper.apply(value)) : empty();
}
public Exceptional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (value == null) return this;
final boolean b;
try {
b = predicate.test(value);
} catch (Throwable t) {
return ofException(t);
}
return b ? this : empty();
}
public <X extends Throwable> Exceptional<T> recover(
Class<? extends X> excType, Function<? super X, T> mapper)
{
Objects.requireNonNull(mapper);
return excType.isInstance(exception) ? ofNullable(mapper.apply(excType.cast(exception))) : this;
}
public <X extends Throwable> Exceptional<T> recover(
Iterable<Class<? extends X>> excTypes, Function<? super X, T> mapper)
{
Objects.requireNonNull(mapper);
for (Class<? extends X> excType : excTypes)
if (excType.isInstance(exception))
return ofNullable(mapper.apply(excType.cast(exception)));
return this;
}
public <X extends Throwable> Exceptional<T> flatRecover(
Class<? extends X> excType, Function<? super X, Exceptional<T>> mapper)
{
Objects.requireNonNull(mapper);
return excType.isInstance(exception) ? Objects.requireNonNull(mapper.apply(excType.cast(exception))) : this;
}
public <X extends Throwable> Exceptional<T> flatRecover(
Iterable<Class<? extends X>> excTypes, Function<? super X, Exceptional<T>> mapper)
{
Objects.requireNonNull(mapper);
for (Class<? extends X> c : excTypes)
if (c.isInstance(exception))
return Objects.requireNonNull(mapper.apply(c.cast(exception)));
return this;
}
public <E extends Throwable> Exceptional<T> propagate(Class<E> excType) throws E {
if (excType.isInstance(exception))
throw excType.cast(exception);
return this;
}
public <E extends Throwable> Exceptional<T> propagate(Iterable<Class<? extends E>> excTypes) throws E {
for (Class<? extends E> excType : excTypes)
if (excType.isInstance(exception))
throw excType.cast(exception);
return this;
}
public <E extends Throwable, F extends Throwable> Exceptional<T> propagate(
Class<E> excType, Function<? super E, ? extends F> translator)
throws F
{
if (excType.isInstance(exception))
throw translator.apply(excType.cast(exception));
return this;
}
public <E extends Throwable, F extends Throwable> Exceptional<T> propagate(
Iterable<Class<E>> excTypes, Function<? super E, ? extends F> translator)
throws F
{
for (Class<? extends E> excType : excTypes)
if (excType.isInstance(exception))
throw translator.apply(excType.cast(exception));
return this;
}
public <E extends Throwable> Exceptional<T> handle(Class<E> excType, Consumer<? super E> action) {
if (excType.isInstance(exception)) {
action.accept(excType.cast(exception));
return empty();
}
return this;
}
public <E extends Throwable> Exceptional<T> handle(Iterable<Class<E>> excTypes, Consumer<? super E> action) {
for (Class<? extends E> excType : excTypes)
if (excType.isInstance(exception)) {
action.accept(excType.cast(exception));
return empty();
}
return this;
}
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) return value;
if (exception != null) sneakyThrow(exception);
throw exceptionSupplier.get();
}
public boolean isPresent() {
return value != null;
}
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
if (exception != null) sneakyThrow(exception);
}
public boolean isException() {
return exception != null;
}
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
return obj instanceof Exceptional && Objects.equals(value, ((Exceptional)obj).value);
}
#Override
public int hashCode() {
return Objects.hashCode(value);
}
#SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
throw (T) t;
}
}
#FunctionalInterface
public interface TrySupplier<T> {
T tryGet() throws Throwable;
}
#FunctionalInterface
public interface TryRunnable {
void run() throws Throwable;
}
What if every functional interface provided by java.util.function was allowed to throw an exception?
public interface ThrowingSupplier<R, X extends Throwable> {
public R get() throws X;
}
We could use some default methods to provide the behavior you want.
You could fallback to some default value or action
Or you could try to perform another action which may throw an exception
I've written a library which redefines most of the interfaces in java.util.function this way. I even provide a ThrowingStream which let's you use these new interfaces with the same API as a regular Stream.
#FunctionalInterface
public interface ThrowingSupplier<R, X extends Throwable> {
public R get() throws X;
default public Supplier<R> fallbackTo(Supplier<? extends R> supplier) {
ThrowingSupplier<R, Nothing> t = supplier::get;
return orTry(t)::get;
}
default public <Y extends Throwable> ThrowingSupplier<R, Y> orTry(
ThrowingSupplier<? extends R, ? extends Y> supplier) {
Objects.requireNonNull(supplier, "supplier");
return () -> {
try {
return get();
} catch (Throwable x) {
try {
return supplier.get();
} catch (Throwable y) {
y.addSuppressed(x);
throw y;
}
}
};
}
}
(Nothing is a RuntimeException that can never be thrown.)
Your original example would become
ThrowingFunction<String, Integer, NumberFormatException> parse = Integer::parseInt;
Function<String, Optional<Integer>> safeParse = parse.fallbackTo(s -> null)
.andThen(Optional::ofNullable);
Stream.of(s1, s2)
.map(safeParse)
.map(i -> i.orElse(-1))
.forEach(System.out::println);
Here's some discussions I had previously on this topic.
I made an interface Result<T> along the reasonings. A Result<T> is either a success with a value of type T, or a failure with an Exception. It's a subtype of Async<T>, as an immediately completed async action, but that is not important here.
To create a result -
Result.success( value )
Result.failure( exception )
Result.call( callable )
Result can then be transformed in various ways - transform, map, then, peek, catch_, finally_ etc. For example
Async<Integer> rInt = Result.success( s )
.map( Integer::parseInt )
.peek( System.out::println )
.catch_( NumberFormatException.class, ex->42 ) // default
.catch_( Exception.class, ex-> { ex.printStacktrace(); throw ex; } )
.finally_( ()->{...} )
Unfortunately the API is focusing on Async, so some methods return Async. Some of them can be overridden by Result to return Result; but some cannot, e.g. then() (which is flatmap). However, if interested, it's easy to extract a standalone Result API that has nothing to do with Async.
There's a third-party library called better-java-monads. It has the Try monad which provides the necessary functions. It also has TryMapFunction and TrySupplier functional interfaces to use the Try monad with checked exceptions.