How will the transaction started in callSessionBean2() behave in the following scenario? Is it suspended? What happens if an exception is thrown in SessionBean2? SessionBean2 was setup with BEAN transaction management type because it does not communicate with any database, only with AD server via LDAP.
I'm asking because I've been having issues in a production server some week after deployment that calls to SessionBean2 starts to hang, with transaction timeouts as the only error. I figured this setup might be a bad thing, can anyone shed light on this?
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class SessionBean1 {
@Inject private SessionBean2 sessionBean2;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void callSessionBean2(){
sessionBean2.doThingsThatMightCauseException();
}
}
@Singleton
@TransactionManagement(TransactionManagementType.BEAN)
public class SessionBean2 {
public void doThingsThatMightCauseException(){...}
}
As declared in EJB 3.1 spec (§13.6.1), the caller's transaction will be suspended:
If the client request is associated with a transaction T1, and the instance is not associated with a transaction, the container suspends the client’s transaction association and invokes the method with an unspecified transaction context. The container resumes the client’s transaction association (T1) when the method (together with any associated interceptor methods) completes.
So the transaction associated with SessionBean1
gets suspended, and the exceptions thrown by SessionBean2
in either case will be handled by the calling bean, with the appropriate semantics (i.e. treated by the CMT session)
Your code is correct, though I'd rather use:
@TransactionManagement(TransactionManagementType.CONTAINER)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class SessionBean2 {
public void doThingsThatMightCauseException(){...}
}
to the same effect.
The problem you are experiencing may be related to the @Singleton
annotation, which
according to §4.8.5.3 and §4.8.5.3 defaults the bean to be:
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Lock(LockType.WRITE)
This serializes the invocations of doThingsThatMightCauseException
, causing random ConcurrentAccessTimeoutException
. Although the concurrent access timeout is configurable through @AccessTimeout
, it will lead to a transaction timeout if the (serialized) doThingsThatMightCauseException
access delay exceeds the timeout defined for CMT transactions (remember, the CMT transaction associated with SessionBean1
gets suspended, but the clock is still counting...).
Wrapping it up:
You need (with proper care to shared state) to change the access lock on doThingsThatMightCauseException
with:
@Lock(LockType.READ)
public void doThingsThatMightCauseException(){...}
This will remove the access serialization, hopefully solving your timeout problems.
If you still experience timeouts, it will be related to the slowness of the operations included in doThingsThatMightCauseException
.
In this case you'll need to either:
SessionBean1
to BMT, thus leveraging UserTransaction.setTransactionTimeout