Question

The following code demonstrates a case where a checked exception, ExecutionException, is thrown by an operation and Clojure is wrapping it with RuntimeException.

Why is Clojure doing this? Is this normal? Clojure appears to be doing something different than Java would in this case. What is the idiomatic way to handle the actual exception, in this case Exception, that caused the failure?

user=> (def f (future (Thread/sleep 10000) (throw (Exception. "hello world"))))
#'user/f

user=> (.get f)
Exception hello world  user/fn--318 (NO_SOURCE_FILE:81)

user=> (.printStackTrace *e)
java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.Exception: hello world
    at clojure.lang.Util.runtimeException(Util.java:165)
    at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:97)
    at clojure.lang.Reflector.invokeNoArgInstanceMember(Reflector.java:316)
    at user$eval320.invoke(NO_SOURCE_FILE:82)
    at clojure.lang.Compiler.eval(Compiler.java:6465)
    at clojure.lang.Compiler.eval(Compiler.java:6431)
    at clojure.core$eval.invoke(core.clj:2795)
    at clojure.main$repl$read_eval_print__5967.invoke(main.clj:244)
    at clojure.main$repl$fn__5972.invoke(main.clj:265)
    at clojure.main$repl.doInvoke(main.clj:265)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.main$repl_opt.invoke(main.clj:331)
    at clojure.main$main.doInvoke(main.clj:427)
    at clojure.lang.RestFn.invoke(RestFn.java:397)
    at clojure.lang.Var.invoke(Var.java:397)
    at clojure.lang.AFn.applyToHelper(AFn.java:159)
    at clojure.lang.Var.applyTo(Var.java:518)
    at clojure.main.main(main.java:37)
Caused by: java.util.concurrent.ExecutionException: java.lang.Exception: hello world
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
    at java.util.concurrent.FutureTask.get(FutureTask.java:83)
    at clojure.core$future_call$reify__5684.get(core.clj:6064)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:92)
    ... 16 more
Caused by: java.lang.Exception: hello world
    at user$fn__318.invoke(NO_SOURCE_FILE:81)
nil
    at clojure.core$binding_conveyor_fn$fn__3713.invoke(core.clj:1817)
    at clojure.lang.AFn.call(AFn.java:18)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:680)
Was it helpful?

Solution

According to this link it seems clojure does not throw checked exceptions so I would assume clojure catches any that might be thrown and wraps it: http://dev.clojure.org/display/doc/1.3

As for handling it idiomatically(?) in Java, then the old rules still apply. I would check if the exception wrapped in the ExecutionException was one of the checked exceptions I expected could happen, and handle them as I would in any other circumstance (read: emulate the otherwise compiler enforced try catch). If it wasnt, I would either wrap it in a runtime exception and throw it again or log it and swallow it depending on the situation.

OTHER TIPS

Note that an ExcecutionException is indeed being thrown - it's just that the exception is getting caught and handled by Clojure.

Clojure will wrap certain exceptions in a RuntimeException when it handles them - I believe this is done to avoid the need to handle checked exceptions in the Clojure source code.

If you want to access the underlying exception, then (.getCause e) should work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top