11 Replies Latest reply on Sep 12, 2010 10:00 AM by Leo van den berg

    How to pass error messages returned from an @Asynchronous method to a different component?

    Chun Shin Newbie

      Hi,


      Could someone please tell me how I can display error messages captured in an @Asynchronous method to the front-end?


      I have a stateless event scoped bean which contains an @Asynchronous method like




          @Asynchronous
          @Observer("eventToWatch")
          public void process(final MyBean myBean,
                  final List<MyDto> records) {
      
              final MyFacade facade = new MyFacade(myBean
                      .getClient());
      
              try {
      
                  for (List<MyDto> temp : records) ) {
      
                      facade.process(temp);
      
                  }
      
              } catch (final Exception e) {
      
                  LOGGER.error(e.getMessage(), e);
      
                  // how to return this error - e.getMessage or just e
                  // to a different component, which is in Conversation scope?
      
                  return;
      
              }




      I would like to pass whatever error messages from the asynch method to a component in Conversation scope. How can I achieve this?


      I appreciate your time and help!

        • 1. Re: How to pass error messages returned from an @Asynchronous method to a different component?
          Shervin Asgari Master

          Please next time use 10 seconds to search the documentation before asking a question.


          http://docs.jboss.org

          • 2. Re: How to pass error messages returned from an @Asynchronous method to a different component?
            Chun Shin Newbie

            Thanks Shervin,


            Yes, I've looked at the document before. I don't know if this is right, but, in my case, I just need a message. So, what I did is


            I passed an object when I raised an event and when an error occurs in the asynchronous method, I set appropriate fields like below:



            Events.instance().raiseAsynchronousEvent("eventToWatch", myBean, obj);







                @Asynchronous
                @Observer("eventToWatch")
                public void process(final MyBean myBean,
                        final List<MyDto> records, final MyObjectToCapture obj) {
            
                    final MyFacade facade = new MyFacade(myBean
                            .getClient());
            
                    try {
            
                        for (List<MyDto> temp : records) ) {
            
                            facade.process(temp);
            
                        }
            
                    } catch (final Exception e) {
            
                        LOGGER.error(e.getMessage(), e);
            
                        // how to return this error - e.getMessage or just e
                        // to a different component, which is in Conversation scope
                        obj.setErrorMessage(e.getMessage);
            
                        return;
            
                    }




            • 3. Re: How to pass error messages returned from an @Asynchronous method to a different component?
              Leo van den berg Master

              Hi,


              I am not sure, but I think you want to get a message to the user. This is a bit trickier. For this I use a a4j:push in combination with the Primefaces growl component )although the normal message component should work also)




              <h:form id="growlForm">
              <a4j:region regionRenderOnly="true">
                  <a4j:outputPanel id="growl"   >
                  <p:growl showDetail="true" globalOnly="false" life="#{growlTime != null ? growlTime : 6000}" sticky="#{growlSticky != null ? growlSticky : false}"/>
                  <a4j:poll id="msgEvent" interval="2500" timeout="5000" reRender="growl" limitToList="true"
                      eventsQueue="inputQueue" action="#{pushBean.growl}" />
                  </a4j:outputPanel>
              </a4j:region>
              </h:form>



              the growlSticky and and growlTime beans vars are Session scoped vars which determine if the message sticks on the screen and the other one is the time it is visible if it is not sticky..
                             



              the pushbean is as follows:




              @Name("pushBean")
              @Scope(ScopeType.APPLICATION)
              public class IncidentMessagePushBean {
              
                   @In(required=false) FacesMessages facesMessages;
                   @In(required=false) protected User currentUser;
                   @Logger Log log;
                   
                   protected HashMap<String,PushEventListener> listeners = new HashMap<String, PushEventListener>();
              
                   protected HashMap<String, LinkedList<FacesMessage>> operatorMessages = new HashMap<String, LinkedList<FacesMessage>>();
                   
                   @In(required=false) private Map<String, String> messages;
                   
                   public void addListener(EventListener l) {
                        synchronized(this){
                             listeners.put(currentUser.getUserName(), (PushEventListener) l);
                        }
                        log.info("Added a listener for user " + currentUser.getUserName() + " we now have " + listeners.size() + " listeners." );
                   }
              
                   /** This method catsches all the defined events and sends an update 
                    * request to all the views. Its minimum delay time is 5 seconds to ensure that
                    * the display stays relatively stable.
                    */
                   @Observer(value={"es.esam.im4u.incidentmessage.new", "es.esam.im4u.operator.login"} )
                   public synchronized void processEvent(String o, String h, String d){
                        
                        FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_WARN, messages.get(h), messages.get(d));
                        
                        /** Check if this message if directed to all users at-once */
              
                        if (o != null && o.equalsIgnoreCase("all")){
                             Set<String> keys = operatorMessages.keySet();
                             for (String name: keys){
                                  if (operatorMessages.get(name).size() < 8) operatorMessages.get(name).add(msg);
                                  else {
                                       LinkedList<FacesMessage> list = operatorMessages.get(name);
                                       if(!list.isEmpty())
                                            list.removeFirst();
                                       operatorMessages.get(name).add(msg);
                                  }
                             }
                        } else {
                             if (operatorMessages.get(o) == null ){
                                  LinkedList<FacesMessage> list = new LinkedList<FacesMessage>();
                                  list.add(msg);
                                  operatorMessages.put(o, list);
                             } else {
              
                                  if (operatorMessages.get(o).size() < 8) operatorMessages.get(o).add(msg);
                                       else{
                                            LinkedList<FacesMessage> list = operatorMessages.get(o);
                                            if(!list.isEmpty())
                                                 list.removeFirst();
                                            operatorMessages.get(o).add(msg);
                                       }
                                  
              
                             }
                        }
                        
                        
                   }
                   
                   /** Provides a value which will be used to give a message to the current user */
                   public void growl(){
                        if (currentUser != null && operatorMessages.get(currentUser.getUserName()) != null && operatorMessages.get(currentUser.getUserName()).size() > 0 ){
                             FacesContext context = FacesContext.getCurrentInstance();
                             for (FacesMessage m: operatorMessages.get(currentUser.getUserName())) {
                                  m.setSummary(messages.get(m.getSummary()));
                                  m.setDetail(messages.get(m.getDetail()));
                                  context.addMessage("test", m);
                             }
                             operatorMessages.get(currentUser.getUserName()).clear();
                        }
                   }
                   
              }
              



              What I basically do in the processEvent method is the username (o) and the normal and detail message of the component. I call this meyhod with a simple (async) event and the system does the rest for you.


              Hopefully this helps!


              Leo


              • 4. Re: How to pass error messages returned from an @Asynchronous method to a different component?
                Shervin Asgari Master

                Again this is very easy to do.


                When you look at the link I showed you, you can create your exceptionhandler. In the exceptionhandler you can do whatever you want.


                If you want to send the message to another component, this is very easily done:




                @Override
                
                   public void handleException(Exception exception) {
                
                      //Get the message
                      Writer w = new StringWriter();
                      exception.printStackTrace(w);
                      //Now you send the stacktrace to some other component that listens to this.
                      //Alternatively you can send the entire exception
                      Events.raiseEvent("asynchException",w.toString());      
                
                   }



                • 5. Re: How to pass error messages returned from an @Asynchronous method to a different component?
                  Chun Shin Newbie

                  Leo,


                  Thank you for your example. Is there a replacement of growl in RichFaces? Yes, basically, what I'm trying to do is to display any error messages to users. I'm using


                  <a4j:poll>



                  currently, but I wasn't able to pass errors to a component calling the asynchronous method until I found a way by passing an object.

                  • 6. Re: How to pass error messages returned from an @Asynchronous method to a different component?
                    Leo van den berg Master

                    Hi James,


                    Rich doesn't have a growl-type component. Beceuase of browser compatibility problems I 'm not using PrimeFaces a lot, but growl is an great exception. It works great for warning the user. Another nice component is NotificationBar.


                    The problem wiyj poll is that it executes the action-method, while push just call the listener and only calls the action method when there is really something to do.


                    Leo

                    • 7. Re: How to pass error messages returned from an @Asynchronous method to a different component?
                      Chun Shin Newbie

                      Thanks, Shervin and Leo!


                      I'll try to implement my own exception handler and try to use push.

                      • 8. Re: How to pass error messages returned from an @Asynchronous method to a different component?
                        Serkan Eskici Novice

                        Keep it simple !


                        Pass a List of Exception into your asynchronous method as a parameter and add the exception to that list.


                        Thus:


                        //your calling bean:
                        
                        public class CallingBean {
                           
                          List<Exception> exceptions;
                        
                          public void foo() {  
                            bar.asynchronousCall(..., exceptions);
                            for(Exception e : exceptions) {
                                //do something
                            }
                          }
                        }
                        



                        public class AsyncBean { 
                        
                          @Asynchronous
                          public void asynchronousCall(..., List<Exception> exp) {  
                            try { ... } 
                            catch (Exception e) {
                              ...
                              exp.add(e);
                            }
                          }
                        }
                        

                        • 9. Re: How to pass error messages returned from an @Asynchronous method to a different component?
                          Leo van den berg Master

                          Hi Serkan,


                          Your solution won't work because of two reasons:


                          (a) you will get a NPE, because the List with exceptions is not initialized So you could add:




                          new ArrayList<Exception>();
                          



                          but it still doesn't work because


                          (b)  the call to the async method will return immediately (because the method creates a new Thread ) so after 


                          bar.asynchronousCall(..., exceptions);
                          



                          There is still nothing in the List


                          It is NOT that simple.



                          Leo


                          • 10. Re: How to pass error messages returned from an @Asynchronous method to a different component?
                            Serkan Eskici Novice

                            Leo van den Berg wrote on Sep 09, 2010 05:56:


                            Hi Serkan,

                            Your solution won't work because of two reasons:

                            (a) you will get a NPE, because the List with exceptions is not initialized So you could add:



                            new ArrayList<Exception>();
                            



                            but it still doesn't work because

                            (b)  the call to the async method will return immediately (because the method creates a new Thread ) so after 

                            bar.asynchronousCall(..., exceptions);
                            



                            There is still nothing in the List

                            It is NOT that simple.


                            Leo




                            It is really that simple, because I've done it like that and it works. You simply make a call by reference and why should that not work (even though you pass the list in a seperate thread) ?


                            Although there is one thing that needs extra attention and it has to do with (b).


                            In your CallingBean you need to register those asynchronous calls in a Collection, otherwise you can't keep up with the speed (because they're executed in a seperate thread and it goes too fast).



                            Thus, in code it should look something like this:


                            @Name("callingBean")
                            public class CallingBean {
                               
                              List<Exception> exceptions = ...;
                              Set<Long> calledThreads = ...;
                            
                              public void foo(someUniqueIdForThisRequest) {  
                                if(calledThreads.contains(someUniqueIdForThisRequest))
                                   return;
                                
                                calledThreads.add(someUniqueIdForThisRequest);
                                bar.asynchronousCall(..., exceptions);
                              }
                            
                              public boolean isFinished() {
                                 boolean finished = //place the condition, for example calledThreads.size() == 0; 
                                    
                                 if(finished)
                                    return true;
                                 
                                 synchronized(exceptions) {
                                    for(Exception e : exceptions) {
                                        //do something
                                        //and increase/decrease some counter for the handled threads
                                        //example: calledThreads.remove(someUniqueIdForThisRequest);
                                    } 
                                 }
                                 return finished;
                              }
                            }
                            




                            In your .xhtml file:
                            
                            <a4j:poll action="#{callingBean.foo(someUniqueIdForThisRequest)}" 
                                      enabled="#{not callingBean.calledThreads.contains[someId]}" 
                                      reRender="comp1, comp2, etc."/>
                            
                            <a4j:outputPanel>
                              <s:div id="comp1" rendered="#{callingBean.isFinished()}"> ... </s:div>
                            </a4j:outputPanel>
                            

                            • 11. Re: How to pass error messages returned from an @Asynchronous method to a different component?
                              Leo van den berg Master

                              Hi Serkan,


                              My first reaction was not to respond, because we get into a yes-no discussion on basic jave matters and this degrades the quality of this forum substantially. However I want to respond, because the main function of this forum is to help and share experience.


                              From your reply:



                              In your CallingBean you need to register those asynchronous calls in a Collection, otherwise you can't keep up with the speed (because they're executed in a seperate thread and it goes too fast).


                              The problem with a standard Thread is that is out of your control once fired. There are differences between platforms in the way they handle Threads in their VM.


                              First you have added the responsiblity of using a sort of Async-id to your calling bean and make the view-layer responsible for using it.  So where do you store and retrieve someId?
                              Secondly - as indicated by you - there's the Thread-speed (and priority and exececution order etc.) which still isn't solved. So this approach can fail horribly on other platforms (OS or processorpeed).


                              The nice thing of Seam is getting rid of  all these cross-cutting concerns which don't belong in your business logic. Look at Shervin's solution which is very simple and can be used in combination with ajax-push/poll to render.


                              Leo