ArtOfWarfare
ArtOfWarfare

Reputation: 21486

Implementing AutoCloseable - How do I know if an exception occurred in the try block?

We have a class we've written which opens up a connection to a server. When you're done with it, you need to either tell it to commit if everything was successful, or tell it to rollback if something went wrong. So right now we have a lot of spots in our code that look like this:

OurConnectionClass conn = null;
try {
    conn = OurConnectionClass(parameters);
    // Do some stuff here...
    conn.commit();
} catch (Throwable t) {
    if (conn != null) {
        conn.rollback();
    }
    throw t;
}

If you forget to commit or rollback, there's no immediate issues, but eventually you exhaust a connection pool and then have to figure out where you made a mistake.

I'd like to find a way so OurConnectionClass implements AutoClosable, so I could do somsething like this instead:

try (OurConnectionClass conn = new OurConnectionClass(parameters)) {
    // Do some stuff here...
}

I feel like there has to be a way to do this, but I'm not seeing it. AutoCloseable only calls a close method, with no arguments passed to it. As far as I can see, there's no way to know whether close is being called because the end of the try block was successfully reached or because an exception was thrown.

Upvotes: 4

Views: 1490

Answers (3)

Bohemian
Bohemian

Reputation: 425043

Do both!

try (OurConnectionClass conn = new OurConnectionClass(parameters)) {
    // Do some stuff here...
    conn.commit();
} catch (Throwable t) {
    conn.rollback();
    throw t;
}

The closeable is still auto-closed (in the implicit finally block) if stuff explodes.

BTW, it would be better to throw a domain exception:

throw new MyStuffExploded(t);

because re-throwing the connection exception lets implementation details leak out via the method contract, which is a form of coupling, which is bad.

Upvotes: -1

MTilsted
MTilsted

Reputation: 5545

I think the semantic you want is that the transaction is rolled back in close, unless the code which uses OurConnectionClass explicit calls OurConnectionClass.commit().

Then you don't have any problem, because your close method, then just need to test if there is an open transaction. And if there is roll it back, and log an error.

Upvotes: 1

Lino
Lino

Reputation: 19926

When executing this snippet.

try (OurConnectionClass conn = new OurConnectionClass(parameters)) {
    // Do some stuff here...
    conn.commit();
}

OurConnectionClass.close() will always be called after an instance is created. So you can just add some logic to check if a commit was made. E.g. with a boolean flag. With that you could then check in the close() method if the connection should be closed peacefully or it should rollback:

public class OurConnectionClass implements AutoCloseable{

    private boolean committed; // initialized to false

    public void commit(){
         // commit
         committed = true;
    }

    public void close() throws Exception{
         if(!committed){
             // rollback
         }
    }
}

Upvotes: 3

Related Questions