Portable Applikationen mit CDI und Apache DeltaSpike

Was ist eigentlich… Apache DeltaSpike?

Gerhard Petracek, Mark Struberg
Quelle: Deltaspike-Website
Titel

Apache DeltaSpike

Version

1.0

Erscheinungsdatum

Juli 2014

Hersteller

Apache Software Foundation

Lizenz

Open Source

Download

DeltaSpike ist der Nachfolger von Apache MyFaces CODI und JBoss Seam. CDI-basierte Projekte können von den technischen Stärken und der aktiven Community profitieren. Dank innovativer Konzepte und der Portabilität von DeltaSpike kommt das Projekt bereits in vielen produktiven Applikationen erfolgreich zum Einsatz.

Mit der Veröffentlichung von Java EE 6 wurde 2009 ein neues Komponentenmodell namens CDI vorgestellt. Ein wichtiger Aspekt von CDI ist die Möglichkeit die spezifizierten Funktionen mit Hilfe eines sehr flexiblen SPI (Service Provider Interface) portabel zu erweitern. Die beiden CDI-Implementierungen Apache OpenWebBeans und JBoss Weld wurden bereits nach kurzer Zeit von der Entwickler-Community für die Umsetzung vieler Projekte herangezogen. Im Vergleich zu ähnlichen Komponentenmodellen reduziert CDI die Komplexität bei der Erstellung von Applikationen und ist gleichzeitig nicht zu minimalistisch. Dennoch ist es nicht möglich und teilweise nicht immer sinnvoll, dass ein Standard wie CDI sämtliche Konzepte abdeckt. Daher begann einige Wochen später die Entwicklung einer kleinen, aber erfolgreichen portablen Erweiterung für CDI namens MyFaces CODI. Dank innovativer Konzepte ermöglicht es die effizientere Entwicklung von Java-EE-Applikationen auf Basis von CDI. Zur gleichen Zeit wurde Seam3 von JBoss entwickelt. Einige Grundideen der beiden CDI-Erweiterungen sind sehr ähnlich und so war es naheliegend die beiden Projekte zu fusionieren. Ende 2011 war es schließlich soweit und Apache DeltaSpike wurde ins Leben gerufen. Neben CODI und Seam3 beteiligten sich auch kleinere Projekte, wodurch Apache DeltaSpike auf eine sehr breite Basis zurückgreifen konnte.

Community und Portabilität im Mittelpunkt

Der Anfang gestaltete sich etwas schwerfällig, weil viele der ähnlichen Konzepte diskutiert werden mussten. Schließlich wurden die besten Ansätze übernommen und in einigen Fällen vereinheitlicht, erweitert und manchmal sogar komplett neu implementiert. Dank der breiten Community Beteiligung flossen zudem auch neue Konzepte und Ideen in das Projekt ein, wodurch viele DeltaSpike-Module schon bald nach dem Start mehr Funktionalität bieten konnten als die entsprechenden Module der Vorgänger.
Ein weiterer Schwerpunkt von Apache DeltaSpike ist die Portabilität im Hinblick auf verschiedene CDI-Implementierungen und Java EE Server. Die mittlerweile stark angewachsene Testsuite wird regelmäßig mit sämtlichen spezifikationskonformen Versionen der beiden CDI-Implementierungen Apache OpenWebBeans und JBoss Weld auf einem CI-Server von Apache ausgeführt. Darüber hinaus gibt es auch CI-Jobs in Kombination mit einigen EE-Servern, sofern dies rechtlich möglich ist. Neben Apache selbst geht JBoss als kommerzieller Serverhersteller mit einem positiven Beispiel voran und unterstützte das Projekt bereits sehr früh mit einem eigenen CI-Server, um die Kompatibilität mit dem jeweils nächsten Release ihres EE-Servers sicherzustellen. Andere EE-Server werden in einigen Fällen durch die Community selbst getestet. Dadurch ist DeltaSpike derzeit mit Apache TomEE, JBoss AS7/Wildfly8, IBM WebSphere 8 und Oracle WebLogic 12.1.3 kompatibel.

Java SE und CDI

Apache DeltaSpike ist jedoch nicht nur auf Java EE fokussiert. Mit DeltaSpike Container-Control lassen sich sowohl Apache OpenWebBeans, JBoss Weld als auch einige embedded Java EE Container wie z.B. Apache TomEE mit einer vereinheitlichten API starten, stoppen und die Standard CDI-Contexte steuern. Listing 1 zeigt dies anhand eines einfachen Beispiels. Damit dieser Ausschnitt funktioniert müssen lediglich die erforderlichen Module von OpenWebBeans oder Weld, sowie die entsprechenden Module von DeltaSpike Container-Control hinzugefügt werden.

public class AppStarter {
    public static void main(String[] args) {
        CdiContainer cdiContainer = CdiContainerLoader.getCdiContainer();
        cdiContainer.boot();
        cdiContainer.getContextControl().startContexts();
        MyAppBean app = BeanProvider.getContextualReference(MyAppBean.class);
        try {
            app.process(args);
        } finally {
            cdiContainer.shutdown();
        }
    }
}

DeltaSpike Container-Control dient auch als Basis für das Test-Control- und Scheduler-Modul von DeltaSpike. Ersteres ermöglicht einfache JUnit-Tests mit CDI. Hierzu steht ein eigener Test-Runner bzw. Testsuite-Runner zur Verfügung. Dieser startet und stoppt im Hintergrund den CDI-Container Ihrer Wahl, sowie die verschiedenen Standard-Contexte und injiziert CDI-Beans bei Bedarf in die Test-Klassen. Listing 2 veranschaulicht die einfache Verwendung des Test-Runners.

@RunWith(CdiTestRunner.class)
public class SimpleTest {
    @Inject
        private QuestionOfLifeBean bean;

    @Test
    public void ultimateAnswer() {
        assertEquals(42, bean.getAnswer());
    }
}

Der Vorteil von DeltaSpike Test-Control ist die einfache und schnelle Erstellung und Ausführung von JUnit-Tests ohne zusätzlichen Konfigurationsaufwand. Außerdem können optional Mocking-Frameworks verwendet werden, um einzelne CDI-Beans durch eine gemockte Implementierung zu ersetzen. Wie die Beispiele des online verfügbaren Buchs CDI@Work zeigen ist dies in vielen Fällen nicht nötig, da spezialisierte bzw. alternative CDI-Beans im Test-Classpath zur Verfügung gestellt werden können. In Kombination mit der Embedded-Version von Apache TomEE können darüber hinaus auch EJBs, Container Managed DataSources, sowie JTA einfach getestet werden.

Java EE 7 für EE 6

Einige Serverhersteller nehmen sich etwas länger Zeit, um neue Revisionen der EE-Spezifikationen zu unterstützen. Sollten Sie derzeit an einen solchen Server gebunden sein, so können Sie dennoch einzelne Funktionalitäten von EE 7 bereits mit einem EE-6-Server verwenden. Hierfür sorgt bspw. das Bean-Validation-, sowie das Servlet-Modul von DeltaSpike. Im Falle des Bean-Validation Moduls können Sie CDI basierte Injizierung in Constraint-Validatoren nutzen. In Listing 3 wird ein User-Repository in den UniqueUserName-Validator injiziert, um zu überprüfen ob es den fraglichen User-Namen bereits in der Datenbank gibt.

public class UniqueUserNameValidator
      implements ConstraintValidator<UserName, String> {
    @Inject
    private UserRepository userRepository;

    public void initialize(UserName constraint) {
    }

    public boolean isValid(String userName, ConstraintValidatorContext cvc) {
        return this.userRepository.loadUser(userName) == null;
    }
}

DeltaSpike inspiriert sogar neue Revisionen von Java EE. So wurde die Grundidee hinter @Exclude in EE 7 unter dem Namen @Vetoed übernommen. Doch selbst mit EE 7 ist die Verwendung von @Exclude weiter sinnvoll, da Beans mit dieser Annotation nicht nur statisch, sondern auch dynamisch während des Applikationsstartes gefiltert werden können. Listing 4 veranschaulicht, dass alternative CDI-Beans mit diesem Mechanismus nur bei bestimmten Project-Stages aktiviert beziehungsweise deaktiviert werden können.

@Exclude(ifProjectStage = ProjectStage.Production.class)
@Alternative
public class TestMailService implements MailService { /*...*/ }

Dies ermöglicht einen einmaligen Build-Prozess und das resultierende Archiv der Applikation kann durch verschiedene Teststufen bis hin zum produktiven Deployment ohne weitere Änderung verwendet werden. Statt einer Flut an Kontrollstrukturen kann je Deploymentschritt eine entsprechend spezialisierte Implementierung aktiviert werden. In unserem Beispiel könnten wir die anfallenden E-Mails in die Datenbank sichern und später die Inhalte überprüfen, statt effektiv Nachrichten zu versenden. Listing 5 zeigt darüber hinaus, dass mit @Exclude auch konfigurationsabhängige Beans implementieren werden können.

@Exclude(onExpression="dbvendor!=postgreSQL")
@Specializes
public class PostgresNativeQuerySearchService extends SearchService { /*...*/ }

Zusätzliche Konzepte für Java EE

Die erwähnten Project-Stages stellen ebenfalls eine hilfreiche Erweiterung von Java EE dar. Sowohl in EE 6 als auch in EE 7 gibt es das Grundkonzept nur in JSF. DeltaSpike verfeinert diese Idee und stellt typsichere und zugleich erweiterbare Project-Stages für die gesamte Plattform zur Verfügung. Neben der Integration in @Exclude und dem Test-Control-Modul kann der aktuelle Project-Stage auch injiziert werden. Die Auswertung kann anschließend mit Vergleichsoperatoren durchgeführt werden, wie es in Listing 6 illustriert ist.

@Inject
private ProjectStage projectStage;

    if (projectStage == ProjectStage.Development) {
    //...
}

DeltaSpike legt hohen Wert auf Typsicherheit. Ein weiteres Beispiel hierfür ist die typsichere View-Config, die standardmäßig für JSF implementiert ist. Implementierungen für andere UI-Frameworks sind derzeit nicht verfügbar, aber grundsätzlich möglich. In der minimalen Ausbaustufe muss das Interface ViewConfig implementiert bzw. erweitert werden. Folgende Konfiguration wird auf die Seite /myPage.xhtml abgebildet:

public class MyPage implements ViewConfig {}

Die Namenskonvention kann hierbei angepasst werden. Statt einer klassischen JSF Action-Methode können wir mit dieser rudimentären Konfiguration eine typsichere Version implementieren, die statt einem String eine Klasse vom Type ViewConfig als Rückgabetyp definiert:

public Class<? extends ViewConfig> toNextPage() {
   return MyPage.class;
}

Beim Start der Applikation wird darüber hinaus noch geprüft, ob es für jede View-Config eine entsprechende XHTML-Datei gibt. Bereits vor dem Startup helfen IDE und Compiler Navigationen schneller zu definieren und Fehler zu finden. Durch diese Konzepte wird nicht nur die initiale Implementierung effizienter, sondern auch Umstrukturierungen in der Applikation können nicht mehr so leicht Teile dieser brechen.
Klassen werden konkreten Seiten zugeordnet und Interfaces zu Verzeichnissen. Der Pfad ergibt sich dabei durch die Verschachtelung von Interfaces und Klassen. Die in Listing 7 dargestellte Konfiguration repräsentiert somit den Pfad /pages/admin/overview.xhtml. Die ebenfalls in Listing 7 gezeigte JSF Action-Methode veranschaulicht, dass Navigationsziele eingeschränkt werden können. In diesem Fall kann die Navigation nur in den Admin-Bereich der Applikation durchgeführt werden. Bei einer Umstrukturierung der Applikation fällt somit sofort auf, wenn versucht wird zu einer Overview-Seite eines anderen Bereichs der Applikation zu navigieren. Neben der Überprüfung durch den Compiler ist diese Information auch eine Hilfe für die IDE, um das vorgeschlagene Ergebnis für Auto-complete einzuschränken.

public interface Pages extends ViewConfig {
    interface Admin extends Pages {
        class Overview implements Admin {}
    }
}
public Class<? extends Pages.Admin> toAdminOverview() {
   return Pages. Admin. Overview.class;
}

Navigationsparameter werden sowohl dynamisch als auch statisch unterstützt. Um bspw. den Navigationsstring auf /pages/admin/overview.xhtml?faces-redirect=true zu erweitern, stellt die View-Config in Listing 8 eine mögliche Variante dar.

@View(navigation = REDIRECT)
public interface Pages extends ViewConfig {
    interface Admin extends Pages {
        class Overview implements Admin {}
    }
}

Konfigurationen für eine Seite würde man direkt bei der Klasse selbst vermuten. Dies ist selbstverständlich möglich. In Listing 8 wird hingegen die Konfiguration bei einem Verzeichnisknoten, im konkreten Fall Pages, hinterlegt und durch den Vererbungspfad an die Klasse Overview vererbt, da diese das Interface Admin implementiert, welches das Interface Pages erweitert. Mit diesem Konzept ist es möglich Metadaten zentral zu definieren und an mehrere Verzeichnisse und Seiten zu vererben. Sollte es Ausnahmen geben, so können diese durch die optionalen Annotationen @Folder und @View angegeben werden, wodurch die vererbten Konfigurationswerte überschieben werden.
Für die Vererbung von Metadaten können auch reine Marker-Interfaces verwendet werden. In Listing 9 wird das Marker-Interface SecuredPages mit einer weiteren Annotation von DeltaSpike namens @Secured annotiert. In der angegebenen AccessDecisionVoter-Implementierung kann der Security-Check an ein beliebiges Security-Framework delegiert werden. DeltaSpike stellt hier somit kein vollwertiges Security-Framework zur Verfügung, sondern ein einfaches Adapter-Interface. In unserem Beispiel erweitert das Interface Admin neben Pages auch das Marker-Interface SecuredPages, wodurch @Secured sowohl an Admin als auch an Overview vererbt wird.

@Secured(MyAccessDecisionVoter.class)
public interface SecuredPages {}

@View(navigation = REDIRECT)
public interface Pages extends ViewConfig {
    interface Admin extends Pages, SecuredPages {
        @ViewControllerRef(AdminPage.class)
        class Overview implements Admin { }
    }
}

Diese Beispiele lassen bereits die Vielseitigkeit dieses Konzepts erahnen. In Demos und Kleinprojekten mag es wie ein überflüssiger Overhead aussehen. In mittleren bis großen Projekten kann dieser Ansatz allerdings seine Stärken ausspielen.
Metadaten können sehr projektspezifisch sein und DeltaSpike selbst könnte nie alle erforderlichen Annotationen zur Verfügung stellen. Daher ist es möglich mit Hilfe von @ViewMetaData eigene Metadaten zu erstellen. An einer beliebigen Stelle in der Applikation können diese Metadaten mit Hilfe von ViewConfigResolver abgefragt und anschließend ausgewertet werden. DeltaSpike selbst verwendet @ViewMetaData für eigene View-Config Annotationen. Ein Beispiel hierfür ist die Annotation @ViewControllerRef, welche ebenfalls in Listing 9 verwendet wird. Mit dieser Annotation kann die View-Controller-Klasse für eine Seite definiert werden, wodurch Callback-Methoden aktiviert werden. Wird bspw. eine Methode mit @PreRenderView annotiert, so wird diese vor dem Rendering-Prozess der entsprechenden Seite aufgerufen.

Auf den Context kommt es an

Unter anderem profitieren die zuvor erwähnten View-Controller von zusätzlichen Scopes, die von DeltaSpike zur Verfügung gestellt werden. So ermöglicht DeltaSpike die serverseitige Unterteilung des Session-Scopes in Slots. Jeder dieser Slots repräsentiert ein Fenster. Solche Fenster können im Falle von JSF an clientseitige Browser-Tabs gebunden sein. Somit kann die Applikation in mehreren Browser-Tabs unabhängig verwendet werden und es kommt zu keiner Kollision der Daten auf der Serverseite. Window-Scoped Beans können durch die Annotation @WindowScoped definiert werden. Darüber hinaus sind Grouped-Conversation-Context (@GroupedConversationScoped) und View-Access-Context (@ViewAccessScoped) auf Basis vom Window-Context umgesetzt und unterstützen somit automatisch die getrennte Datenhaltung für clientseitige Fenster. Die Konzepte aller drei Scopes wurden von MyFaces CODI übernommen und ermöglichen schon seit Jahren die Verwendung besserer Scopes als es mit Java EE in purer Form möglich ist. Der Grouped-Conversation-Context ist eine Alternative zum Conversation-Context von CDI und beseitigt dessen Einschränkungen und Fallstricke durch ein flexibleres Konzept für Conversations. Die Stärke dieses Scopes ist die manuelle Kontrolle über einzelne Beans oder Gruppen von Beans, die ohne Verzögerung umgesetzt wird. Das Gegenstück hierzu ist der View-Access-Context. Beans mit diesem Scope existieren so lange, bis sie auf der nachfolgenden JSF-Seite nicht mehr verwendet werden.

Fazit

Apache DeltaSpike ist der direkte Nachfolger von Apache MyFaces CODI, JBoss Seam3 und einiger anderer CDI-Erweiterungen und legt großen Wert auf Portabilität. Einige Bestandteile wurden direkt übernommen, andere wurden auf Basis der zuvor gesammelten Erkenntnisse im Praxiseinsatz überarbeitet. Obwohl DeltaSpike erst vor kurzem Version 1 erreicht hat, stecken somit indirekt bereits über vier Jahre Entwicklungszeit in diesem Projekt. Ein nicht zu vernachlässigender Aspekt ist die Community. Fragen werden sehr schnell beantwortet und der überwiegende Teil der Tickets wird zügig abgearbeitet. Jeder ist eingeladen DeltaSpike zu testen, in CDI basierten Projekten jeder Größenordnung einzusetzen und Ideen auf der Mailingliste einzubringen. Wie bei Apache üblich ist DeltaSpike vor allem ein offenes und aufgeschlossenes Community-Projekt bei dem Beteiligungen jeglicher Art sehr willkommen sind.

Geschrieben von
Gerhard Petracek
Gerhard Petracek
Gerhard Petracek ist Apache MyFaces und DeltaSpike PMC chair; PMC Member bei Apache OpenWebBeans; Mitbegründer von Apache MyFaces ExtVal, CODI und Apache DeltaSpike; Apache Software Foundation Member sowie Mitglied der Bean-Validation Expert Group.
Mark Struberg
Mark Struberg
Mark Struberg ist Softwarearchitekt mit über zwanzig Jahren Programmiererfahrung. Er arbeitet seit 1996 mit Java und ist aktiv in Open-Source-Projekte im Bereich Java und Linux involviert. Mark ist Apache Software Foundation Member und PMC bei Apache OpenWebBeans, MyFaces, DeltaSpike und vielen anderen Apache-Projekten. Als Java Expert Group Member arbeitet er aktiv an der CDI und anderen EE-Spezifikationen mit. Er arbeitet unter anderem für die Research Group for Industrial Software (INSO) der TU Wien.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: