5 Replies Latest reply on Mar 24, 2009 10:13 AM by lance byrd

    transaction propagation

    Arbi Sookazian Master

      for the following SFSB:

      @Stateful
      @Name("testTransactionsAction")
      @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
      public class TestTransactionsAction implements TestTransactionsLocal
      {
      
       @Logger
       private Log log;
      
       @In
       StringUtils stringUtils;
      
       @PersistenceContext
       private EntityManager entityManager;
      
       private String serialNumbers;
      
       private String serialNumber;
      
       /*--------------------------------------------------------------------------BEGIN METHODS-------------------------------------------------------------------*/
      
       public void searchSerialNumbers()
       {
       //parse serial numbers from HtmlInputTextarea control....
       List<String> serialNumberList = parseSerialNumber();
      
       if (serialNumberList != null && serialNumberList.size() > 0)
       {
       for (String serialNumber : serialNumberList) //persist records one serialNumber at a time...
       {
       this.persistA();
       String[] sArray = new String[4];
       //cause IndexOutOfBoundsException to see if the first insert will commit or not....
       String s = sArray[10];
       this.persistB();
       }
       }
       }
      
       @TransactionAttribute(TransactionAttributeType.REQUIRED)
       public void persistA()
       {
       TestTransactions testTransactions = new TestTransactions();
       testTransactions.setSerialNumber(serialNumber);
       testTransactions.setAddedDate(new Date());
       entityManager.persist(testTransactions);
       //entityManager.flush();
       }
      
       @TransactionAttribute(TransactionAttributeType.REQUIRED)
       public void persistB()
       {
       TestTransactions testTransactions = new TestTransactions();
       testTransactions.setSerialNumber(serialNumber);
       testTransactions.setAddedDate(new Date());
       entityManager.persist(testTransactions);
       //entityManager.flush();
       }
      
       @Remove @Destroy
       public void destroy() {}
      
       public String getSerialNumbers()
       {
       return serialNumbers;
       }
      
       public void setSerialNumbers(String serialNumbers)
       {
       this.serialNumbers = serialNumbers;
       }
      
       private List<String> parseSerialNumber()
       {
       //parse serialNumber assuming that the regex to use is CR (carriage return), which will be scan gun post-fire append <ENTER>
       List<String> serialNumberList = stringUtils.splitAndTrimStringAsArray(serialNumbers, "\\r");
      
       //testing: delete this later...
       if (serialNumberList != null)
       {
       for(String serialNumber : serialNumberList)
       {
       log.info("parseSerialNumber(): serialNumber = "+serialNumber);
       }
       }
       return serialNumberList;
       }
      
      }
      


      I'm getting this exception in persistA() method. Why does this happen? What are the rules for tx propagation in JBoss/EJB3? I was expecting there to be a tx active in persistA() due to the REQUIRED tx type. The goal here is to persist the record prior to the exception being thrown and thus not rollback any tx (which should not exist for the searchSerialNumbers() method as the tx type is marked NOT_SUPPORTED for the class which should default to auto commitmode, as per pg. 512 of Bauer/King book. It doesn't matter if I use private or public visibility for the persistX() methods, same result. No records are inserted to the db table either way. thx.

      Caused by: javax.persistence.TransactionRequiredException: EntityManager must be access within a transaction
       at org.jboss.ejb3.entity.ManagedEntityManagerFactory.verifyInTx(ManagedEntityManagerFactory.java:156)
       at org.jboss.ejb3.entity.TransactionScopedEntityManager.persist(TransactionScopedEntityManager.java:189)
       at org.jboss.seam.persistence.EntityManagerProxy.persist(EntityManagerProxy.java:135)
       at com.cox.bets.session.TestTransactionsAction.persistA(TestTransactionsAction.java:86)
       at com.cox.bets.session.TestTransactionsAction.searchSerialNumbers(TestTransactionsAction.java:61)


        • 1. Re: transaction propagation
          Arbi Sookazian Master

          are the tx propagation rules different if foo() calls a method in the same class (e.g. SFSB) as compared to calling a method remotely in a different JVM?

          is there a good resource for this info? I looked at JSR220-core and there wasn't much there except in the case of distributed tx's (remote calls).

          I'm concerned with merely one SFSB case and tx propagation (if any?) for the methods in that class only.

          • 2. Re: transaction propagation
            lance byrd Newbie

            similar issue for me and same exception. the difference for me is that i don't have a class-wide transaction attribute set. just one method calling another. caller does not support transactions and the called method requires a new transaction.

            my methods:

             @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
             public void persistInt(SimpleInt myInt) {
             persistIntImplementation(getUser(), myInt);
             }
            
            
             @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
             private void persistIntImplementation(User user, SimpleInt myInt) {
             myInt.setValue(myInt.getValue() + 1);
             em.persist(myInt);
             }
            


            • 3. Re: transaction propagation
              Arbi Sookazian Master

               

              "semanticLance" wrote:
              similar issue for me and same exception. the difference for me is that i don't have a class-wide transaction attribute set. just one method calling another. caller does not support transactions and the called method requires a new transaction.

              my methods:

               @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
               public void persistInt(SimpleInt myInt) {
               persistIntImplementation(getUser(), myInt);
               }
              
              
               @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
               private void persistIntImplementation(User user, SimpleInt myInt) {
               myInt.setValue(myInt.getValue() + 1);
               em.persist(myInt);
               }
              


              this case is the same issue I posted and solved here:
              http://www.seamframework.org/Community/EJB3AndSeamNonatomicTransactionsWithLoopScenario

              bottom line: you can't use @TransactionAttribute annotation on private methods in a session bean. It just ignores it unfortunately which is what took me so long to figure this limitation out until I read JSR220.

              • 4. Re: transaction propagation
                Arbi Sookazian Master

                here is a reply from Ken Saks, spec lead of JSR 318:


                On Mar 23, 2009, at 4:26 PM, Arbi.Sookazian@cox.com wrote:


                Hello,

                I would like to draw attention to this specific part of JSR 318, proposed final draft dated Feb 24, 2009, which is the same as in JSR 220, section 13.3.7.1:

                Specifying the TransactionAttribute annotation on the bean class means that it applies to all applicable business interface methods of the class.



                In EJB 3.1 (or later), I'm wondering if it will be possible to demarcate transaction attribute value (e.g. REQUIRED, REQUIRES_NEW, etc.) on private, protected, or package-private in a SFSB or SLSB (i.e. any method with less than public visibility). The current behavior in EJB 3.0 seems to be that the EJB container (in my case JBoss AS) ignores the transaction demarcation annotation on a private method and there is no warning from Eclipse, or exception/warning during deployment or runtime exception.

                I ran into this problem recently and it took me over *two days* to determine the root cause of the behavior in terms of transaction semantics and CMT in my SFSB. My workaround to this problem was to refactor the private method to a public method defined in the local interface implemented by a new SFSB. The REQUIRES_NEW transaction demarcation for the public method in the new SFSB is now honored by the ejb container.

                Why is it necessary to do this? We should be able to demarcate non-local interface methods or non-remote interface methods with transaction demarcation when using CMT.



                Hi Arbi,


                The behavior you're seeing is not specific to transaction attributes. All the special semantics associated with an EJB component invocation (method authorization, container exception handling, threading guarantees, container-managed transactions, etc.) only apply to invocations made through an EJB reference. When code already running within a business method invocation calls another method on the same class through the "this" pointer, the EJB container isn't involved. Such a method invocation is just a plain Java SE method call.


                If you want to invoke a business method on your bean from another business method on the same bean, you'll need to acquire an EJB reference to yourself. The easiest way is to call SessionContext.getBusinessObject().




                TestTransactionsLocal ejbRefToMyself = sessionCtx.getBusinessObject(TestTransactionsLocal.class)
                ejbRefToMyself.otherMethod();


                Regards,


                Ken


                • 5. Re: transaction propagation
                  lance byrd Newbie

                  thx! the combination of not putting annotations on private methods and making "same ejb" calls through locals or remotes seems to have done it :)