Vaadin + Guice + JSR-330, czyli jak nie przespałem nocy

Miało być miło, bo integracja Vaadin z Guice jest nawet opisana na wiki Vaadin… tyle tylko, że nie działa, bo nie ma prawa działać. Dlaczego, bo żaden kontener serwletów nie potrafi utworzyć instancji serwletu jeżeli ten ma konstruktor z parametrem, a nie ma bezparametrowego. Cóż… u mnie nie działa zatem trzeba znaleźć obejście.
Na początek dwa słowa o Guice i JSR-330. Pierwsze to jest framework DI dostarczony przez Google. Drugie to standard DI dla Java. Generalnie pierwsze implementuje drugie. Przy czym ważna rzecz JSR nie mówi jak ma odbywać się samo wstrzykiwanie zależność, a tylko z jakich adnotacji i interfejsów należy korzystać. Co ciekawe po połączeniu tego JSRa z Guice to i tak całość nie działa… Zatem z JSR-330 zrezygnowałem.

Konfiguracja Guice i własny serwlet

Zaczniemy od napisania własnego serwletu obsługującego Vaadin+Guice. Jest to rozszerzenie AbstractApplicationServlet, który „odwala” całą robotę związaną z zarządzaniem aplikacją i istnieniem w ramach sesji. Rozszerzenie nie jest skomplikowane ponieważ wystarczy, że implementuje dwie metody. Pierwszą, która zwróci nową instancję aplikacji i drugą, która zwróci obiekt Class, który jest używany do dupereli typu klasy CSS.

Listing 1. GuiceApplicationServlet

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;

import com.vaadin.Application;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;

@SuppressWarnings("serial")
@Singleton
public class GuiceApplicationServlet extends AbstractApplicationServlet {

	protected Provider applicationProvider;

	public GuiceApplicationServlet() {
		VaadinGuiceConfiguration.createInjector.injectMembers(this);
	}

	@Override
	protected Class getApplicationClass()
			throws ClassNotFoundException {
		return Application.class;
	}

	@Inject
	public void setApplicationProvider(Provider applicationProvider) {
		this.applicationProvider = applicationProvider;
	}

	@Override
	protected Application getNewApplication(HttpServletRequest request)
			throws ServletException {
		return applicationProvider.get();
	}
}

W kolejnym kroku należy trochę pozmieniać konfigurację w web.xml. Generalnie musimy dostosować ten plik do współpracy z Guice, czyli przede wszystkim zmusić injector tak by singletony były tworzone per sesja, a nie per aplikacja.

Listing 2. web.xml


	Vaadin Web Application
	
		Vaadin production mode
		productionMode
		false
	

	
		guiceFilter
		com.google.inject.servlet.GuiceFilter
	

	
		guiceFilter
		VAS
	

	
		pl.koziolekweb.vaadin.guice.servlet.VaadinGuiceConfiguration
	

	
		VAS
		pl.koziolekweb.vaadin.guice.servlet.GuiceApplicationServlet
	

	
		VAS
		/*
	

Na zakończenie należy napisać własne rozszerzenie GuiceServletContextListener, które będzie dostarczać nam injector…

Listing 3. GuiceServletContextListener

public class VaadinGuiceConfiguration extends GuiceServletContextListener {

	public static Injector createInjector;

	@Override
	protected Injector getInjector() {
		createInjector = Guice.createInjector(new MyServletModule());
		return createInjector;
	}

}

Aplikacja Vaadin jako moduł Guice

Guice „myśli” modułami. Aplikacja Vaadin powinna być dostarczona w całości jako moduł Guice ze względu na to, że silnik Guice jest tak skonstruowany, że myśli „globalnie” w kontekście całego procesu serwera. Oznacza to, że po podpięciu dodatku do Guice, który pozwala na połączenie injectora z sesją serwera jedyną rozsądną drogą jest dostarczenie aplikacji jako specjalnej wersji modułu przystosowanego do pracy w środowisku webowycm.

Listing 4. MyServletModule

public class MyServletModule extends ServletModule {

	@Override
	protected void configureServlets() {
		serve("/*").with(GuiceApplicationServlet.class);
		bind(Application.class).to(ApplicationRoot.class).in(
				ServletScopes.SESSION);

		bind(ListBookView.class).to(ListBookViewImpl.class);
		bind(BookDao.class).to(SimpleBookDao.class);
		bind(ListBookPresenter.class).to(ListBookPresenterImpl.class);

	}
}

Na koniec należy sobie odpalić wszystko jako aplikację i gotowe:

Listing 5. MyServletModule

public class ApplicationRoot extends Application {
	private Window window;

	private ListBookPresenter listBookPresenter;
	private ListBookView listBookView;

	@Override
	public void init() {
		window = new Window("My Vaadin Application");
		setMainWindow(window);
		Button button = new Button("Zaladuj ksiązki");
		button.addListener(new Button.ClickListener() {
			public void buttonClick(ClickEvent event) {
				listBookPresenter.setListBookView(listBookView);
				listBookPresenter.showAll();
			}
		});
		window.addComponent(listBookView.getComponent());
		window.addComponent(button);

	}

	@Inject
	public void setListBookPresenter(ListBookPresenter listBookPresenter) {
		this.listBookPresenter = listBookPresenter;
	}

	@Inject
	public void setListBookView(ListBookView listBookView) {
		this.listBookView = listBookView;
	}

}

Odpalamy i działa

Podsumowanie

Da się połączyć Vaadin i Guice to jest pocieszające. Sposób połączenia jest trochę inny niż piszą o tym w necie to jest mniej pocieszające.
Generalnie się da.

3 myśli na temat “Vaadin + Guice + JSR-330, czyli jak nie przespałem nocy

  1. a wiesz moze jak przy pomocy mavena uzyskac hotdeploy? bo bez tego to koszmar…

  2. @Kzysiek. Maven nie ma takiej możliwości, jest to narzędzie do budowania. Opcje masz dwie: JRebel (płatne), IDE z odpowiednim adapterem serwera i uruchomienie aplikacji na serwerze w trybie „Debug” z poziomu IDE.
    Następnie podanie w URL’u parametr „?restartApplication”.

    Pozdrawiam

Napisz odpowiedź

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax