Domanda

When implementing an AutoCloseable to work with the Java 7 try-with-resources statement, I would like to know if there had been an exception within the try block. E.g.:

class C implements AutoCloseable {
    @Override
    public void close() {
        if (exceptionOccurred)
            something();
        else
            somethingElse();
    }
}

To illustrate this:

try (C c = new C()) {

    // This should cause a call to "something()"
    if (something)
        throw new RuntimeException();

    // This should cause a call to "somethingElse()"
    else
        ;
}

Now, from understanding how the try-with-resources statement translates to bytecode, I guess that's not possible. But is there any (reliable!) trick through instrumentation / reflection / some undocumented compiler feature that allows me to access the above RuntimeException from within AutoCloseable.close() ?

Note: I'm an API designer, and I cannot control API consumers' try-with-resources code. The implementation must thus be done at the AutoCloseable site

È stato utile?

Soluzione

The normal way to do this is just to explicitly make a call at the end of the try block. For example:

try (CustomTransaction transaction = ...) {
    // Do something which might throw an exception...

    transaction.commitOnClose();
}

Then in close, you'd either abort the transaction or commit it, based on whether commitOnClose() had been called or not.

It's not automatic, but it's pretty simple to achieve - and very simple to read.

Altri suggerimenti

I've been struggling with this for awhile. I dislike Jon Skeet's answer because a developer (i.e. me) might accidentally forget to call commitOnClose(). I want a way for the developer to be forced to call either commit() or rollback() when he leaves the block of code.

Lambda's and checked exceptions don't play nice together so a proper solution took a bit of puzzling, but eventually me and a coworker of mine came up with a piece of code that allows you to work like this:

TransactionEnforcer.DbResult<String> result = transactionEnforcer.execute(db -> {
  try {
    db.someFunctionThatThrowsACheckedException();
  } catch (TheException e) {
    return failure("fallback value");
  }
  return success(db.getAFancyValue());
});

result.ifPresent(v -> System.out.println(v));

Note how you can return values, can check if the code succeeded or not, and java's codepath return check enforces you to always be explicit about whether the code should be committed or not.

It is implemented using the code below:

package nl.knaw.huygens.timbuctoo.database;

import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

public class TransactionEnforcer {
  private final Supplier<DbClass> dbClassFactory;

  public TransactionEnforcer(Supplier<DbClass> dbClassFactory) {
    this.dbClassFactory = dbClassFactory;
  }

  public <U> DbResult<U> execute(Function<DbClass, DbResult<U>> actions) {
    DbClass db = dbClassFactory.get();
    try {
      DbResult<U> result = actions.apply(db);
      if (result.isSuccess()) {
        db.close(true);
      } else {
        db.close(false);
      }
      return result;
    } catch (RuntimeException e) {
      db.close(false);
      throw e;
    }
  }

  public static class DbResult<T> {
    private final Optional<T> value;
    private final boolean success;

    private DbResult(T value, boolean success) {
      this.value = Optional.of(value);
      this.success = success;
    }

    public static <T> DbResult<T> success(T value) {
      return new DbResult<T>(value, true);
    }

    public static <T> DbResult<T> success() {
      return new DbResult<T>(null, true);
    }

    public static <T> DbResult<T> failure(T value) {
      return new DbResult<T>(value, false);
    }

    public static <T> DbResult<T> failure() {
      return new DbResult<T>(null, false);
    }

    public boolean isSuccess() {
      return success;
    }

    public Optional<T> getValue() {
      return value;
    }
  }
}

(I leave the DbClass as an exercise to the reader)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top