Llamadas a multiples Thread pero sin actualización en View

Hola a todos.
Estoy desarrollando una aplicación Vaadin 8.4.2 con Spring, con dashboard, el cual muestra una serie de datos que se recuperan de unas llamadas a WEBs.
Unos de esos WS tarda bastante (unos 4 minutos), por lo que he pensado en crear Threads que llamen a los distintos WS y cuando se reciban los datos se vayan pintando en el Dashboard.

Para ello en la clase UI he puesto es @Push (@Push y @Push(value = PushMode.MANUAL, transport = Transport.WEBSOCKET) )

Y en la vista que muestra los datos:

@Override
public void enter(ViewChangeEvent event) {
	UI.getCurrent().accessSynchronously(new Runnable() {
		@Override
		public void run(){
			getPedidosStatus();
			UI.getCurrent().push();
		}
	});

	UI.getCurrent().accessSynchronously(new Runnable() {
		@Override
		public void run() {
		getHomologaciones();
		   UI.getCurrent().push();
		}
	});

	UI.getCurrent().accessSynchronously(new Runnable(){
		@Override
		public void run(){
			getRFQ();
			UI.getCurrent().push();
		}
	});

	UI.getCurrent().accessSynchronously(new Thread(){
		@Override
		public void run(){
			getSubastas();
			UI.getCurrent().push();
		}
	});

	UI.getCurrent().accessSynchronously(new Runnable(){
		@Override
		public void run(){
			getPedidosOnTime();
			UI.getCurrent().push();
		}
	});

	UI.getCurrent().accessSynchronously(new Runnable(){
		@Override
		public void run(){
			getUserData();
			UI.getCurrent().push();
		}
	});
}

Sindo las llamadas a los WS y actualización del UI lo que hay en el run (por ejemplo):

protected void getRFQ() {
		try {
    		hlRFQ.setVisible(false);
    		rfq = talend.getRFQ();
    		if(rfq.isOK()) {
    			lbRCerrado.setValue(traductor.getTranslation("lbClosed")+": "+rfq.getCerradas()+" ");
    			lbRPendiente.setValue(traductor.getTranslation("pending")+": "+rfq.getPendientes()+" ");
    			lbREnEspera.setValue(traductor.getTranslation("awaiting")+": "+rfq.getEnespera()+" ");
    			lbRTotal.setValue(traductor.getTranslation("totalrfq")+": "+rfq.getTotal()+" ");
    			hlRFQ.setVisible(true);
    		}else {
        		lbInfoRFQ.setValue(traductor.getTranslation("E014"));
        		hlRFQ.setVisible(false);	
        		GeneralUtils.showErrorNotification(traductor.getTranslation("E014")+" (E014)");
        		rfqError = true;
        	}
    	}catch(Exception e) {
    		GeneralUtils.showErrorNotification(traductor.getTranslation("E014")+" (E014)");
    		rfqError = true;
    		lbInfoRFQ.setValue(traductor.getTranslation("E014"));
    		hlRFQ.setVisible(false);
    	}
		UI.getCurrent().markAsDirtyRecursive();
	}

El problema que tengo es que no se muestra la página hasta que no se han terminado de llamar a todos los métodos que están dentro del run() (aunque seguro que se han lanzado en paralelo porque en depuración veo que si que se hace).

¿Alguien sabe que puede estar pasando?, ¿estoy puniendo el código mal? (He probado tanto con Runnable como con Thread…)

Un saludo

Hola.
Leyendo de un sitio y otro he encontrado una solución que os comento (por si le vale a alguien), o por si sabéis de una solución mejor.

Lo primero que he hecho es poner el push en el UI como:

@Push(value = PushMode.MANUAL, transport = Transport.WEBSOCKET)

Luego en la vista he creado tantas clases como hilos necesito:

class PedidosStatusThread implements Runnable{
	       @Override
	       public void run(){
	    	   try {
	    		   getPedidosStatus();
	   			   UI.getCurrent().push();
	    	   }catch(Exception e) {};
	   	}
	};
	
	class HomologacionesThread implements Runnable{
	       @Override
	       public void run(){
	    	   try {
	   	    	   getHomologaciones();
	   	    	   UI.getCurrent().push();
	    	   }catch(Exception e) {};
	   	}
	};
	
	class RFQThread implements Runnable{
	       @Override
	       public void run(){
	    	   try {
	   	    		getRFQ();
	   	    		UI.getCurrent().push();
	    	   }catch(Exception e) {};
	   	}
	};
	class UserDataThread implements Runnable{
	       @Override
	       public void run(){
	    	   try {
	   	    		getUserData();
	   	    		UI.getCurrent().push();
	    	   }catch(Exception e) {};
	   	}
	};
	class PedidosOnTimeThread implements Runnable{
	       @Override
	       public void run(){
	    	   try {
		    		getPedidosOnTime();
		    		UI.getCurrent().push();
	    	   }catch(Exception e) {};
	   	}
	};
	class SubastasThread implements Runnable{
	       @Override
	       public void run(){
	    	   try {
		    		getSubastas();
		    		UI.getCurrent().push();
	    	   }catch(Exception e) {};
	   	}
	};

El código está entre try/catch porque me da exception por el MarkDirty o por el push()

Y luego en el enter:

@Override
	public void enter(ViewChangeEvent event) {
		runAsync(new RFQThread());
		runAsync(new PedidosStatusThread());
		runAsync(new HomologacionesThread());
		runAsync(new UserDataThread());
		runAsync(new PedidosOnTimeThread());
		runAsync(new SubastasThread());
	}

Siendo runAsyn una función que vi en el foro que lo que hace es obtener el UI, asignarlo y lanzar:

public static void runAsync(Runnable runnable) {
        final UI ui = UI.getCurrent();
        final VaadinSession session = VaadinSession.getCurrent();
        if (ui == null || session == null) {
            throw new RuntimeException("UI or Session should not be null at this point!");
        }
        new Thread(() -> {
            UI.setCurrent(ui);
            VaadinSession.setCurrent(session);
            runnable.run();
        }).start();
    }

Con todo esto el funcionamiento es lanzar los thread y entrar en la vista, y conforme se tiene los datos se van pintando en segundo plano.

Un saludo

Hola,

vaadin se ejecuta en único thread hasta lo que entiendo.

UI.getCurrent() no es thread safe, es mejor que setees la instancia de la ui por el constructor de la clase donde tengas que usarla

Usar también

getUI().access(()-> {

});

Siempre cuando tengas q actualizar desde el lado del server al cliente(websockets) de manera asíncrona como es tu caso, usa el método access, para que no se te bloquee tu UI

La clase

CompletableFuture<T>

es muy buenas también para este caso, sino me equivoco con solo poner @Push ya estabas bien.

Otra pregunta, si en medio de una actualización de la aplicación, o sea F5 en la pestaña o tab, tú deseas que se actualizen contantemente esos datos? o tienes algún listener que inicie la actualización de esos datos?

saludos.