Beispiel: Stateless Session Bean


Inhalt:

Anlegen der Enterprise Application
Anlegen der GeometricModelBean
Anlegen des WebClient
Server einrichten
Anlegen des Application Clients
Ausführen des Application Clients
Export des Workspace
Re-Import
Ohne Annotations

Beispiel für eine Stateless Session Bean, auf die per Webclient und mittels Application Client zugegriffen wird.
Hier gibt es das Projekt zum Download (dies ist ein EAR-Export, die Importanleitung findet man am Ende dieses Dokuments): Stateless.ear

Aufbau des Beispieles


a) Bean-Klasse mit Local und Remote-Interfaces
b) Webclient: JSP-Seite
c) WebClient: Servlet
d) App-Client


Anlegen der Application

Schritt 1: Über "File" -> "New" -> "Other..." gelangen wir in den Wizard zum Hinzufügen von neuen Komponenten. wir wählen im Zweig "J2EE" ein "Enterprise Application Project".
Erzeugen einer Application (Schritt 1)
Schritt 2: In dem erscheinenden Dialog geben wir dem Projekt einen Namen und wählen den JBoss 4.2.0-Server aus.
Erzeugen einer Application (Schritt 2)
Auf "Next" klicken.
Schritt 3: Hier belassen wir alles auf dem Default. Die gewählte "EAR version" "1.4" ist zwar für unsere Fälle falsch, aber leider unterstützt WTP noch nicht die 5er-Version.
Erzeugen einer Application (Schritt 3)
Schritt 4: Modulprojekte zufügen. Dazu auf den Button "New Module..." klicken. Wir wünschen uns ein EJB-Projekt, einen Application Client und einen Webclient. Connectorprojekte werden wir im Rahmen dieses Praktikums nie anfassen.
Erzeugen einer Application (Schritt 4)
Schritt 4: Zurück im Hauptdialog stellen wir sicher dass diese drei Module ausgewählt sind.
Erzeugen einer Application (Schritt 5)

Man klickt auf "Finish" und sieht folgende Hierarchie im Project Explorer:
Erzeugte J2EE-Hierarchie

Jetzt müssen wir das ganze auf den JEE5-Standard bringen, denn im Moment sind alle Deployment-Deskriptoren noch auf J2EE-Niveau.
Eigentlich bräuchten wir diese Dateien nicht zwingend, da in EJB3 alles über Annotations gesteuert werden kann. Um die etwas veraltete Projektstruktur der WebTools-Platform nicht zu verwirren behalten wir die Dateien aber bei.
Im EJB-Projekt stellen wir den EJB-Validator ab, da dieser wiederum nur die EJB-2.1-Struktur validiert und deshalb eine gut gefüllt ejb-jar.xml erwartet.
Dazu in die Properties des EJB-Projekts gehen. Im Punkt "Validation" setzen wir den Haken "Override validation preferences" und schalten den "EJB Validator" ab.
EJB validation


Anlegen der GeometricModelBean

Die WebTools-Platform bietet zwar einen Assistenten für das Anlegen von Beans an, diese sind aber nach EJB-2.1-Standard aufgebaut und verwenden außerdem XDoclet zur Generierung der Deployment-Deskriptoren. Deshalb gehen wir hier zur Handarbeit über.
Schritt 1: In der J2EE-Hierarchie den Knoten "EJB Projects" \ "StatelessEJB" wählen. Rechtsklick, im Contextmenü den Punkt "New" -> "Class" wählen.
Stateless Session Bean
Schritt 2: Wir vergeben einen Namespace und einen Bean-Namen. Den Haken "Generate Comments" setzen wir natürlich.
Stateless Session Bean

Schritt 3: Vorbereiten der Business-Methoden:
Die beiden Business-Methoden werfen eine eigene Exception. Diese wird dem Projekt so zugefügt: Das Package "de.fhw.swtvertiefung.knauf.stateless" im Folder "StatelessEJB\ejbModule" im Projekt "StatelessEJB" auswählen. Rechtsklick, "New" -> "Class". Eine Klasse namens "InvalidParameterException" zufügen, die von "java.lang.Exception abgeleitet ist.
  public class InvalidParameterException extends Exception
  {
    private static final long serialVersionUID = 1L;
    
    public InvalidParameterException(String str_Message)
    {
      super(str_Message);
    }
  } 

Schritt 4: Jetzt legen wir die Business-Methoden "computeCuboidVolume" und "computeCuboidSurface" in der GeometricModelBean an. Die gesamte Klasse sieht so aus:
  public class GeometricModelBean implements GeometricModel, GeometricModelLocal
  {  
    protected static final Logger logger = Logger.getLogger(GeometricModelBean.class.getName());
  
    public GeometricModelBean()
    {
    }
  
    public double computeCuboidVolume(double a, double b, double c) throws InvalidParameterException
    {
      logger.info("computeCuboidVolume with a = " + a + ", b = " + b + ", c = " + c);
      if (a <= 0 || b <= 0 || c <= 0)
        throw new InvalidParameterException("Side length <= 0");
  
      return a * b * c;
    }
  
    public double computeCuboidSurface(double a, double b, double c) throws InvalidParameterException
    {
      logger.info("computeCuboidSurface with a = " + a + ", b = " + b + ", c = " + c);
      if (a <= 0 || b <= 0 || c <= 0)
        throw new InvalidParameterException("Side length <= 0");
  
      return 2 * (a * b + b * c + c * a);
    }
  } 


Schritt 5: Wir fügen das Local- und Remote-Interface der Bean zu. Dazu können wir uns die Refactoring-Möglichkeiten von Eclipse zu Nutze machen. Im Project Explorer die Bean-Klasse wählen, Rechtsklick, "Refactor", "Extract Interface..." wählen.
Refactor
Zuerst erzeugen wir das Remote Interface namens "GeometricModel". Wir wählen die zwei Methoden aus.
Refactor
Das gleiche wiederholen wir für das Local-Interface "GeometricModelLocal".

Schritt 6: Die Bean-Klasse soll diese beiden Interfaces implementieren (dies wird durch "Extract Interface" automatisch erledigt) und erhält außerdem die Annotation "Stateless".
@Stateless() 
public class GeometricModelBean implements GeometricModel, GeometricModelLocal
{
	... 
Das Remote-Interface bekommt die Annotation "Remote":
@Remote()
public interface GeometricModel
{
	... 
Das Local-Interface bekommt die Annotation "Local":
@Local()
public interface GeometricModelLocal
{
	... 


Anlegen des Webclients

Modul-Abhängigkeiten Zuerst müssen wir eine Referenz auf das EJB-Projekt zufügen, damit wir die Interfaces der Bean verwenden können: Auf "StatelessWeb" einen Rechtsklick ausführen und in die "Properties" gehen. Im Punkt "J2EE Module Dependencies" aktivieren wir die Referenz auf "StatelessEJB.jar".
J2EE Module Dependencies
JSP anlegen
Den Ordner "StatelessWeb" -> "WebContent" auswählen und per Rechtsklick eine JSP zufügen.
Neue JSP (1)
Im ersten Schritt des Assistenten der JSP den Namen "GeometricModelTest.jsp" geben.
Neue JSP (2)
In Schritt 2 verwenden wir das vorgeschlagene Template.
Neue JSP (3)
Jetzt noch auf "Finish" klicken und den JSP-Code einbauen. Die vollständige JSP-Seite sieht so aus:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"
    import="javax.rmi.PortableRemoteObject, javax.naming.InitialContext,de.fhw.swtvertiefung.knauf.stateless.*"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>GeometricModelTest</title>
</head>
<body>
<H1>Test for the GeometricModel stateless session bean</H1>
<%
InitialContext initialContext = new InitialContext();

//Ist es der erste Aufruf oder wurde auf "Berechnen" geklickt.
if (request.getParameter("submit") == null)
{
  //Erster Aufruf: Nur Formular ausgeben.
}
else
{
  // Zweiter bis n-ter Aufruf: Berechnen.

  double dblA = Double.parseDouble(request.getParameter("a") );
  double dblB = Double.parseDouble(request.getParameter("b") );
  double dblC = Double.parseDouble(request.getParameter("c") );

  ///////////////////////////////////////////////////////////////
  // Berechnung über Remote-Interface.

  Object objGeometricModel = initialContext.lookup ("java:comp/env/ejb/GeometricModel");

  GeometricModel geometricModel = (GeometricModel) PortableRemoteObject.narrow(objGeometricModel, GeometricModel.class);
  
  double dblVolume = geometricModel.computeCuboidVolume( dblA, dblB, dblC);
  double dblSurface = geometricModel.computeCuboidSurface( dblA, dblB, dblC);

  %>

  Volume = <%=dblVolume%>, Surface= <%=dblSurface%>

  <br>And now with the Local Interface:
  <%
  objGeometricModel = initialContext.lookup ("java:comp/env/ejb/GeometricModelLocal");

  //Local Interfaces können direkt über Typecasts geholt werden.
  GeometricModelLocal geometricModelLocal = (GeometricModelLocal) objGeometricModel;

  dblVolume = geometricModelLocal.computeCuboidVolume( dblA, dblB, dblC);
  dblSurface = geometricModelLocal.computeCuboidSurface( dblA, dblB, dblC);

  %>

  Volume = <%=dblVolume%>, Surface = <%=dblSurface%>
  <br><br><br>
<%
}
%>
<form action="GeometricModelTest.jsp" method="post">
  a = <input type="text" name="a" /> <br>
  b = <input type="text" name="b" /> <br>
  c = <input type="text" name="c" /> <br>

  <input type="submit" name="submit" value="submit" />
</form>

</BODY>
</html> 

EJB-Referenzen anlegen:
Obiger Codeausschnitt verwendet eine EJB-Referenz die im JNDI-Environment-Naming-Context (ENC) der Webanwendung abgelegt ist und auf die GeometricModelBean zeigt. Auf dem JBoss könnten wir die Bean zwar auch direkt über ihren JNDI-Namen ansprechen, aber das wäre unsauber.
Wir öffnen die Datei "StatelessWeb\WebContent\WEB-INF\web.xml" und fügen folgendes Stück nach dem Element "welcome-file-list" ein:
	<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.stateless.GeometricModel</remote>
	</ejb-ref>
	<ejb-local-ref>
		<ejb-ref-name>ejb/GeometricModelLocal</ejb-ref-name>
		<ejb-ref-type>Session</ejb-ref-type>
		<local-home>java.lang.Object</local-home>
		<local>de.fhw.swtvertiefung.knauf.stateless.GeometricModelLocal</local>
	</ejb-local-ref> 
Wichtig ist dass wir ein "home"-Tag angeben, auch wenn Beans nach EJB3-Standard keine Home-Interfaces mehr kennen ! Der Grund ist dass JBoss ansonsten eine Fehlermeldung ausspuckt: "org.jboss.deployment.DeploymentException: Failed to parse WEB-INF/web.xml; - nested throwable: (org.jboss.deployment.DeploymentException: expected one local-home tag)". Das liegt wahrscheinlich daran dass der Web-Container noch auf J2EE-1.4-Stand ist.
Würden wir hier anderen Namen vergeben, könnten wir die EJB z.B. auch als "java:comp/env/ejb/EtwasKomplettAnderes" ansprechen.

Jetzt müssen wir noch den JBoss-spezifischen Teil der EJB-Referenz erzeugen (nämlich eine Verbindung zur tatsächlichen Bean im Container-JNDI). Dazu legen wir in "StatelessWeb\WEB-INF\" eine Datei "jboss-web.xml" an mit diesem Inhalt:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC
    "-//JBoss//DTD Web Application 4.2//EN"
    "http://www.jboss.org/j2ee/dtd/jboss-web_4_2.dtd">
<jboss-web>
	<context-root>StatelessWeb</context-root>
	<ejb-ref>
		<ejb-ref-name>ejb/GeometricModel</ejb-ref-name>
		<jndi-name>Stateless/GeometricModelBean/remote</jndi-name>
	</ejb-ref>
	<ejb-local-ref>
		<ejb-ref-name>ejb/GeometricModelLocal</ejb-ref-name>
		<local-jndi-name>Stateless/GeometricModelBean/local</local-jndi-name>
	</ejb-local-ref>
</jboss-web> 
Der Eintrag "context-root" legt fest unter welcher URL wir die Anwendung später auf dem Server erreichen. Im obigen Beispiel also "http://www.meinserver.de/StatelessWeb/meineWebseite.html".
Hier wird der Name der EJB-Referenz mit einem EJB-Namen des Containers verbunden. Woher "Stateless/GeometricModelBean/remote" kommt werden wir weiter unten sehen.

Anmerkung: Gemäß JavaEE5-Spezifikation sollte es möglich sein eine EJB-Referenz per Injection zu erhalten (durch diese Variablendeklaration: @EJB GeometricModel geometricModel;). Dies funktioniert beim JBoss allerdings nicht weil es einen Servlet-Spezifikation-2.5-kompatiblen Container benötigt, und dies ist unterstützt der zugrundeliegende Tomcat 5.5 nicht.


Servlet anlegen:
Wir gehen auf "Dynamic Web Projects" -> "StatelessWeb" und erstellen mit Rechtsklick -> "New" -> "Servlet" ein neues Servlet. Package und Name sollten so aussehen:
Neues Servlet (1)
In den restlichen Schritten belassen wir alles auf den Defaults.
Im nächsten Schritt wird eine URL angegeben unter der das Servlet später angesprochen werden soll. Ich habe den Default "/GeometricModelServlet" entfernt und durch "/servlet/GeometricModelServlet" ersetzt. Diese Angabe finden wir in "web.xml" wieder.
Neues Servlet (2)
In Schritt 3 belassen wir alles auf den Defaults.

Es wird dieser Eintrag in "web.xml" erzeugt:
	<servlet>
		<description>
		</description>
		<display-name>GeometricModelServlet</display-name>
		<servlet-name>GeometricModelServlet</servlet-name>
		<servlet-class>de.fhw.swtvertiefung.knauf.stateless.GeometricModelServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>GeometricModelServlet</servlet-name>
		<url-pattern>/servlet/GeometricModelServlet</url-pattern>
	</servlet-mapping> 
Im "Project Explorer" sieht das so aus:
Neues Servlet (4)


Server einrichten

Das Webprojekt auswählen, Rechtsklick -> "Run As..." -> "Run on Server" wählen. Im Assistenten wählen wir den JBoss-Server.
Server anlegen (1)
In Schritt 2 belassen wir die Defaults. In Schritt 3 sollte unsere Anwendung bereits ausgewählt sein.
Server anlegen (2)

Wir klicken auf "Finish". Die Enterprise Application wird auf den JBoss deployed und der Server wird gestartet.
Falls das ohne Fehlermeldungen klappt erscheint ein integriertes Browserfenster mit einem Verzeichnislisting der URL
http://localhost:8080/StatelessWeb/.
Diese zeigt allerdings den Fehler "Seite nicht gefunden" an. Dies liegt daran dass unser Projekt keine Index-Seite definiert (mehr dazu im nächsten Beispiel).
Webclient testen
Wir hängen also unsere Seite "GeometricModelTest.jsp" and und können endlich Berechnungen ausführen.
Den neu angelegten Server erreichen wir über die Karteikarte "Servers" im unteren Bereich der Anwendung. Nachdem wir die Anwendung jetzt zum ersten Mal deployed haben können wir sie in Zukunft nach einer Änderung aktualisieren indem wir den Server auswählen und im Contextmenü "Publish" wählen. Kurz nach einem Publish sollte auf der Karteikarte "Console", auf der die JBoss-Ausgabe landet, eine Ausgabe über ein Neu-Laden der Anwendung auftauchen.
Publish
Eine Anleitung wie das Logging für den JBoss zu konfigurieren ist findet sich hier.

Anlegen des Application Clients

Zuallererst einmal löschen wir die Klasse "Main.java" im Default-Package !

In der J2EE-Hierarchie "StatelessClient" auswählen. Rechtsklick, und dann "New" -> "Class" wählen.
Application Client, Schritt 1
Wie im Webclient muss hier eine Abhängigkeit zum EJB-Projekt definiert werden (Rechtsklick auf "StatelessClient", "Properties" wählen). Man wählt unter "J2EE Module Dependencies" das Bean-JAR aus.
Application Client, Schritt 2
Jetzt den Code einfügen. Da wir uns im Gegensatz zur Webanwendung hier selbst um die Initialisierung der Clients-Umgebung (und damit des JNDI) kümmern müssen, müssen wir die Verbindungsinfos zum JNDI (für das Bean-Lookup) selbst anlegen. Dies geschieht indem ein Properties-Objekt angelegt wird.
Im folgenden der gesamte Code des Clients:
  public class GeometricModelApplicationClient
  {
    public static void main(String[] args)
    {
      try
      {
        Properties props = new Properties();
        props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
        props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming.client");
        props.setProperty(Context.PROVIDER_URL, "jnp://localhost:1099");
        props.setProperty("j2ee.clientName", "StatelessClient");
        
        InitialContext initialContext = new InitialContext(props);
        Object objRemote = initialContext.lookup("java:comp/env/ejb/GeometricModel");
        GeometricModel geometricModel = (GeometricModel) PortableRemoteObject.narrow(objRemote, GeometricModel.class);
       
        double dblVolume = geometricModel.computeCuboidVolume(10, 5, 7);
        double dblSurface = geometricModel.computeCuboidSurface(10, 5, 7);
  
        System.out.println("Calculated volume: " + dblVolume + ", surface: " + dblSurface);
      }
      catch (Exception ex)
      {
        ex.printStackTrace();
      }
    }
  } 
Wichtig ist hier dass wir die Property j2ee.clientName (JBoss-spezifisch) auf den Wert des Elements jndi-name in "jboss-client.xml" setzen !

Die EJB-Referenzen müssen im Deployment-Deskriptor des Clients deklariert werden. Dazu die Datei "application-client.xml" öffnen und folgendes einfügen:
	<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.stateless.GeometricModel</remote>
	</ejb-ref> 
Auch hier ist wieder wie im Webclient das sinnlose Item "home" nötig :-(.

Wie im Web-Projekt ist ein JBoss-spezifischer Deployment-Deskriptor nötig um die EJB-Referenz mit der Bean im Server-JNDI zu verbinden. Die Datei dazu heißt "jboss-client.xml" und sieht so aus:
	<?xml version="1.0" encoding="UTF-8"?>
	<!DOCTYPE jboss-client PUBLIC
		"-//JBoss//DTD Application Client 4.2//EN"
		"http://www.jboss.org/j2ee/dtd/jboss-client_4_2.dtd">
	<jboss-client>
		<jndi-name>StatelessClient</jndi-name>
		<!-- Connect the declared EJB reference to the JNDI-Name of the EJB: -->
		<ejb-ref>
			<ejb-ref-name>ejb/GeometricModel</ejb-ref-name>
			<jndi-name>Stateless/GeometricModelBean/remote</jndi-name>
		</ejb-ref>
	</jboss-client> 

Jetzt noch die Klasse mit der "main"-Methode in "StatelessClient\appClientModule\META-INF\MANIFEST.MF" eintragen und nicht mal der J2EE-Verifier hat etwas zu meckern:
Manifest-Version: 1.0
Class-Path: StatelessEJB.jar
Main-Class: de.fhw.swtvertiefung.knauf.stateless.GeometricModelApplica
 tionClient
Der Zeilenumbruch in der Zeile "Main-Class" gehört so, denn in einer Manifest-Datei darf eine Zeile maximal 72 Zeichen lang sein !

Ausführen des Application Clients

Manueller Start:
Sobald die komplette Enterprise Application auf den Server deployed wurde können wir die JAR-Datei des Application-Clients mit einem simplen Packprogramm klauen. Alternativ dazu finden wir sie im Verzeichnis ".metadata\.plugins\org.eclipse.wst.server.core\tmp0\Stateless" unseres Workspaces. Für die Interfaces unserer Bean benötigen wir außerdem die EJB-JAR-Datei "StatelessEJB.jar".

So sieht die Kommandozeile zum Start aus:
java -cp c:\...\jboss-4.2.0.CR1\client\jbossall-client.jar;c:\...\jboss-4.2.0.CR1\client\jboss-ejb3-client.jar;c:\...\jboss-4.2.0.CR1\client\jboss-aop-jdk50-client.jar;c:\...\jboss-4.2.0.CR1\client\jboss-aspect-jdk50-client.jar;StatelessEJB.jar;StatelessClient.jar de.fhw.swtvertiefung.knauf.stateless.GeometricModelApplicationClient

Wir benötigen hier insgesamt 4 weitere JBoss-Dateien.


Ausführen aus Eclipse heraus:
Wir wählen das Application-Client-Projekt aus und gehen im Menü "Run" auf "Run...". Der Dialog für die diversen Launch-Konfigurationen öffnet sich. Wir wählen links den Punkt "Java Application" und fügen durch Klick auf "New" (der unscheinbare Button links in der Toolbarleiste oben). eine neue Konfiguration zu. Die Konfiguration bekommt den Namen "StatelessClient", als MainClass wird natürlich "com.knauf.ejb.stateless.GeometricModelApplicationClient" gewählt. Auf der Registerkarte "Classpath" finden wir auch die benötigten JBoss-Libraries wieder.
Application Client starten
Nach dem Klick auf "Run" läuft unsere Anwendung los, und wir sollten auf der JBoss-Konsole ein paar Ausgaben über die Bean-Methoden-Aufrufe sehen. Damit wir die Ausgabe unseres Clients sehen müssen wir die entsprechende Konsole auswählen (siehe Screenshot):
Application Client-Ausgabe


Export des Workspace

Um den kompletten Workspace so zu exportieren, dass er auf einem anderen Rechner wieder importiert werden kann, im Menü "File" den Punkt "Export..." wählen. Als "export destination" wählen wir "EAR file".
Export, Schritt 1
Im nächsten Schritt die zu exportierende EAR-Anwendung "Stateless" auswählen und eine Zieldatei für den Export angeben. Ich würde empfehlen diese Datei genauso zu benennen wie die EAR-Anwendung da der Dateiname beim Import als Vorschlag für die neu zu erstellen EAR-Anwendung verwendet wird.
Wichtig: den Haken bei "Export source files" setzen !
Export, Schritt 2
Fertig !


Re-Import:

Falls wir das zu importierende Projekt bereits im Workspace haben dann sollten wir es vorher löschen.
Im Menü "File" ruft man "Import..." auf. Man wählt wiederum die Quelle "EAR file".
Import, Schritt 1
Im nächsten Schritt wählt man die Quelldatei (unsere eben exportierte Zip-Datei), den Namen der Ziel-EAR-Anwendung ("Stateless"), und den eben angelegten Target-Server.
Import, Schritt 2
Unsere Anwendung verfügt über keine Hilfsprojekte, wir können den nächsten Schritt also ignorieren.
Import, Schritt 3
Im letzten Schritt wählen wir die gewünschten Module aus.
Import, Schritt 4


Außerdem müssen wir den EJB-Validator des EJB-Projekts wieder abschalten.


Ohne Annotations

Alle Annotations des JavaEE5-Standards lassen sich komplett durch Deployment-Deskriptoren ersetzen.
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>StatelessEJB</display-name>
	<enterprise-beans>
		<session>
			<description><![CDATA[This is the Stateless SessionBean "GeometricModel",
				which might be used for simple geometric calculations.]]>
			</description>
			<display-name>GeometricModelBean</display-name>
			<ejb-name>GeometricModelBean</ejb-name>
			<remote>de.fhw.swtvertiefung.knauf.stateless.GeometricModel</remote>
			<local>de.fhw.swtvertiefung.knauf.stateless.GeometricModelLocal</local>
			<ejb-class>de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean</ejb-class>
			<session-type>Stateless</session-type>
		</session>
	</enterprise-beans>
</ejb-jar> 
Pflichtangaben gibt es eigentlich keine mehr, da alles durch Annotations gesteuert werden kann. Wird ein Element "session" eingefügt dann muss allerdings zumindest ein "ejb-name" angegeben werden.

Schieben wir das Projekt auf den JBoss scheint dieser intern die Annotations an die Klassen zu basteln (in "server\default\log\server.log" zu erkennen):

2007-04-23 21:19:55,546 DEBUG [org.jboss.ejb3.Ejb3DescriptorHandler] adding class annotation javax.ejb.Stateless to de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean org.jboss.ejb.StatelessImpl@b65a68
2007-04-23 21:19:55,562 DEBUG [org.jboss.ejb3.Ejb3DescriptorHandler] adding class annotation javax.ejb.Remote to de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean org.jboss.ejb.RemoteImpl@1d8c046
2007-04-23 21:19:55,562 DEBUG [org.jboss.ejb3.Ejb3DescriptorHandler] adding class annotation javax.ejb.Local to de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean org.jboss.ejb.LocalImpl@1715510
2007-04-23 21:19:55,562 DEBUG [org.jboss.ejb3.Ejb3AnnotationHandler] found EJB3: ejbName=GeometricModelBean, class=de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean, type=STATELESS 

Durch Angabe des "ejb-name" können wir steuern unter welchem Namen JBoss die Bean im Default ins JNDI hängt ("Stateless/GeometricModelBean/local" und "Stateless/GeometricModelBean/remote", fett markiert ist der "ejb-name"). Der Name der Bean-Klasse, also "GeometricModelBean", ist gleichzeitig der Default wenn kein EJB-Name angegeben ist.

Wollen wir mehr Kontrolle über den globalen JNDI-Namen dann können wir eine Datei "jboss.xml" ins EJB-Projekt hängen.
Beispiel:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC
	"-//JBoss//DTD JBOSS 4.2//EN"
	"http://www.jboss.org/j2ee/dtd/jboss_4_2.dtd">
<jboss>
	<enterprise-beans>
		<session>
			<ejb-name>GeometricModelBean</ejb-name>
			<jndi-name>Keks</jndi-name>
			<local-jndi-name>KeksLocal</local-jndi-name>
		</session>
	</enterprise-beans>
</jboss> 
Das führt zu dieser Ausgabe auf der JBoss-Konsole:

2007-04-23 21:19:55,546 DEBUG [org.jboss.ejb3.Ejb3DescriptorHandler] adding class annotation javax.ejb.Stateless to de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean org.jboss.ejb.StatelessImpl@b65a68
2007-04-23 21:19:55,562 DEBUG [org.jboss.ejb3.Ejb3DescriptorHandler] adding class annotation javax.ejb.Remote to de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean org.jboss.ejb.RemoteImpl@1d8c046
2007-04-23 21:19:55,562 DEBUG [org.jboss.ejb3.Ejb3DescriptorHandler] adding class annotation javax.ejb.Local to de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean org.jboss.ejb.LocalImpl@1715510
2007-04-23 21:19:55,562 DEBUG [org.jboss.ejb3.Ejb3DescriptorHandler] adding class annotation org.jboss.annotation.ejb.LocalBinding to de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean org.jboss.annotation.ejb.LocalBindingImpl@5eb748
2007-04-23 21:19:55,562 DEBUG [org.jboss.ejb3.Ejb3DescriptorHandler] adding class annotation org.jboss.annotation.ejb.RemoteBinding to de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean [RemoteBindingImpl:, jndi=Keks, stack=, bindUrl=, proxyFactory=interface org.jboss.ejb3.remoting.RemoteProxyFactory]
2007-04-23 21:19:55,562 DEBUG [org.jboss.ejb3.Ejb3DescriptorHandler] adding class annotation org.jboss.annotation.ejb.RemoteBinding to de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean [RemoteBindingImpl:, jndi=Keks, stack=, bindUrl=, proxyFactory=interface org.jboss.ejb3.remoting.RemoteProxyFactory]
2007-04-23 21:19:55,562 DEBUG [org.jboss.ejb3.Ejb3AnnotationHandler] found EJB3: ejbName=GeometricModelBean, class=de.fhw.swtvertiefung.knauf.stateless.GeometricModelBean, type=STATELESS 
Es werden also noch zusätzliche Annotations angelegt. Der Meldung kann man übrigens entnehmen dass diese JNDI-Bindungen (die über keine Standard-JavaEE-Annotation realisierbar sind) durch eine JBoss-spezifische Annotation "org.jboss.annotation.ejb.RemoteBinding" realisierbar wären ! Damit würde unsere Bean allerdings endgültig die Containerunabhängigkeit verlassen, da der Code selbst ohne eine Chance auf Änderung an den JBoss getackert wäre.

Wichtig ist dass wir jetzt die EJB-Referenzen im Web- und ApplicationClient-Projekt anpassen, denn die Bean gibt es unter dem dort deklarierten Namen nicht mehr.
Beispiel für "jboss-client.xml" aus dem Application-Client-Projekt:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-client PUBLIC
	"-//JBoss//DTD Application Client 4.2//EN"
	"http://www.jboss.org/j2ee/dtd/jboss-client_4_2.dtd">
<jboss-client>
	<jndi-name>StatelessClient</jndi-name>
	<ejb-ref>
		<ejb-ref-name>ejb/GeometricModel</ejb-ref-name>
		<jndi-name>Keks</jndi-name>
	</ejb-ref>
</jboss-client> 

Die JNDI-View liefert diese Ausgabe:
JNDI-View


Die modifizierte Version des Projekts (einschließlich des geänderten JNDI-Namens "Keks") gibt es hier:
StatelessNoAnnotation.ear.
ACHTUNG: Dieses Projekt kann nicht neben dem obigen Stateless-Beispiel existieren !
Auf Bean-Ebene wäre dies zwar problemlos möglich da die gleiche Bean-Klasse an unterschiedliche JNDI-Namen gebunden wird. Allerdings wird zweimal der gleiche Environment Naming Context für die Application Clients erzeugt, und das gibt beim Deploy eine Fehlermeldung.



Stand 23.04.2007
Historie:
27.03.2007: Erstellt (Kopie der 2006er-Version, nur 3 Eclipse-Warnungen korrigiert und Screenshots an JBoss 4.2.0 angepaßt)
09.04.2007: Falsche Main-Class in den Manifest-Dateien der Application Clients korrigiert
13.04.2007: Quellcodes der Klassen/JSPs sind jetzt komplett. JBoss-Deploymentdeskriptoren auf JBoss 4.2.0 aktualisiert.
16.04.2007: Kommandozeilenaufruf des Application Clients auf JBoss 4.2.0 aktualisiert.
23.04.2007: Ausschnitt aus Logausgabe des Annotations-freien-Beispiels auf JBoss 4.2.0 aktualisiert.