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 extends Application>, 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 ProviderapplicationProvider; public GuiceApplicationServlet() { VaadinGuiceConfiguration.createInjector.injectMembers(this); } @Override protected Class extends Application> 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.
a wiesz moze jak przy pomocy mavena uzyskac hotdeploy? bo bez tego to koszmar…
Spróbuj z OSGi.
@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