I have found out that the transaction annoation for my MDB was wrong. It should be using TransactionAttributeType.REQUIRED instead of REQUIRES_NEW. I also double check all datasource configurations to make sure they are XA transactions. With @ApplicationException, the default for rollback is FALSE, make sure it is TRUE so that the transaction can be rolledback and retried when exception occurs.
Having done all of the above, my transactions indeed retried and it was processed successfully upon retry. To minimise the possibility of StaleObjectStateException, I invoke ksession.dispose() at the end after complete using ksession.
Hope this helps.