ADF Faces: Handle task in background process and show real time progress indicator for asynchronous job using Server Push in ADF

Recently I received an email from Peru. An ADF developer from Peru was facing a challenge with ADF. In short: ‘the upload of a (large) file should be followed by a potentially long running job. Ideally, the browser would not freeze while the uploaded file is processed and on top of that it would be great to report the progress of the job to the user’.

I like this kind of challenge, especially since I consider both asynchronous processing and server push two of my areas of interest. So I took on the challenge and tried a quickly put together an application that demonstrates this behavior.

This article discusses how I used standard Java concurrency functionality to take the job off line (in a scheduled, background job) and how I leveraged Active Data Service in ADF Faces to have the background job report its progress through an active bean and server push to the browser.

After the user kicks off the job by pushing a button:

Image

the user will be in control again (synchronous but background parrtial request completes) and and will also be informed on the job’s progress through the server push:

Image

In this example, the job progress in steps of 10% that take between 2 and 4 seconds. As soon as a step is completed, the client is updated and the user thus informed.

The outline of the solution can be described as follows:

when the user pushes a button, an action listener in a (request scope) managed bean is triggered (in a partial page request, although that does not really matter). This beans spawns a second thread that will do the background processing of the job.

ADF Faces: Handle task in background process and show real time progress indicator for asynchronous job using Server Push in ADF 223A2DA2 061C 4F3F 8F4C CA72EE60F7920

The actionListener is implemented like this:

public void runBigJob(ActionEvent ae) {
  // start job in parallel thread
  // (the activeBean is available as the object to inform of the progress of the big job)
  ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
  ses.schedule
  ( this
   , 3 // let's wait one second before starting the job
   , TimeUnit.SECONDS
    );
  // then complete the synchronous request

}

The managed bean jobCoordinator is injected with a reference to the activeBean. This activeBean implements the BaseActiveDataModel – a class dictated by ADF Active Data Service. Values passed to this bean can be pushed to the client.

The bean configuration from the faces-config.xml file:

<managed-bean>
  <managed-bean-name>activeBean</managed-bean-name>
  <managed-bean-class>nl.amis.hrm.view.ActiveBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
  <managed-bean-name>jobCoordinator</managed-bean-name>
  <managed-bean-class>nl.amis.hrm.view.LongRunningJobCoordinator</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <managed-property>
    <property-name>activeBean</property-name>
    <property-class>nl.amis.hrm.view.ActiveBean</property-class>
    <value>#{activeBean}</value>
  </managed-property>
</managed-bean>

The reference to the activeBean is passed from the jobCoordinator to the second thread that is scheduled to process the job in the background. This thread will be able to directly update activeBean (and indirectly push to the client).

Here is the Java code that represents the background job and informs the activeBean:

public void run() {
  activeBean.triggerDataUpdate("Start - 0 %");
  // normally you would do the real work such as processing the big file here
  for (int i=0;i<10;i++) { // sleep between 2 and 4 seconds
          try {
              Thread.sleep(((Double)((2+2* Math.random())*  1000)).longValue());
          } catch (InterruptedException e) {
          }
          activeBean.triggerDataUpdate((i+1)*10+" %");
  }
  activeBean.triggerDataUpdate("Job Done - 100 %");
}

ADF Faces: Handle task in background process and show real time progress indicator for asynchronous job using Server Push in ADF D4C26DCC A181 46CB BFBD CFFC59946D151

When the thread has been scheduled, the synchronous HTTP request cycle that started with the button push in the browser is now complete. The user can continue to work ; the job is in progress (or soon will be) in a separate thread in the Application Server’s JVM. This thread has its own reference to the activeBean and can inform this bean of the progress it makes on the job.

ADF Faces: Handle task in background process and show real time progress indicator for asynchronous job using Server Push in ADF E1DC5435 1E74 4B18 BDE8 B6428E858C0D2

The activeBean is referenced from an activeOutputText component.

<af:activeOutputText id="jobStatus"
                     value="#{activeBean.state}"
                     inlineStyle="color:brown;font-size:18px;font-weight:bold;text-align:center;">
  <af:clientListener type="propertyChange"
                     method="activeDataCallback"/>
</af:activeOutputText>

Note: the clientListener is triggered whenever a new value is pushed to the activeOutputText. It invokes a JavaScript function that uses the pushed value to update various client side components. That is why the progress is reported in various locations in the client.

Because the activeBean implements the ActiveDataModel that allows for push, the component can receive push messages from the bean.

public class ActiveBean extends BaseActiveDataMode
    @PostConstruct
    public void setupActiveData() {
        ActiveModelContext context =
            ActiveModelContext.getActiveModelConte
        Object[] keyPath = new String[0];
        context.addActiveModelInfo(this, keyPath,

    }

    public void triggerDataUpdate(String message)
        counter.incrementAndGet();

        ActiveDataUpdateEvent event =
            ActiveDataEventUtil.buildActiveDataUpd
            ( ActiveDataEntry.ChangeType.UPDATE
            , counter.get ()
            , new String[0], null
            , new String[] { "state" }
            , new Object[] { message }
            );

        fireActiveDataUpdate(event);
    }
   .....

ADF Faces: Handle task in background process and show real time progress indicator for asynchronous job using Server Push in ADF 88696183 63F1 40A2 AAEB 1DD0E7D0DAE93

Any update sent from the background thread to the activeBean is passed onwards to the activeOutputText component.

When the job is complete, a final update is sent to the activeBean, just before the background process completes. This final message is also passed to the browser through the Push mechanism.

ADF Faces: Handle task in background process and show real time progress indicator for asynchronous job using Server Push in ADF 4D33C22A A153 46FF 94C0 1FE3D49591D14

The page looks as follows after the job has completed. Remember: after pressing the button to start the job, the user did not have to do anything in order to get the status updates pushed to the browser.

ADF Faces: Handle task in background process and show real time progress indicator for asynchronous job using Server Push in ADF D5A499B7 E0D1 4787 AD04 1E12B5C901705

Resources

Download JDeveloper 11.1.1.4 application with this server push example: ProgressIndicator.

7 Comments

  1. Adi February 22, 2012
  2. Lucas Jellema February 15, 2012
  3. Adi February 14, 2012
    • durga December 17, 2014
  4. Kiran Sankpal January 4, 2012
  5. Xiandong October 24, 2011
  6. Emiel Paasschens October 21, 2011