Reputation: 51
I would like to two have two different methods running in catch
and final
blocks. I have found AutoCloseable
interface, but I need something to fire in case of exception only.
Like:
SomeService service = CreateService().andOpenTransaction()
try {
service.doSomeMessyThingsInsideDB();
} catch (Exception e) {
service.rollbackTransaction();
throw e;
} finally {
service.closeConnection();
}
Is there any way to make it simpler? As I said I am familiar with AutoCloseable, but it helps me only with finally block. I still cannot use it inside the catch.
Upvotes: 2
Views: 235
Reputation: 109557
First step: Handling the exception
You evidently want the exception handled before some close. Then you need inside a try-with-resources to handle the exception.
/** throws RuntimeException */
void process(Callable<Void> work, Consumer<Exception> onFail) {
try {
work.call();
} catch (Exception e) {
onFail(e);
}
}
try (SomeService service = CreateService().andOpenTransaction()) {
process(() -> service.doSomeMessyThingsInsideDB(),
e -> {
service.rollbackTransaction();
throw new IllegalStateException(e);
});
}
This is not very satisfactory, but again also integrating the AutoCloseable, might give too few use-cases.
Second step: with AutoCloseable
<SV extends AutoCloseable> void processAutoClosing(Supplier<SV> serviceFactory,
Callable<Void> work, Consumer<Exception> onFail) {
try (SV service = serviceFactory.get()) {
process(work, onFail);
}
}
processAutoClosing(...);
Upvotes: 1
Reputation: 19926
Well you could define your own interface, and then some static
runner method:
public interface ErrorHandlingCloseable extends AutoCloseable {
void run() throws Exception;
void onError(Exception e);
static void execute(ErrorHandlingClosable ehc) throws Exception {
try(ErrorHandlingClosable temp = ehc) {
ehc.run();
} catch(Exception e) {
ehc.onError(e);
throw e;
}
}
}
Which you then could then call like this:
SomeService service = CreateService().andOpenTransaction();
ErrorHandlingCloseable.execute(new ErrorHandlingCloseable() {
public void run() throws Exception { service.doSomeMessyThingsInsideDB(); }
public void onError(Exception e) { service.rollbackTransaction(); }
public void close() throws Exception { service.closeConnection(); }
});
But you see, it's still messy.
You could even implement this interface
in your SomeService
but then you're restricted that the run()
method will always call doSomeMessyThingsInsideDB()
.
Another way but still similar would be to use Java8 and create a helper functional interface
:
public interface ThrowingRunnable {
void run() throws Exception;
}
And then a static
method somewhere:
public static void execute(ThrowingRunnable action,
ThrowingRunnable onCatch,
ThrowingRunnable onFinally) throws Exception {
try(AutoCloseable ao = onFinally) {
action.run();
} catch(Exception e) {
onCatch.run();
throw e;
}
}
The interesting part is probably this: try(AutoCloseable ao = onFinally)
, which "registers" your onFinally
method to be called when finally
is reached.
This could then be called like this:
execute(
service::doSomeMessyThingsInsideDB,
service::rollbackTransaction,
service::closeConnection
);
Upvotes: 1
Reputation: 1347
You said you are familiar with AutoCloseable
, but you don't use it.
Have you considered using try-with-resources
statement?
Your code can be simplified to:
try (SomeService service = CreateService().andOpenTransaction()) {
service.doSomeMessyThingsInsideDB();
} catch(exception e){
service.rollbackTransaction();
throw e;
}
Oracle has great doc for that, including examples.
Note: A try-with-resources statement can have catch and finally blocks just like an ordinary try statement. In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed.
Answering your question, this is as simple as it can get.
If your class doesn't implement Closeable
then you can either implement it or use finally
.
Upvotes: 1