Document Information

Last modified:
2008/02/09 18:24 by c.boulanger

Simple IFrame/PHP-based progress bar for long-running jsonrpc processes

Just as “normal” http-requests, the json-rpc transport that comes with the qooxdoo library cannot provide very much information on the state of the request. In particular, there is no way for the server to communicate with the client during the request. In “normal” desktop applications, users expect to have some kind of feedback such as progress bars, messages on the state of the operation, etc. In a client-server application based on http, when we have a long running server process (for example, the server is sorting a huge dataset or has to wait for information from a different server), the client has to wait until the response returns, which is often unsatisfactory for a user, since he or she does not know if the request is stalled or just takes very long.

In some rudimentary form, we can emulate server responses and progress bars with an IFrame that is fed with the output of a PHP script, which, in turn, listens for messages from a concurrently running PHP script. There are certainly more elegant ways of doing this, but we use a simple file here for exchanging the messages.

Since this filename has to be unique to each request, we generate a timestamp or random number to use as a filename and transmit it both as part of the URL source of the IFrame and as parameter of the JSONRPC request. Lets look at the javascript source code first:

 
// create window
var windowObj = new qx.ui.window.Window("Server progress");
windowObj.setWidth(400);
windowObj.setHeight(200);
windowObj.setShowMinimize(false);
windowObj.setShowMaximize(false);
 
// create Iframe
var iframeObj = new qx.ui.embed.Iframe(null);
iframeObj.setHeight("100%");
iframeObj.setWidth("100%");
windowObj.add(this.iframeObj);
 
iframeObj.addEventListener("load",function(event){
  // when the document is fully loaded, the window can be closed
  windowObj.close();
});
 
// we need a unique request id as name of message transfer file so that different request do not interfere with each other
var requestId  = "" + (new Date).getTime();
 
// iframe source 
var source = "path/to/monitor.php?"
iframeObj.setSource(source + requestId);
 
// open window and launch request
windowObj.open();
 
// do your JSONRPC request, telling the service method the name of the requestId/message file
var rpc = new ...
 

Then let us have a look at the IFrame source script. The following PHP script “monitor.php” generates a HTML page with positioned DIVs for message, progressbar, and log messages:

<html>
  <head>
    <script type="text/javascript">
      function progress( p )
      {
        document.getElementById('progressbar').style.width="" + p + "%";
      }
      function message (m)
      {
        document.getElementById('message').innerHTML = "<span>" + m + "</span>";
      }
    </script>
  </head>
  <body style="font-family: Arial; font-size: 12px;">
    <div id="message" style="margin:2px;padding:3px;border: 1px solid black;text-align:center;left:0px;right:0px;height:20px">
    </div>
    <div style="margin:2px;padding:1px;border: 1px solid black;left:0px;right:0px;height:20px">
      <div id="progressbar" style="width:0%;height:100%;background-color:blue"></div>
    </div>
    <div id="log" style="margin:2px;padding:3px;border: 1px solid black;overflow:auto;top:40px;left:0px;right:0px;height:90px">
    <?php
    // prevent script timeout 
    set_time_limit(0); 
    // get request id / file name from query string 
    $requestId = str_replace( "..","", $_SERVER["QUERY_STRING"]);
    $socketfile = "path/to/tmpdir/$requestId";
    $length = 0;
    // create socketfile in case the other script hasn't started yet
    touch ( $socketfile );
    
    while ( file_exists($socketfile) )
    {
      $content = @file_get_contents($socketfile);
      $newLength = strlen( $content );
      if ( $newLength > $length )
      {
        echo substr( $content, $length-$newLength );
        flush();
      }
      $length = $newLength;
    }
    sleep(2); // keep the window open for another 2 seconds after the IFrame is loaded
    ?>
    </div>
  </body>
</html>

In the PHP script (it could be any language), we write to the message file:

 
class class_myRpcServiceClass
 
  /**
   * Write to the message file monitored by the above script
   */
  function progress( $i="" )
  {
   $messagefile = $this->_messagefile;
 
    if ( $i === null )
    {
      // delete messagefile on a null value
      @unlink( $messagefile );
      return;
    }
    
    if ( is_numeric($i) )
    {
      $msg = "<script> progress($i);</script>";
    }
    else
    {
      $msg = "<script> message('" . addslashes($i)  . "');</script>";
      $msg .= $i . "<br/>";
    }
    
    // output to log file
    error_log( $msg, 3, $messagefile );
  }
 
  /**
   * service method which needs the ability to send feedback to the 
   * user during long-running processes. You need to pass the name
   * of the message file to this method.
   **/
  function method_myLongRunningMethod( $params )
  {
    // store message file name as object variable so that the progress method has access to it
    $this->_messsagefile = "path/to/tmpdir/" . $params[0]; // request id-filename
    
    // simulate a long-running process
    $this->progress("Starting process"); // string argument - message
    for($i=0; $i<100; $i++)
    {
      $this->progress($i); // numeric argument - progress bar
      if ( $i % 10 == 0 ) $this->progress("$i % completed.");
      sleep(1);
    }          
    $this->progress("Process completed.");
 
    sleep(2); // wait 2 seconds so that the user can see the "Process completed" message 
    
    // delete message file - important! this completes the Iframe request and closes the IFrame
    $this->progress(null);
  }
}

More elaborate solutions could monitor the content of an invisible Iframe document with an interval timer and control qooxdoo widgets this way, for example a real qooxdoo progress bar.

Have fun!

Information

Last modified:
2008/02/09 18:24 by c.boulanger

Account

Not logged in

 
 

Job Offers

To further improve qooxdoo we are seeking javascript developers. Read more...

Rich Ajax Platform (RAP)

RAP uses qooxdoo, Java and the Eclipse development model to build rich web applications. Read more...

qooxdoo Web Toolkit (QWT)

Similar to GWT this framework allows to create impressive qooxdoo applications just using Java. Read more...

Pustefix

Pustefix is a MVC-based web application framework using Java and XML/XSLT. Read more...

 
SourceForge.net Logo

Bad Behavior has blocked 0 potential spam attempts in the last 7 days.