It’s nice that JDK 7 includes new ”try (…) { … }” construct, but *WHY*, oh, *WHY*, they managed to fail on implementing it in usable manner.
To put it shortly, problem is described here: Detect exception in AutoCloseable close()
Thus they implemented logic which allows doing automatic close(), but API doesn’t this logic anyway to know if there was some exception occurring or not. This makes pattern useless for cases like described on that article. For example, described workaround contains fatal flaw, namely the fact that t.success() must be really placed so that there cannot be happening any error after it (in real code, it’s a bit tricky sometimes).
Sure, case described in stack overflow was first concept, where I thought that this structure could be utilized. Strangely enough, my basic case wasn’t clearly basic case for JDK 7 implementors. Too bad.
Luckily, however, they got something at least something right: Using Java 7 to target much older JVMs.
However, it sounds unlikely that trying to compile try with block to be compatible with JDK 6 will work due to Autocloseable and suppressed exceptions concepts.
One attempt:
[code lang=”java”]
class Foo {
static final class Tx implements AutoCloseable {
static final ThreadLocal
private int mNested;
private boolean mFailed;
static Tx start() {
Tx tx = STACK.get();
if (tx == null) {
tx = new Tx();
STACK.set(tx);
System.out.println(”start”);
}
tx.mNested++;
return tx;
}
static void fail() {
Tx tx = STACK.get();
tx.mFailed = true;
}
public void close() throws Exception {
System.out.println(”close”);
Tx tx = STACK.get();
tx.mNested–;
if (tx.mNested == 0) {
STACK.remove();
if (tx.mFailed) {
System.out.println(”rollback”);
} else {
System.out.println(”commit”);
}
}
}
}
static abstract class Eval {
public final Object run() throws Exception {
Tx tx = Tx.STACK.get();
boolean success = false;
try {
Object r = eval();
success = true;
return r;
} finally {
if (!success) {
Tx.fail();
}
}
}
public abstract Object eval() throws Exception;
}
public static void main(String[] arg) {
try (Tx t = Tx.start()) {
Object r = new Eval() {
public Object eval() throws Exception {
if (Math.random() > 0.5) {
throw new Exception(”Failed”);
}
return ”foobar”;
}
}.run();
System.out.println(”result=” + r);
} catch (Exception e) {
e.printStackTrace();
}
}
}
[/code]
However, such structure starts to be actually as cumbersome as one what I’ve once implemented. Problem being how to ensure that generic code can easily ensure that commit vs. rollback condition is triggered correctlt (i.e. only if there ain’t any runtime or whatever exception occurring).
Btw, I also hide broken logic on that sample (actually accidentally, but that’s beside the point):
[code]
sh: /tmp/java> java Foo
start
result=foobar
close
commit
sh: /tmp/java> java Foo
start
close
rollback
java.lang.Exception: Failed
at Foo$1.eval(Foo.java:62)
at Foo$Eval.run(Foo.java:45)
at Foo.main(Foo.java:59)
[/code]
Looks right? NO! It’s broken. Look into first output, it says ”result=foobar” before ”close/commit”. Actual intented behaviour was that this would have been after commit, but I did slight mistake in ordering. Of course, it was unfair trying to pinpoint such logic bug, since only I could know what was behaviour how it was supposed to work.
Corrected main:
[code lang=”java”]
public static void test() throws Exception {
Object r;
try (Tx t = Tx.start()) {
r = new Eval() {
public Object eval() throws Exception {
if (Math.random() > 0.5) {
throw new Exception(”Failed”);
}
return ”foobar”;
}
}.run();
}
System.out.println(”result=” + r);
}
public static void main(String[] arg) {
try {
test();
} catch (Exception e) {
e.printStackTrace();
}
}
[/code]
And lesson learned from such simple exercise is that when dealing with transactions, stupid little mistakes creep very easily in. Which is very reason, why I stated that ”t.success()” approach to be flawed in stackoverflow.
References:
New Java 7 Features: The Try-with-resources Language Enhancement
Detect exception in AutoCloseable close()
Using Java 7 to target much older JVMs
New Java 7 Features: Suppressed Exceptions and Try With Resources from Project Coin