3 Replies Latest reply on Jun 20, 2012 6:18 AM by bevor

    Chrome/Opera/IE7 ignore a4j:push event when modal rich:popupPanel emerges

    bevor

      I have a rich:dataTable where you can export all data by a download button to a csv file. If download has been clicked, a modal processing dialog appears as long as the download is in progress. The only method to let the processing dialog disappear is to use a push event from backing bean which informs the view to hide the processing panel. This looks like this...

       

      xhtml page 

      <h:commandLink action="#{customerListController.download()}" style="float: right" onclick="#{rich:component('exportProcessPanel')}.show(); return true;"> 
           <h:graphicImage value="resources/images/page_excel.png" />
      </h:commandLink>
      <a4j:push address="pushCustomer" onerror="alert(event.rf.data)" ondataavailable="#{rich:component('exportProcessPanel')}.hide();" />
      <rich:popupPanel id="exportProcessPanel" autosized="true" modal="true" moveable="true" resizeable="false">
           <h:panelGrid columns="2"> <h:graphicImage value="resources/images/loading.gif" />
           <h:outputText value="Collecting data... (please be patient)" style="margin-left:10px" />
      </rich:popupPanel>

      important part of the backing bean (customerListController) 

      @Inject 
      @Push(topic = "pushCustomer")
      Event<String> pushEvent;  
      .
      .
      .

      public void download() {
           download("Customer"); 
           pushEvent.fire("");
      }

      download method (in the bean's base class)

      public void download(String reportName) { 
           FacesContext faces = FacesContext.getCurrentInstance();
           HttpServletResponse response = (HttpServletResponse)faces.getExternalContext().getResponse();
           response.setContentType("application/force-download");
           response.setHeader("Content-disposition", "inline; filename=\"" + reportName + "Report-" + new Date() + ".csv\""); 
           try {
               ServletOutputStream os = response.getOutputStream();
               byte[] byteOrderMark = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};
               os.write(byteOrderMark); OutputStreamWriter osw = new OutputStreamWriter(os);
               exportHeader(osw, reportName);
               dataModel.exportData(osw); 
               osw.append("\n\n");
               osw.flush();
           } catch (IOException e)
           {
           } 
          
           faces.responseComplete();
      }

      used web.xml settings for pushing....

      <context-param>
           <param-name>org.richfaces.push.handlerMapping</param-name>
           <param-value>/__richfaces_push</param-value>
      </context-param> 

      <!-- if we don't set this, JBoss runs continuously into and endless loop of Exceptions that the servlets doesn't support async (although this should be fixed since 7.0.2) -->
      <context-param>
           <param-name>org.atmosphere.useBlocking</param-name>
          <param-value>true</param-value>
      </context-param>

      <servlet>
         <servlet-name>Push Servlet</servlet-name>
         <servlet-class>org.richfaces.webapp.PushServlet</servlet-class>
         <load-on-startup>1</load-on-startup>
         <async-supported>true</async-supported>
      </servlet>

      <servlet-mapping>
         <servlet-name>Push Servlet</servlet-name>
         <url-pattern>/__richfaces_push</url-pattern>
      </servlet-mapping>

      All this works perfect, rich:component('exportProcessPanel')}.hide(); will be executed only on Firefox and Internet Explorer 8. Other browsers completely ignore it, so the process dialog never disappear there. To be more specific: As long as the modal dialog appears, those browsers don't receive any push events. Maybe this has something to do with faces.responseComplete().
      The backing beans always fires the push event when download is finished, even in Chrome/Opera,..., so it's not a problem of bean invocation, but obviously a render problem. I'm not sure how to solve that. There is (due to faces.responseComplete() in download method) no possibility to invoke some kind of redirect/refresh of the view. That's why I solved that with a push event, which unfortunately doesn't work in some browsers.

       

      Any ideas? 

       

      I'm using Richfaces 4.2 with Jboss 7.1.1.

       

      By the way: If you know a better solution than using a push event, I would be glad to hear it.

        • 1. Re: Chrome/Opera/IE7 ignore a4j:push event when modal rich:popupPanel emerges
          lfryc

          Hi bevor,

           

          this is interesting use-case.

           

          You need to block user from doing any actions before the download finishes, right?

           

          ----

           

          As for your comment in web.xml - useBlocking:

          Have you read https://community.jboss.org/wiki/StartingWithRichFacesPushOnVariousServletContainers ?

          Did you make sure you don't declare web.xml version 2.5?

           

          ----

           

          For the top-level issue, why Push event isn't executed?

           

          Have you checked that Push works for you in simpler case on those browsers?

          • 2. Re: Chrome/Opera/IE7 ignore a4j:push event when modal rich:popupPanel emerges
            bevor

            You need to block user from doing any actions before the download finishes, right?

            Yes, that's true. I have an open mind about other solutions.

             

            Have you read https://community.jboss.org/wiki/StartingWithRichFacesPushOnVariousServletContainers ?

            Yes, that's where I've got those settings.

             

            Did you make sure you don't declare web.xml version 2.5?

            The header of my web.xml looks like this.

             

            <?xml version="1.0" encoding="UTF-8"?>

            <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

            Do I need any additional settings?

             

            For the top-level issue, why Push event isn't executed?

            Have you checked that Push works for you in simpler case on those browsers?

            Yes, push event generally works (at least with Chrome, other browser are unchecked yet). That's why I think this problem is related to  faces.responseComplete();

            Maybe this breaks somethings? But it's interesting that it works in FF and IE8.

            • 3. Re: Chrome/Opera/IE7 ignore a4j:push event when modal rich:popupPanel emerges
              bevor

              Obviously it is not possible to find a working solution using a processing dialog due to HTTP / backing bean invocation issues, but I found another solution which works pretty good (although <h:commandLink> in a dataTable still causes crashes). This solution causes a latch for all buttons in the view, so if a download is in progress and I press any buttons, this request will be discarded.

               

              In my download method at the beginning I add a flag to the current session:

               

              HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(false);
              session.setAttribute("DOWNLOAD_IN_PROGRESS", "true");
              

               

              At the end of the download method I set this flag to false...

               

              session.setAttribute("DOWNLOAD_IN_PROGRESS", "false");
              

               

              In a phase listener (which has been added to faces-config) I check if there is currently a download in progress. If so, discard the request.

              @Named
              @Logged
              public class DownloadLatchPhaseListener implements PhaseListener
              {
                  private static final long serialVersionUID = -8603272654541248512L;
              
                  @Override
                  public void beforePhase(PhaseEvent event)
                  {
                      HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
              
                      if (session != null && "true".equals(session.getAttribute("DOWNLOAD_IN_PROGRESS")))
                      {
                          FacesContext.getCurrentInstance().responseComplete();
                      }
                  }
              
                  @Override
                  public void afterPhase(PhaseEvent event)
                  {
                      // not used
                  }
              
                  @Override
                  public PhaseId getPhaseId()
                  {
                      return PhaseId.RESTORE_VIEW;
                  }
              }