I have been looking at modifying the Aries JPA code to add the Ability to be
able to specify exception types to rollback on and not rollback on
The current org.apache.aries.jpa.template.JpaTemplate has the following
public interface JpaTemplate {
<R> R txExpr(TransactionType type, EmFunction<R> code);
void tx(TransactionType type, EmConsumer code);
<R> R txExpr(EmFunction<R> code);
void tx(EmConsumer code);
}
which allows a client to use a Lambda expression e.g.
jpa.tx(TransactionType.RequiresNew,
em -> {
em.persist(task);
em.flush();
});
What is the best way to specify/pass the list of exceptions to rollback on and
not rollback on to the underlying implementations of the JpaTemplate (currently
XAJpaTemplate and ResourceLocalJpaTemplate in the jpa-support project)?
I have added extra methods to accept 2 new arguments to the JpaTemplate e.g.
<R> R txExpr(TransactionType type,
Class<? extends Throwable>[] exceptionTypesToRollBackOn,
Class<? extends Throwable>[] exceptionTypesToNotRollBackOn,
EmFunction<R> code);
opinions/criticisms? Are there any standards from specs that need to be adhered
to?
Upon modifying the code it became apparent I was running into the issue with
throwing checked exceptions within the Lambda body.
see
http://stackoverflow.com/questions/14039995/java-8-mandatory-checked-exceptions-handling-in-lambda-expressions-why-mandato
http://stackoverflow.com/questions/27644361/how-can-i-throw-checked-exceptions-from-inside-java-8-streams
e.g. consider the snippet below
@Override
public void addTasks(Task1 task1, Task2 task2) throws ParseException {
jpa.tx(TransactionType.Required,
new Class[]{ParseException.class}, <--- exceptions to rollback on
new Class[]{UnsupportedOperationException.class}, <--- exceptions to not
rollback on
em -> {
addTask1(task1);
dummyCheckedExceptionThrow(); <--- assume this method throws a checked
ParseException which we don't want to wrap in a Runtime exception
addTask2(task2);
});
}
I don't think this is possible to write without further enhancement to the
EmConsumer class which I changed to sneakily throw checked exceptions (see
http://www.philandstuff.com/2012/04/28/sneakily-throwing-checked-exceptions.html)
@FunctionalInterface
public interface EmConsumer {
default void accept(EntityManager em) {
try {
acceptThrows(em);
} catch (final Exception e) {
EmConsumer.<RuntimeException>sneakyThrow(e);
}
}
void acceptThrows(EntityManager em) throws Exception;
public static <T extends Throwable> T sneakyThrow(Throwable t) throws T {
// Cast to the parameterized type T, in this case that type is
RuntimeException. However
// at runtime the generic types are erased, so that there is no T type anymore
to cast to,
// so the cast disappears. So the compiler sees the code with the cast e.g.
throw (RuntimeException) t
// so it allows the now-unchecked exception to propagate, but the runtime
doesn't see the generic types,
// so it sees no cast e.g. throw t and therefore it doesn’t complain about a
ClassCastException.
// (credit where credit is due -
http://www.mail-archive.com/[email protected]/msg05984.html)
throw (T) t;
}
}
Unfortunately this change requires that Aries JPA is compiled with 1.8 as the
EmConsumer interface now contains a default method (>= 1.8).
An unsatisfactory (I think) alternative is for the client to put a try catch
block around the method e.g. dummyCheckedExceptionThrow() that throws the
checked exception and wrap the exception in a Runtime exception and then
perhaps unwrap the exception afterwards and rethrow.
Can anyone see other better alternatives?
Is it possible/advisable to maintain an extra 1.8 branch as is done with the
Postgres JDBC driver?
Regards,
Tim Jones
Floor 1, 100 McLeod St, Hastings 4120, New Zealand
PO Box 2006, Hastings 4153, New Zealand
P: +64 6 871 5700 F: +64 6 871 5709 E: [email protected]