1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
# -*-org-*-
#+TITLE: murphy (What could go wrong?)
*NOTE*: While we'd love for people to try this out, as long as the
version is less than 1.0, we reserve the right (which we may well
exercise) to change the API.
Consider:
#+BEGIN_SRC clojure
(try
(print-masterpiece) ; Throws lp0-on-fire
(finally
(turn-off-light))) ; Throws switch-broken
#+END_SRC
At this point, you'll know that you need to fix your light switch, but
have no idea that your printer is on fire. That's because a throw
from a finally clause simply discards any pending exception.
To preserve all of the failure information, we can use [[https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html#addSuppressed-java.lang.Throwable-][exception suppression]],
which is provided by newer versions of the JDK and write this instead:
#+BEGIN_SRC clojure
(try!
(print-masterpiece) ; Throws lp0-on-fire
(finally
(turn-off-light))) ; Throws lp0-on-fire, with switch-broken
; available via (.getSuppressed lp0-on-fire).
#+END_SRC
As mentioned in the exception suppression documentation linked above,
whether or not a suppressed exception is recorded or discarded depends
on the constructor arguments for the original exception.
In any case, if an exception does contain suppressed exceptions, they
should be displayed by the normal top-level JVM exception handler,
assuming they make it that far.
* Facilities
** (try! ...)
Behaves like the normal try, except that it supports multiple finally
clauses which are executed in order, and if any given finally clause
throws while an exception is already pending, the new exception will
be suppressed via the Throwable addSuppressed method."
** (with-open! ...)
Behaves like the normal with-open except that exceptions thrown by any
of the close methods will be suppressed. And of course any exception
pending at the end of the cleanup will be thrown.
** (with-final ...)
Configurable cleanup, suppressing exceptions, either on :error (any
throw) or :always:
#+BEGIN_SRC clojure
(with-final [foo (.acquire lock) :always .release
bar (open something) :always .close
baz {:foo foo :bar bar}]
...)
#+END_SRC clojure
:error can be particularly useful in cases where normal cleanup must
happen in a completely different scope.
#+BEGIN_SRC clojure
(defn start-server [...]
(with-final [foo (open-foo ...) :error .close
bar (connect-bar ...) :error .disconnect
...]
...do many things...
{:foo foo :bar bar ...}))
(defn stop-server [info]
(with-final [_ (:foo info) :always .close
_ (:bar info) :always .disconnect
...]
true)
#+END_SRC clojure
* Contributing
You can run all of the existing tests (including ~lein test~) with
~lein check-all~.
All patches must be "signed off" by the author before official
inclusion (see ./SIGNED-OFF-BY). If you like, git can can add the
appropriate pseudo-header for you via the --signoff argument to
commit, amend, etc.
* License
This project is free software; you can redistribute it and/or modify
it under the terms of (at your option) either of the following two
licenses:
1) The GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1, or (at your option) any
later version: https://www.gnu.org/licenses/lgpl-2.1.html
2) The Eclipse Public License; either version 1.0 or (at your
option) any later version: http://www.eclipse.org/legal/epl-v10.html
Copyright © 2017-2018, 2020-2021 Rob Browning <rlb@defaultvalue.org>
|