Home ORDIX AG             Dienstleistung             Trainingsshop    Kunden / Referenzen Aktuelles    Kontakt
Home  Pfeil  ORDIX News  Pfeil  1/2007  Pfeil  Java/J2EE
suche: 
Dieser Artikel richtet sich an Entwickler, die einen Überblick über die Implementierung von Session Beans im Kontext der EJB 3.0 Spezifikation unter Nutzung von Annotations erhalten möchten.

Glossar

Annotations
Anmerkungen im Java Source Code, die zur Laufzeit des Programms ausgewertet werden können. Annotations sind seit Java 5 Bestandteil von Java. Als geistigen Vater dieser Annotations kann man das Open Source Projekt XDoclet ansehen. XDoclet erlaubte es, auch schon in früheren Java Ver­sionen mit Annotations zu programmieren.
EJB
Enterprise Java Beans (EJB) sind standardisierte Komponenten, aus denen J2EE-konforme Anwendungen erstellt werden, die auf einem J2EE-Application Server laufen. Für unterschiedliche Zwecke definiert der J2EE-Standard verschiedene Arten von EJBs wie z. B. Session Beans, Entity Beans und Message-driven Beans.
Session Bean
Session Beans sind EJBs, die insbesondere Vorgänge abbilden, die der Nutzer mit dem System durchführt.
POJI
Plain Old Java Interface. Dabei handelt es sich um ein normales Interface in der Programmiersprache Java.
POJO
Plain Old Java Object. Dabei handelt es sich um ein normales Objekt in der Programmiersprache Java.
Home Interface
Deklariert die Lebenszyklusmethoden einer EJB. d. h. Methoden zur Erzeugung, Löschung usw. von EJBs. Bis EJB 2.x vorgeschrieben und in zwei Ausprägungen (remote, local) vorhanden.
Component Interface
Deklariert die fachlichen Geschäftsmethoden einer EJB. Bis EJB 2.x vorgeschrieben und in zwei Ausprägungen (remote, local) vorhanden.

Reihe EJB 3.0 (Teil II): Keep it Simple

An dieses Motto haben wohl die Entwickler der EJB-Spezifikation gedacht, als sie die Version EJB 3.0 ins Leben gerufen haben. Wer sich mit EJB 2.x befasst hat, kennt die große Anzahl der EJB-Artefakte und den damit verbundenen Implementierungsaufwand, der für eine lauffähige EJB notwendig war. Und genau das ändert sich in EJB 3.0 - nicht zuletzt durch den massiven Einsatz von Annotations.

Im ersten Teil der EJB 3.0 Reihe haben wir uns die konzeptionellen Unterschiede zwischen Annotations und XDoclet angeschaut. Dieser Teil geht nun genauer auf die Annotations im Kontext von EJB 3.0 ein. Um den Rahmen nicht zu sprengen, werden hier nur die wichtigsten Annotations einer Session Bean vorgestellt.

Damit die Annotation-Vorstellung nicht zu einer zu trockenen Aufzählung ausufert, wird in Abbildung 1 und 2 fast jede Annotation auch in ihrem "natürlichen Lebensraum", dem Source Code, gezeigt.

package de.ordix.ejb.examples;
	
@javax.ejb.Remote
public interface Addressbook {
  public java.util.List searchContactByName(String name);
  public Contact changeContact(Contact c);
  public Contact insertContact(Contact c);
  public void removeContact(int cPK);
  public void clearAllContacts();
  public void logout();
}
Abb. 1: Beispiel eines Business Interface.

Das Beispiel stellt eine abstrakte Implementierung einer Adressbuch Stateful Session Bean vor. Weil der Schwerpunkt dieses Artikels auf den Annotations liegt, sind die Methoden nicht ausimplementiert.

In Abbildung 3 ist jede hier vorgestellte Annotation mit ihrem voll qualifizierten Namen aufgeführt, da der Artikeltext aus Gründen der Lesbarkeit darauf verzichtet und jeweils den reinen Annotation-Namen verwendet.

package de.ordix.ejb.examples;
	
import javax.annotation.*;
import javax.annotation.security.*;
import javax.ejb.*;
import javax.sql.DataSource;
	
@Stateful
@DeclareRoles ({"userA", "userB", "admin"})
@TransactionManagement (TransactionManagementType.CONTAINER)
@TransactionAttribute (TransactionAttributeType.REQUIRED)
public class AddressbookBean implements Addressbook {
  @Resource
  SessionContext ctx;
  @Resource (name="myDB")
  DataSource contactDB;
  @EJB
  ContactChecker checker; 
	
  @PostConstruct
  @PostActivate
  public void initRemoteConnection() {
    // initialisiert die Remote Verbindung
  }
  @PreDestroy
  @PrePassivate
  public void closeRemoteConnection() {
    // schließt die Remote Verbindung
  }
  @PermitAll
  @TransactionAttribute (TransactionAttributeType.NEVER)
  public java.util.List searchContactByName(String name) {
    // sucht alle Kontakte gemäß dem übergebenen Namen
  }
  @RolesAllowed({"userA", "admin"})
  public Contact changeContact(Contact c) {
    // ändert einen Kontakt
  }
  @RolesAllowed ({"userA", "admin"})
  public Contact insertContact(Contact c) {
    // fügt einen neuen Kontakt hinzu
  }
  @RolesAllowed ({"userA", "admin"})
  public void removeContact(int cPK) {
    // löscht einen Kontakt
  }
  @RolesAllowed ({"admin"})
  public void clearAllContacts() {
    // löscht alle Kontakte
  }
  @Remove
  public void logout() {
    // Ausloggen
  }
} // class AddressbookBean 
Abb. 2: Beispiel einer Stateless Session Bean.

Business Interface

Session Beans beinhalten nach der EJB-Spezifikation die Geschäfts­logik einer EJB-Anwendung. Über welche Methoden auf diese Geschäftslogik zugegriffen wird, deklariert die Geschäftsschnittstelle, in EJB 2.x auch Component Interface genannt.

Diese wird in EJB 3.0 als einfaches Plain Old Java Interface (POJI) implementiert und nennt sich Business Interface.

Ob die Geschäftsschnittstelle lokal oder entfernt zugreifbar sein soll, gibt die Annotation @Local bzw. @Remote auf Klassenebene an. Wird keine Annotation verwendet, so ist die Geschäftsschnittstelle gemäß der Spezifikation lokal.

Unter EJB 3.0 ist es nicht mehr notwendig, dass die Geschäftsschnittstelle von javax.ejb.EJBObject oder javax.ejb.EJBLocalObject erbt. Damit müssen die Geschäftsmethoden auch keine java.rmi.Remote­Exception mehr deklarieren und die oft kritisierte, technische "Verschmutzung" verschwindet aus dieser rein fachlichen Schnittstelle.

@javax.ejb.Local
@javax.ejb.Remote
@javax.ejb.Stateless
@javax.ejb.Stateful
@javax.ejb.Remove
@javax.annotation.PostConstruct
@javax.annotation.PreDestroy
@javax.ejb.PostActivate
@javax.ejb.PrePassivate
@javax.ejb.TransactionManagement
@javax.ejb.TransactionAttribute
@javax.annotation.security.DeclareRoles
@javax.annotation.security.DenyAll
@javax.annotation.security.PermitAll
@javax.annotation.security.RolesAllowed
@javax.annotation.security.RunAs
@javax.ejb.EJB
@javax.annotation.Resource
Abb. 3: Voll qualifizierte Annotation-Namen.

Session Bean

Eine Session Bean ist in EJB 3.0 ein normales Plain Old Java Object (POJO), das nicht von javax.ejb.SessionBean erben muss. Dieses ist nicht automatisch eine Session Bean, sondern muss erst mit den Annotations @Stateless oder @Stateful auf Klassenebene annotiert werden.

Wie an den Annotation-Namen leicht erkennbar ist, definiert @Stateless eine Stateless Session Bean und @Stateful eine Stateful Session Bean. Der Annotation können optional zwei Parameter übergeben werden: ein eindeutiger Name und eine Beschreibung der Session Bean.

Wird kein Name angegeben, so wird als Default-Wert stattdessen der nicht voll qualifizier­te Klassenname verwendet. Die Session Bean Klasse muss natürlich die weiter oben be­schriebene(n) Geschäftsschnittstelle(n) implementieren.

Home sweet home …

Home Interfaces (lokal oder entfernt), wie aus EJB 2.x bekannt, sind in EJB 3.0 nicht mehr zwingend vorgeschrieben. Es stellt sich daher die Frage, wo in EJB 3.0 die sonst im Home Interface deklarierten Lebenszyklus-Methoden deklariert werden?

Des Rätsels Lösung: Anstelle der Lebenszyklus-Methoden werden jetzt beliebige Geschäftsmethoden mit entsprechenden Annotations versehen. Eine Ausnahme bilden hier die aus frü­heren EJB-Versionen bekannten create-Methoden, zu denen es keine entsprechenden Annotations gibt.

Zur Initialisierung einer Session Bean in EJB 3.0 muss der Client entsprechende Geschäftsmethoden aufrufen, nachdem der EJB-Container eine Bean-Instanz erzeugt hat. Diese Geschäftsmethoden müssen auch nicht mit dem Präfix "create" beginnen, sondern können beliebige Namen besitzen.

Anstelle der remove-Methode werden eine oder mehrere Geschäftsmethoden mit der Annotation @Remove versehen. Ruft der Client eine dieser Methoden auf, so wird die Verbindung zwischen dem Client und der ihm zugeordneten Stateful Session Bean vom EJB-Container aufgehoben.

Für Stateless Session Beans ist das nicht notwendig, da keine explizite Bindung zwischen einem Client und der Bean über einen Methodenaufruf hinweg existiert.

Die restlichen Lebenszyklus-Methoden aus dem Home Interface werden auf die Annotations @PostConstruct, @PreDestroy, @Post­Activate und @PrePassivate abgebildet und sind auf der Methodenebene angesiedelt. Dabei darf jede Annotation maximal einmal pro Session Bean benutzt werden.

Die annotierten Methoden werden nach bzw. vor der jeweiligen Aktion (Erzeugung, Passivierung und Zerstörung) aufgerufen. Die beiden letzten Annotations können nur bei Stateful Session Beans verwendet werden, da Stateless Session Beans nicht passiviert werden.

Transaktionsmanagement

Auch das Transaktionsmanagement kann per Annotations direkt in der Session Bean Klasse angegeben werden. Hierfür sind die beiden Annotations @TransactionManagement und @TransactionAttribute zuständig.

Die erste Annotation gibt an, ob Container Managed Transactions (CMT) oder Bean Managed Transactions (BMT) eingesetzt werden. Sie ist auf Klassenebene anzugeben und kann folgende Formen haben:

Der Parameter vom Typ TransactionManagement ist nicht zwingend anzugeben. In diesem Fall gilt der Default-Wert TransactionManagementType.CONTAINER.

Die @TransactionAttribute Annotation kann sowohl auf Klassen- als auch auf Methodenebene angegeben werden. Auf Klassen­ebene gilt die Einstellung für alle Geschäftsmethoden der Ses­sion Bean. Bei Anwendung auf der Methodenebene hat die Einstellung nur Auswirkungen auf die jeweilige Methode.

Als Parameter erwartet die Annotation einen Parameter vom Typ TransactionAttributeType. Die Enumeration TransactionAttributeType definiert hierfür die folgenden Konstanten:

Wird kein Parameter angegeben, so gilt der Default-Wert TransactionAttributeType.REQUIRED.

Sicherheitseinstellungen

Für Sicherheitseinstellungen basierend auf JAAS definiert EJB 3.0 die fünf Annotations

@DeclareRoles ist auf Klassenebene angesiedelt und ermög­licht es, die für die Session Bean relevanten Rollen zu definieren. Als Parameter erwartet die Annotation ein String Array von Rollen­namen.

Die Annotation @PermitAll gibt an, dass eine Methode von allen Rollen ausgeführt werden darf. Wird diese Annotation auf Klassenebene eingesetzt, so gilt die Einstellung für alle Methoden der Session Bean. Auf Methodenebene verwendet, gilt sie nur für die jeweilige Methode.

Ähnlich wie @PermitAll ist die Annotation @RolesAllowed. Diese Annotation erwartet eine Liste von Rollennamen, für die der Methodenzugriff seitens des Containers erlaubt wird. Auch diese Annotation ist auf Klassen- und Methodenebene anwendbar.

Die Annotation @DenyAll bewirkt das Gegenteil zu @PermitAll. Keiner Rolle ist es erlaubt, eine mit @DenyAll annotierte Methode aufzurufen.

Praktisch hat das zur Folge, dass eine solche Methode nicht innerhalb des EJB-Containers aufgerufen werden kann. Diese Annotation ist nur auf Methodenebene zulässig.

Die letzte Annotation @RunAs kann ebenfalls nur auf Methodenebene verwendet werden. Mit ihr wird angegeben, unter welcher Rolle eine Methode ausgeführt werden soll. d. h. die Annotation erwartet als Parameter einen Rollennamen, der dem EJB-Container bekannt sein muss.

Überblick über die Neuerungen von EJB 3.0

  • Deployment-Deskriptoren sind nicht nötig, können aber Standard-Verhalten überschreiben.
  • Viele vordefinierte Einstellungen. Man spezifiziert nur die Ausnahmen von den Regeln.
  • Es sind keine Schnittstellen wie Remote, EntityBean, SessionBean sowie Callback Interfaces nötig, die oft gar nicht benötigt werden. (Etwa bei Stateless Session Beans und Passivierung.) Falls es zum Beispiel eine remove-Methode geben muss, wird diese annotiert. Für eine Methode wie setSessionContext() wird setter injection genutzt.
  • Exceptions müssen nicht mehr deklariert werden. (Ein Ärgernis mit Business Interfaces für den lokalen und remote Fall). Die Objekte sind viel leichter zu testen und ein First-Test-Ansatz ist damit viel leichter.
  • Home Interfaces sind nicht mehr nötig.
  • Es lassen sich EntityBeans objektorientiert modellieren, denn Vererbung ist möglich.
  • EntityBeans müssen nicht mehr abstrakt sein und lassen sich so besser testen.
  • Während EJBs unter der 2.x Spezifikation diverse Schnittstellen implementieren, sind sie unter EJB 3.0 viel mehr "Plain Old Java Objects" (POJOs).
  • EJB-QL wird mit Projektion, Inner und Outer Join, Bulk Updates, Bulk-Deletes, Sub-Queries und GROUP BY vervollständigt.
  • Nutzt die in Java 5 eingeführten Annotationen, um Metadaten zu beschreiben.
Abb. 4: Zusammenfassung der Neuerungen von EJB 3.0.
(Quelle: http://www.java-tutor.com/java/ejb-3.0-links.htm)

Dependency Injection

Eine wirklich neue Funktion von EJB 3.0 ist der Einsatz des Entwurfsmusters Dependency Injection. Hierbei geht es um die Minimierung der Abhängigkeiten zwischen Komponenten oder Objekten. In unserem Fall zwischen einer EJB und den zur Laufzeit benötigten Ressourcen wie z. B. anderen EJBs oder DB-Verbindungen.

Die Idee ist, dass nicht die EJB dafür verantwortlich ist, sich entsprechende Ressourcen zu erzeugen und zu verwalten, sondern das umgebende Framework. d. h. der EJB-Container muss einer EJB die benötigten Ressourcen "injizieren".

Damit der EJB-Container weiß, welche Ressourcen eine EJB zur Laufzeit braucht, muss die EJB diese mit Hilfe der Annotation @Resource bzw. @EJB im Falle von anderen EJBs angeben.

Beide Annotations können auf Klassen-, Attribut- und Methodenebene angewendet werden.

Die Annotation @EJB spezifiziert die Referenz zu einem EJB Business oder Home Interface. Auf Attributebene versucht der EJB-Container, das versehene Attribut mit der Referenz der benötigten EJB zu belegen. Das geschieht nach dem Setzen des EJB-Kontextes und vor dem Aufruf von Geschäftsmethoden.

Als Alternative kann eine setter-Methode mit der Annotation @EJB versehen werden. In diesem Fall injiziert der EJB-Container die EJB-Referenz durch Aufruf der setter-Methode.

Etwas anders gestaltet sich die Verwendung der Annotation auf Klassenebene. Hierdurch erzeugt der EJB-Container die referenzierte EJB und legt diese im JNDI ab.

Die referenzierende EJB muss durch einen vereinfachten JNDI-Lookup die Referenz auf diese EJB selbst holen.

Die Annotation @Resource ähnelt stark der Annotation @EJB. Mit dem Unterschied, dass sie die Abhängigkeit von externen Ressourcen wie JDBC Data Sourcen, JMS Queues/Topics oder Connection Factories spezifiziert.

Angewandt auf Methoden- oder Attributebene versucht der EJB-Container, die benötigte Ressource direkt zu injizieren. Auf Klassenebene wird die Ressource im JNDI abgelegt und muss von der EJB per JNDI-Lookup geholt werden.

Beide Annotations können bzw. müssen in bestimmten Fällen durch diverse Angaben parametrisiert werden, um die benötigte Ressource eindeutig zu spezifizieren. Das ist aber nicht immer notwendig.

Hält sich der Entwickler an bestimmte Konventionen, so kann der EJB-Container z. B. auf Attributebene durch Auswertung des Attributnamens und -typs die benötigte Ressource eindeutig identifizieren.

Was bringts?

Obwohl, wie eingangs angekündigt, bewusst nur eine Auswahl an Annotations vorgestellt wurde, hat Ihnen der Artikel einen Eindruck von der Verwendung und den Vorteilen der EJB 3.0 Annotations vermittelt.

Weniger EJB-Artefakte und ein geringerer Implementierungsaufwand sind ein großer Schritt in Richtung der Ziele, denen sich die EJB-Spezifikation schon vor langer Zeit verschrieben hat. Im nächsten Teil geht es um die EJB 3.0 Persistence API, die den Umgang mit der Persistenz erheblich vereinfacht.

Christoph Borowski (info@ordix.de).