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".
Schritt 2: In dem erscheinenden Dialog geben wir dem Projekt einen Namen und wählen den JBoss 4.2.0-Server aus.
Auf "Next" klicken.
Schritt 3: Hier belassen wir alles auf dem Default.
Schritt 4: Hier könnten wir direkt die einzelnen Modulprojekte zufügen. Leider klappt das nicht, denn wir benötigen für den JBoss 4.2 zwingend
Deployment-Deskriptoren, und durch einen Bug in Eclipse werden diese in den Unterprojekten über diesem Assistenten nicht erzeugt.
Also setzen wir nur den Haken bei "Generate Deployment Descriptor" und beenden den Assistenten.
Man klickt auf "Finish".
Beim ersten Aufruf erscheint die Abfrage ob wir zur "Java EE perspective" wechseln wollen. Diese bejahen wir.
Jetzt sieht man folgende Hierarchie im Project Explorer:
Wir fügen die einzelnen Module hinzu.
EJB-Projekt
Rechtsklick auf das Projekt "Stateless", und die Option "New" - "EJB Project" wählen.
Im erscheinden Dialog wird als Name "StatelessEJB" (Name des EAR-Projekts mit Zusatz "EJB" eingegeben). Als Server wird wiederum "JBoss 4.2" gewählt.
Wichtig ist, dass die Option "Add project to an EAR" gesetzt ist und in der Combobox unser eben erzeugtes EAR-Projekt gewählt ist.
Im nächsten Schritt belassen wir alles bei den Defaults.
Im letzten Schritt wird die Checkbox "Create an EJB client module..." zurückgesetzt, und dafür die Checkbox "Generate Deployment Descriptor" gesetzt.
Web-Projekt
Rechtsklick auf das Projekt "Stateless", und die Option "New" - "Dynamic Web Project" wählen.
Im erscheinden Dialog wird als Name "StatelessWeb" (Name des EAR-Projekts mit Zusatz "Web" eingegeben). Als Server wird wiederum "JBoss 4.2" gewählt.
Wichtig ist, dass die Option "Add project to an EAR" gesetzt ist und in der Combobox unser eben erzeugtes EAR-Projekt gewählt ist.
Im nächsten Schritt belassen wir alles bei den Defaults.
Im letzten Schritt stellen wir sicher dass die Checkbox "Generate Deployment Descriptor" gesetzt ist (das ist per Default der Fall)
Application Client-Projekt
Rechtsklick auf das Projekt "Stateless", und die Option "New" - "Application Client Project" wählen.
Im erscheinden Dialog wird als Name "StatelessClient" (Name des EAR-Projekts mit Zusatz "Client" eingegeben). Als Server wird wiederum "JBoss 4.2" gewählt.
Wichtig ist, dass die Option "Add project to an EAR" gesetzt ist und in der Combobox unser eben erzeugtes EAR-Projekt gewählt ist.
Im nächsten Schritt belassen wir alles bei den Defaults.
Im letzten Schritt stellen wir sicher dass die Checkbox "Generate Deployment Descriptor" gesetzt ist (das ist per Default der Fall).
Die Checkbox "Create a default Main class" setzen wir zurück, da wir unsere Main Class sowieso per Hand anlegen.
Das war es, der Project Explorer sieht so aus:
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: Im Project Explorer den Knoten "StatelessEJB" wählen.
Rechtsklick, im Contextmenü den Punkt "New" -> "Class" wählen.
Schritt 2: Wir vergeben einen Namespace und einen Bean-Namen. Den Haken "Generate Comments" setzen wir natürlich.
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.
Zuerst erzeugen wir das Remote Interface namens "GeometricModel". Wir wählen die zwei Methoden aus.
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".
JSP anlegen
Den Ordner "StatelessWeb" -> "WebContent" auswählen und per Rechtsklick eine
JSP zufügen.
Im ersten Schritt des Assistenten der JSP den Namen "GeometricModelTest.jsp" geben.
In Schritt 2 verwenden wir das vorgeschlagene Template.
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 "StatelessWeb" und erstellen mit Rechtsklick -> "New" -> "Servlet" ein neues Servlet.
Package und Name sollten so aussehen:
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.
In Schritt 3 belassen wir alles auf den Defaults.
Der Code des Servlets sieht so aus:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//This printwriter is used for outputting the response HTML:
PrintWriter printWriter = response.getWriter();
try
{
//Page header:
printWriter.write("<HTML>\r\n" + "<HEAD>\r\n" + "<TITLE>GeometricModelTest</TITLE>\r\n"
+ " <meta http-equiv=\"content-type\" content=\"text/html; charset=Windows-1252\">\r\n" + "</HEAD>\r\n"
+ "<BODY>\r\n" + "<H1>Test for the GeometricModel stateless session bean</H1>\r\n");
//Should only the form be shown or is this a submitted form and we have to calculate ?
if (request.getParameter("submit") == null)
{
//First call to this page: just create the form.
}
else
{
//Page was submitted: do some calculations.
double dblA = Double.parseDouble(request.getParameter("a"));
double dblB = Double.parseDouble(request.getParameter("b"));
double dblC = Double.parseDouble(request.getParameter("c"));
///////////////////////////////////////////////////////////////
// Calculate by using the remote interface:
InitialContext initialContext = new InitialContext();
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);
printWriter.write("Volume = " + dblVolume + " Surface= " + dblSurface + "\r\n"
+ "<br>And now with the Local Interface: <br>\r\n");
objGeometricModel = initialContext.lookup("java:comp/env/ejb/GeometricModelLocal");
//Local Interfaces can be fetched by direct typecast:
GeometricModelLocal geometricModelLocal = (GeometricModelLocal) objGeometricModel;
dblVolume = geometricModelLocal.computeCuboidVolume(dblA, dblB, dblC);
dblSurface = geometricModelLocal.computeCuboidSurface(dblA, dblB, dblC);
printWriter.write("Volume = " + dblVolume + ", Surface = " + dblSurface + "\r\n" + "<br><br><br>\r\n");
}
//Write the form:
printWriter.write("<form action=\"./GeometricModelServlet\" method=\"post\"> \r\n"
+ " a = <input type=\"text\" name=\"a\" /> <br> \r\n" + " b = <input type=\"text\" name=\"b\" /> <br> \r\n"
+ " c = <input type=\"text\" name=\"c\" /> <br> \r\n"
+ " <input type=\"submit\" name=\"submit\" value=\"submit\" /> \r\n" + "</form> \r\n" + "</BODY> \r\n"
+ "</HTML> \r\n");
}
catch (Exception ex)
{
//Output in Servlet-Log:
this.log("An error has occured: " + ex.getMessage(), ex);
printWriter.write("An error has occured: " + ex.getMessage());
}
//Aufräumen:
printWriter.flush();
printWriter.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
this.doPost(request, response);
}
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:
Server einrichten
Das Webprojekt auswählen, Rechtsklick -> "Run As..." -> "Run on Server" wählen.
Im Assistenten wählen wir den JBoss-Server.
Falls wir den Server bereits angelegt haben (dies ist außerdem die Default-Option beim Definieren der Server-Runtime), dann
wählen wir hier "Choose an existing server" und wählen den Server aus, ansonsten "Manually define a new server").
In Schritt 2 belassen wir die Defaults. In Schritt 3 sollte unsere Anwendung bereits ausgewählt sein.
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).
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.
Eine Anleitung wie das Logging für den JBoss zu konfigurieren ist findet sich hier.
Anlegen des Application Clients
Im Project Explorer "StatelessClient" auswählen. Rechtsklick, und dann "New" -> "Class" wählen.
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.
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" (siehe weiter unten) 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 aus "Stateless.ear" klauen. Alternativ dazu finden wir sie im Verzeichnis ".metadata\.plugins\org.eclipse.wst.server.core\tmp0\Stateless" unseres
Workspaces (dieses Verzeichnis ist erst vorhanden nachdem wir die Anwendung das erste Mal auf den Server deployed haben).
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.1.GA\client\jbossall-client.jar;c:\...\jboss-4.2.1.GA\client\jboss-ejb3-client.jar;c:\...\jboss-4.2.1.GA\client\jboss-aop-jdk50-client.jar;c:\...\jboss-4.2.1.GA\client\jboss-aspect-jdk50-client.jar;StatelessEJB.jar;StatelessClient.jar de.fhw.swtvertiefung.knauf.stateless.GeometricModelApplicationClient
Natürlich muss der Pfad zu den JBoss-Dateien angepaßt werden !
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 "Open Run Dialog...". 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.
Nach dem Klick auf "Run" läuft unsere Anwendung los und gibt etwas auf der Konsole aus. Parallel dazu gibt es in der JBoss-Konsole ein paar Ausgaben über die Bean-Methoden-Aufrufe.
Zwischen diese Aufgaben können wir umschalten::
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".
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 !
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".
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.
Unsere Anwendung verfügt über keine Hilfsprojekte, wir können den nächsten Schritt also ignorieren.
Im letzten Schritt wählen wir die gewünschten Module aus.
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:
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 25.10.2007
Historie:
01.10.2007: Erstellt (Kopie der Sommersemester-2007-Version, nur Screenshots und Beschreibung des Projekt-Anlegens an Eclipse 3.3 angepaßt)
25.10.2007: Code des Servlets zugefügt. JavaDoc-Warnungen korrigiert.