10 Replies Latest reply on Aug 20, 2010 10:56 AM by Nikolay Elenkov

    Conversation-scoped component's method annotated both @Destroy and @Asynchronous

    Derek MacDonald Newbie

      How is it possible to have a @Destroy (or @PreDestroy) annotated method run in its own thread? I have a conversation where all changes made to a backing bean are logged and I want to email those changes once the conversation itself ends, either by explicitly pressing a 'Close' button or if end-users navigate to another area of the site and the conversation times out.


      The issue is the email process also attaches a PDF so I don't want this code blocked when navigating to another page because the latency is too long. Annotating a method @Asynchronous overrides @Destroy (or @PreDestroy), so I need to find another way to merge the two concepts.

        • 1. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
          Nikolay Elenkov Master

          Just call another component's async method, passing all the data you need to generate the email.

          • 2. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
            Shervin Asgari Master

            Its strange, but it seems to me that I have something similar. It only seems it is truly asynchronous when I use


            Consider the following code:



            mailService.sendOneTimepassword(user, theOnetimepass);
            Events.instance().raiseAsynchronousEvent("sendOneTimepassword", user, theOnetimepass);
            
            //mailService
            @Observer("sendOneTimepassword")
            @Asynchronous
            public void sendOneTimepassword(ProcessUser emailUser, String oneTimePassword) {
                 Contexts.getEventContext().set(OnetimePasswordAction.EMAILUSER, emailUser);
                 Contexts.getEventContext().set(OnetimePasswordAction.ONTIME_PW, oneTimePassword);
                 renderer.render("/generic/email-template/onetimepassword-email-template.xhtml");
            }



            If I use the raiseAsynchronous method, it doesn't wait for the email to be sent. However, if I use the mailService.sendOneTimepassword it seems like it is synchronous. Any ideas why?

            • 3. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
              Nikolay Elenkov Master

              Yes, raising an async event is another idea that will work. When you call raiseAsynchronousEvent, what happens is that a one-off job that is executed immediately is scheduled. So the observer method is executed in this job's async context. As to why it doesn't work when called directly, I don't know, not enough info. As long as mailService is a Seam component and sendOneTimepassword is called from another component, the call should be intercepted and it should work. Are you calling it from @Destroy? Don't have the code now, but maybe there is some special handling for destroy methods.


              • 4. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
                Shervin Asgari Master

                Nikolay Elenkov wrote on Aug 20, 2010 04:54:


                Yes, raising an async event is another idea that will work. When you call raiseAsynchronousEvent, what happens is that a one-off job that is executed immediately is scheduled. So the observer method is executed in this job's async context. As to why it doesn't work when called directly, I don't know, not enough info. As long as mailService is a Seam component and sendOneTimepassword is called from another component, the call should be intercepted and it should work. Are you calling it from @Destroy? Don't have the code now, but maybe there is some special handling for destroy methods.




                Yes mailService is a Seam Statless EJB component and it is called from another component.
                Maybe it doesn't work because I need to catch Exception (if mail server is not located) and present error message to the user.
                This I think doesn't happen if I use raiseAsynch event. Or maybe I need to use the AsynchronousExceptionHandler.


                How do you solve exceptions?

                • 5. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
                  Nikolay Elenkov Master

                  Should work. Can you show some code? I handle exceptions in the async job (just dump to log), so no messages to user.

                  • 6. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
                    Shervin Asgari Master

                    EmailService




                    /**
                     * This service is responsible for sending emails asynchronously
                     * @author Shervin Asgari
                     *
                     */
                    @Stateless
                    @Local(MailService.class)
                    @Name("mailService")
                    @AutoCreate
                    public class MailServiceImpl implements MailService {
                    
                         @In(create = true)
                         private Renderer renderer;
                         
                         @Observer("sendOneTimepassword")
                         @Asynchronous
                         public void sendOneTimepassword(ProcessUser emailUser, String oneTimePassword) {
                              Contexts.getEventContext().set(OnetimePasswordAction.EMAILUSER, emailUser);
                              Contexts.getEventContext().set(OnetimePasswordAction.ONTIME_PW, oneTimePassword);
                              renderer.render("/generic/email-template/onetimepassword-email-template.xhtml");
                         }
                         
                    }



                    Some other component:
                    I am not sure if the exception is catched here if I am raising asynchronous event


                    try {
                            mailService.sendOneTimepassword(user, theOnetimepass); //This is synchronous for some reason
                            Events.instance().raiseAsynchronousEvent("sendOneTimepassword", user, theOnetimepass); //This is asynchronous
                    } catch(Exception ejbEx) {
                         ejbEx.printStackTrace();
                         statusMessages.add(ejbEx.getCause().getMessage());
                         StringWriter sw = new StringWriter();
                         PrintWriter pw = new PrintWriter(sw);
                         ejbEx.printStackTrace(pw); //Put the stracktrace in StringWriter
                                        
                         Events.instance().raiseAsynchronousEvent(SystemlogListener.EVENT_ADD_DETAILED_SYSTEMLOG, sw.toString());
                         Events.instance().raiseEvent("LoginSupport.sendingOneTimePassFailed");
                         return ProcessHandler.OUTCOME_FAILURE;
                    }




                    • 7. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
                      Shervin Asgari Master

                      I can confirm that the mailService.sendOneTimepassword(user, theOnetimepass); is handled synchronously and the other asyncrhonously.
                      Why, I have no idea...

                      • 8. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
                        Nikolay Elenkov Master

                        Shervin Asgari wrote on Aug 20, 2010 09:38:


                        I can confirm that the mailService.sendOneTimepassword(user, theOnetimepass); is handled synchronously and the other asyncrhonously.
                        Why, I have no idea...


                        Are you actually calling both? If you raise an async event, there will be no exception, as sendOneTimepassword() will be called on another thread. Still don't see why it doesn't work... Who/what is triggering the 'other component'?

                        • 9. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
                          Shervin Asgari Master

                          No of course I am not calling them both :-)
                          I just wrote them there for your convenience.


                          Actually there will be exception. Haven't you seen the AsyncrhonousExceptionHandler in seam?
                          All you need to do is:




                          @Scope(ScopeType.STATELESS)
                          @Name("org.jboss.seam.async.asynchronousExceptionHandler")
                          public class MyAsynchronousExceptionHandler extends AsynchronousExceptionHandler {
                          
                               @Logger
                               private Log log;
                               
                               @In
                               private QuartzTriggerHandle timer;
                          
                               @Override
                               public void handleException(Exception ex) {
                                    StringWriter sw = new StringWriter();
                                    PrintWriter pw = new PrintWriter(sw);
                                    ex.printStackTrace(pw); //Put the stracktrace in StringWriter
                                    
                                    try {
                                         log.warn("Cancelling timer job with name #0", timer.getTrigger().getFullName());
                                         timer.cancel();
                                    } catch (SchedulerException e1) {
                                         log.error("Could not cancel asynchronous job");
                                         e1.printStackTrace();
                                         sw.append("Could not cancel asyncrhonous job " + e1.getMessage());
                                    }
                                    
                                    //Here you can log the exception if you want
                                    
                               }



                           

                          • 10. Re: Conversation-scoped component's method annotated both @Destroy and @Asynchronous
                            Nikolay Elenkov Master

                            Shervin Asgari wrote on Aug 20, 2010 10:19:


                            Actually there will be exception. Haven't you seen the AsyncrhonousExceptionHandler in seam?


                            I meant, you can't catch it with the code you posted.