Suche
OSGi und Vaadin

Und ewig grüßt das Rentier: Dynamische Webapplikationen mit OSGi und Vaadin

Kai Tödter

Seit einiger Zeit hat sich OSGi in vielen Serverinfrastrukturen etabliert – allerdings eher im Business-Tier als im User Interface. Nach dem Einführungsartikel „Vaadin – ein Java-Webframework im Visier“ von Florian Müller zeigt dieser Beitrag, wie man mithilfe von Vaadin auch dynamische und modulare User-Interface-Komponenten zusammen mit serverseitigem OSGi implementieren kann.

OSGi [1] war ursprünglich für den Embedded-Bereich gedacht, hat aber, besonders nachdem das Eclipse SDK mit der Version 3.0 auf OSGi umgestiegen war, auch Einzug in den Desktopbereich gehalten und wird seit einiger Zeit auch gerne im Serverumfeld eingesetzt. So setzen z. B. heutzutage alle gängigen Application-Server intern auf OSGi. OSGi ist besonders wegen seines hervorragenden Modulsystems und seines leichtgewichtigen Servicemodells so beliebt. Es ermöglicht, modulare, skalierbare und dynamische Applikationen zu entwickeln. Das ist aber meistens auf die Businesslogik beschränkt. Wenn es um serverseitige User-Interface-Technologien geht, z. B. JavaServer Faces (JSF) oder Google Web Toolkit (GWT), ist eine Verbindung mit OSGi, um auch dynamische und modulare GUIs (Graphical User Interface) zu erzeugen, oft nur sehr schwer möglich.

Vaadin [2] ist ein serverseitiges RIA-(Rich-Internet-Application-)Framework, das schon für den Gebrauch mit OSGi vorbereitet ist. Vaadin benutzt GWT intern als reine Rendering Engine. Die GUI-Logik läuft, wie bei JSF, rein serverseitig. Vaadin ist übrigens das finnische Wort für „weibliches Rentier“, das Vaadin-Logo }> soll auch daran erinnern. Da Vaadin das UI serverseitig aufbaut, können OSGi Bundles dynamisch UI-Komponenten zu einer laufenden Vaadin-Applikation beisteuern. In diesem Artikel zeige ich an einem konkreten Beispiel, wie man mithilfe von OSGi Declarative Services eine Appliktationsinfrastruktur aufbauen kann, die genau das ermöglicht. Sie können gerne das in diesem Artikel verwendete Beispiel selbst ausprobieren. Alles was man braucht, ist ein Eclipse SDK (z. B. 3.6.x oder 3.7) und das Zip-File, das Sie unter [3] herunterladen können

Serverseitiges OSGi

Es gibt grundsätzlich zwei Möglichkeiten, um OSGi auf einem Server einzusetzen:

  1. Man bettet OSGi in einen existierenden Servlet-Container ein
  2. Man bettet einen HTTP-Server in einen OSGi-Container ein

Beide Möglichkeiten haben Vor- und Nachteile. Einen guten Überblick, wie man Equinox, die Eclipse-OSGi-Implementierung, einbetten kann, finden Sie unter [4]. Für diesen Artikel habe ich mich entschieden, die Möglichkeit 2 zu verwenden und einen HTTP-Server (Jetty) in einen OSGi-Container (Equinox) zu integrieren. Das macht es für Sie auch sehr einfach, das Beispiel lokal in Ihrem Eclipse SDK auszuführen.

Target Platform

Wenn Sie mit Eclipse vertraut sind, sagt Ihnen der Begriff „Taget Platform“ sicherlich schon etwas. Die Target Platform kann global für einen Eclipse Workspace gesetzt werden, alle OSGi Bundles werden dann gegen diese Target Platform kompiliert, und auch beim Ausführen einer Run Configuration werden die Bundle-Abhängigkeiten gegen die Target Platform aufgelöst. In unserem Beispiel liefert Ihnen das Projekt cm.siemens.ct.osgi.vaadin.target eine komplette Taget Platform, inklusive OSGi-Container, OSGi Declarative Services, HTTP-Server (Jetty), Logging (LogBack [5[ und SLF4J [6]) und natürlich Vaadin. Mithilfe einer Target-Definition können Sie die Target Platform leicht setzten. Öffnen Sie hierfür einfach die Datei vaadin.target in einem Editor und klicken Sie auf SET AS TARGET PLATFORM in der rechten oberen Ecke. Abbildung 1 zeigt alle Bundles unserer Target Platform.

Abbildung 1

Eine Besonderheit sind die beiden Bundles org.vaadin.osgi und org.vaadin.osgi.static res. Auf meiner Suche nach Vaadin in Verbindung mit OSGi bin ich auf einige interessante Artikel und Ressourcen gestoßen, die ich Ihnen natürlich nicht vorenthalten möchte:

Der Ansatz von Chris Brind hat mir sehr gut gefallen, deshalb verwende ich seine beiden (oben erwähnten) Bundles in meiner Demo (Kasten: „Die Demo“).

[ header = Seite 2: Die Demo ]

Die Demo
Bevor ich auf die Einzelheiten der Implementierung eingehe, möchte ich kurz erklären, wie die Demo bedient und gestartet wird. Es folgt eine kleine Anleitung zu Installation und Start der Demo:

  • Öffnen Sie ein Eclipse SDK, am besten 3.6.x oder 3.7.x, und öffnen Sie einen neuen Workspace
  • Laden Sie die Demo herunter: osgi-vaadin-demo.zip [3](6.8 MB)
  • Importieren Sie alle Projekte des Zip-Files in Ihr Eclipse SDK
  • Öffnen Sie das Projekt com.siemens.ct.osgi.vaadin.target
  • Doppelklicken Sie vaadin.target (das öffnet die Target-Platform-Definition in einem Editor)
  • Klicken Sie auf SET AS TARGET PLATFORM in der rechten oberen Ecke des Editors
  • Nun sollten alle Projekte fehlerfrei kompilieren
  • Starten Sie die Run Configuration OSGi Vaadin Demo
  • Öffnen Sie den folgenden URL in Ihrem Webbrowser: http://localhost/com.siemens.ct.osgi.vaadin.pm.main
  • Wenn alles geklappt hat, sieht Ihr Browser in etwa wie in Abbildung 2 aus
  • Wenn Sie Jetty nicht auf dem Default Port 80 starten wollen, spendieren Sie ein VM-Argument in der Run Configuration, z. B. -Dorg.osgi.service.http.port=8080
  • Wählen Sie die Bundle View und starten oder stoppen Sie dort Bundles mit Klicks auf die entsprechenden Checkboxen
  • Sie werden sehen, dass dynamisch Views und Actions (UI-Contributions) zur Applikation hinzugefügt werden oder wieder verschwinden
  • Wenn Sie auf DESELECT ALL klicken, werden alle Bundles, die dynamische Beiträge zum User Interface liefern, gestoppt und sind dann im StatusRESOLVED. Sofort verschwinden die entsprechenden UI-Contributions aus dem User Interface (Abb. 3).
  • Mit SELECT ALL werden alle Bundles wieder gestartet, ihr Status wird ACTIVEund die UI-Contributions sind wieder sichtbar.

Abb. 2: Die Vaadin-OSGi-Demo beim ersten Start

Abb. 3: Die Vaadin-OSGi-Demo nach dem Klicken von „Deselect All“

Vaadin

Vaadin ist ein mächtiges serverseitiges RIA-Framework, das GWT als Rendring Engine einsetzt. Detaillierte Informationen finden Sie unter [2] und im erwähnten Artikel „Vaadin – ein Java-Webframework im Visier„. Es würde den Umfang dieses Artikels sprengen, Vaadin hier detailliert zu beschreiben, daher möchte ich nur ganz kurz auf ein paar Aspekte von Vaadin eingehen. Unter [10] finden Sie eine Liste der Vaadin-Features. Erwähnen möchte ich hier nur einige Merkmale:

  • Einfaches Programmiermodell: Nur Java, (so gut wie) keine Konfiguration, kein JavaScript. Wer schon mal Swing/SWT oder GWT programmiert hat, findet sich sehr schnell zurecht.
  • Serverseitige UI-Logik, aber GWT als Rendering Engine. Im Gegensatz zu „reinem“ GWT, bei dem sich ein Großteil der Applikations- und UI-Logik in JavaScript auf dem Client abspielt und explizite Remote Prcedure Calls zum Server gemacht werden, läuft bei Vaadin die komplette Applikations- und UI-Logik auf dem Server. Deshalb funktioniert auch die Verbindung mit serverseitigem OSGi so gut.
  • Das Look and Feel der Applikation ist einfach zu ändern und zu erweitern. Die mitgelieferten Standard-Themes gefallen mir recht gut.
  • Einfaches Runtime Environment: Vaadin besteht nur aus einem einzigen JAR-File.

In Listing 1 sehen Sie ein „Hello World“-Beispiel mit Vaadin.

import com.vaadin.ui.*;

public class HelloWorld extends com.vaadin.Application {
public void init() {
Window main = new Window("Hello window");
setMainWindow(main);
main.addComponent(new Label("Hello World!"));
}
}

Eine Vaadin-Applikation ist eine Erweiterung der Klasse com.vaadin.application. In der init-Methode wird das Hauptfenster (main) der Applikation erzeugt. In unserem Fall ein einfaches Fenster. Dieses muss der Applikation dann noch mit setMainWindow()als Hauptfenster bekannt gemacht werden. Zum Schluss wird noch ein einfaches Label mit dem Text „Hello World!“ hinzugefügt.

Die Demoapplikation

Die dynamische OSGi-Vaadin-Demo besteht aus einem Applikationsskelett und diversen Bundles, die UI-Beiträge (Contributions) in Form von Views oder Actions liefern, doch dazu später mehr. Das Skelett der Applikation ist sehr einfach aufgebaut. Es besteht im Wesentlichen aus einem Hauptmenü, einer Toolbar und einem TabSheet. Die eigentliche Dynamik der Applikation kommt durch zwei Aspekte zum Tragen: Die Applikation stellt zwei Serviceinterfaces für UI-Contributions bereit: IActionContribution und IViewContribution. Die Listings 2 und 3 zeigen diese beiden Interfaces.

package com.siemens.ct.osgi.vaadin.pm.main.service;

import com.vaadin.Application;

public interface IActionContribution {
String getIcon();
String getText();
void execute(Application application);
}

package com.siemens.ct.osgi.vaadin.pm.main.service;

import com.vaadin.Application;
import com.vaadin.ui.Component;

public interface IViewContribution {
public Component getView(Application application);
public String getIcon();
public String getName();
}

Die Applikation selbst macht vom Whiteboard Pattern [11] Gebrauch: Anstelle eines Toolbar- oder View-Service, bei dem sich Contributors als Listener einhängen können, ist die Applikation selbst eine (OSGi-Declarative-Services-)Komponente, die eine 0..n-Abhängigkeit zu den Services IActionContribution und IViewContribution deklariert. Kommt nun ein Bundle (beim Start-up und dynamisch) ins System, das einen solchen Service anbietet, werden die entsprechenden Methoden bindActionContribution und bindViewContribution aufgerufen. Verlassen die Bundles das System, z. B. durch Stoppen oder Deinstallation, werden die entsprechenden Methoden unbindActionContribution und unbindViewContribution aufgerufen. Um all diese Dynamik muss sich der Entwickler nicht mehr selber kümmern. Die in OSGi Declarative Services (DS) enthaltene Service Component Runtime kümmert sich automatisch darum. Die für DS benötigten Informationen werden in einer kleinen XML-Datei bereitgestellt, die in Listing 4 dargestellt ist. Wenn Sie mit Eclipse arbeiten, bekommen Sie einen formularbasierten Editor mitgeliefert und müssen selbst kein XML schreiben.

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http: //www.osgi.org/xmlns/scr/v1.1.0"
name="com.siemens.ct.osgi.vaadin.pm.main" factory='vaadin.app'>
<implementation class="com.siemens.ct.osgi.vaadin.pm.main.MainApplication"/>
<reference
bind="bindViewContribution"
cardinality="0..n"
interface="com.siemens.ct.osgi.vaadin.pm.main.service.IViewContribution"
name="IViewContribution"
policy="dynamic"
unbind="unbindViewContribution"/>
<reference
bind="bindActionContribution"
cardinality="0..n"
interface="com.siemens.ct.osgi.vaadin.pm.main.service.IActionContribution"
name="IActionContribution"
policy="dynamic"
unbind="unbindActionContribution"/>
</scr:component>

Durch die Verwendung von Chris Brinds Vaadin OSGi Binding ist die Vaadin-Applikation selbst wieder eine DS-Komponente und kann einfach Abhängigkeiten zu weiteren OSGi-Services deklarieren. Kommen nun IActionContribution-Services in das OSGi-System, werden diese von der Applikation einfach in die Toolbar und das Menü eingefügt bzw. wieder entfernt, falls der Service verschwindet. Analog dazu werden IViewContribution-Services in das TabSheet eingefügt und auch wieder daraus entfernt. Der Sourcecode für das Einfügen einer IViewContribution ist dabei trivial, wie Listing 5 zeigt.

public void bindViewContribution(IViewContribution viewContribution) {
logger.debug("bindViewContribution()");
viewContributions.add(viewContribution);
if (initialized) {
tabSheet.addTab(viewContribution.getView(this),
viewContribution.getName(),
new ThemeResource(viewContribution.getIcon()));
}
}

[ header = Seite 3: Action Contributions ]

Action Contributions

Ein Beispiel für eine Action Contribution ist im Projekt com.siemens.ct.osgi.vaadin.pm.runoaction enthalten. Im Verzeichnis OSGI-INF befindet sich die XML-Datei runoAction.xml, die Sie in Listing 6 sehen.

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http: //www.osgi.org/xmlns/scr/v1.1.0"
name="com.siemens.ct.osgi.vaadin.pm.runoaction">
<implementation class="com.siemens.ct.osgi.vaadin.pm.runoraction.RunoAction" />

<service>

<provide

interface="com.siemens.ct.osgi.vaadin.pm.main.service.IActionContribution"/>

</service>
</scr:component>

Hier wird also für OSGi Declarative Services spezifiziert, dass dieses Bundle einen Service com.siemens.ct.osgi.vaadin.pm.main.service.IActionContribution liefert, der durch die Klassecom.siemens.ct.osgi.vaadin.pm.runoraction.RunoAction implementiert wird. Die Implementierung der RunoAction finden Sie in Listing 7.

public class RunoAction implements IActionContribution {

@Override
public String getIcon() {
return "icons/color_swatch.png";
}

@Override
public String getText() {
return "Runo Theme";
}

@Override
public void execute(Application application) {
application.setTheme(Runo.THEME_NAME);
}

}

Die Methode getText() liefert den Text, der von der Applikationskomponente dynamisch im Menü als MenuItem und in der Toolbar als Button eingefügt wird. Die Methode getIcon()liefert ein Theme-spezifisches Icon, z. B. für den Toolbar-Button. Auf das Thema „Theming“ gehe ich weiter unten noch gesondert ein. Die execute()-Methode bekommt schließlich die Vaadin-Applikation als Parameter übergeben und kann in dieser das für die gesamte Applikation gültige Theme setzen. Wenn Sie die Demo lokal am Laufen haben, klicken Sie einfach einmal auf den Toolbar-Button RUNO THEME. Das Ergebnis sehen Sie in Abbildung 4.

Abb. 4: Die Vaadin-OSGi-Demo mit „Runo Theme“

View Contributions

Ein Beispiel für eine View Contribution befindet sich im Projekt com.siemens.ct.osgi.vaadin.pm.bundleview. Im Prinzip funktioniert eine IViewContribution genauso wie eine IActionContribution. Die Applikationskomponente fügt eingehende IViewContribution-Services in das TabSheet ein, den eigentlichen Inhalt für das neue Tab liefert die Methode getView().Analog zum Action-Projekt gibt es auch hier im Verzeichnis OSGI-INF eine XML-Datei, die die benötigten Deklarationen für OSGi DS beinhaltet.

Starten und Stoppen von Bundles

Die Bundle View ist eine besondere View, nämlich die Implementierung eines so genannten Agents, mit dem man interaktiv Bundles im OSGi-Laufzeitsystem starten und stoppen kann. In der Bundle-View-Implementierung werden dabei nur die Bundles angezeigt, für die ein Starten und Stoppen auch Sinn macht. Die Bundle View ist im Prinzip nur ein GUI-Hilfsmittel, um Bundles auf dem Server zu manipulieren. Der Vorteil für die Demo ist dabei, dass Sie kein explizites Browser-Refresh machen müssen, da Vaadin das in diesem Fall automatisch erledigt. Man kann über die Equinox-Konsole natürlich auch manuell Bundles starten und stoppen. Klicken Sie dafür in die Console-View und geben „ss“ ein. „ss“ steht für „Short Status“ und gibt eine Liste aller im Laufzeitsystem vorhandenen Bundles inklusive Status aus.

Die Zeile 13 ACTIVE com.siemens.ct.osgi.vaadin.pm.treeview_1.0.0.qualifier gibt an, dass das Bundle mit der Nummer 13 gerade aktiv ist. Geben Sie nun stop 13 auf der Console ein, stoppen Sie das Bundle. Zum Test geben Sie ss tree ein und bekommen wie erwartet die Ausgabe 13 RESOLVED com.siemens.ct.osgi.vaadin.pm.treeview_1.0.0.qualifier. Das Bundle ist also im Status RESOLVED. Wenn Sie jetzt ein Refresh auf dem Webbrowser machen, werden Sie sehen, dass die View Contribution Tree View verschwunden ist.

Theme Switching

Beim Wechseln des Themes für die gesamte Vaadin-Applikation gibt es einige Besonderheiten zu beachten. Die Theme-Ressourcen, z. B. Icons und Stylesheets, müssen vom Vaadin Bundle kontrolliert werden. Daher müssen alle Bundles, die neue Ressourcen zu Themes hinzufügen wollen, Fragmente vom Vaadin Bundle sein. Als Beispiel dient das Projektcom.siemens.ct.osgi.vaadin.pm.theme. Dieses ist ein Fragment vom Vaadin Bundle und liefert einige Icons im Verzeichnis VAADIN.themes.reindeer.icons. Das führt dazu, dass diese Icons auch nur im Reindeer-Theme zur Verfügung stehen. Wie Sie in Abbildung 4 sehen, sind die Icons im Runo-Theme nicht enthalten. Ich empfehle folgende Vorgehensweise: Stellen Sie ein einziges Bundle zur Verfügung, in dem alle Ressourcen für ein Theme enthalten sind. So können Sie z.B. die Konsistenz der Icons garantieren. Das spricht natürlich ein wenig gegen das Modulkonzept, nach dem Bundles, die UI-Contributions liefern, auch die entsprechenden Theme-Ressourcen liefern könnten. Sicher können Sie das auch so implementieren, müssen aber auf jeden Fall die Theme-Ressourcen in ein eigenes Fragment packen und können diese nicht mit einem normalen OSGi Bundle deployen.

Fazit

In diesem Artikel habe ich gezeigt, wie einfach es ist, mit Vaadin und OSGi dynamische Webapplikationen zu erstellen, die auch auf UI-Seite dynamisch Komponenten zum User Interface der Applikation beisteuern können. Die Demo ist dabei aber natürlich nur als einfaches Anschauungsbeispiel gedacht, um die Prinzipien zu erläutern. Realistische Use Cases wären zum Beispiel folgende:

  • Erweiterung oder Update einer Applikation inklusive GUI-Komponenten zur Laufzeit, ohne den Server herunterfahren zu müssen
  • Verschiedene Deployments mit verschiedenen GUI-Modulen, z.B. für verschiedene Kunden
  • Einfache Administrationsapplikationen, mit denen man das OSGi-Laufzeitsystem auf dem Server steuern kann

Die Zukunft wird zeigen, wie ausgereift die vorgestellten Technologien schon sind und für welche Klasse von Anwendungen sie sich am besten eignen. Serverseitige OSGi-Anwendungen und auch Vaadin bieten meiner Meinung nach jedenfalls ein großes Potenzial. Falls Sie selbst schon Erfahrungen mit OSGi und Vaadin gesammelt haben, wäre ich sehr an einem Erfahrungsaustausch interessiert.

Geschrieben von
Kai Tödter
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.