Migrieren von EJB 2.x-Anwendungen zu EJB 3.0

Enterprise JavaBeans 3.0 vereinfacht die Enterprise Bean-Architektur und bietet erweiterte und leistungsfähigere Funktionen. Die neue Spezifikation nutzt die in Java 5 eingeführte Metadatenfunktion für Anmerkungen, Best Practices für Persistenz und objektrelationale Zuordnung von Tools wie Hibernate und TopLink sowie das Abhängigkeitsinjektionsmuster, das durch einfache Java-Frameworks wie Spring populär gemacht wurde.

Dieser Artikel beschreibt mögliche Migrationsstrategien zum Verschieben von Anwendungen, die mit EJB 2.1 oder einer früheren Spezifikation in eine EJB 3.0-basierte Architektur geschrieben wurden. Die möglichen Migrationspfade werden sowohl aus Sicht des Designs als auch der Implementierung bewertet. Dieser Artikel soll die Migrationsoptionen nicht vollständig veranschaulichen. Nachdem Sie diesen Artikel gelesen haben, sollten Sie in der Lage sein, in Ihrem spezifischen Kontext die beste Option für die Migration des alten EJB-Codes auf die neue Spezifikation auszuwählen.

In diesem Artikel wird davon ausgegangen, dass Sie mit den Funktionen und Konzepten von Enterprise Bean, Java 5 und objektrelationalen Zuordnungen vertraut sind.

EJB 2.1 bis EJB 3.0: Was hat sich geändert?

Um einen Kontext für die Erörterung möglicher Migrationspfade in diesem Artikel bereitzustellen, beginne ich mit der Erörterung der Änderungen in der neuen Spezifikation im Kontext der verschiedenen Bean-Typen und dann auf einer allgemeinen Ebene, die für mehrere Bean-Typen relevant ist.

Session Bean

In EJB 2.1 und früheren Spezifikationen waren für jede Session-Bean zwei Schnittstellen erforderlich - die Heim- und die lokale oder Remote- oder Geschäftsschnittstelle - und die Bean-Implementierungsklasse. Die Home-Schnittstelle war erforderlich, um die EJBHomeoder die EJBLocalHomeSchnittstelle zu erweitern und die Lebenszyklusmethode zu deklarieren, z create(). Die lokale oder Remote-Geschäftsschnittstelle war erforderlich, um die EJBObjectoder die EJBLocalObjectSchnittstelle zu erweitern und die Geschäftsmethoden zu deklarieren. Die Bean-Implementierungsklasse selbst war ein EnterpriseBeanTyp und erweiterte im Fall von Session-Beans dieSessionBeanSubschnittstelle. Implementierungen von Rückrufmethoden in der Bean-Klasse mussten bereitgestellt werden, damit der Container sie beim Auftreten der entsprechenden Lebenszyklusereignisse auslösen konnte. Darüber hinaus wurden kritische Elemente der Bean, einschließlich ihrer Transaktions- und Sicherheitsdefinition und ob sie statusbehaftet oder zustandslos war, in den zugehörigen Bereitstellungsdeskriptoren definiert.

Listing 1 zeigt ein Beispiel für eine Stateful Session Bean, die mithilfe der EJB 2.1-Spezifikation definiert wurde.

Listing 1. Eine EJB 2.1-basierte Stateful Session Bean für Bankdienstleistungen

public interface BankingService extends EJBObject { public void deposit(int accountId, float amount) throws RemoteException; public void withdraw(int accountId, float amount)throws RemoteException; public float getBalance(int accountId) throws RemoteException;

public void doServiceLogout() throws RemoteException; }

public interface BankingServiceHome extends EJBHome { public BankingService create() throws CreateException, RemoteException; }

public class BankingServiceEJB implements SessionBean {

public void deposit(int accountId, float amount) throws RemoteException { //Business logic to deposit the specified amount and update the balance }

public void withdraw(int accountId, float amount)throws RemoteException { //Business logic to withdraw the desired amount and update the balance }

public float getBalance(int accountId) throws RemoteException { //Business logic to get the current balance }

public void doServiceLogout() throws RemoteException { //Service completion and logout logic }

public void ejbCreate(){} public void ejbActivate(){} public void ejbPassivate(){} public void ejbRemove(){} public void setSessionContext(SessionContext context){}

}

In der EJB 3.0-Spezifikation muss eine Session-Bean nur eine Geschäftsschnittstelle und eine Bean-Implementierungsklasse definieren. Die Home-Schnittstelle wurde entfernt. Die Geschäftsschnittstelle ist eine reguläre Java-Schnittstelle, manchmal auch POJI genannt , oder die einfache alte Java-Schnittstelle. Die Geschäftsschnittstelle muss die EJBObjectoder die EJBLocalObjectSchnittstelle nicht erweitern. Bei Bedarf kann es stattdessen in der Schnittstellenhierarchie definiert werden, die das Geschäftsdomänenmodell darstellt.

Die Bean-Implementierungsklasse ist eine reguläre Java-Klasse, die manchmal auch als POJO oder einfaches Java-Objekt bezeichnet wird. Es wird kein EnterpriseBeanTyp implementiert . Die Deklaration und die Konfiguration im Deployment-Deskriptor können im Java-Code mithilfe der Annotations-Metadatenfunktion definiert werden. Darüber hinaus werden für die meisten Konfigurationen Standardwerte bereitgestellt, wodurch die Bean-spezifischen Konfigurationsanforderungen minimiert werden. Unter der neuen Spezifikation könnten Session Beans ohne ejb-jar.xml-Deployment-Deskriptoren bereitgestellt werden, obwohl diese noch vorhanden sind und verwendet werden können, wenn der Entwickler dies dem Annotationsmodell vorzieht.

Bei EJB 3.0-Sitzungs-Beans, die einen Webdienst implementieren, werden die als Webdienstoperationen bereitgestellten Methoden mit dem WebMethodDeskriptor versehen. Session Beans, die als Webdienstendpunkte dienen, werden als WebService.

Listing 2 zeigt das frühere Beispiel (aus Listing 1) der Stateful Session Bean unter Verwendung der EJB 3.0-Spezifikation.

Listing 2. Eine EJB 3.0-basierte Stateful Session Bean für Bankdienstleistungen

@Remote public interface BankingService { public void deposit(int accountId, float amount); public void withdraw(int accountId, float amount); public float getBalance(int accountId); publlic void doServiceLogout();

}

@Stateful public class BankingServiceBean implements BankingService {

public void deposit(int accountId, float amount) { //Business logic to deposit the specified amount and update the balance }

public void withdraw(int accountId, float amount) { //Business logic to withdraw the desired amount and update the balance }

public float getBalance(int accountId) { //Business logic to get the current balance }

@Remove public void doServiceLogout () { //Service completion and logout logic }

}

Nachrichtengesteuerte Bohnen

In EJB 2.1 implementierte eine nachrichtengesteuerte Bean-Klasse die MessageDrivenBeanSchnittstelle und die Nachrichten-Listener-Schnittstelle. Die Rückrufmethoden wurden in der Bean-Klasse und im Container bei einem bestimmten Ereignis implementiert, das als entsprechende Methode bezeichnet wird. Nachrichtengesteuerte Beans beinhalteten nie das Konzept eines Heims und einer entfernten oder lokalen Schnittstelle.

In EJB 3.0 wird die MessageDrivenAnnotation verwendet, um eine nachrichtengesteuerte Bean zu markieren und anzugeben. Der Bereitstellungsdeskriptor kann auch verwendet werden, um eine Bean als nachrichtengesteuert anzugeben. Daher muss die Bean-Klasse die MessageDrivenBeanSchnittstelle nicht implementieren . Die Geschäftsschnittstelle einer nachrichtengesteuerten Bean ist die Nachrichten-Listener-Schnittstelle, die dem Nachrichtentyp entspricht, für den die Bean ein Listener ist. Im Fall von Java Message Service javax.jms.MessageListenerist dies die Nachrichten-Listener-Schnittstelle oder die Geschäftsschnittstelle. Die Bean-Klasse muss die Nachrichten-Listener-Schnittstelle implementieren oder die Nachrichten-Listener-Schnittstelle mithilfe der MessageDrivenAnnotation mit Anmerkungen versehen.

Die neue Spezifikation unterstützt Rückrufmethoden ( PostConstructund PreDestroy), stellt das Abhängigkeitsinjektionsmuster für den Zugriff auf Ressourcen bereit und ermöglicht die Definition von Interceptor-Methoden für nachrichtengesteuerte Beans.

Entity Beans

EJB 2.1 und frühere Spezifikationen erforderten die Definition einer Home- und Remote-Schnittstelle sowie die Implementierung der Bean-Klasse für eine Entity-Bean, ähnlich der eines Session-Bean-Typs. Für ein Szenario mit Container-Managed Persistence (CMP) und Container-Managed Relationship (CMR) wurden die Zuordnungen und Definitionen in den Bereitstellungsdeskriptoren angegeben.

Die EJB 3.0-Spezifikation ändert die früheren Konzepte und die Implementierung von Entity-Beans von schwergewichtigen Enterprise-Bean-Objekten mit Home- und Remote- / lokalen Schnittstellen radikal in ein leichtes persistentes Domänenobjekt gemäß den objektrelationalen Zuordnungskonzepten. In einer späteren Diskussion zur Migration in diesem Artikel werden einige dieser Änderungen erläutert und detailliert beschrieben.

Weitere Änderungen - relevant für mehrere Bean-Typen

Die EJB 3.0-Spezifikation führt eine wichtige Änderung in der Art und Weise ein, wie auf Enterprise-Beans zugegriffen und diese aufgerufen werden. Das frühere Service Locator-Muster, das JNDI-Lookups (Java Naming and Directory Interface) verwendet hat, wird jetzt durch das Dependency Injection-Muster ersetzt. Somit wird die Komplexität der Verwendung der JNDI-API aus Sicht des Bean-Entwicklers und des Clients beseitigt.

EJB 3.0 führt auch das Konzept der Abfangjäger ein. Interceptor-Methoden fangen einen Geschäftsmethodenaufruf oder einen Rückruf eines Lebenszyklusereignisses ab. Interceptor-Methoden können entweder für die Bean-Klasse oder für die der Bean zugeordnete Interceptor-Klasse oder für beide definiert werden.

Listing 3 zeigt die Verwendung AroundInvokeeiner Interceptor-Annotation.

Listing 3:

@Stateful public class BankingServiceBean implements BankingService {

public void deposit(int accountId, float amount) { .... }

.... .... .... .... @Remove public void doServiceLogout () { .... }

@AroundInvoke public Object auditBankingService(InvocationContext inv) throws Exception { try { Object nextEntry = inv.proceed();

//Log the method name and parameters by using getMethod() on the InvocationContext instance. //The getMethod() returns the method of the bean class for which the interceptor was invoked. //The return type for getMethod() is a Method class, the reference to which can be used to get additional //method-related information. Target and context data can also be obtained using the InvocationContext instance.

return nextEntry; } catch (Exception ex) {

//Exception handling code goes here.

} }

}

Migration auf EJB 3.0

The EJB 3.0 (Java Specification Request 220) expert group specifically identified easy migration as an objective and hence provided for both backward compatibility and interoperability between enterprise bean components written in the old and the new specifications. This also makes it possible for EJB 2.x and earlier beans to be gradually and incrementally modified to utilize the EJB 3.0 specification. Interestingly enough, an entire chapter titled "Compatibility and Migration" is included in the EJB 3.0 simplified API specification document.

Some of the possible migration strategies are discussed below.

New functionality in EJB 3.0 with existing code in the old specification

An existing EJB application can be deployed in a container that supports EJB 3.0. The old bean components could be deployed and used without any modification.

EJB 2.x components could call EJB 3.0 components and vice-versa. Some scenarios where the client and server parts of the bean components are of different specifications are discussed below.

EJB 2.x and earlier clients of EJB 3.0 components

Enterprise bean components, which need to work with EJB 2.1 and earlier specifications, don't need to be written using old specifications any more. EJB 3.0 beans can utilize metadata annotations to make them work with older clients that expect to access them using the home and the remote interface. The methods required as per older specifications are mapped to corresponding methods in the enterprise bean written using the EJB 3.0 specification. As an example, the create() method as desired in the old specification could now map to a method that initializes the bean. This example was presented by Linda DeMichiel, one of the specification leads, as part of her Java One 2005 presentation on EJB 3.0.

EJB 3.0 clients of EJB 2.x components

EJB 3.0-Beans können auf EJB 2.1- und frühere Beans zugreifen. Die Abhängigkeitsinjektion wird verwendet, um die EJB 2.1-Komponentenreferenzen zu injizieren. JNDI-Suchaufrufe werden vermieden. Sobald ein Handle für die injizierte EJB-Home-Schnittstelle erhalten wurde, wird die create()Methode aufgerufen, um die Bean zu instanziieren und anschließend zu verwenden.

Listing 4 zeigt ein Beispiel, in dem ein EJB 3.0-Client auf eine EJB 2.1-Komponente zugreift.

Listing 4:

.... ....

@EJB BankingServiceHome bsHome; BankingService bs = bsHome.create(); .... bs.deposit(....); .... bs.getBalance(....); .... bs.doServiceLogout(); .... ....