Wicket calling a lengthy operation and updating through ajax

▼魔方 西西 提交于 2019-12-04 10:39:54

The easiest way would be to have the initial Ajax request return fast (without any results) and add an AjaxSelfUpdatingTimerBehavior to the target component. This Behavior would then check an intervals (like every 10 seconds or so) if there's a result. If there's a result it should update the component ans remove itself.

This way you can do the operation in a separate task without blocking your Ajax calls.

To elaborate a bit, I created a runnable quickstart that issues 5 Ajax calls like you have described, each running a random amount of time between 10 seconds and one minute. At the same time, there is a responsive AjaxLink with a counter.

The main idea is to separate the actual Ajax calls from the calls to the slow method.

add(new ListView<DataHolder>("list", list) {

    @Override
    protected void populateItem(ListItem<DataHolder> item) {
        DataHolder dh = item.getModelObject();
        item.add(new Label("itemNumber", new PropertyModel<Integer>(dh, "number")));
        Label result = new Label("itemResult", new PropertyModel<String>(dh, "result"));
        result.setOutputMarkupId(true);
        result.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(2)));
        item.add(result);
        Thread thread = new Thread(new Processor(item.getModelObject()));
        thread.start();
    }
});

As you can see, the label model doesn't directly call doSomeLengthyOperation() anymore. Instead a new Thread is spawned that does the heavy lifting. The Processor class just implements the Runnable interface and uses the run-method to do the work (in your case, in the demo it just waits some time).

The getter for the PropertyModel encapsulates this stunt and makes it transparent while allways returning fast to prevent blocking.

public String getResult() {
    String retValue;
    if (!processed) {
        retValue = String.format("Still busy after %d requests", counter++);
    } else {
        retValue = result;
    }
    return retValue;
}

The processed member is just a flag, that the Processor uses to indicate whe it's done waiting (ehr working).

Since you'll probably issuing more than 5 Threads at the same time, I'd recommend to use some sort of Threadpool but that's beyond the scope of this little demo.


Disclaimer: This is no production code. It's just for demoing. It will not be nice to your resources nor will it handle any lack thereof gracefully. It will not work when the user hit's reload or anything else happens.

I am not totally sure if WicketStuff Async Task could help you but give it a try:

https://github.com/wicketstuff/core/wiki/Async-tasks

Here is the short demo that is in the Async Tasks project:

public class DemoPage extends WebPage implements IRunnableFactory {

public DemoPage() {

    Form<?> form = new Form<Void>("form");
    AbstractTaskContainer taskContainer = DefaultTaskManager.getInstance()
        .makeContainer(1000L, TimeUnit.MINUTES);
    ProgressButton progressButton = new ProgressButton("button", form, 
        Model.of(taskContainer), this, Duration.milliseconds(500L));
    ProgressBar progressBar = new ProgressBar("bar", progressButton);

    add(form);
    form.add(progressButton);
    form.add(progressBar);
}

@Override
public Runnable getRunnable() {
    return new IProgressObservableRunnable() {
        // Runnable implementation.
    };
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!