2 Replies Latest reply on May 4, 2009 10:04 PM by egoosen

    Do not start long-running conversations in direct calls to EJBs on async call

    egoosen

      I'm getting the above exception after a long running process completes on a Conversation scoped POJO.


      The process gets kicked off from a JSF page. I made it asynchronous because I'm using the a4j:progressBar, which polls this bean.



      1. Please advise me on when/how to start the conversation.

      2. How to handle exceptions thrown from an asynchronous call, currently no error appears to the user. (Added the try/catch now...so will test and see if it displays the error)

      3. I've packaged my application as an EAR running on JBoss-4.2.3GA, do I need to add the @Transcational annotation to the index() method?



      Here's the code


      @Name("indexer")
      @Scope(ScopeType.CONVERSATION)
      public class IndexerBean  {
      
              @Logger
              private Log log;
      
              static final int BATCH_SIZE = 1000;
      
              private boolean enabled = false;
              private boolean buttonRendered = true;
              private float index;
              private long total;
              private long percentage = -1;
      
              @In
              EntityManager entityManager;
      
              @In
          FacesMessages facesMessages;
      
              private Stopwatch stopwatch = new Stopwatch();
      
              @Asynchronous
              public String startProcess(IndexerBean indexerBean){
                      indexerBean.setEnabled(true);
                      indexerBean.setButtonRendered(false);
                      indexerBean.index();
                      return null;
              }
      
              @Begin(join=true)
              public void index() {
                      stopwatch.start();
      
                      Session session = (Session)entityManager.getDelegate();
      
                      session.setFlushMode(FlushMode.MANUAL); //Disable flush operations
                      session.setCacheMode(CacheMode.IGNORE); //Disable 2nd-level cache operations
      
                      final FullTextSession ftSession =
                              org.hibernate.search.Search.createFullTextSession( session );
      
                      indexAllClasses(ftSession, ProductInfo.class);
      
                      stopwatch.stop();
                      setButtonRendered(true);
              }
      
              @SuppressWarnings("unchecked") @End
              private void indexAllClasses(FullTextSession fullTextSession, Class... entityTypes){
                      try {
                              //First calculate the total records to index
                              for (Class entityType : entityTypes){
                                      Query query = entityManager.createQuery("select COUNT(e) from "+entityType.getName()+" e");
                                      total += (Long)query.getSingleResult();
                              }
                              //Perform indexing
                              for (Class entityType : entityTypes){
                                      //read the data from the database
                                      //Scrollable results will avoid loading too many objects in memory
                                      ScrollableResults results = fullTextSession.createCriteria( entityType )
                                      .scroll( ScrollMode.FORWARD_ONLY );
      
                                      while( results.next() ) {
                                              index++;
                                              percentage = (long)((index / total) * 100);
                                              fullTextSession.index( results.get(0) );
                                              if (index % BATCH_SIZE == 0) {
                                                      fullTextSession.clear();
                                              }
                                      }
                              }
                      } catch (Throwable t) {
                              log.error("Exception during indexing operation.", t);
                              facesMessages.add(Severity.ERROR, "Exception during indexing operation. See log for details.");
                      }
              }
      
              public long getPercentage() {
                      return percentage;
              }
      
              public long getRunningTime(){
                      return stopwatch.getRunningTime();
              }
      
              public boolean isEnabled() {
                      return enabled;
              }
      
              public void setEnabled(boolean enabled) {
                      this.enabled = enabled;
              }
      
              public boolean isButtonRendered() {
                      return buttonRendered;
              }
      
              public void setButtonRendered(boolean buttonRendered) {
                      this.buttonRendered = buttonRendered;
              }
      
      }

        • 1. Re: Do not start long-running conversations in direct calls to EJBs on async call
          gonorrhea

          First of all, which component is the EJB?


          @Transactional defaults to REQUIRED, similar to tx support/semantics in EJB3.


          Have you tried moving the @Begin to the startProcess() method (wild guess here).


          If that doesn't work, try to start the LRC via the pages.xml or s:link, if possible (i.e. before the @Asynchronous method call).

          • 2. Re: Do not start long-running conversations in direct calls to EJBs on async call
            egoosen

            I'm not using an EJB, so I'm not sure why I got that error.


            I made some changes which seems to have fixed my problems.


            I removed the @Begin(join=true) annotation from the index() method.
            I'm starting the conversation when the user clicks the Start Process button.


            <s:button action="#{indexer.startProcess(indexer)}" rendered="#{indexer.buttonRendered}" value="Start Process" propagation="begin">
                 <a4j:support event="onclick" eventsQueue="async" requestDelay="200" ignoreDupResponses="true" reRender="progressPanel2" ajaxSingle="true"/>
            </s:button>



            I kind of based my code around this example, but I didn't like the author's approach of starting the conversation with a button click, and then starting the process with a second button click...strange.


            I don't seem to need the @Transactional annotation, since the process worked fine without it.