Beispiel: Stateful Session Bean


Inhalt:

Anlegen der Enterprise Application
Anlegen der GeometricModelBean
Anlegen der GeometricModelStoreBean
Anlegen des WebClient
Testen des WebClient
Ohne Annotations

Beispiel für eine Stateful Session Bean, auf die per Webclient zugegriffen wird.
Hier gibt es das Projekt zum Download (dies ist ein EAR-Export, die Importanleitung findet man im Stateless-Beispiel): Stateful.ear

Dieses Beispiel kann übrigens parallel zum Stateless-Beispiel auf den Server deployed werden, da die GeometricModelBean zwar in beiden Beispielen vorkommt, aber mit unterschiedlichen Namen ins JNDI gehängt wird.

Aufbau des Beispieles

a) Stateless Bean-Klasse mit Remote-Interfaces.
b) Stateful Bean-Klasse mit Remote-Interfaces.
c) Webclient: JSP-Seite


Anlegen der Application

Ein "Enterprise Application Project" mit dem Namen "Stateful" erstellen.
Zu erzeugende Module definieren. Dieses Beispiel benötigt ein EJB-Projekt und ein Webprojekt.
Erzeugen einer Application (Module)
Die J2EE-Hierarchie sollte so aussehen:
Erzeugte J2EE-Hierarchie
Jetzt müssen wir noch die Deployment-Deskriptoren "ejb-jar.xml", "web.xml" und "application.xml" anpassen (siehe Stateless-Beispiel).
Der EJB-Validator wird abgeschaltet.


Anlegen der StatelessBean

Die Schritte sind identisch mit denen des letzten Beispiels, einschließlich der Geschäftsmethoden und der Exceptionklasse "InvalidParameterException".
Ein Unterschied ist dass das Package hier "de.fhw.swtvertiefung.knauf.stateful" lautet.

Außerdem legen wir nur ein Remote Interface an, das LocalInterface "GeometricModelLocal" entfällt. Die Bean-Klasse implementiert entsprechend nur "GeometricModel".


Anlegen der StatefulBean

Die Bean hat den Namen "GeometricModelStoreBean" im Package "de.fhw.swtvertiefung.knauf.stateful".
Stateful Session Bean, Step 1
Die Bean erhält die Annotation "Stateful":
@Stateful()
public class GeometricModelStoreBean
{ 
Wir deklarieren drei Membervariablen für die Tripel der Kantenlängen:
  private Vector<Double> vectorA = null;
  private Vector<Double> vectorB = null;
  private Vector<Double> vectorC = null; 
Außerdem gibt es zwei Business-Methoden "addCalculation" (hinzufügen eines neuen Satzes von Kantenlängen) und "getAllCalculations" (alle bisherigen Berechnungen in String-Form zurückliefern, für die Zeilenumbrüche wird ein HTML-Zeilenumbruch in die Rückgabe eingefügt):
  public void addCalculation(double a, double b, double c) throws InvalidParameterException
  {
    if (a <= 0 || b <= 0 || c <= 0)
      throw new InvalidParameterException("Seitenlänge <= 0");

    this.vectorA.add(new Double(a));
    this.vectorB.add(new Double(b));
    this.vectorC.add(new Double(c));
  }

  public String getAllCalculations()
  {
    String strReturn = "";
    for (int intIndex = 0; intIndex < this.vectorA.size(); intIndex++)
    {
      if (intIndex > 0)
        strReturn += "<br>\r\n";
      strReturn += "Anfrage " + intIndex + ": " + this.vectorA.get(intIndex) + "/" + this.vectorB.get(intIndex) + "/"
          + this.vectorC.get(intIndex);
    }

    return strReturn;
  } 
Eine Besonderheit werden wir hier anwenden: unsere Stateful Session Bean wird nicht unbedingt physisch neu erzeugt wenn ein neuer Client sie anfordert, sondern es wird eventuell eine "freie" (keinem anderen Client zugeordnete) Instanz recycled. Deshalb reicht es nicht die Vektoren im Konstruktor zu erzeugen, sondern wir müssen eine Methode implementieren die der Container bei jedem neuen Binden an einen Client aufruft.
Dieses Binden geschieht über die Annotation "@PostConstruct":
  @PostConstruct()
  public void postConstruct()
  {
    this.vectorA = new Vector<Double>();
    this.vectorB = new Vector<Double>();
    this.vectorC = new Vector<Double>();
  } 

Jetzt bauen wir uns das Remote Interface:
@Remote()
public interface GeometricModelStore
{
  public abstract void addCalculation(double a, double b, double c) throws InvalidParameterException;

  public abstract String getAllCalculations();
} 


Anlegen des Webclients

In der J2EE-Hierarchie einen Rechtsklick auf "StatefulWeb" ->"WebContent" ausführen und im Contextmenü "New" -> "JSP" wählen.
Als Dateiname wird "GeometricModelTest" angegeben.
JSP-Seite
Nach dem Klick auf "Finish" hat man die JSP-Seite erstellt und kann den Quellcode der JSP-Datei einfügen.

Nachbearbeitung:
Damit die JSP-Seite die EJB referenzieren kann, muss eine Referenz auf die JAR-Datei zugefügt werden.
Dazu einen Rechtsklick auf den J2EE-Hierarchie-Knoten "StatefulWeb" ausführen und "Properties" wählen. Unter dem Punkt "J2EE Module Dependencies" -> "J2EE Modules" wählt man die Option "Use EJB JARs" und setzt den Haken beim JAR der EJB-Datei.
Webclient, EJB-Referenz
Deklaration der EJB-Referenzen:
"StatefulWeb" -> "WebContent" -> "WEB-INF" -> "web.xml" öffnen, im Elelemt "web-app" wird folgendes eingefügt (auch hier: sinnlose Einträge in das Pflichtelement "<home>"):
	<ejb-ref>
		<ejb-ref-name>ejb/GeometricModel</ejb-ref-name>
		<ejb-ref-type>Session</ejb-ref-type>
		<home>java.lang.Object</home>
		<remote>de.fhw.swtvertiefung.knauf.stateful.GeometricModel</remote>
	</ejb-ref>
	<ejb-ref>
		<ejb-ref-name>ejb/GeometricModelStore</ejb-ref-name>
		<ejb-ref-type>Session</ejb-ref-type>
		<home>java.lang.Object</home>
		<remote>de.fhw.swtvertiefung.knauf.stateful.GeometricModelStore</remote>
	</ejb-ref>

Die JSP-Seite "GeometricModelTest.jsp" soll als Startseite möglich sein. Auch das stellen wir über "web.xml" ein:
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
		<welcome-file>GeometricModelTest.jsp</welcome-file> 
	</welcome-file-list> 

Für die EJB-Referenzen müssen wir außerdem in "StatefulWeb\WebContent\WEB-INF" die Datei jboss-web.xml mit diesem Inhalt anlegen:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.4//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd">

<jboss-web>
	<context-root>StatelessWeb</context-root>
	<!-- Referenz auf "GeometricModelBean" -->
	<ejb-ref>
		<ejb-ref-name>ejb/GeometricModel</ejb-ref-name>
		<jndi-name>Stateful/GeometricModelBean/remote</jndi-name>
	</ejb-ref>
	<!-- Referenz auf "GeometricModelStoreBean" -->
	<ejb-ref>
		<ejb-ref-name>ejb/GeometricModelStore</ejb-ref-name>
		<jndi-name>Stateful/GeometricModelStoreBean/remote</jndi-name>
	</ejb-ref>
</jboss-web> 

Testen des Webclients

Der Test erfolgt über diese URL: http://localhost:8080/StatefulWeb
Achtung: Die Stateful Session Bean ist im Session Context der Webanwendung abgelegt. Der Session Context bleibt uns so lange zugeordnet wie wir den Browser offen haben (Erkennung erfolgt über ein Cookie), das heißt wenn wir eine neue Session und damit eine frische Stateful Session Bean haben wollen müssen wir alle gerade offenen Instanzen des Browsers schließen.


Ohne Annotations

Jetzt der Weg ohne Annotations, nur mit Deployment-Deskriptoren:
Die Datei "ejb-jar.xml" im EJB-Projekt muss so geändert werden:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar id="ejb-jar_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
	<display-name>StatefulEJB</display-name>
	<enterprise-beans>
		<session>
			<description>
				<![CDATA[Stateless Session Bean für einfache geometrische Berechnungen.]]>
			</description>
			<display-name>GeometricModelBean</display-name>
			<ejb-name>GeometricModelBean</ejb-name>
			<remote>de.fhw.swtvertiefung.knauf.stateful.GeometricModel</remote>
			<ejb-class>de.fhw.swtvertiefung.knauf.stateful.GeometricModelBean</ejb-class>
			<session-type>Stateless</session-type>
		</session>
		<session>
			<description>
				<![CDATA[Stateful Session Bean für die Speicherung der Berechnungs-Historie.]]>
			</description>
			<display-name>GeometricModelStoreBean</display-name>
			<ejb-name>GeometricModelStoreBean</ejb-name>
			<remote>de.fhw.swtvertiefung.knauf.stateful.GeometricModelStore</remote>
			<ejb-class>de.fhw.swtvertiefung.knauf.stateful.GeometricModelStoreBean</ejb-class>
			<session-type>Stateful</session-type>
			<post-construct>
				<lifecycle-callback-class>
					de.fhw.swtvertiefung.knauf.stateful.GeometricModelStoreBean
				</lifecycle-callback-class>
				<lifecycle-callback-method>postConstruct</lifecycle-callback-method>
			</post-construct>
		</session>
	</enterprise-beans>
</ejb-jar> 
Es gibt zwei Besonderheiten im Vergleich zum Stateless-Beispiel (fett markiert): die Deklaration der GeometricModelStoreBean als "Stateful" und das Mapping des Lifecycle-Callbacks "PostConstruct" auf die Methode der Bean. Der Rest des Projekts ist absolut identisch mit dem Annotation-igen Beispiel.


Die modifizierte Version des Projekts gibt es hier:
StatefulNoAnnotation.ear.
ACHTUNG: Dieses Projekt kann nicht neben dem obigen Stateful-Beispiel existieren !

WICHTIG: Vor dem EAR-Export muss in ejb-jar.xml das Element post-construct auskommentiert werden, da sonst der Re-Import nicht klappt (Eclipse spuckt eine Fehlermeldung aus). ! In der verlinkten EAR-Datei muss dieses Element deshalb wieder einkommentiert werden.



Stand 10.03.2007
Historie:
07.09.2006: Erstellt
10.03.2007: Import-Problem bei Beispiel ohne Annotations, Eclipse-Warnungen wegen falschem ejb-link in web.xml korrigiert.