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] 

Reply via email to