13 Replies Latest reply on Sep 25, 2009 1:04 PM by Osian Hughes

    Refreshing Details With a Thread

    Osian Hughes Newbie

      Hi Everyone,


      I have a situation where I need a status page to be updated every few seconds, so I thought the easiest way to do this is to have a thread started when the SeamComponent that drives the page is created.


      I then thought that within the loop in the thread I could simply use the 'Lifecycle.beginCall()' as seen below


           @Override
           public void run() {
                try {
                   Thread.sleep(5000);
              } catch (InterruptedException e1) {
                   e1.printStackTrace();
              }
                    while (keepRunning) {
                     Lifecycle.beginCall();
                     ConsoleView consoleView = ((ConsoleView) Component.getInstance("consoleView"));
                     //perform some updates to the consoleView component
                     SessionRenderer.render(groupName);
                     Lifecycle.endCall();
                     try {
                                 Thread.sleep(2000);
                        } catch (InterruptedException e) {
                             e.printStackTrace();
                        }
                    }
                SessionRenderer.removeCurrentSession(groupName);
           }




      But the object that is returned by Component.getInstance is null.  Any thoughts or ideas either how to implement this behaviour in another way, or how do I get this method working properly?


      Thanks,


      Osh

        • 1. Re: Refreshing Details With a Thread
          Julien Kronegg Novice

          Your Component.getInstance(...) is null because your Thread is not attached to a Seam scope.


          You should have a look to org.jboss.seam.servlet.ContextualHttpServletRequest or to the @Asynchronous annotation.

          • 2. Re: Refreshing Details With a Thread
            Michael Wohlfart Expert

            can I just call




            org.jboss.seam.contexts.Lifecycle.beginCall();



            to attach the Thread to the seam contexts or is this not so brilliant?

            • 3. Re: Refreshing Details With a Thread
              Osian Hughes Newbie

              I've rewritten the thread a little so that it is now a seam component and not a standalone thread:




              @Scope(ScopeType.SESSION)
              @Name("testThreadTesting123")
              public class TestThread implements Runnable {
              
                   private boolean keepRunning = true;
                   private int loopCount = 0;
                   private String groupName;
                   
                   /**
                    * 
                    */
                   /*public TestThread(String groupName) {
                        this.groupName = groupName;
                   }*/
                   
                   @Create
                   public void create() {
                        
                   }
                   
                   @Destroy
                   public void destroy() {
                        
                   }
                   
                   public void setup(String groupName) {
                        this.groupName = groupName;
                   }
              
                   /* (non-Javadoc)
                    * @see java.lang.Runnable#run()
                    */
                   @Override
                   public void run() {
                            while (keepRunning) {
                                Lifecycle.beginCall();
                            ConsoleView consoleView = ((ConsoleView) Component.getInstance("consoleView"));
                            consoleView.setTestString("TestString: " + loopCount++);
                            SessionRenderer.render(groupName);
                            Lifecycle.endCall();
                            try {
                                    Thread.sleep(2000);
                               } catch (InterruptedException e) {
                                    e.printStackTrace();
                               }
                            }
                        SessionRenderer.removeCurrentSession(groupName);
                   }
              
                   /**
                   * @param keepRunning the keepRunning to set
                   */
                  public void setKeepRunning(boolean keepRunning) {
                       this.keepRunning = keepRunning;
                  }
              
              }



              and to instansiate the thread:




              @Name(value = "consoleView")
              @Scope(ScopeType.PAGE)
              public class ConsoleView extends HibernateEntityHome<DataSource> {
              
                   ...
              
                   /**
                    * @param id
                    */
                   @RequestParameter
                   public void setMediationFlowId(Integer id) {
                        if (getId() == null) {
                             //setup some things ....
                             
                             TestThread testThread = (TestThread) Component.getInstance("testThreadTesting123");
                             testThread.setup(getSessionRenderName());
                             (new Thread(testThread)).start();
                        }
                   }
              
                   ...
              }






              Is this what you meant?  I'm still getting a NPE for the consoleView object.


              I've googled ContextualHttpServletRequest, but can't find anything useful, any pointers?


              Thanks.

              • 4. Re: Refreshing Details With a Thread
                Michael Wohlfart Expert

                sorry for the noise, I had some success using the



                 Lifecycle.beginCall();




                also see:
                http://seamframework.org/Community/UseSeamComponentsFromANonseamClass
                (you probably only need to call it once per Thread)


                I didn't really check your code but maybe your component is not yet created,
                maybe



                Component.getInstance("testThreadTesting123", true);




                does the trick? (sorry just wild guessings here)

                • 5. Re: Refreshing Details With a Thread
                  Osian Hughes Newbie

                  Hmmm, I have tried these approaches but I can't seem to get any of them to work, they always say that the


                  ConsoleView consoleView = ((ConsoleView) Component.getInstance("consoleView"))



                  is null. 


                  Does anyone have any sample code where they have a separate thread calling a seam component every n seconds or something similar?

                  • 6. Re: Refreshing Details With a Thread
                    Nikolay Elenkov Master

                    Use @Asynchronous methods instead of threads. That gives you injection and access to request and application scope. Also much easier :)


                    HTH


                    • 7. Re: Refreshing Details With a Thread
                      Osian Hughes Newbie

                      Thanks Nikolay, I'll investigate further down the @Asynchronous route.  Do you know of a good example anywhere? with good sample code?

                      • 8. Re: Refreshing Details With a Thread
                        Nikolay Elenkov Master

                        Seam manual: 22.2. Asynchronicity.


                        There are some examples in the Seam distribution (at least seampay and quartz, IIRC).

                        • 9. Re: Refreshing Details With a Thread
                          Osian Hughes Newbie

                          Well, I've rewritten a couple of bits, and now I have a class which simply has the @Asynchronous method, and it calls it beautifully, but now for the next problem.


                          As you see from the code I posted previously, the ConsoleView component is in scope Page, and therefore the Component.getInstance doesn't seem to pick this up, i.e. it always returns me a null.  If I change this to be conversation scope, it seems that the Component.getInstance is trying to create a brand new instance of the seam component, and not picking up the one that has been cretaed.  Any thoughts or suggestions?


                          Here is the code for the Asynchronous bit:



                          @Name("testThreadTesting123")
                          @AutoCreate
                          public class TestThread {
                          
                               private int loopCount = 0;
                               
                               @Create
                               public void create() {
                                    
                               }
                               
                               @Destroy
                               public void destroy() {
                                    
                               }
                               
                               @Asynchronous
                               public QuartzTriggerHandle testing(@Expiration Date start, @IntervalDuration Long interval) {
                                    Lifecycle.beginCall();
                                    ConsoleView consoleView = ((ConsoleView) Component.getInstance("consoleView"));
                                    consoleView.setTestString("TestString: " + loopCount++);
                                    SessionRenderer.render(consoleView.getSessionRenderName());
                                    Lifecycle.endCall();
                                    return null;
                               }
                          
                          }





                          And here is the code that instantiates it:


                          @Name(value = "consoleView")
                          @Scope(ScopeType.PAGE)
                          public class ConsoleView extends NexusEntityHome<DataSource> implements ActionButtonListener {
                               ...
                               @In
                               private TestThread testThreadTesting123;
                          
                               @Override
                               public void create() {
                                   super.create();
                                   setup();
                                   SessionRenderer.addCurrentSession(getSessionRenderName());
                                   testThreadTesting123.testing(new Date(), new Long(1000));
                               }
                          
                               ...
                          
                          }




                          • 10. Re: Refreshing Details With a Thread
                            Nikolay Elenkov Master

                            Osian Hughes wrote on Sep 24, 2009 13:09:


                            As you see from the code I posted previously, the ConsoleView component is in scope Page, and therefore the Component.getInstance doesn't seem to pick this up, i.e. it always returns me a null.  If I change this to be conversation scope, it seems that the Component.getInstance is trying to create a brand new instance of the seam component, and not picking up the one that has been cretaed.  Any thoughts or suggestions?



                            Well, since this works in a new thread (not the request thread), you don't have access to session/conversation/page contexts. You can
                            only use application and event contexts.



                            Here is the code for the Asynchronous bit:


                            First, you do not need to call Lifecycle, that is done for you by Seam. What exactly are you trying to achieve?
                            If you want to update some page, say every 5 minutes, your best bet is to do your background processing in an
                            async method, and do polling on your page (with <ajax:poll> or some such.


                            HTH

                            • 11. Re: Refreshing Details With a Thread
                              Osian Hughes Newbie

                              Hi Nikolay,


                              The refreh is exactly what I am trying to acheive, but I am using Icefaces, which uses a push from the server to refresh the page, and not the page requesting, hence why I need to make changes, and then push them to the server from a separate thread (or now the asynchronous call).  Therefore I need to access the consoleView component from the Asynchronous call, perform some updates, and then I tell the SessionRenderer (Icefaces push stuff) that changes have occurred, and then the page is updated.


                              I'll take a look at combining Icefaces and Richfaces and see if this helps, if not, any ideas?


                              Thanks

                              • 12. Re: Refreshing Details With a Thread
                                Nikolay Elenkov Master

                                I haven't used Icefaces, so can't help you there really. I don't know how Icefaces push works, but I'm
                                reasonably sure it needs the access to the request thread to make it work (and you cannot get to it from an
                                async method). Does it keep an open http connection  for push (Comet style)?


                                If all else fails, Icefaces probably has a polling component, just
                                use it, instead of putting richfaces in the mix.

                                • 13. Re: Refreshing Details With a Thread
                                  Osian Hughes Newbie

                                  Hi Nikolay,


                                  It is now finally working, I found an example for Icefaces and Seam (JBoss Seam Integration), which allows me to push chagnes to the page, so now, every second I have the page updated as appropriate.


                                  Here is some sample code if anyone else needs something similar:




                                  @Name(value = "consoleView")
                                  @Scope(ScopeType.PAGE)
                                  public class ConsoleView extends NexusEntityHome<DataSource> implements ActionButtonListener, Renderable {
                                  
                                       @In
                                       private RenderManager renderManager;
                                       private PersistentFacesState state;
                                       private IntervalRenderer intervalRenderer;
                                  
                                       ...
                                       @Override
                                       public void create() {
                                           super.create();
                                           setup();
                                           SessionRenderer.addCurrentSession(getSessionRenderName());
                                           state = PersistentFacesState.getInstance();
                                           intervalRenderer = renderManager.getIntervalRenderer("consoleViewRenderer");
                                           intervalRenderer.setInterval(1000);
                                           intervalRenderer.add(this);
                                           intervalRenderer.requestRender();
                                       }
                                       
                                       ...
                                       //Method hooked up to the web page
                                       public String getDataSourceLabel() {
                                            state = PersistentFacesState.getInstance();
                                            performRefresh();
                                            if (getSelectedDataSources().size() == 1) {
                                                 return getSelectedDataSources().firstElement().getFullDisplayString();
                                            }
                                            return getTranslator().get("label.multiple.upstreamsources");
                                       }
                                       
                                       private void performRefresh() {
                                            //Do some stuff
                                            SessionRenderer.render(getSessionRenderName());
                                       }
                                       
                                       ...
                                       @Override
                                       public PersistentFacesState getState() {
                                            return state;
                                       }
                                  
                                       @Override
                                       public void renderingException(RenderingException arg0) {
                                            if (intervalRenderer != null && intervalRenderer.contains(this)) {
                                                 intervalRenderer.remove(this);
                                                 if (intervalRenderer.isEmpty()) {
                                                      intervalRenderer.requestStop();
                                                 }
                                            }
                                       }
                                  }