java - Wicket llamando a una operación larga y actualizando a través de ajax
web-applications web (2)
Sobre la base de esta pregunta SO , he llegado a un acuerdo de que Wicket pone en cola las solicitudes posteriores de AJAX. Ahora mi página está llena de varias solicitudes AJAX y me gustaría agregar una más que genere una operación larga.
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)
}
Una vez que esta solicitud de Ajax se dispare, podría tardar hasta un minuto para que termine de ejecutarse. El problema aquí es que someService.doSomeLengthyOperation()
se ejecutará n times the number of rows
que tengo, lo que significa que haré cola n times two-minutes
. Ahora, como ya he mencionado, Wicket pone en cola las solicitudes posteriores de AJAX.
Lo que sucede es que me lleva number-of-rows * minutes-it-take-to-finish-the-operation
para cargar la página o hacer otras cosas que requieren AJAX como
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()
}
}
Me gustaría evitar crear un servicio web que exponga mi servicio y tenga que escribir mis propias llamadas AJAX. ¿Cuáles son mis opciones? (Usando Wicket 1.5 / Pax-Wicket)
La manera más fácil sería hacer que la solicitud inicial de Ajax vuelva rápidamente (sin ningún resultado) y agregar un AjaxSelfUpdatingTimerBehavior al componente de destino. Este Comportamiento verificará los intervalos (como cada 10 segundos más o menos) si hay un resultado. Si hay un resultado, debe actualizar el componente y eliminarse.
De esta forma puede hacer la operación en una tarea separada sin bloquear sus llamadas Ajax.
Para elaborar un poco, creé un inicio rápido ejecutable que emite 5 llamadas Ajax como las que usted describió, cada una ejecutando una cantidad aleatoria de tiempo entre 10 segundos y un minuto. Al mismo tiempo, hay un AjaxLink receptivo con un contador.
La idea principal es separar las llamadas Ajax reales de las llamadas al método lento.
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();
}
});
Como puede ver, el modelo de etiqueta ya no llama directamente a doSomeLengthyOperation()
. En cambio, se genera un nuevo hilo que hace el trabajo pesado. La clase Processor simplemente implementa la interfaz Runnable y utiliza el método run para hacer el trabajo (en su caso, en la demostración solo espera algo de tiempo).
El getter para PropertyModel encapsula este truco y lo hace transparente mientras siempre regresa rápido para evitar el bloqueo.
public String getResult() {
String retValue;
if (!processed) {
retValue = String.format("Still busy after %d requests", counter++);
} else {
retValue = result;
}
return retValue;
}
El miembro procesado es solo una bandera, que el procesador usa para indicar cuándo está listo (ehr working).
Dado que probablemente emitirá más de 5 Threads al mismo tiempo, recomendaría utilizar algún tipo de Threadpool, pero eso está más allá del alcance de esta pequeña demostración.
Descargo de responsabilidad: este no es un código de producción. Es solo para hacer demostraciones. No será bueno para sus recursos ni manejará la falta de ellos con gracia. No funcionará cuando el usuario golpee la recarga o ocurra cualquier otra cosa.
No estoy del todo seguro de si WicketStuff Async Task podría ayudarlo pero pruébelo:
https://github.com/wicketstuff/core/wiki/Async-tasks
Aquí está la breve demostración que está en el proyecto Async Tasks:
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.
};
}