2 Replies Latest reply on Nov 10, 2010 7:31 AM by Martin Frey

    Observer raiseTransactionSuccessEvent and new transactions?

    Martin Frey Newbie

      Hello all


      I have a little question about an implementation with an observer that needs access to persistency.


      In my application I'd like to send emails with a changelog once an entity changed. First we had that piece of code directly inside the EntityHome update/persist methods after the super call.


      I would say that this is a perfect candidate to use the observer pattern, since in the current implementation the transaction could still fail but the email is sent already.


      This is the behavior i like to have:



      • User updates entity X

      • update method in EntityHome raises calls Events.instance().raiseTransactionSuccessEvent(sendMail, eventObj)

      • Observer gets called and sends email

      • For this the observer needs to fetch the entity and the history



      If we use now the TransactionSuccessEvent i run into this exception:




      ERROR [org.jboss.seam.transaction.SynchronizationRegistry] Exception processing transaction Synchronization after completion
      java.lang.RuntimeException: exception invoking: listen
              at org.jboss.seam.util.Reflections.invokeAndWrap(Reflections.java:154)
              at org.jboss.seam.Component.callComponentMethod(Component.java:2253)
      .
      Caused by: javax.transaction.NotSupportedException: BaseTransaction.checkTransactionState - [com.arjuna.ats.internal.jta.transaction.arjunacore.alreadyassociated] [com.arjuna.ats.internal.jta.transaction.arjunacore.alreadyassociated] thread is already associated with a transaction!
              at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.begin(BaseTransaction.java:79)
              at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.begin(BaseTransactionManagerDelegate.java:77)



      If i change the code to Events.instanct().raiseEvent(..) i don't get this issue because we are running the email sender inside the update transaction, but this leads to the same issue we have as we would code it directly in the update method.


      Should it not be possible to send an event after the transaction completed successfully and in the observer create another transaction to fetch some stuff from the persistenceContext?


      I had some problems using the @Transactional and @Observer annotation together on the same method. Thats why the observer just calls the mailSender.sendMail which has the @Transactional annotation.


      Does anyone have a hint how to solve this?


      Here's the simplified code:



       @Transactional
       public String persist(boolean doAudit, StatusTransitionName transitionName) {
         String result = super.persist(doAudit);
         if (PERSISTED.equals(result)) {
           Events.instance().raiseTransactionSuccessEvent(SendMailEvent.EVENT_TYPE,
               new SendMailEvent(getInstance().getClass(), getInstance().getId()));
         }
         return result;
       }





      @Name("mailSendObserver")
      @Scope(ScopeType.SESSION)
      @AutoCreate
      public class MailSendObserver implements Serializable {
      
        private static final long serialVersionUID = 1L;
      
        @In
        private MailSender mailSender;
      
        @Observer(value = SendMailEvent.EVENT_TYPE, create = true)
        public void listen(SendMailEvent event) {
          mailSender.sendMail(event);
        }
      
      }
      



      @Name("mailSender")
      @Scope(ScopeType.EVENT)
      public class MailSender implements Serializable {
        @Transactional
        public void sendMail(SendMailEvent event) {
          Object obj = editEntityManager.find(event.getEntityClass(), event.getId());
          sendMail(obj);
        }
      }



      Thanks alot.


        • 1. Re: Observer raiseTransactionSuccessEvent and new transactions?
          Leo van den berg Master

          Hi,


          The first observer runs in the cycle of the transactional method, so anything you do there will give you problems. What you do is a second event which fires asunchronously: And with the listener on the send email:


          So:





          @Observer(value = SendMailEvent.EVENT_TYPE, create = true)
            public void listen(SendMailEvent event) {
               events.raiseAsynchronousEvent("YOUREMAILLISTENER", event);
            }
          
          



          And this should be observerd by:




          @Transactional
          @Asynchronous
          @Observer(value = "YOUREMAILLISTENER", create = true)
            public void sendMail(SendMailEvent event) {
              Object obj = editEntityManager.find(event.getEntityClass(), event.getId());
              sendMail(obj);
            }
          



          If you use the Asynchronous annotation, the event chain is terminated ASAP an the email-sender runs in a separate thread.


          Leo




          • 2. Re: Observer raiseTransactionSuccessEvent and new transactions?
            Martin Frey Newbie

            Thanks a lot!


            I did not know that we can mix Asynchronous and Observer. This solves in fact not only my email sending but some other issues i had.


            Martin