Follow by Email

Monday, 15 February 2016

Show live progress of a long running task using af:progressIndicator in Oracle ADF

This post is about using af:progressIndicator to show live status of a particular task
af:progressIndicator state is tracked and maintained by it's value property that supports org.apache.myfaces.trinidad.model.BoundedRangeModel
This class has two methods

public abstract long getMaximum() { }
returns Maximum value for Model

public abstract long getValue() { }
returns value for Model (current state)

Now to see live progress on page we have to refresh progressIndicator component periodically and this can be achieved using af:poll component , poll component delivers poll events to server periodically and we can ppr (refresh) progress indicator after a particular interval


So I have created a page and dropped these ADF Faces components
af:button- to start processing (start a thread that loop up to 100 and sleeps for 100 milliseconds to make it long)
af:progressIndicator- to show live progress
af:outputText- to show text percentage completed
af:poll- to refresh progressIndicator after a fixed time interval


Next i have created a bean class that extends BoundedRangeModel and implements Runnable (For thread purpose)
See Managed Bean -

import javax.faces.event.ActionEvent;

import oracle.adf.view.rich.context.AdfFacesContext;

import org.apache.myfaces.trinidad.event.PollEvent;
import org.apache.myfaces.trinidad.model.BoundedRangeModel;

public class ProgressIndicatorBean extends BoundedRangeModel implements Runnable {


    public ProgressIndicatorBean() {
    }
    // An Integer value that presents current state of progress
    int procVal = 0;
    //To set poll interval, on start button click it will start polling
    private int pollInterval = -1;

    //Variable accessors to be used by ADF Faces Components on page
    public void setPollInterval(int pollInterval) {
        this.pollInterval = pollInterval;
    }

    public int getPollInterval() {
        return pollInterval;
    }

    public int getProcVal() {
        return procVal;
    }

    public void setProcVal(int procVal) {
        this.procVal = procVal;
    }

    @Override
    public long getMaximum() {
        return 100;
    }

    @Override
    public long getValue() {
        return procVal;
    }

    @Override
    public void run() {
        // Loop up to Maximum value of Progress Model
        while (procVal < getMaximum()) {
            try {
                //Sleeps for 100ms to make it long
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Increase current status of progress model
            procVal++;
        }
    }

    /**Method to process Action Event, starts polling and a thread that makes processing long
     * @param actionEvent
     */
    public void processValueAction(ActionEvent actionEvent) {
        //Set poll interval to postive value to start polling
        pollInterval = 100;
        //Set initial progrss to zero
        procVal = 0;
        //Start a thread
        Thread t = new Thread(this);
        t.start();
    }


    /**Poll Listener to stop poll (set poll interval to -1) once task is done
     * @param pollEvent
     */
    public void pollListnerEvt(PollEvent pollEvent) {
        //Check if processing is complete
        if (procVal == getMaximum()) {
            //Setting poll interval to negative value to stop polling
            pollInterval = -1;
            //Refresh poll component
            AdfFacesContext.getCurrentInstance().addPartialTarget(pollEvent.getComponent());
        }
    }
}

Now see page XML source - (How this bean is mapped on page)


<af:panelGroupLayout id="pgl1" layout="vertical" halign="center">
                    <af:button text="Start" id="b1"
                               actionListener="#{viewScope.ProgressIndicatorBean.processValueAction}"/>
                    <af:spacer width="10" height="10" id="s1"/>
                    <af:poll id="p1" interval="#{viewScope.ProgressIndicatorBean.pollInterval}" partialTriggers="b1"
                             clientComponent="true" timeout="10000"
                             pollListener="#{viewScope.ProgressIndicatorBean.pollListnerEvt}"/>
                    <af:progressIndicator id="pi1" value="#{viewScope.ProgressIndicatorBean}" partialTriggers="p1"/>
                    <af:outputText value="Proceesing.. #{viewScope.ProgressIndicatorBean.procVal}% Done" id="ot1"
                                   partialTriggers="p1" inlineStyle="color:blue;font-weight:bold;"/>
                </af:panelGroupLayout>


All done :) Page looks like this

 Run and check application, Click on start button

Sample ADF Application (Jdeveloper 12.1.3) -Download
Cheers :) Happy Learning

7 comments :

  1. Good work , but how can i use it for upload process like uploading file to the server , where and how can i use loop to change progress indicator

    ReplyDelete
  2. Hi , On clicking on start if I am calling some operation binding poll interval is not getting executed . why its not triggering the progress indicator. Do you have any idea about this or how to overcome this issue.

    ReplyDelete
    Replies
    1. It should not be like that, Which Jdev version are you using ?

      Delete
  3. 12..2.1.0 version.

    Pls refer this question on OTN forum: https://community.oracle.com/thread/4042928

    ReplyDelete
  4. Hi Ashish,


    If we add below piece of code inside while loop then this application breaks.

    BindingContext bctx = BindingContext.getCurrent();
    BindingContainer bindings = bctx.getCurrentBindingsEntry();

    Itseems UI thread can't be run inside this thread. If this is the case then how to execute operation binding inside thread enviroment.

    ReplyDelete
  5. And what task you are trying to perform ?
    Try adding your code in poll listener and check what happens?

    Ashish

    ReplyDelete