"
 
 1 Einführung in CDI
"
 
 2 CDI Grundkonzepte
"
 
 3 CDI und Java EE
"
 
 4 Portable CDI-Erweiterungen
"
 
 5 Apache DeltaSpike
"
 
 6 CDI Lite
"
 
 7 CDI oder nicht CDI
"
 
 8 CDI in der Cloud

4 Portable CDI-Erweiterungen

Im Dezember 2009 war es soweit und CDI stand in einer ersten Version als Teil von Java EE6 zur Verfügung. Das vorherige Kapitel hat gezeigt, dass CDI sehr früh mit anderen Java EE6 Spezifikationen wie bspw. JSF, EJB, u.v.m. integriert wurde. Allerdings gibt es bei der standardmäßigen Integration teilweise Verbesserungspotential. Dieses wurde in einigen Fällen schnell erkannt und bei der nächsten Gelegenheit, dem Release von Java EE7, nachgereicht. Abgesehen davon können bestimmte Funktionalitäten wie bspw. die Integration anderer Frameworks nur schwer oder gar nicht durch Spezifikationen abgedeckt werden. Damit diese Einschränkungen bei der täglichen Arbeit nicht zu einem limitierenden Faktor werden, wurde das SPI (Service Provider Interface) von CDI sehr flexibel gehalten. Dadurch wird es möglich sogenannte portable CDI Erweiterungen zu implementieren, um das Standard-API von CDI zu erweitern und mögliche Lücken zu schließen. Mittlerweile gibt es viele solcher Erweiterungen. In diesem Kapitel erhalten Sie Grundkenntnisse, mit deren Hilfe Sie CDI relativ einfach erweitern können. Darüber hinaus ermöglichen diese Grundlagen die Analyse und evt. Verbesserung von bestehenden CDI-Erweiterungen.

 

Im weiteren Sinne handelt es sich bei portablen CDI-Erweiterungen um Aufsätze für CDI, welche nur auf Basis des APIs und SPIs von CDI implementiert sind. Sofern ein paar Feinheiten der CDI-Implementierungen beachtet werden, ist ein solcher Aufsatz mit jeder spezifikationskonformen Implementierung kompatibel. Im Optimalfall gilt dies auch für den Einsatz in Java EE6+ Servern. Hier kann jedoch die tiefe Integration von CDI und die unterschiedliche Auslegung von Teilen der Spezifikation eine zusätzliche Herausforderung darstellen. Bei den BDA-Regeln haben wir im Kapitel CDI und Java EE bereits verschiedene Interpretationen kurz kennengelernt.

 

Von einer portablen Erweiterung im engeren Sinne spricht man, wenn das Marker-Interface javax.enterprise.inject.spi.Extension implementiert wird, um bspw. während des Startprozesses der Applikation das Default-Verhalten abzuändern oder zu erweitern. Dieses Marker-Interface wird nur benötigt, um die entsprechenden Implementierungen für den ServiceLoader Mechanismus ( java.util.ServiceLoader ) zu konfigurieren. Implementierungen dieses Interfaces können beliebig viele CDI-Observer Methoden enthalten, welche vordefinierte Phasen des Container-Lifecycles überwachen. Abhängig von der Lifecycle-Phase können Sie die Standardkonzepte von CDI mit eigenen Mechanismen erweitern, um zusätzliche Anwendungsfälle möglichst komfortabel zu unterstützen.

4.1 Der Container-Lifecycle von CDI

Bevor wir mit einer konkreten Erweiterung beginnen, sehen wir uns den Container-Lifecycle im Detail an, da dieser die Basis für die erfolgreiche Implementierung von Extension-Klassen ist. Der Lifecycle besteht grundsätzlich aus zwei Hauptteilen. Zu Beginn steht der Containerstart, auch Bootstrapping-Prozess genannt, und am Ende der Containerstopp. Während des Containerstartes können Bean-Definitionen optional verändert, hinzugefügt oder entfernt werden. Dies ermöglicht die Integration mit beinahe beliebigen Frameworks, welche selbst keine explizite CDI-Unterstützung zur Verfügung stellen. Im nachfolgenden Teil lernen wir sämtliche Events des Lifecycles kennen.

 

Wie eingangs erwähnt notifiziert der CDI-Container aktive Implementierungen von javax.enterprise.inject.spi.Extension durch CDI-Events. Für die Konfiguration und somit die Aktivierung eigener Extension -Klassen wird das Service-Loader Konzept, welches mit JDK6 eingeführt wurde, verwendet. Mit Hilfe der Klasse java.util.ServiceLoader werden im Falle von CDI alle konfigurierten Implementierungen des Markerinterfaces javax.enterprise.inject.spi.Extension durch den CDI-Container abgefragt, welche in einer oder mehreren Dateien namens /META-INF/services/javax.enterprise.inject.spi.Extension konfiguriert sind. Je Archiv, bspw. einer JAR-Datei, ist eine solche Konfigurationsdatei mit beliebig vielen Einträgen möglich. Ein Eintrag besteht aus dem vollständig qualifizierten Klassennamen einer Implementierung des zuvor erwähnten Markerinterfaces. Für jeden dieser Einträge erzeugt der CDI-Container eine Instanz, welche für die gesamte Applikation gültig ist. Hierbei handelt es sich um eine fixe Vorgabe. Die explizite Angabe eines Scopes ist dadurch nicht vorgesehen. Wie die Bezeichnung Markerinterface bereits vermuten lässt, sind durch javax.enterprise.inject.spi.Extension keine Methodensignaturen vorgegeben, wodurch nicht zu jedem Lifecycle-Event eine Methode zur Verfügung gestellt werden muss.

 

CDI-Events des Container-Lifecycles können in aktiven Extension -Klassen mit Hilfe von Observer-Methoden überwacht werden. Manche der Lifecycle-Events feuert der CDI-Container einmalig und andere für jedes gefundene Artefakt wie bspw. Beans. Die meisten dieser Events stehen bereits seit CDI 1.0 zur Verfügung. CDI 1.1 führt mit AfterTypeDiscovery , ProcessInjectionPoint und ProcessBeanAttributes drei zusätzliche Events ein, auf welche ebenfalls im nachfolgenden Abschnitt eingegangen wird.
Tipp: Selbst Extension s sind später in andere CDI-Beans injizierbar. Durch diesen Ansatz ist es möglich während des Applikationsstartes Informationen zu sammeln, um diese zu einem späteren Zeitpunkt zu verwenden.
Im nachfolgenden Teil erfahren Sie mehr über die einzelnen Lifecycle-Events und deren Einsatzgebiete.

4.1.1 BeforeBeanDiscovery

Bevor mit der Verarbeitung der Bean-Kandidaten begonnen wird, feuert der CDI-Container das Event BeforeBeanDiscovery . Über dieses Event lassen sich Qualifier-, Scope-, Stereotyp- und Interceptor- Annotationen hinzufügen, welche nicht sämtliche Vorgaben der CDI-Spezifikation erfüllen. Darüber hinaus ist es sogar möglich für Klassen außerhalb von BDAs (Bean Deployment Archiv) einen sog. AnnotatedType , die Vorstufe von Managed-Beans, zu erzeugen und über dieses Event hinzuzufügen. Die später daraus resultierenden Beans unterscheiden sich kaum von Beans, welche regulär in einem BDA enthalten sind. Einen kleinen Unterschied gibt es jedoch auch hier durch BDAs. Für solche Bean-Kandidaten existiert keine beans.xml -Datei, wodurch nur global (Interceptor-,...) Konfigurationen, welche seit CDI 1.1 verfügbar sind, für die endgültigen Beans verwendbar sind. Abgesehen von der Einschränkung in Kombination mit BDAs ermöglicht die manuelle Registrierung von AnnotatedType s die Integration beliebiger Archive und Konfigurationsformate, um die standardmäßige Suche nach Bean-Kandidaten zu erweitern.

4.1.2 ProcessAnnotatedType

Nach den manuellen Registrierungen via BeforeBeanDiscovery führt der CDI-Container einen Scan aller BDAs durch und erstellt für jede potentielle Bean-Klasse einen sog. AnnotatedType , welcher über ein Event mit dem Namen ProcessAnnotatedType gefeuert wird. Dieses Event ist das am häufigsten verwendete Event im Bootstrapping-Prozess, da portable CDI-Erweiterungen über dieses Event Bean-Definitionen verändern oder entfernen können. In IdeaFork nutzen wir dies, um im zweiten Teil dieses Kapitels bestimmte Typen für den CDI-Container zu deaktivieren. Eine solche Deaktivierung von Bean-Kandidaten ist durch den Aufruf der AnnotatedType -Methode #veto in einer Observer-Methode für das ProcessAnnotatedType -Event möglich.

4.1.3 AfterTypeDiscovery

Dieses Lifecycle-Event ist seit CDI 1.1 verfügbar und wird gefeuert sobald sämtliche Typen registriert wurden. Die Methoden #getAlternatives , #getInterceptors und #getDecorators retournieren Listen der global aktivierten Klassen. Sowohl die Reihenfolge als auch der Inhalt der Listen kann zu diesem Zeitpunkt noch verändert werden. Globale Aktivierungen sind seit CDI 1.1 mit Hilfe der Annotation @Priority standardisiert. Wird bspw. ein Interceptor nur für ein BDA via beans.xml aktiviert, so ist dieser hier nicht enthalten.

 

Mit #addAnnotatedType ist es möglich zusätzlich eigene Typen hinzuzufügen. Danach kennt der CDI-Container alle Definitionen, für welche auch tatsächlich Bean-Definitionen erstellt werden sollen. Alle via #veto exkludierten Typen sind spätestens jetzt nicht mehr in den internen Datenstrukturen des CDI-Containers vorhanden. Mit ProcessBeanAttributes , auf welches wir etwas später im Detail eingehen, ist seit CDI 1.1 ein solches Veto auch auf Basis der endgültigen Bean-Metadaten möglich.

4.1.4 ProcessInjectionPoint

Dieses ebenfalls mit CDI 1.1 eingeführte Event wird für jeden Injection-Point einer verwalteten Ressource aufgerufen. Hierzu zählen nicht nur CDI-Beans, sondern auch Artefakte wie bspw. EJBs. Dieses Lifecycle-Event stellt seit CDI 1.1 den ersten Schritt in der Phase zur Definition der finalen Managed-Bean-Metadaten dar. Davor war dies das nachfolgende Event namens ProcessInjectionTarget .

 

ProcessInjectionPoint wird nicht nur für Injection-Points in registrierten Typen, sondern auch für Injection-Points aktiver Observer-Methoden und Producer erzeugt. Via #getInjectionPoint und #setInjectionPoint können Sie in speziellen Fällen den jeweiligen Injection-Point überprüfen bzw. ersetzen/dekorieren. Wird bei der Überprüfung eine (für die Applikation) ungülte Definition gefunden, so kann der Applikationsstart durch den Aufruf von #addDefinitionError(Throwable) abgebrochen werden.

4.1.5 ProcessInjectionTarget

Das Event ProcessInjectionTarget ist auf den Typ der Komponente typisiert und wird für jede Komponente (inkl. Java EE Komponenten) erzeugt, welche Injizierung unterstützt. javax.enterprise.inject.spi.InjectionTarget ist für die gesamte Erzeugung und Zerstörung, inklusive der Befüllung von Injection-Points, sowie für Aufrufe von Callback-Methoden (Post-Construct- und Pre-Destroy-Callbacks) verantwortlich. Da über dieses Event eine eigene Implementierung von InjectionTarget , bzw. ein Wrapper für die Default-Implementierung gesetzt werden kann, ist es auf einfache Weise möglich die erwähnten Prozesse zu erweitern oder anzupassen. Allerdings handelt es sich hierbei um sehr spezielle und tiefreichende Eingriffe, welche nur in seltenen Fällen erforderlich sind. Etwas interessanter ist hingegen die Auswertung der dazugehörenden AnnotatedType -Instanz, welche über #getAnnotatedType verfügbar ist. Über diese Instanz sind eigene Validierungen durchführbar und im Fehlerfall können Sie den Containerstart via #addDefinitionError(Throwable) abbrechen.

4.1.6 ProcessBeanAttributes

Dieses Lifecycle-Event stellt das letzte mit CDI 1.1 eingeführte Event in unserer Beschreibung des Lifecycles dar. Es wird für jedes Bean, sowie Interceptoren und Decoratoren gefeuert und erlaubt mit Hilfe von #getBeanAttributes und #getAnnotated die bestehenden Metadaten zu überprüfen. Bei Bedarf ist es durch den Aufruf von #addDefinitionError(Throwable) möglich einen Fehler zu melden, wodurch wie üblich der Containerstart abgebrochen wird. In seltenen Fällen kann es erforderlich sein die bestehenden Bean-Attribute mit #setBeanAttributes zu verändern oder das gesamte Bean durch den Aufruf von #veto zu deaktivieren.

4.1.7 ProcessProducer

Für jeden CDI-Producer, sowohl Producer-Methoden als auch Producer-Felder, wird dieses Lifecycle-Event gefeuert. Das Interface dieses Events erlaubt die Validierung des Producers und bei Bedarf kann mit dem Aufruf von #addDefinitionError(Throwable) der Applikationsstart abgebrochen werden. Darüber hinaus kann der Producer auch verändert werden. Anpassungen sollten jedoch nur mit einem Wrapper vorgenommen werden, welcher zumindest teilweise Aufrufe an die ursprüngliche Instanz delegiert. Um Fehler wie bspw. Memory-Leaks zu vermeiden, ist es empfehlenswert eine vollständige Implementierung des Producer -Interfaces mit Vorsicht umzusetzen.

4.1.8 ProcessBean

Bevor ein aktives Bean effektiv registriert wird, feuert der CDI-Container das Event ProcessBean . Dies stellt den letzten Punkt vor der Registrierung des entsprechenden Beans dar. Zu diesem Zeitpunkt gibt es bereits die endgültige Managed-Bean Definition (Instanz von javax.enterprise.inject.spi.Bean<T> ). Somit besteht die Möglichkeit die finale Repräsentation der Managed-Bean Metadaten zu überprüfen und bei Bedarf den Containerstart via #addDefinitionError(Throwable) abzubrechen. Sind Sie nur an einem bestimmten Managed-Bean Typ interessiert, dann ist es möglich die konkreten Untertypen ProcessManagedBean , ProcessProducerMethod und ProcessProducerField anzugeben. Mit ProcessBean selbst können Sie alle Subtypen des Events überwachen.

4.1.9 ProcessObserverMethod

Für Observer-Methoden gibt es ein separates Event namens ProcessObserverMethod , da für die Validierung solcher Methoden andere Metadaten erforderlich sind. Neben der Bean-Klasse und den Qualifier-Annotationen stehen auch sämtliche Informationen über die Methode selbst zur Verfügung. Das Grundkonzept entspricht dem von ProcessBean .

4.1.10 AfterBeanDiscovery

Sobald der CDI-Container mit dem Verarbeitungsprozess inklusive der Validierung aktiver Managed-Beans fertig ist, wird das Event AfterBeanDiscovery gefeuert. Abgesehen von der bereits bekannten Methode #addDefinitionError(Throwable) stellt dieses Event Methoden für die manuelle Registrierung von eigenen Managed-Beans, Observer-Methoden und CDI-Context Implementierungen zur Verfügung. Wird ein eigenes Managed-Bean mit Hilfe der Methode #addBean hinzugefügt, so wird das zuvor vorgestellte Event ProcessBean gefeuert, bevor das Bean effektiv hinzugefügt wird. Zu beachten ist, dass es sich hierbei nicht um vollwertige Managed-Beans handelt, da bspw. keine Interceptoren unterstützt werden. Solche Beans sind somit mehr mit Producern vergleichbar und ermöglichen bspw. die Integration anderer Bean-Container.

4.1.11 AfterDeploymentValidation

AfterDeploymentValidation stellt das letzte Event dar, bevor der CDI-Container vollständig gestartet ist. Zu diesem Zeitpunkt müssen sämtliche Validierungen, für welche der Container verantwortlich ist, abgeschlossen sein. Sollten Sie in einem Observer für dieses Event eine invalide Situation in der Applikation feststellen, so können Sie mit Hilfe der Methode #addDeploymentProblem das erfolgreiche Deployment der Applikation auch an diesem Punkt noch verhindern.
Tipp: Das Event AfterDeploymentValidation wird oft zur manuellen Implementierung eines Startup-Events verwendet. Dies funktioniert allerdings nur beschränkt und ermöglicht somit keine vollständig portable Implementierung. Aus diesem Grund wurde die Annotation @Initialized mit CDI 1.1 eingeführt. Seit CDI 1.1 ist somit der bevorzugte Ansatz zur Implementierung von portabler Initialisierungslogik ein Observer mit dem Qualifier @Initialized(ApplicationScoped.class) .

4.1.12 BeforeShutdown

Dieses Lifecycle-Event wird gefeuert, bevor der CDI-Container gestoppt wird und ermöglicht bspw. die explizite Freigabe von geöffneten Ressourcen, bevor die Applikation beendet wird.

4.2 Eigene CDI Erweiterungen entwickeln

Um eine eigene portable CDI-Erweiterung zu entwickeln, muss nicht jedes der zuvor vorgestellten Events überwacht werden. Die Stärke von CDI-Events, die absolute Entkoppelung zwischen Erzeuger und den Observer-Methoden, wird auch hier genutzt, um CDI-Erweiterungen möglichst leichtgewichtig zu halten. In IdeaFork möchten wir das ProcessAnnotatedType -Event dazu verwenden, um JPA-Entitäten für den CDI-Container zu deaktiviren. Wie bereits im Kapitel CDI und Java EE erwähnt, sollen Instanzen einer Klasse nur durch einen Container verwaltet werden, da es sonst zu Seiteneffekten mit den verschiedenen Proxy-Bibliotheken kommen kann. JPA-Entitäten können wir anhand der Annotation @Entity erkennen. Diesen Umstand nutzen wir, um Entity-Klassen für den CDI-Container unsichtbar zu machen. Hierfür legen wir die Klasse EntityVetoExtension an und implementieren das Marker-Interface javax.enterprise.inject.spi.Extension . Damit eine Extension-Klasse durch den CDI-Container gefunden und verwendet wird, müssen Sie diese in einer Datei namens /META-INF/services/javax.enterprise.inject.spi.Extension vollständig qualifiziert angeben. Listing Aktivierung einer portable CDI Erweiterung zeigt dies für unsere EntityVetoExtension .
//content of /META-INF/services/javax.enterprise.inject.spi.Extension
at.irian.cdiatwork.ideafork.core.impl.infrastructure.EntityVetoExtension

Vorerst sind wir nur an dem Event ProcessAnnotatedType interessiert und fügen daher eine entsprechende Observer-Methode hinzu. Wie Listing Eine Klasse für CDI exkludieren illustriert, ist es möglich die Implementierung sehr einfach zu halten. Über ProcessAnnotatedType#getAnnotatedType#getJavaClass können wir direkt auf die zugrundeliegende Klasse zugreifen. In unserem Fall nehmen wir jedoch eine Abkürzung über ProcessAnnotatedType#getAnnotatedType#isAnnotationPresent . Sobald wir die Annotation @Entity vorfinden, deaktivieren wir mit dem Aufruf ProcessAnnotatedType#veto den dazugehörigen AnnotatedType . Die nachfolgenden Container-Lifecycle-Events werden für diesen AnnotatedType somit nicht mehr gefeuert. Die Entity-Klassen sind außerdem nicht mehr via @Inject in andere Beans injizierbar, da sie der CDI-Container durch den Aufruf der #veto -Methode nicht mehr kennt.
public class EntityVetoExtension implements Extension {
    protected void excludeEntityClasses(
      @Observes ProcessAnnotatedType pat) {
        if (pat.getAnnotatedType().isAnnotationPresent(Entity.class)) {
            pat.veto();
        }
    }
}
Wie zuvor erwähnt, können wir auch Validierungen von Beans mit Hilfe von CDI-Erweiterungen umsetzen. Die im Kapitel CDI und Java EE aufgezeigten Kombinationsmöglichkeiten von CDI, EJBs und JSF sind vielfältig. Ein paar der erwähnten Fallstricke sind nicht unmittelbar erkennbar bzw. können sich erst im produktiven Betrieb auswirken und setzen teilweise ein gutes Detailwissen voraus. Um problematischen Konstellationen entgegenzuwirken und gleichzeitig eine etwas einheitlichere Applikationsstruktur sicherzustellen, validieren wir mit Hilfe einer zusätzlichen CDI Erweiterung applikationsspezifische Richtlinien. Dem Umfang für Validierungsregeln von Implementierungsdetails sind kaum Grenzen gesetzt. Sämtliche Mechanismen, welche durch die std. Reflection-API zur Verfügung stehen, können für die Validierung der Applikationsstruktur genutzt werden.

 

In IdeaFork beschränken wir uns auf die Überprüfung von View-Controller-Beans und das Package für Services. Im vorherigen Kapitel haben wir teilweise EJBs als View-Controller verwendet. Dadurch haben wir uns in diesen Fällen ein eigenständiges transaktionales Service erspart. In komplexeren Applikationen ist es allerdings oftmals erforderlich, dass nur ein Teil der JSF Action-Methode(n) transaktional ausgeführt wird. Aus diesem und anderen Gründen, welche bereits im Kapitel CDI und Java EE erwähnt wurden, soll unsere erste Validierungsregel daher sicherstellen, dass EJBs nicht gleichzeitig mit @ViewController annotiert sind. Jeder Regelverstoß soll aufgezeichnet werden. Am Ende des Bootstrapping-Prozesses wollen wir im Fehlerfall den Applikationsstart abbrechen und alle Verstöße gesammelt als Grund für das Deployment-Problem angeben.
Da wir die effektiven Bean-Metadaten validieren möchten, verwenden wir in Listing Applikationsregeln validieren einen Observer für das Event ProcessManagedBean . Die Methode #getAnnotatedBeanClass gibt nicht direkt die Klasse selbst zurück, sondern eine Instanz vom Typ AnnotatedType . Über diese Instanz können wir nicht nur physisch verfügbare Metadaten überprüfen, sondern auch evt. dynamisch hinzugefügte, welche später effektiv für den CDI-Container sichtbar sind. Soll hingegen nur die jeweils physische Klasse und deren Metadaten geprüft werden, so können Sie die Methode #getJavaClass von AnnotatedType verwenden. Regelverstöße sammeln wir als Fehlermeldungen in einer Liste. In einem zweiten Observer, dieses Mal für das Event AfterDeploymentValidation , werten wir die gefundenen Verstöße aus. Über ProcessManagedBean könnten wir zwar ebenfalls den Startprozess abbrechen, jedoch wäre hier eine gesammelte Ausgabe aller Regelverstöße nicht möglich.
public class AppStructureValidationExtension implements Extension {
  private static final Logger LOG = Logger.getLogger(/*...*/);
  private List<String> violations = new ArrayList<String>();

  public void validateArtifacts(
    @Observes ProcessManagedBean pmb) {
      if (pmb.getAnnotatedBeanClass()
        .isAnnotationPresent(ViewController.class)) {
          validateViewController(pmb.getAnnotatedBeanClass());
      }
  }

  private void validateViewController(AnnotatedType annotatedType) {
    for (Annotation annotation : annotatedType.getAnnotations()) {
      if (annotation.annotationType()
        .getPackage().getName().equals("javax.ejb")) {
          this.violations.add(/*...*/); //violation message
      }
    }
  }

  public void checkAndAddViolations(
    @Observes AfterDeploymentValidation afterDeploymentValidation) {
      if (this.violations.isEmpty()) {
        LOG.info(/*...*/); //success message
        return;
      }

      StringBuilder violationMessage = new StringBuilder();

      for (String violation : this.violations) {
        violationMessage.append(violation);
      }
      this.violations.clear();
      afterDeploymentValidation.addDeploymentProblem(
        new IllegalStateException(violationMessage.toString()));
  }
}
Mit einer zweiten Regel wollen wir sicherstellen, dass alle Beans in einem Service-Package als @Stateless -EJBs umgesetzt wurden. Zusätzlich überprüfen wir Beans welche mit @javax.ejb.Singleton annotiert sind und loggen ein Warnung, da durch Singleton-EJBs mit ConcurrencyManagementType.CONTAINER schnell ein ungewollter Flaschenhals in der Applikation entstehen kann bzw. Beans mit ConcurrencyManagementType.BEAN nur in seltenen Fällen wirklich erforderlich sind. Eine Warnung soll ebenfalls gelogged werden, wenn View-Controller-Beans nicht einer vorgegebenen Namenskonvention entsprechen. Listing Erweiterte Applikationsregeln validieren zeigt die erforderlichen Änderungen im Vergleich zu Applikationsregeln validieren .
public class AppStructureValidationExtension implements Extension {
  //...
  public void validateArtifacts(
    @Observes ProcessManagedBean pmb) {
      //...
      if (pmb.getAnnotatedBeanClass().getJavaClass()
        .getPackage().getName().endsWith(".service")) {
          validateService(pmb.getAnnotatedBeanClass());
      }
      if (pmb.getAnnotatedBeanClass()
        .isAnnotationPresent(Singleton.class)) {
          validateSingletonEjb(pmb.getAnnotatedBeanClass());
      }
  }

  private void validateViewController(AnnotatedType annotatedType) {
    //...
    if (!annotatedType.getJavaClass().getName().endsWith("ViewCtrl")) {
      LOG.warning(/*...*/);
    }
  }

  private void validateService(AnnotatedType annotatedType) {
    if (!annotatedType.isAnnotationPresent(Stateless.class)) {
      this.violations.add(/*...*/);
    }
  }

  private void validateSingletonEjb(AnnotatedType annotatedType) {
    ConcurrencyManagement cmAnnotation =
      annotatedType.getAnnotation(ConcurrencyManagement.class);

    if (cmAnnotation == null ||
      ConcurrencyManagementType.CONTAINER == cmAnnotation.value()) {
        LOG.warning(/*...*/);
    } else if (ConcurrencyManagementType.BEAN == cmAnnotation.value()) {
      LOG.warning(/*...*/);
    }
  }
}
Die eben selbst definierten Applikationsrichtlinien führen mit dem aktuellen Stand von IdeaFork zu zwei Warnungen und fünf Verstößen. Die erforderlichen Anpassungen sind sehr einfach. Neben dem Austausch von einzelnen Annotationen gegen äquivalente Annotationen, welche ebenfalls im Kapitel CDI und Java EE vorgestellt werden, wird vor allem ein zusätzliches EJB namens IdeaService erforderlich. Sämtliche Änderungen dieser Umstellung sowie die Aktivierung von AppStructureValidationExtension sind im Git-Repository von IdeaFork in einem Commit zusammengefasst.

 

Den Erweiterungsmöglichkeiten sind nur wenige Grenzen gesetzt, wodurch CDI beinahe mit beliebigen Konzepten erweiterbar ist. Das nachfolgende Kapitel, zum Thema Apache DeltaSpike , illustiert weitere Möglichkeiten wie CDI portabel erweitert werden kann.