Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring AsyncTask: update jsf view component

I have a long run job must run in background and after it finished I want to update jsf view component.

I used SimpleAsyncTaskExecutor to do the work. It works good but when comming to updating ui then I am getting NullPointerException.

Here is my code

SimpleAsyncTaskExecutor tasks = new SimpleAsyncTaskExecutor(); 
tasks.submitListenable(new Callable<String>() {

    @Override
    public String call() throws Exception {
        //Do long time taking job in approximately 16 seconds
        doTheBigJob();

        //then update view component by it's id
        FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add(myComponentId);
        return "";          
    }       
});

Not: When the time is short (like 2 seconds), no NullPointerException occurs

Thanks in advence.

like image 446
Ismail Sahin Avatar asked Mar 16 '23 21:03

Ismail Sahin


1 Answers

FacesContext.getCurrentInstance() returns null because it tries to get the context from thread local variable. But because the executing thread was not initialized by JSF (which is done by javax.faces.webapp.FacesServlet) but created by executor then the thread local variable is null.

I have no idea why NullPointerException does not occur sometimes. By default SimpleAsyncTaskExecutor creates new thread each time unless you specify a thread pool. When I recreated the example it happened every time. Maybe it does but is not logged properly...

To solve your problem you need to resort to polling. For instance you can use property of backing bean to indicate that job was finished.

@Named("someBean")
@SessionScoped
public class SomeBean {
    private volatile boolean jobDone = false;

    public String execute() {
        SimpleAsyncTaskExecutor tasks = new SimpleAsyncTaskExecutor(); 
        tasks.submitListenable(new Callable<String>() {

            public String call() throws Exception {
                //Do long time taking job in approximately 16 seconds
                doTheBigJob();
                jobDone = true
                return "";          
            }       
        });
        return null;
    }

    public boolean isJobDone() {
        return jobDone;
    }

}

On your page you enter component which is rendered when jobDone==true. For instance:

<h:outputText id="jobDoneText" rendered="#{someBean.jobDone}" value="Job finished"/>

Then using polling and AJAX you update your current page.

In pure JSF the only way to do polling is to use combination of JavaScript and JSF AJAX requests.

Alternatively you can use Primefaces component p:poll to poll for changes.

<p:poll interval="1" update="jobDoneText" />

More about polling in JSF can be found in answers to the following question: JSF, refresh periodically a component with ajax?

like image 112
Dawid Pytel Avatar answered Apr 01 '23 15:04

Dawid Pytel