NOTE: Adding this question in short form with an answer after spending far more time than I'd like to admit on finding the cause. Hopefully I'll save someone else some pain.
When delegating a method call to an EJB
annotated with @Singleton
, the container throws an Exception along the lines of:
TransactionRolledbackLocalException Client's transaction aborted
The Singleton bean has no data access occurring.
ServiceBeanImpl.java
@Stateless
@Local
public class ServiceBean extends BaseBean{
@EJB private CacheService cacheService;
public FooObj getFooFromCache(int id) {
FooObj fooObj = (FooObj) cacheService.get(id);
if (fooObj == null) {
fooObj = getEntityById(FooObj.class, id);
cacheService.put(id, fooObj); //This throws exception
}
return cacheService.get(id);
}
}
CacheServiceImpl.java
@Singleton
@Startup
public class CacheServiceImpl implements CacheService {
private Cache cache;
@PostConstruct
public void init() {
CacheManager instance = CacheManager.getInstance();
cache = instance.getCache("cache");
}
@PreDestroy
public void destroy() {
CacheManager.getInstance().shutdown();
}
public Object get(Object id) {
return cache.get(id);
}
public void put(Object id, Object obj) {
return cache.put(id, obj);
}
}
Question Why would calling a Singleton bean that does no data access throw a Transaction exception?
Short answer: check the code stack that executed prior (assuming there are no clues in the StackTrace) for an Exception that is trapped and not propagated (thus no clues in the StackTrace).
In this particular case there was a try/catch
around creating a named query. If that named query failed, a secondary query was executed in the catch block. The queries didn't require a transaction, so the fallback query executed properly and returned the expected entities.
However...
That also (I think) marks the transaction as needing a rollback. The @Singleton
bean is somewhat of a red-herring. Something about transferring control to that bean made the container check the Transaction, which in turn threw the exception at that point, but the exception was appropriate since the transaction was no longer valid.
Moral of the story: