
| 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 Versionen 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. |
Weiterführende Links
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. |
Session Beans beinhalten nach der EJB-Spezifikation die Geschäftslogik 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.RemoteException 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. |
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 qualifizierte Klassenname verwendet. Die Session Bean Klasse muss natürlich die weiter oben beschriebene(n) Geschäftsschnittstelle(n) implementieren.
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, @PostActivate 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.
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 Klassenebene gilt die Einstellung für alle Geschäftsmethoden der Session 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.
Für Sicherheitseinstellungen basierend auf JAAS definiert EJB 3.0 die fünf Annotations
@DeclareRoles ist auf Klassenebene angesiedelt und ermöglicht es, die für die Session Bean relevanten Rollen zu definieren. Als Parameter erwartet die Annotation ein String Array von Rollennamen.
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
|
| Abb. 4: Zusammenfassung der Neuerungen von EJB 3.0. (Quelle: http://www.java-tutor.com/java/ejb-3.0-links.htm) |
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.
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).