Blog

Das reine Vergnügen – RIA mit Vaadin und Guice

Vorweg: Vaadin ist cool. Das auf dem Google Web Toolkit basierende Framework erlaubt die komponenten-orientierte Entwicklung hochqualitativer Rich Internet Applications. Dabei wird weitestgehend vom HTML/Servlet Code abstrahiert – das Programmiermodell erinnert somit stark an die vom Desktop gewohnte Entwicklung mit Swing oder RCP. JSP-Taglibs, XML-Wüsten und Platzhalter-Replacements sucht man vergebens, und man vermisst sie auch nicht.

Eine typische Vaadin-Applikation (aus Book of Vaadin, Getting started):

public void init() {
   final Window mainWindow =
      new Window("Myproject Application");

   Label label = new Label("Hello Vaadin user");
   mainWindow.addComponent(label);

   mainWindow.addComponent(
      new Button("What is the time?",
         new Button.ClickListener() {
         public void buttonClick(ClickEvent event) {
            mainWindow.showNotification(
               "The time is " + new Date());
         }
      }));

 setMainWindow(mainWindow);
}

Sobald die Anwendung etwas größer wird, bekommt man jedoch zwangsläufig die klassischen Stand-Alone-Anwendungsprobleme: verschachtelte Komponenten, ausgelagerte Handler, implementierte Listener, gekapselte Service-Facaden … ein Haufen Instanzen will erzeugt und verwaltet werden. Nebenbei arbeitet man auf 3 Scopes (Application, Session, Request), bemüht sich um Separation of Concern und achtet auf maximale Testbarkeit.
Das wird „per Hand“ schnell unübersichtlich. Naheliegend ist, sich bei der Verwaltung der Instanzen helfen zu lassen. Wenn man das Pech hat, nicht auf JEE6 und CDI zurückgreifen zu können (konkret arbeiten wir nach wie vor auf JBoss 5.1), ist die Frage:

Wie?
Oder anders formuliert: Spring oder Guice?

Wir haben uns gegen Spring und für Guice entschieden. Ausschlaggebend waren die leichtgewichtige Integration (wenige Jars ins WEB-INF/lib – und fertig) und die Tatsache, dass wir ohnehin die Geschäftslogik mit EJB3 entwickeln, also für die diversen Spring-Erweiterungen in dem Bereich keinen Bedarf haben.

Die Kombination von Guice und Vaadin ist mit der guice-servlet-Extension eigentlich schnell gemacht. Der Teufel steckt dann natürlich wie so oft im Detail, weshalb wir mit diesem Blog etwas Licht ins Dunkel bringen wollen.

Grundgerüst

Wir verwenden Eclipse mit dem Vaadin Plugin und Maven. Ein neues Projekt lässt sich dann einfach mit „new Maven Project“ und dem Archetype „vaadin-clean“ erstellen. Wenn dieser noch nicht installiert ist, kann er wie folgt hinzugefügt werden:

Vaadin Archetype

Der Archetype ist leider, was die Versionsnummern angeht, etwas veraltet. im Beispielprojekt, das am Ende des Artikels heruntergeladen werden kann, sind die Einstellungen für das aktuelle Vaadin 6.7.6 angepasst.

Guicify

Guice benötigt einen Bootstrapper, der den Injector erzeugt, welcher dann die Erzeugung weiterer Instanzen übernimmt. Es wäre zwar möglich, diesen in der init()-Methode der Vaadin-Applikation aufzubauen und anschließend die Members der Application-Klasse injizieren zu lassen. Wesentlich eleganter und praktikabler ist es jedoch, bereits die Applikation selbst und somit alle Instanzen von Anfang an von Guice verwalten zu lassen.

Dazu muss ein Guice-Filter in der web.xml registriert werden und auf einen ServletContextListener umgeleitet werden.

web.xml

<display-name>vaadin-guice</display-name>

	<context-param>
		<description>Vaadin production mode</description>
		<param-name>productionMode</param-name>
		<param-value>false</param-value>
	</context-param>

	<filter>
		<filter-name>guice.filter</filter-name>
		<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>guice.filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<listener>
		<listener-class>de.holisticon.blog.vaadin.configuration.ApplicationServletContextListener</listener-class>
	</listener>

ApplicationServletContextListener.java und ApplicationModule.java

public class ApplicationServletContextListener extends GuiceServletContextListener {

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

}
public class ApplicationModule extends AbstractModule {
    private Properties properties = new Properties();
    public ApplicationModule() {
        properties.put("window.caption", "Main Application");
        properties.put("label.text", "Hello Vaadin user");
    }

    protected void configure() {
        install(new ServletModule() {
            protected void configureServlets() {
                serve("/*").with(ApplicationServlet.class);
                bind(Application.class).to(MainApplication.class).in(ServletScopes.SESSION);
            }
        });
        Names.bindProperties(binder(), properties);
    }

    @Provides
    @Named("MainWindow")
    public Window provideMainWindow(@Named("window.caption") String windowCaption) {
        return new Window(windowCaption); // liefert das MainWindow mit konfiguriertem Titel
    }
}

Nun wird jeder Zugriff auf „vaadin-guice/*“ von Guice verwaltet und an eine Instanz der MainApplication geroutet. Die MainApplication liegt im Scope „Session“, jeder User bekommt also eine eigene Instanz erzeugt. Nun können wir die Beispiel-Anwendung auf Injektion umstellen:

MainApplication.java

public class MainApplication extends Application {
    @Inject @Named("MainWindow")
    private Window mainWindow; // Window mit konfigurierter Caption

    @Inject @Named("label.text")
    private String labelText;

    @Inject
    private Provider<Date> date;

    @Override
    public void init() {
        setMainWindow(mainWindow);
        mainWindow.addComponent(new Label(labelText));

        mainWindow.addComponent(
                new Button("What is the time?",
                        new Button.ClickListener() {
                            public void buttonClick(ClickEvent event) {
                                mainWindow.showNotification("The time is " + date.get());
                            }
                        }));
    }
}

Nach Start der Anwendung mit mvn jetty:run können wir das Ergebnis im Browser begutachten:

Screenshot Beispielanwendung vaadin-guice

Wird der Einsatz von @Inject auf allen verwendeten Komponenten und Handlern konsequent durchgezogen, kann mit einfachen Mitteln eine transparente, lose gekoppelte Rich Internet Application aufgebaut werden, bei der jede Instanz genau die Dependencies erhält, die sie benötigt.

Fazit

Vaadin und Guice kooperieren hervorragend. Das Injection-Verhalten ist transparent und programmatisch durch Erweiterung der Modules konfigurierbar.
Zu beachten bleibt: Da die Applikation im Session Scope läuft, ist bei Zugriff auf Request-spezifische Daten (etwa die Request-Parameter) besondere Vorsicht angeraten: auf keinen Fall dürfen solche Elemente als Instanzfelder der Applikation gespeichert werden. Guice-Provider können jedoch eingesetzt werden, um diese Scope-Grenze zu umgehen.
Im Gegensatz zu CDI werden keine Anforderungen an den Servlet-Container gestellt, so dass die Entwicklung leicht mit Tomcat oder Jetty erfolgen kann.
Werden die (seit Guice 3.0 unterstützten) JSR330 Annotationen verwendet, ist sogar ein späterer Umzug in einen CDI bzw. Spring Container möglich.

Das komplette Maven-Beispielprojekt kann hier heruntergeladen werden

Über den Autor

Jan Galinski

Jan Galinski ist Senior Consultant bei der Holisticon AG und seit vielen Jahren als Architekt und Entwickler in agilen Kundenprojekten unterwegs. Er ist ein leidenschaftlicher Prozessautomatisierer und BPM-Craftsman, am liebsten mit Camunda BPM. Als Contributor zu zahlreichen Open Source Projekten aus den Bereichen BPM und JEE gibt er seine Erfahrung und Wissen gerne weiter. 2016 wurde er mit dem Camunda Community Award ausgezeichnet.

Antwort hinterlassen