Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can my AutoCloseable.close() implementation detect a potential exception?

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

like image 804
Lukas Eder Avatar asked Jan 25 '14 17:01

Lukas Eder


People also ask

Which language feature ensures that objects implementing the AutoCloseable interface are closed?

The try -with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java. lang. AutoCloseable , which includes all objects which implement java.

What is AutoCloseable what is it advantage?

An Object that implements the AutoCloseable interface holds on to a resource until it is done using it within a try block and then it is automatically closed. Whereas an object that implements a Closeable interface can be closed by calling .

What is AutoCloseable in Java?

public interface AutoCloseable. An object that may hold resources (such as file or socket handles) until it is closed. The close() method of an AutoCloseable object is called automatically when exiting a try -with-resources block for which the object has been declared in the resource specification header.

What is try with resources in Java example?

In Java, the Try-with-resources statement is a try statement that declares one or more resources in it. A resource is an object that must be closed once your program is done using it. For example, a File resource or a Socket connection resource.


1 Answers

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)

like image 129
Jauco Avatar answered Sep 24 '22 18:09

Jauco