问题
Based on this SO question, I've come to an understanding that Wicket queues subsequent AJAX requests. Now my page is ridden with several AJAX requests and I'd like to add one more that spawns a lengthy operation.
public void populateItem(final Item item) {
final MyObject object = (MyObject) item.getModelObject();
// ... a couple of fields
Label statusLabel = new Label("status", new AbstractReadOnlyModel() {
@Override
public Object getObject() {
return someService.doSomeLengthyOperation();
}
});
statusLabel.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(5)));
item.add(statusLabel)
}
Once this Ajax request fires, it could take up to one minute for it to finish executing. The problem here is that someService.doSomeLengthyOperation()
will be executed n times the number of rows
that I have, meaning I'll be queueing up n times two-minutes
. Now, as I've mentioned, Wicket queues subsequent AJAX requests.
What happens is it takes me number-of-rows * minutes-it-take-to-finish-the-operation
to load the page or do other stuff that requires AJAX like
new AjaxButton("ajax-button"){
@Override
protected void onSubmit(AjaxRequestTarget target, Form form) {
//.. this won't be executed until all the statusLabels have finished invoking getObject()
}
}
I'd like to avoid creating a web service exposing my service and having to write my own AJAX calls. What are my options? (Using Wicket 1.5 / Pax-Wicket)
回答1:
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.
回答2:
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.
};
}
来源:https://stackoverflow.com/questions/15337198/wicket-calling-a-lengthy-operation-and-updating-through-ajax