Advanced Java Services | Stateful Session Beans 3.0 |
Einführung
Eine stateful Session Bean ist eine Bean die clientspezifischen Daten speichert und so eine 1 zu 1 Beziehung zwischen
Client und Bean herstellt, bzw. herstellen muß. Zusätzlich zur stateless Sessionbean hat sie noch den Zustand "Passive". Die Zustände
werden vom Beancontainer gesteuert.
Eine Hello World EJB 3.0 stateful Sessionbean
Auch diesmal verwenden wir keine Entwicklungsumgebung. Des weiteren verzichten wir der Einfachheit halber wieder auf
packages. Als Übung sollten Sie das Beispiel so abändern, daß Sie packages einsetzen und verweisen nochmal darauf
daß in der Praxis die Klassen immer in packages gelegt werden.
Unser Beispiel besteht aus drei Dateien, dem Remoteinterface, der eigentlichen Beanklasse und dem
Client der den Dienst dieser EJB über das Protokoll "iiop" (Internet Inter-ORB Protocol) in Anspruch nehmen will.
Zu diesem Protokoll gibt es eine kurze Information auf Wikipedia.
Das Remoteinterface
import javax.ejb.Remote; @Remote public interface SayHello { public String sayHello(); public java.util.Date getCreationDate(); }
Das Business-Interface ist wieder ein ganz normales Interface. Eine Ableitung vom Interface
javax.ejb.EJBObject ist nicht mehr notwendig. Wir markieren das Interface mit einer Annotation als
remote interface. Zu jeder Annotation gibt es ein entsprechendes Import. Die neue Methode soll uns das Geburtsdatum
der Bean liefern. Wir werden damit feststellen können, daß jeder Client seine eigene Bean zugewiesen bekommt.
Wie im vorigen Beispiel: Zum Kompilieren braucht man das Archiv javaee.jar .
Die Beanklasse
/* die beanklasse stateful session bean (man beachte die importe) implementiert das interface SayHello2 */ import java.util.Date; import javax.ejb.Remote; import javax.ejb.Remove; import javax.ejb.Stateful; import javax.ejb.PrePassivate; import javax.ejb.PostActivate; @Stateful(name="SayHello2", mappedName="ejb/SayHelloStateful") @Remote(SayHello2.class) public class SayHelloBean2 implements SayHello2 { private String message = "Hello EJB 3.0 Stateful"; private java.util.Date creationDate; public SayHelloBean2() { creationDate = new Date(); System.out.println("constructor SayHelloBean2() called"); } @Override public String sayHello2() { return message; } @Override public java.util.Date getCreationDate() { return creationDate; } // für stateful session beans um auf eine zustandsänderung reagieren zu können @PrePassivate public void ejbPassivate() // name der methode ist ab 3.0 beliebig { } @PostActivate public void ejbActivate() // name der methode ist ab 3.0 beliebig { } // optional @Remove public void ejbRemove() // name der methode ist ab 3.0 beliebig { System.out.println("ejbRemove called"); } }
Wir haben ein Datumsobjekt im privaten Datenteil, das über den Konstruktor initialisiert wird. Damit enthält
creationDate das Geburtsdatum des Objekts, das über die öffentliche Methode getCreationDate() erfragt werden kann.
Die Methoden ejbPassivate() und ejbActivate() ermöglichen die Reaktion auf eine Zustandsänderung der Bean und sind hier
(noch) leer.
Erstellen des Bean-Archivs und der Deployvorgang
Das Remotinterface und die Beanklasse müssen zusammen mit einem META-INF Verzeichnis zu einer jar-Datei (oder auch ear-Datei) gepackt werden. Das META-INF Verzeichnis enthält eine Manifestdatei MANIFEST.MF und optional einen Deploymentdescriptor im XML-Format namens ejb-jar.xml. Diesmal verwenden wir als Packer das Java-Utility jar.exe aus dem vin-verzeichnis des JDK. der einfachste Aufruf ist
jar cvf <name-der-ear-datei> .
jar.exe legt automatisch ein META-INF Verzeichnis mit einer Manifestdatei an. Der Hauptname des ear-Archivs ist übrigens völlig beliebig. Bei der Verwendung von Eclipse oder Netbeans können wir das Erstellen des Archivs zur Gänze delegieren. Das fertige Archiv bringen wir nun zum Server und legen es ins Autodeployverzeichnis von Glassfish.
<SUNHOME>/SDK/domains/domain1/autodeploy
Glassfish kontrolliert periodisch den Inhalt dieses Verzeichnis und versucht neu hinzugekommene ear-Dateien (oder jar-Dateien
oder war-dateien) zu starten. Gelingt dies wird eine leere Datei mit dem Namen "SayHellobean.ear_deployed" angelegt.
Gelingt es nicht, so heißt die entsprechende Datei "SayHellobean.ear_deployFailed"
Sieht man die Meldung "SayHellobean.ear_deployed" auftauchen, kann man zum Client zurückgehen und die
Clientdatei starten.
Der iiop-Client
import java.util.*; import javax.naming.*; public class SayHelloClient { public static void main(String[] args) { System.out.println("about to create initialcontext"); String url = "iiop://<server-ip-adress>" ; String jndiName = "ejb/SayHelloStateful"; Properties env = new Properties(); env.put("org.omg.CORBA.ORBInitialHost","<server-ip-adress>"); // default ist localhost !! //env.put("org.omg.CORBA.ORBInitialPort","3700"); // ist default env.put("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory"); //env.put("java.naming.factory.url.pkgs","com.sun.enterprise.naming"); // hier noch nicht notwendig //env.put("java.naming.provider.url","com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl"); // hier noch nicht notwendig //env.put("java.naming.factory.state",url); // hier noch nicht notwendig try { Context ctx = new InitialContext(env); // NamingException System.out.println("initial context received"); SayHello2 remote = (SayHello)ctx.lookup(jndiName); // NamingException if( remote != null) System.out.println("remote class = " + remote.getClass().getName() ); else throw new NullPointerException("lookup failed"); // der wrapper hat den typ _SayHello_Wrapper und hat das remote interface implementiert, // man kann also ohne cast einfach die businessmethoden rufen System.out.println(remote2.sayHello()); System.out.println(remote.getCreationDate()); } catch(NamingException ex) { System.out.println("NamingException " + ex); } catch(Exception ex) { System.out.println("Exception " + ex); } // ende try - catch } // end main }
Die Konsolausgaben der Clients
Wenn Sie mehrere Instanzen dieses Clients starten, so werden Sie feststellen, daß jedes mal ein anderes Datum ausgegeben wird.
Daraus können wir schließen, daß jeder Client eine eigene Beaninstanz erhält.
Als Übung sollten Sie dieses Beispiel so abändern, daß Sie Packages verwenden.