Dawno temu opisałem jak zintegrować ICE Push z Vaadin z pomocą Guice. Tamto dotyczyło Vaadin 6.x. Dziś to samo tyle, że z Vaadin 7.1.

Wstęp

Gdy pojawiły się pierwsze informacje o nowej „dużej” wersji Vaadin praktycznie od razu zaczęto zastanawiać się nad integracją Vaadin z Server Push. Początkowo miało to nastąpić już w wersji 7.0 jednak z różnych przyczyn przesunięto tą opcję do wersji 7.1. Wersja ta ukazała się parę dni temu zatem czas najwyższy przyjrzeć się nowemu rozwiązaniu.

Podstawy

Po wygenerowaniu projektu za pomocą mavena otrzymamy klasę MyVaadinUI z konfiguracją w stylu Servlets 3.0. Będzie ona zawierać klasę statyczną Servlet:

Listing 1. Klasa Servlet

@WebServlet(value = "/*", asyncSupported = true)
	@VaadinServletConfiguration(productionMode = false, 
		ui = MyVaadinUI.class, 
		widgetset = "pl.koziolekweb.vaadin.push.AppWidgetSet")
	public static class Servlet extends VaadinServlet {
	}

Najważniejszym parametrem dla nas jest asyncSupported, ktory musi mieć wartość true. Jeżeli korzystasz ze starszej wersji serwletów i pliku web.xml musisz dodać ten parametr do konfiguracji serwletu Vaadin.

Kolejnym krokiem jest zaadnotowanie naszej klasy za pomocą @Push. W ten sposób oznaczamy klasę jako wykorzystującą technologię Comet/Server Push. Styknie. Nasza aplikacja jest gotowa do działania.

Zegar

Za klasę zegara posłuży nam prosta klasa rozszerzająca TimerTask:

Listing 2. Klasa ClockTask

class ClockTask extends TimerTask {

	private final MyVaadinUI ui;

	public ClockTask(MyVaadinUI ui) {
		this.ui = ui;
	}

	@Override
	public void run() {
		String message = new Date().toString();
		ui.update(message);
		System.out.println(message);

	}
}

Trochę to bezczelnie zrobione, bo walę bezpośrednio do głównej klasy UI, ale… generalnie polecam dobudować sobie interfejs zgodny z przekonaniami. Tak samo ze sposobem wysyłania komunikatów (o tym następny wpis). Tu prosty przykład.

Uruchomienie

Czas najwyższy poskładać wszystko do kupy. Poniżej metoda init aplikacji. Chyba wszystko jasne 🙂

Listing 3. Metoda init

@Override
	protected void init(VaadinRequest request) {
		final VerticalLayout layout = new VerticalLayout();
		layout.setMargin(true);
		setContent(layout);
		timeLabel = new Label();

		Button start = new Button("Start");
		start.addClickListener(new Button.ClickListener() {
			public void buttonClick(ClickEvent event) {

				TimerTask task = new ClockTask(me());
				timer.schedule(task, 1000, 1000);
			}
		});

		Button stop = new Button("Stop");
		stop.addClickListener(new Button.ClickListener() {
			public void buttonClick(ClickEvent event) {
				timer.cancel();
				timer = new Timer();
			}
		});
		layout.addComponent(start);
		layout.addComponent(stop);
		layout.addComponent(timeLabel);
	}

	public MyVaadinUI me(){
		return this;
	}

Podsumowanie

Jak widać całość jest bardzo prosta. Jest tu jednak kilka drobnych pułapek. Pierwszą z nich jest metoda accept, przyjmująca Runnable, ale nie odpalająca oddzielnego wątku. Niby szczegół, ale znając życie wiele osób automatycznie kojarzy ten interfejs z wątkami. Po drugie jeżeli już korzystamy z wątków to trzeba to robić bardzo ostrożnie ponieważ wątki wewnątrz serwletów zawsze lubiły strzelać fochy.

W sumie jest to fajne rozwiązanie.

Na koniec ciekawostka jak podaje JRebel za pomocą swojego trackera zaoszczędzonego czasu w trakcie pracy nad tym tekstem dzięki użyciu JRebela zaoszczędziłem 51 minut. Jak klikniecie w powyższy link to możecie śledzić moje „oszczędności”.