1 2 Previous Next 17 Replies Latest reply on Jun 4, 2010 4:19 PM by hemantsaxena

    How can you carry conversation in @Asynchronous call

    hemantsaxena
      I have the following situation where :

      I invoke the asynchronous instance of the following class:

      **************************************
      @Name("asyncRetrieveResultHelper")
      @Scope(ScopeType.CONVERSATION)
      public class AsyncRetrieveResultHelper{

          /** log property */
          @Logger
          Log log;

           @Begin(join = true)
           public void init() {
                test = "left";
           }

           @Asynchronous
           public QuartzTriggerHandle initiateAysncRetrieveResultFor() {
                   // here trying to access long running conversation context instance of ResultsHistoryAction class
              }
      *************************


      from already running long conversation context, for eg below:

      ************************
      @Name("resultsHistoryAction")
      @Scope(ScopeType.CONVERSATION)
      public class ResultsHistoryAction {

           // will be called by UI action button
           public void displayResultsAsync(AdsResultsHeader header) {

                asyncRetrieveResultHelper.init();
                 retrieveStatus.setQuartzHandle(asyncRetrieveResultHelper
                        .initiateAysncRetrieveResultFor(this, header));
           }
           
      ************************

      But somehow I cannot access the conversation context instance of ResultHistoryAction class in AsyncRetrieveResultHelper Asynchronous method. Is it somehow possible to carry the conversation context to Asynchronous call?

      Please help current I am stuck with one of this usecase. Or if this is not possible then last solution would be to go with shared object between these two classes.

      Thanks,

      - Hemant
        • 1. Re: How can you carry conversation in @Asynchronous call
          kapitanpetko

          hemant saxena wrote on Jun 02, 2010 19:05:


          Is it somehow possible to carry the conversation context to Asynchronous call?



          No. Pass the data or a pointer to the data to your async method. Poll for results or use <ajax:push> or similar to notify UI.


          • 2. Re: How can you carry conversation in @Asynchronous call
            serkan.s.eskici.online.nl

            Just remove your asynchronous method to another class and call it from there by passing the data to its method.


            For example:


            @Name("theHandler")
            @Scope(ScopeType.CONVERSATION)
            public class AsyncRetrieveResultHelper {
            
             @Begin(join = true)
             public void init() {
              test = "left";
             }
            
             public void retrieveResults() {
                  QuartzTriggerHandle timer = ((YourAsyncComponent) Component.getInstance(YourAsyncComponent.class)).getResult(data1, data2, etc);
                  //etc. do your stuff
             }
            }
            



            @AutoCreate
            @Name("yourAsyncComponent")
            public class YourAsyncComponent {
                @Logger
                private Log log;
            
                @In
                QuartzTriggerHandle timer;
                
                
                @Asynchronous
                public QuartzTriggerHandle getResult(Object...data, @Expiration etc) {
                    this.log.info("Asynchronous call !");
                    return timer;
                }
            }
            



            Hope this helps.

            • 3. Re: How can you carry conversation in @Asynchronous call
              hemantsaxena
              Hey,

              Thanks Nikolay and Serkan for your help.

              I have few more thoughts floating around:

              1) Is it possible to initiate and join the conversation from where the asynchronous call was initiated by slating the asynchronous class as "conversational" and the asynchronous method marked by @Begin:

                   @Asynchronous
                   @Begin(join = true)
                   public QuartzTriggerHandle initiateAysncRetrieveResultFor() {

              2) or if I know the conversation id, then could i switch to the given conversation context like below:

                   @Asynchronous
                   public QuartzTriggerHandle initiateAysncRetrieveResultFor(String lastConvId) {

                        Manager.instance().switchConversation(lastConvId);
                      }

              Do you think any of this will work? or could be tweaked more to work?

              - Hemant
              • 4. Re: How can you carry conversation in @Asynchronous call
                kapitanpetko

                Serkan Eskici wrote on Jun 03, 2010 09:36:


                Just remove your asynchronous method to another class and call it from there by passing the data to its method.

                For example:



                A couple of problems with this code:



                1. you can use use injection to get the async component

                2. where does the @In QuartzTriggerHandle timer come from? You don't need it, just return null from the async method.


                • 5. Re: How can you carry conversation in @Asynchronous call
                  kapitanpetko

                  hemant saxena wrote on Jun 03, 2010 11:31:


                  I have few more thoughts floating around:



                  No and no. Async components cannot access session or conversation because they live on a different thread. Just accept it :)
                  In general, it is not a very good idea to access conversational/session components (by passing them in), because the session/conversation may expire/be destroyed before your async call is over.


                  HTH


                  • 6. Re: How can you carry conversation in @Asynchronous call
                    cash1981

                    Nikolay Elenkov wrote on Jun 03, 2010 20:54:



                    hemant saxena wrote on Jun 03, 2010 11:31:


                    I have few more thoughts floating around:



                    No and no. Async components cannot access session or conversation because they live on a different thread. Just accept it :)
                    In general, it is not a very good idea to access conversational/session components (by passing them in), because the session/conversation may expire/be destroyed before your async call is over.

                    HTH




                    I think you can work around this issue.
                    This is how we do it.


                    I raise an asynchronous call, and in the method parameter I receive everything I want to store in the different contexts.
                    Like this.




                    //Some class executes this
                    Events.instance().raiseAsynchronousEvent("sendOneTimepassword", user, theOnetimepass);
                    
                    //Another component listens on it
                    @Observer("sendOneTimepassword")
                         //@Asynchronous, we dont need to annotate with @Asynchronous if we are raising the event asynchronously
                         public void sendOneTimepassword(ProcessUser emailUser, String oneTimePassword) {
                              Contexts.getConversationContext().set(OnetimePasswordAction.EMAILUSER, emailUser);
                              Contexts.getConversationContext().set(OnetimePasswordAction.ONTIME_PW, oneTimePassword);
                              renderer.render("/generic/email-template/onetimepassword-email-template.xhtml");
                         }
                    


                    Here I am storing some information in the conversation context so that I can get hold of it later.


                    • 7. Re: How can you carry conversation in @Asynchronous call
                      kapitanpetko

                      Shervin Asgari wrote on Jun 04, 2010 03:13:


                      //Some class executes this
                      Events.instance().raiseAsynchronousEvent("sendOneTimepassword", user, theOnetimepass);
                      
                      //Another component listens on it
                      @Observer("sendOneTimepassword")
                           //@Asynchronous, we dont need to annotate with @Asynchronous if we are raising the event asynchronously
                           public void sendOneTimepassword(ProcessUser emailUser, String oneTimePassword) {
                                Contexts.getConversationContext().set(OnetimePasswordAction.EMAILUSER, emailUser);
                                Contexts.getConversationContext().set(OnetimePasswordAction.ONTIME_PW, oneTimePassword);
                                renderer.render("/generic/email-template/onetimepassword-email-template.xhtml");
                           }
                      


                      Here I am storing some information in the conversation context so that I can get hold of it later.



                      Hm, interesting. Which conversation is this? raiseAsynchronousEvent stars an async job, so the thread running it is not
                      associated with a request/session. How are you getting the data later? What is the handle to your conversation? Btw, for
                      sending email, you can just use event context.


                      • 8. Re: How can you carry conversation in @Asynchronous call
                        serkan.s.eskici.online.nl

                        This is what the Seam manual says, I quote (see chapter 22.2.1. Asynchronous methods):




                        The asynchronous method is processed in a completely new event context and does not have access to the session or conversation context state of the caller. However, the business process context is propagated.


                        So when you insert some objects in the conversation from your async. method, it is passed in a NEW (not long running) conversation.

                        • 9. Re: How can you carry conversation in @Asynchronous call
                          serkan.s.eskici.online.nl

                          Nikolay Elenkov wrote on Jun 03, 2010 20:51:



                          Serkan Eskici wrote on Jun 03, 2010 09:36:


                          Just remove your asynchronous method to another class and call it from there by passing the data to its method.

                          For example:



                          A couple of problems with this code:


                          1. you can use use injection to get the async component

                          2. where does the @In QuartzTriggerHandle timer come from? You don't need it, just return null from the async method.





                          1) I know that you can use @In instead of using Component.getInstance(..) nothing wrong with both. It's just a matter of taste. But if you only make an async. call within 1 method, then prefer Component.getInstance() instead of using @In.


                          2) You can inject the timer and return it back, IF you want to pause, stop or resume the thread. It depends on your use case.

                          • 10. Re: How can you carry conversation in @Asynchronous call
                            kapitanpetko

                            Serkan Eskici wrote on Jun 04, 2010 04:57:


                            1) I know that you can use @In instead of using Component.getInstance(..) nothing wrong with both. It's just a matter of taste. But if you only make an async. call within 1 method, then prefer Component.getInstance() instead of using @In.


                            OK, getInstance is actually a bit better for performance since you skip the interceptor, just looks ugly with all the casts...



                            2) You can inject the timer and return it back, IF you want to pause, stop or resume the thread. It depends on your use case.


                            Where does it come from the first time?

                            • 11. Re: How can you carry conversation in @Asynchronous call
                              cash1981

                              Nikolay Elenkov wrote on Jun 04, 2010 03:20:


                              Hm, interesting. Which conversation is this? raiseAsynchronousEvent stars an async job, so the thread running it is not
                              associated with a request/session. How are you getting the data later? What is the handle to your conversation? Btw, for
                              sending email, you can just use event context.


                              This is the same conversation as the current conversation. However, since you are executing a future task, you are setting the information you want to use in that task in the same thread, so it is accessible. I haven't tested this in a long-running conversation, so unsure if that would work. However, you can use a session context as a workaround, and then later manually remove the objects from the session once it is finished by calling an event




                              @Observer(value = {"cleanupUserInfo"})
                              public void cleanup() {
                                   // Clean up
                                   Contexts.getSessionContext().remove(ONETIME_PW_TIMEOUT_TIMER);
                                   Contexts.getSessionContext().remove(ONETIME_PW_SENT);
                                   Contexts.getSessionContext().remove(THE_ACTUAL_ONETIME_PW);
                                   Contexts.getSessionContext().remove(PW_ATTEMPTS);
                                        
                                   //Cleanup incase its still in conversation context
                                      Contexts.getConversationContext().remove(EMAILUSER);
                                   Contexts.getConversationContext().remove(ONTIME_PW);
                              }





                              I am using this code to send a one time generated password to email. So basically I am just using Seam mail and getting the one time password from the conversation context using normal EL expression. (So basically a normal injection)




                              <m:body>
                                 Your temporary password is: <i>#{oneTimePassword}</i>
                              </m:body>



                              • 12. Re: How can you carry conversation in @Asynchronous call
                                kapitanpetko

                                Shervin Asgari wrote on Jun 04, 2010 05:18:


                                This is the same conversation as the current conversation. However, since you are executing a future task, you are setting the information you want to use in that task in the same thread, so it is accessible. I haven't tested this in a long-running conversation, so unsure if that would work. However, you can use a session context as a workaround, and then later manually remove the objects from the session once it is finished by calling an event



                                So this conversation lasts as long as your async call, isn't that effectively the same as event scope? And as for session, again, which session is that? Certainly not the user who started the task's session. The only way to access the actual session is to pass the HttpSession object into the async method, but again you get no guarantees that it won't expire. There are no clean ways to do this, so your code is better off not relying on accessing the (fake) session/conversation.




                                I am using this code to send a one time generated password to email. So basically I am just using Seam mail and getting the one time password from the conversation context using normal EL expression. (So basically a normal injection)

                                <m:body>
                                   Your temporary password is: <i>#{oneTimePassword}</i>
                                </m:body>






                                Should work just as well with event scope.

                                • 13. Re: How can you carry conversation in @Asynchronous call
                                  cash1981

                                  Nikolay Elenkov wrote on Jun 04, 2010 05:28:


                                  So this conversation lasts as long as your async call, isn't that effectively the same as event scope? And as for session, again, which session is that? Certainly not the user who started the task's session. The only way to access the actual session is to pass the HttpSession object into the async method, but again you get no guarantees that it won't expire. There are no clean ways to do this, so your code is better off not relying on accessing the (fake) session/conversation.

                                  Should work just as well with event scope.



                                  I haven't tested with the event context, but it would probably work as well.
                                  I don't know who's session it is, and frankly I don't care. All I know is that the user that starts the task can get all those objects from the same session. So I am guessing it must be the users session.


                                  Because I am setting calling this listener from one component, that authenticates a user and calls async event to send an email with one time password.
                                  Then user gets redirected to another page where he must type inn the password, and when login, it will use the Authenticator class which injects all the session scoped objects.



                                  @Name("authenticator")
                                  public class Authenticator {
                                       @In
                                       private Identity identity;
                                  
                                       @In
                                       private Credentials credentials;
                                       
                                       @In(scope=ScopeType.SESSION)
                                       private Long oneTimePwdTimeoutTimer;
                                       
                                       @In(scope=ScopeType.SESSION)
                                       private Boolean oneTimePasswordIsSent;
                                  
                                       @In(required = false, value=OnetimePasswordAction.THE_ACTUAL_ONETIME_PW, scope = ScopeType.SESSION)
                                       private String otherOnetimepw;
                                  }



                                  So as you see I can get hold of all the objects here, so it must have somehow be available for the user.

                                  • 14. Re: How can you carry conversation in @Asynchronous call
                                    cash1981

                                    Actually you might be right. Disregard what I said. I actually call a synchronous event to set the objects in session context.

                                    1 2 Previous Next