title: Is it possible to catch a runtime exception and throw an application exception instead in the same transaction ? (ejb)
body: <p>Summary :</p>
  
  <p>Somebody decided that EntityExistsException is a RuntimeException to avoid to force the developer to catch the exception. What is the way to avoid this exception to rollback the transaction ? If you catch the EntityExistsException, the transaction is already rolled back by the container... imho EntityExistsException is not really a run time exception... It should be possible to recover from such an exception... How to catch the runtime exception and rethrow an application exception (checked). It was tested in 2 stateless session beans.</p>
  
  <p>In details :</p>
  
  <p>In my example I have 2 stateless session beans.</p>
  
  <p>The first session bean will start a new transaction (REQUIRES_NEW)
  The beans will persist a new entity.
  Then a second stateless session bean is called.</p>
  
  <p>The second session bean does not start a new transaction (MANDATORY or REQUIRED) because it was called by a bean that started the transaction 
  The second session bean will persist a second time the same entity.
  JPA throws javax.persistence.EntityExistsException but it is  javax.ejb.EJBTransactionRolledbackException that is actually thrown.
  (It is only an example, imho any method that will throw a RuntimeException will rollback the transaction. I found a way to artificially produce an entity exists exception...)</p>
  
  <p>I have created an ApplicationException. We know that by default that ApplicationException does not rollback the transaction.</p>
  
  <p>In the second ejb I catch EJBTransactionRolledbackException (or EntityExistsException)
  and I throw an application exception instead. The transaction should not be rolled back !</p>
  
  <p>The second bean joined the transaction and it has an influence on the transaction because the transaction is ALWAYS rolled back.</p>
  
  <p>Is there a way to avoid that that transaction is rolled back because of an RuntimeException (EntityExistsException) ? I catch the EntityExistsException or the EJBTransactionRolledbackException and I rethrow an application exception.</p>
  
  <p>IMHO this can be avoided if the second transaction also requires a new transaction (REQUIRES_NEW). But I would like to avoid that and keep only 1 transaction...
  Can you help me ?</p>
  
  <p>Logging from openejb :</p>
  
  <p>first session bean and first method</p>
  
  <p>DEBUG 10-07 12:20:53,002 (Log4jLogStream.java:debug:81)  -TX NotSupported: No transaction to suspend
  DEBUG 10-07 12:20:53,002 (Log4jLogStream.java:debug:81)  -TX RequiresNew: No transaction to suspend
  DEBUG 10-07 12:20:53,003 (Log4jLogStream.java:debug:81)  -TX RequiresNew: <strong>Started transaction org.apache.geronimo.transaction.manager.TransactionImpl@25ef757f</strong>
  1
  after persist 1 sleeping 10</p>
  
  <p>second session bean and second method</p>
  
  <p>DEBUG 10-07 12:21:03,009 (Log4jLogStream.java:debug:81)  -invoking method create on bcmc-core.be.awl.clearing.bcmc.core.utils.TestService001
  DEBUG 10-07 12:21:03,011 (Log4jLogStream.java:debug:81)  -finished invoking method create. Return value:proxy=be.awl.clearing.bcmc.core.utils.TestService001;deployment=bcmc-core.be.awl.clearing.bcmc.core.utils.TestService001;pk=null
  DEBUG 10-07 12:21:03,012 (Log4jLogStream.java:debug:81)  -invoking method writeToDatabase on bcmc-core.be.awl.clearing.bcmc.core.utils.TestService001 with identity null
  DEBUG 10-07 12:21:03,014 (Log4jLogStream.java:debug:81)  -TX NotSupported: Suspended transaction org.apache.geronimo.transaction.manager.TransactionImpl@25ef757f
  DEBUG 10-07 12:21:03,014 (Log4jLogStream.java:debug:81)  -TX NotSupported: Resuming transaction org.apache.geronimo.transaction.manager.TransactionImpl@25ef757f
  get rollback only false
  2</p>
  
  <p>DEBUG 10-07 12:21:03,018 (Log4jLogStream.java:debug:85)  -The bean instance business method encountered a system exception: a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]
  javax.persistence.EntityExistsException: a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]
  ...</p>
  
  <p>DEBUG 10-07 12:21:03,020 (Log4jLogStream.java:debug:81)  -finished invoking method writeToDatabase with exception org.apache.openejb.core.transaction.TransactionRolledbackException: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.persistence.EntityExistsException : a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]</p>
  
  <p>DEBUG 10-07 12:21:03,021 (Log4jLogStream.java:debug:85)  -The bean instance business method encountered a system exception: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.persistence.EntityExistsException : a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]
  javax.ejb.EJBTransactionRolledbackException: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.persistence.EntityExistsException : a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]
  ...
  Caused by: javax.persistence.EntityExistsException: a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]
  ...</p>
  
  <p>DEBUG 10-07 12:21:03,022 (Log4jLogStream.java:debug:81)  -TX RequiresNew: Rolling back transaction org.apache.geronimo.transaction.manager.TransactionImpl@25ef757f</p>
  
  <p>DEBUG 10-07 12:21:03,024 (Log4jLogStream.java:debug:81)  -TX RequiresNew: No transaction to resume</p>
  
  <p>DEBUG 10-07 12:21:03,024 (Log4jLogStream.java:debug:81)  -finished invoking method writeToDatabase with exception java.rmi.RemoteException: The bean encountered a non-application exception; nested exception is: 
      javax.ejb.EJBTransactionRolledbackException: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.persistence.EntityExistsException : a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]
  Exception in thread "Thread-49" javax.ejb.EJBException: The bean encountered a non-application exception; nested exception is: 
      javax.ejb.EJBTransactionRolledbackException: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.persistence.EntityExistsException : a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]
  ...
  Caused by: javax.ejb.EJBTransactionRolledbackException: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.persistence.EntityExistsException : a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]
  ...
  Caused by: javax.persistence.EntityExistsException: a different object with the same identifier value was already associated with the session: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8]
  ...</p>
  
  <p>first session bean :</p>
  
  <pre><code>@Stateless(name = "bcmc-core.be.awl.clearing.bcmc.core.utils.TestService")
  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public class TestServiceImpl implements TestService {
  @PersistenceContext(unitName = "bcmc-core")
  private EntityManager em;       
  @Override
  public void writeToDatabase() {         
      try {
  
          RepBcmcParam bcmcParam = new RepBcmcParam();
          RepBcmcParamId bcmcParamId = new RepBcmcParamId();
          bcmcParamId.setProcname("procname");
          bcmcParamId.setParid("parid");
          Date date = new Date();
          bcmcParamId.setDtbeg(date);
          bcmcParam.setId(bcmcParamId);
          bcmcParam.setParval("parval");
          System.out.println("1");
          em.persist(bcmcParam);
          System.out.println("after persist 1 sleeping 10");
          try {
              Thread.currentThread().sleep(10000);
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
          TestService001 testService001 = net.atos.xa.resourcelocator.ResourceLocator.lookup(TestService001.class);
          testService001.writeToDatabase(date);
          System.out.println("after write to database 2 sleeping 10");
          try {
              Thread.currentThread().sleep(10000);
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }           
      } catch(BcmcDuplicateException be) {
          System.out.println("facade");
      }                   
  }
  </code></pre>
  
  <p>second session bean :</p>
  
  <pre><code>@Stateless(name = "bcmc-core.be.awl.clearing.bcmc.core.utils.TestService001")
  @TransactionAttribute(TransactionAttributeType.MANDATORY)
  public class TestService001Impl implements TestService001 { 
  @PersistenceContext(unitName = "bcmc-core")
  private EntityManager em;   
  private static final Logger LOGGER = Logger.getLogger(Constants.APP_NAME);  
  @Resource
  private SessionContext context;
      try {           
          System.out.println("get rollback only " + context.getRollbackOnly());
          RepBcmcParam bcmcParam = new RepBcmcParam();
          RepBcmcParamId bcmcParamId = new RepBcmcParamId();
          bcmcParamId.setProcname("procname");
          bcmcParamId.setParid("parid");
          bcmcParamId.setDtbeg(date);         
          //bcmcParamId.setDtbeg(new Date());
          bcmcParam.setId(bcmcParamId);
          bcmcParam.setParval("parval");
          System.out.println("2");
          em.persist(bcmcParam);
          System.out.println("get rollback only " + context.getRollbackOnly());
          try {
              System.out.println("1 sec");
              Thread.currentThread().sleep(1000);             
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }           
  
      } catch(EJBTransactionRolledbackException e) {
          System.out.println("get rollback only " + context.getRollbackOnly());
          throw new BcmcDuplicateException();
      }
  }
  </code></pre>