3 Replies Latest reply on Jul 30, 2007 5:16 PM by tonylmai

    Problem with transaction propagation across 2 SLSB.

      Hello all,

      I have 2 SLSB (EJB3) that has the same EntityManager with the same unitName. When the first SLSB (say BeanA) calls the second SLSB (BeanB) a second time within the same transaction, at entityManager.persist(object) on the second bean I encountered the following exception:

      javax.persistence.TransactionRequiredException: EntityManager must be access within a transaction
      at org.jboss.ejb3.entity.ManagedEntityManagerFactory.verifyInTx(ManagedEntityManagerFactory.java:150)
      at org.jboss.ejb3.entity.TransactionScopedEntityManager.persist(TransactionScopedEntityManager.java:181)
      at com.judots.brokerage.accounting.AccountingServerBean....


      Pseudo code:
      class BeanA ... {
       @PersistenceContext(unitName = "myDb")
       private EntityManager em;
       @EJB
       private BeanB beanB;
      
       public void methodA(...) {
       RecordA recordA = new RecordA(...);
       em.persist(recordA);
      
       ObjectA a = createSomeThing(recordA);
       beanB.methodB(a);
      
       ObjectA a' = createSomeThingElse(recordA);
      
       beanB.methodB(a'); // Threw exception with "EntityManager must be access within a transaction"
       }
      }
      
      class BeanB ... {
       @PersistenceContext(unitName = "myDb")
       private EntityManager em;
      
       @TransactionAttribute(TransactionAttributeType.REQUIRED)
       public void methodB(ObjectA objectA) {
       RecordB recordB = computeSomeThingReferencesRecordA(objectA);
       em.persist(recordB); // Threw "EntityManager must be access within a transaction" on second call.
       }
      }


      Since the methodB in the second SLSB (BeanB) was declared as REQUIRED, how did the transaction propagated?

      Did I do something non-conforming or is this EM within JBoss?

      Thanks
      -tony

        • 1. Re: Problem with transaction propagation across 2 SLSB.

          Here is the stacktrace up to the point before it threw the exception:

          TxUtils.isActive(int) line: 110
          TxUtils.isActive(Transaction) line: 75
          ManagedEntityManagerFactory.verifyInTx() line: 150
          TransactionScopedEntityManager.persist(Object) line: 181
          <b>BeanB.methodB(Account, OrderInfo, TradeInfo, Money) line: 120</b>
          NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
          NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available
          DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available
          Method.invoke(Object, Object...) line: not available
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 112
          InvocationContextImpl.proceed() line: 166
          EJB3InterceptorsInterceptor.invoke(Invocation) line: 63
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 101
          TransactionScopedEntityManagerInterceptor.invoke(Invocation) line: 54
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 101
          AllowedOperationsInterceptor.invoke(Invocation) line: 47
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 101
          Ejb3TxPolicy(TxPolicy).invokeInCallerTx(Invocation, Transaction) line: 126
          TxInterceptor$Mandatory.invoke(Invocation) line: 305
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 101
          TxPropagationInterceptor.invoke(Invocation) line: 76
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 101
          StatelessInstanceInterceptor.invoke(Invocation) line: 62
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 101
          Ejb3AuthenticationInterceptor(AuthenticationInterceptor).invoke(Invocation) line: 77
          Ejb3AuthenticationInterceptor.invoke(Invocation) line: 106
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 101
          ENCPropagationInterceptor.invoke(Invocation) line: 46
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 101
          AsynchronousInterceptor.invoke(Invocation) line: 106
          EJBContainerInvocation<A,T>(MethodInvocation).invokeNext() line: 101
          StatelessContainer.localInvoke(Method, Object[], FutureHolder, BeanContextLifecycleCallback<StatelessBeanContext>) line: 214
          StatelessContainer.localInvoke(Method, Object[], FutureHolder) line: 184
          StatelessLocalProxy.invoke(Object, Method, Object[]) line: 81
          <b>$Proxy105.methodB(Account, OrderInfo, TradeInfo, Money) line: not available</b>
          <b>BeanA.methodA(long, long, Collection<TradeInfo>) line: 647</b>


          Transaction was not null and so it was propagated. The only thing is, why would the status be 1 which is defined as marked rollback?

          public static final int STATUS_ACTIVE = 0;
          
           // Field descriptor #4 I
           public static final int STATUS_MARKED_ROLLBACK = 1;


          What happened to the transaction before the call was made?

          • 2. Re: Problem with transaction propagation across 2 SLSB.
            jhalliday

            I believe the spec requires that you access a bean through its business interface rather than its implementation class if you want the container to intercept the calls. That aside, I assume that methodA is defaulting to REQUIRED, in which case the existing tx may be marked rollback because something went wrong on it at an earlier point. What happens when methodB is marked MANDATORY or REQUIRES_NEW?

            • 3. Re: Problem with transaction propagation across 2 SLSB.

              I am using the local interface to access BeanB (from BeanA). As you could see in the stacktrace, it's a direct call between the two beans (same thread).

              There was no problem with the transaction up to the point where I invoked the BeanB method the second time. I don't see (no exception thrown) why the transaction would be marked for rollback. Is it a problem for BeanB to persist a record which contains a reference (foreign key) to data recorded by BeanA earlier in the (same) transaction?

              I could use MANDATORY or REQUIRED_NEW but then I would have to access BeanB through the business interface which I would rather not do. Well that is until I have no other choice :-(

              Thanks
              -tony