Enterprise Application Integration
Serverdaten kurzgefasst
Für den Zugriff auf den JBoss-Server werden folgende Einstellungen benötigt:
- Deployment über:
wi-vm215.uni-muenster.de:8080/deployment - Kennung:
gruppexy - Passwort: die Zugangskennung für Ihre Gruppe erfragen Sie bitte bei ihrem Betreuer
- Datenbankschema: (muss im
@ejb.persistence-Tag durch dastable-name-Attribut der XDoclet-Konfiguration definiert werden):gruppexy - Datenquelle: (für das
<datasource>-Attribut der XDoclet-Konfiguration):java:/GruppexyDS - Datenbank-Typ: (für das
<datasource-mapping>-Attribut der XDoclet-Konfiguration):PostgreSQL
Für die Entwicklung von Anwendungen, die auf unserem Anwendungsserver laufen, benötigen Sie folgende Software
- Den JBoss Application Server (in der Version 4.0.3 SP1)
- Das Java 2 SDK.
Die UmgebungsvariableJAVA_HOMEsollte auf das Installationsverzeichnis gesetzt sein. - Die Eclipse 3.1.2 IDE in Verbindung mit der Plugin-Sammlung JBoss Eclipse IDE 1.5.1.GA.
Entpacken Sie sowohl Eclipse als auch die Plugin-Sammlung in das gleiche Verzeichnis, oder laden Sie direkt von der JBoss-Seite das komplette Bundle zusammen mit Eclipse (JBossIDE-1.5.1.GA-Bundle-win32.zip) herunter. - Das Java Web Services
Developer Pack 2.0.
Installieren Sie dieses ohne "Web Container", fügen Sie danach das Unterverzeichnisjaxrpc\bindem Suchpfad (PATH) hinzu und setzen Sie eine neue UmgebungsvariableJWSDP_HOMEauf das Installationsverzeichnis des Developer Packs. - Apache Axis 1.3. Entpacken Sie dieses in ein Verzeichnis Ihrer Wahl.
- XDoclet 1.2.3 wird benötigt, um einige Fehler in der JBoss-IDE zu beheben.
Installation eines lokalen JBoss-Servers
Es wird vorausgesetzt, dass Java bereits installiert ist. Nachdem Sie das Installationspaket für den JBoss-Server heruntergeladen
haben, können Sie dieses normalerweise per Doppelklick auf die jboss-4.0.3SP1-installer.jar
ausführen. Sollte dies
fehlschlagen, können Sie das Installationsprogramm auch von der Kommandozeile aus über java -jar jboss-4.0.3SP1-installer.jar
starten (die JAR-Datei muss sich im aktuellen Verzeichnis befinden).
Nachdem Sie die Lizenzbedingungen akzeptiert und einen Installationspfad für den JBoss ausgewählt haben, können Sie nun eine
Konfiguration Ihres JBoss-Servers auswählen. Wählen Sie hier die default
-Konfiguration, diese enthält alles, was man
für die Vorlesung benötigt. Überspringen Sie den darauffolgenden Dialog, behalten Sie den Konfigurationsnamen default
bei und lassen Sie auch die "Deployment isolation/call by value"-Einstellung deaktiviert. Wenn Sie ihren
Server gegen externe Eindringliche absichern möchten, können Sie im Dialog "JMX Security" noch die ersten zwei Checkboxen zur
Absicherung der Administrationskonsolen aktivieren und eine Kennung und Passwort für den Administrator anlegen. Danach kann der
JBoss installiert werden.
Starten des Servers
Wechseln Sie nun in das Verzeichnis, in das Sie den JBoss installiert haben,
und führen Sie im Unterverzeichnis bin
- je nach System - entweder die run.bat
oder die run.sh
. Der JBoss-Server sollte nun gestartet werden. Wenn der Startvorgang abgeschlossen ist, sollten Sie unter http://localhost:8080 eine Startseite mit Links zu Dokumentationen und den
Administrationskonsolen sehen.
Entwicklungsumgebung einrichten
Einmalige Vorarbeiten
Entpacken Sie die Archive für Eclipse und die JBoss IDE in das gleiche Verzeichnis. Es sollte
ein Unterverzeichnis eclipse
erzeugt werden.
Entpacken Sie danach das XDoclet-Archiv in einer temporäres Verzeichnis und kopieren Sie dort
aus dem Unterverzeichnis "xdoclet-1.2.3\lib" die beiden Dateien xdoclet-ejb-module-1.2.3.jar
sowie xdoclet-jboss-module-1.2.3.jar
in das Eclipse-Unterverzeichnis eclipse\plugins\org.jboss.ide.eclipse.xdoclet.core_1.5.1.GA
. Überschreiben Sie die alten
Versionen.
Wechseln Sie nun in das Eclipse-Verzeichnis und starten Sie dort die eclipse.exe
bzw. das Pendant für ihr Betriebssystem. Geben Sie einen Arbeitsbereich an (dies ist ein Verzeichnis, in
dem Eclipse alle angelegten Projekte und Metadaten speichert). Wenn Sie nur einen Arbeitsbereich mit
Eclipse nutzen, können Sie auch "Use this as the default..." anklicken, so dass diese Frage nicht mehr
gestellt wird.

Eclipse wird nun starten und Sie mit zwei Willkommensbildschirmen begrüßen (einer für Eclipse, einer für JBoss IDE). Schließen Sie beide. Wählen Sie den Menüpunkt "Window" > "Preferences..." aus und öffnen sie dort den Knoten "Java/Build Path". Wählen Sie anstellen von "Project" den Punkt "Folders" aus und klicken Sie auf "Apply". Wählen Sie danach den Knoten "JBoss-IDE/XDoclet", klicken Sie auf "Refresh XDoclet Modules" und danach auf "OK".


Nun muss noch ihr lokaler JBoss-Anwendungsserver angegeben werden. Gehen Sie dazu auf "Run" > "Debug...". Klicken Sie auf "JBoss 4.0.x" und dann auf "New". In dem daraufhin erscheinenden Dialog geben Sie zuerst einen Namen für die Konfiguration an (z. B. "Lokaler JBoss 4.0.3SP1") und wählen Sie dann über den "Browse..."-Button das Verzeichnis Ihrer JBoss-Installation aus. Geben Sie danach noch an, dass die Server-Konfiguration "default" verwendet werden soll und klicken Sie auf "Apply", danach auf "Close".
Enterprise JavaBeans mit Eclipse und JBoss: Ein Beispiel
Im folgenden soll ein kleines Beispiel für die Entwicklung von Enterprise JavaBeans mit Eclipse und JBoss gegeben werden. Dazu wird ein einfaches Telefonbuch erstellt, wie es in dem folgenden UML-Modell angebildet ist.

Die Sourcen der Klassen, die Sie implementieren sollen, finden Sie auch in diesem Archiv.
Anlegen eines Projekts
Wählen Sie im Menü "File" den Punkt "New..." > "Project" aus. Wählen Sie im darauffolgenden Dialog "JBoss-IDE/J2EE Projects/J2EE 1.4 Project" aus und klicken sie auf "Next >". Geben Sie einen Namen für das Projekt ein (z. B. "EAI J2EE Beispiel") und klicken sie wiederum auf "Next >". Fügen Sie über"Add Folder..." einen neuen Quellordner namens "src" hinzu. Klicken Sie dann auf "Finish".
Fügen Sie nun über einen Rechtsklick auf das Projekt Wahl von "New" > "Folder" einen Ordner "xml" zu dem Projekt hinzu.
Mit der JBoss-IDE kann man viel Implementierungs- und Schreibaufwand sparen, indem man XDoclet verwendet. Dies muss für jedes Projekt allerdings einmal eingerichtet werden. Rechtsklicken Sie dazu im "Package Explorer" auf das Projekt und wählen Sie "Properties..." aus. Es öffnet sich der Eigenschaftsdialog für das Projekt. Wählen Sie dort den Punkt "XDoclet Configuration" aus. Klicken Sie auf "Enable XDoclet" und danach auf "Add...". Geben Sie einen Namen für die Konfiguration an, z. B. "J2EE-JBoss" und klicken Sie auf "OK". Wählen Sie die Konfiguration aus und rechtsklicken Sie dann in den Bereich darunter. Klicken Sie auf "Add Doclet" und wählen Sie das "ejbdoclet" aus. Klicken Sie wiederum auf "OK".
Rechtsklicken Sie nun auf das "ejbdoclet" und fügen Sie über "Add..." nacheinander die Einträge "jboss", "deploymentdescriptor", "fileset", "homeinterface", "remoteinterface", "localhomeinterface" sowie "localinterface" hinzu. Passen Sie danach die Eigenschaften (Properties" der einzelnen Einträge wie in der folgenden Tabelle gezeigt an:
| Einträge | Eigenschaften |
ejbdoclet
|
ejcSpec: 2.1 destDir: src force: true |
jboss
|
Version: 4.0 (befindet sich ziemlich am Anfang der Liste) destDir: xml datasource: java:/DefaultDS datasourceMapping: Hypersonic SQL |
deploymentdescriptor
|
destDir: xml |
localhomeinterface
|
destDir: src |
localinterface
|
destDir: src |
homeinterface
|
destDir: src |
remoteinterface
|
destDir: src |
fileset
|
dir: src includes: **/*Bean.java |
Klicken Sie auf "OK".
Entity Beans erstellen
Zuerst soll die Bean für die Einträge des Telefonbuchs erstellt werden. Selektieren Sie dazu das "src"-Verzeichnis und wählen Sie dann "File" > "New" > "Other...". Erzeugen Sie dort in dem Knoten "JBoss-IDE/EJB Components" eine neue Entity Bean und klicken Sie auf "Next". Geben Sie einen Namen an, der auf "Bean" endet (z.B. EntryBean), wählen Sie ein Package aus (z. B. lspi.eai.phonebook) und setzen Sie die Eigenschaften der Bean auf "CMP 2.x" und "local". Klicken Sie danach auf "Finish".

Es sollte nun eine neue Entity Bean erzeugt worden sein. Diese enthält am Kopf eine Menge an Schlüsselwörtern in einem JavaDoc-Kommentar - dies sind XDoclet-Tags, mit denen die Programmierung und das Deployment von Enterprise JavaBeans sehr vereinfacht werden kann. Eine Liste dieser Tags finden Sie auf der XDoclet-Webseite; die für die Vorlesung notwendigen Tags werden aber auch in diesem Beispiel vorgestellt.
In der Regel muss das @ejb.bean
-Tag, das Meta-Informationen zu einer Bean liefert,
angepasst werden. Da auf EntityBeans in der Regel
nur lokal zugegriffen werden soll, muss der Parameter jndi-name
auf local-jndi-name
abgeändert werden. Weiterhin sollten Sie noch einen Namen für ein
abstraktes Schema (dies ist ein Bezeichner, über den persistierte EntityBean aus der Datenbank
ausgelesen werden können) angeben. Dies geschieht über Hinzufügen des Parameters schema = "Entry"
.
Für den Zugriff auf Entity und Session Beans werden, wie in der Vorlesung besprochen, sog. LocalHome-/Home- und Local-/Remote-Interfaces benötigt. Der Name dieser Interfaces kann über ein XDoclet-Tag beeinflusst werden, um so den Sun-Namenskonventionen zu entsprechen. Da auf die Entry Bean lokal zugegriffen wird, sind der Bean die folgenden Zeilen hinzuzufügen:
@ejb.home local-class = "lspi.eai.phonebook.EntryLocalHome"
@ejb.interface local-class = "lspi.eai.phonebook.EntryLocal"
Für Beans, auf die nicht lokal, sondern remote zugegriffen wird (z. B. Session Beans), ist
jeweils der Parameter remote-class
anstelle von local-class
zu
wählen.
Da Entity Beans vom Container automatisch in eine Datenbank persistiert werden, müssen die
zugehörigen Persistenzdaten angegeben werden. Fügen Sie dazu unter das Tag @ejb.bean
die folgenden Zeilen ein, die der Bean die Tabelle "phonebook_entries" zuweist und diese
bei Nichtvorhandensein anlegt bzw. bei Bedarf ändert:
@ejb.persistence table-name = "phonebook_entries"
@jboss.persistence create-table = "true" alter-table = "true"
Nun wird es Zeit, die EntryBean mit Inhalten zu füllen. Suchen Sie dazu die Ansicht "Outline"
und rechtsklicken Sie auf den Eintrag "EntryBean" mit dem grünen "C"-Symbol. Wählen Sie dort
"J2EE" > "Add CMP Field" aus, um ein neues persistentes Attribut hinzuzufügen.
Zuerst soll ein Primärschlüssel hinzugefügt werden. Geben Sie als Feldnamen "id" und als Feldtyp
"java.lang.Long" ein (für den Typen können Sie auch die "Browse..."-Funktion verwenden). Wählen
Sie aus, dass das Feld als Primärschlüssel agieren soll, "CMP 2.x" benutzt und das nur lokal
darauf zugegriffen werden soll. Klicken Sie dann auf "Finish".
Der Primärschlüssel ist dem @ejb.bean
-Tag noch als Attribut primkey-field
mit dem Namen des Feldes hinzuzufügen, also als
primkey-field = "id"
.


Fügen Sie der Bean nun noch die in der untenstehenden Tabelle aufgeführten Felder hinzu (diese sind allesamt keine Primärschlüssel, vom Typ CMP 2.x und nur lokal zugreifbar).
| Feldname | Feldtyp |
| lastname | java.lang.String |
| firstname | java.lang.String |
| dateOfBirth | java.lang.Long |
| phoneNumber | java.lang.String |
Jetzt, wo die Felder angelegt wurden, müssen der Entry Bean nur noch Methoden zum Auffinden
hinzugefügt werden. Diese werden mit Hilfe von XDoclet-Tags erzeugt. Fügen Sie deshalb unter dem @jboss.persistence
-Tag die folgenden Tags ein, die alle Einträge bzw. einen Eintrag
des Namens heraussuchen.
@ejb.finder signature = "java.util.Collection findAll()" query = "SELECT OBJECT(e) FROM Entry AS e" @ejb.finder signature = "lspi.eai.phonebook.EntryLocal findByName(java.lang.String lastname, java.lang.String firstname)" query = "SELECT OBJECT(e) FROM Entry AS e WHERE e.lastname = ?1 AND e.firstname = ?2"Das Schema "Entry", aus dem die Objekte ausgelesen wurden, ist das Schema, das vorhin mithilfe des
schema
-Parameters des @ejb.bean
-Tags definiert wurde.Achten Sie darauf, dass hierbei voll-qualifizierte Klassennamen (also Klassennamen inkl. des Packagenamens) verwendet werden, da die find-Methoden sonst nicht funktionieren. Eine Einführung in die EJB Query Language, die zum Suchen von persistenten Klassen verwendet wird, finden Sie in der EJB 2.1 Spezifikation in Kap. 11.
Damit neue Einträge erzeugt werden können, muss außerdem noch eine ejbCreate-Methode erzeugt werden. Wählen Sie
dazu per Rechtsklick auf "EntryBean" in der Outline-Ansicht den Punkt "J2EE" > "Add Create Method" aus und
erzeugen Sie eine neue Methode ejbCreate mit dem Rückgabetypen "java.long.Long"
(dies ist der Typ des Primärschlüssels) und den Parametern "java.lang.String lastname", "java.lang.String firstname",
"java.lang.Long dateOfBirth", "java.lang.String phoneNumber".
Stellen Sie sicher, dass der View type auf "Local" steht und dass die ejbCreate()-Methode für eine Entity Bean
erzeugt wird. Klicken Sie dann auf "Finish". Vervollständigen Sie die ejbCreate(), so dass sie wie folgt aussieht:
/**
* @ejb.create-method view-type = "local"
*/
publicjava.lang.LongejbCreate(java.lang .Stringlastname,
java.lang.Stringfirstname,java.lang.LongdateOfBirth,
java.lang.StringphoneNumber)throwsjavax/pan>.ejb.CreateException{
setLastname(lastname);
setFirstname(firstname);
setDateOfBirth(dateOfBirth);
setPhoneNumber(phoneNumber);
returnnull;
}

Damit ist die Entry Bean soweit fertig, und es können mit XDoclet die Deployment Deskriptoren, die Local- und LocalHome-Interfaces und ein Hilfsobjekt erzeugt werden. Rechtsklicken Sie dazu im "Package Explorer" auf das Projekt und wählen Sie "Run XDoclet" aus. Im Fenster "Console" sollte nach einigen Sekunden "BUILD SUCCESSFUL" erscheinen; wenn nicht, prüfen Sie bitte, ob sämtliche Attribute in der XDoclet-Konfiguration richtig angegeben sind.
Data Transfer Objects (Value Objects)
Da nach der EJB-Spezifikation Entity Beans nicht serialisiert und über das Netzwerk an die Clients eines
J2EE-Anwendungsservers geschickt werden können, hat sich in der Praxis die Verwendung von sog. Data
Transfer Objects bzw. Value Objects etabliert. Hierbei werden die Daten, die in einer Entity
Bean gekapselt sind, in ein serialisierbares Objekt, das nur Getter und Setter anbietet, übertragen und
dann an den Client geschickt. Data Transfer Objects bieten zudem den Vorteil, dass man Datentypen, die
von der EJB QL nicht unterstützt werden, in andere Datentypen umwandeln kann (z.B. indem man Calendar-Objekte
in Long-Objekte umwandelt, um in der EJB QL Zeitvergleiche durchführen zu können; dies wird hier allerdings
nicht gemacht).
Erstellen Sie deshalb bitte über "File" > "New" > "Class" eine Klasse "EntryDTO", die das gleiche
Package besitzt wie die Beans, und lassen Sie diese das Interface "java.io.Serializable" implementieren.

Füllen Sie diese Klasse mit dem folgenden Inhalt:
packagelspi.eai.phonebook;
importjava.io.Serializable;
importjava.util.Calendar;
importjava.util.TimeZone;
publicclassEntryDTOimplementsSerializable{
privateCalendardateOfBirth;
privateStringlastname;
privateStringfirstname;
privateStringtelephoneNumber;
publicEntryDTO(){
}
publicEntryDTO(CalendardateOfBirth,Stringatname,Stringfirstname,
StringtelephoneNumber){
this.dateOfBirth=dateOfBirth;
this.firstname=firstname;
this.lastname=lastname;
this.telephoneNumber=telephoneNumber;
}
publicCalendargetDateOfBirth(){
returndateOfBirth;
}
publicvoidsetDateOfBirth(CalendardateOfBirth)this.dateOfBirth=dateOfBirth;
this.dateOfBirth.setTimeZonepan class="java1-symbol">(TimeZone.getTimeZone("GMT+1"));
}
publicStringgetLastname(){
returnlastname;
}
publicvoidsetLastname(Stringlastname){
this.lastname=lastname;
}
publicStringgetFirstname(){
returnfirstname;
}
publicvoidsetFirstname(Stringfirstname){
this.firstname=firstname;
}
publicStringgetTelephoneNumber (){
returntelephoneNumber;
}
publicvoidsetTelephoneNumber (StringtelephoneNumber){
this.telephoneNumber=telephoneNumber;
}
}
Session Beans erstellen
Nun soll die Logik des Telefonbuchs in Form einer Session Bean implementiert werden. Erzeuge Sie daher im "src"-Ordner eine Session Bean (über "File" > "New" > "Other" im Knoten "JBoss-IDE/EJB Components"). Setzen Sie das Package auf das gleiche wie vorhin ("lspi.eai.phonebook") und geben Sie einen Namen an, z. B. "PhonebookBean". Wählen Sie den Typen "Stateless" aus, stellen Sie den Zugriff auf "Remote" und geben Sie an, dass eine ejbCreate()-Methode erzeugt werden soll. Klicken Sie dann auf "Finish".

Fügen Sie der Bean die Tags @ejb.home
und @ejb.interface
hinzu, diesmal allerdings
jeweils mit dem Attribut remote-class
und den Werten "lspi.eai.phonebook.PhonebookHome" bzw.
"lspi.eai.phonebook.Phonebook".
Erzeugen Sie nun in der Outline-Ansicht über "J2EE" > "Add Create Method" eine ejbCreate-Methode ohne Parameter und Rückgabewert (diese sind für zustandslose Session Beans nicht zulässig). Diese soll für "Remote"-Zugriffe sichtbar sein und für eine Session Bean erzeugt werden.
Fügen Sie anschließend die erste Geschäftsmethode hinzu, indem Sie nun "J2EE" > "Add Business Method" auswählen. Benennen Sie die Methode "findEntry" und geben Sie ihr den Rückgabetyp "EntryDTO". Als Parameter übergeben Sie bitte "java.lang.String lastname" und "java.lang.String firstname". Definieren Sie eine mögliche Exception "java.rmi.RemoteException" (diese muss bei Remote Beans bei jeder Geschäftsmethode geworfen werden können), setzen Sie den Viewtype auf "Remote" und klicken Sie auf "Finish".

In der Methode soll die findByName
-Methode der Entry Entity Bean aufgerufen werden. Dazu
muss zuerst das Home-Interface dieser Bean über den JNDI-Namensdienst aufgefunden und dort die
find-Methode aufgerufen werden. Das Auffinden wird von einer privaten Methode übernommen, die Sie
bitte wie folgt implementieren:
privateEntryLocalHomelookupEntryLocalHome ()throwsNamingExceptionspan class="java1-space"> {
InitialContextctx=newInitialContext();
try{
EntryLocalHomeentryHome=(EntryLocalHome)ctx.loup("ejb/Entry");
returnentryHome;
}finally{
if(ctxnull)ctx.close();
}
}
Füllen Sie die findEntry-Methode mit dem folgenden Inhalt. Wenn einer der Begriff "Collection" oder "Iterator"
rot unterstrichen wird, drücken Sie Strg+Shift+O
(wählen Sie bei Nachfragen
java.util.Collection bzw. java.util.Iterator aus) - dies fügt automatisch die notwendigen import
-Ausdrücke in die Klasse ein.
/**
* @ejb.interface-method view-type = "remote"
*/
publicEntryDTOfindEntry(java.lang.String lastname,
java.lang.Stringfirstname)throwsjava .rmi.RemoteException{
try{
EntryLocalHomeentryHome=lookupEntryLocalHome();
EntryLocalentry=entryHome.findByName(lastnamefirstname);
CalendardateOfBirth=newGregorianCalendar();
dateOfBirth.setTimeInMillis(entry.getDateOfBirth().longValue());
EntryDTOentryDTO=newEntryDTO(dateOfBirth,entry
.getLastname(),entry.getFirstname(),entry
.getPhoneNumber());
returnentryDTO;
}catch(e){
// Verbindungsfehler
thrownewRemoteException( e.getMessage());
}catch(e){
// falls keine Einträge gefunden wurden,
// gebe null zurück
returnnull;
}
}
Wie Sie sehen, werden zwei Exceptions abgefangen. Die NamingException
tritt auf, wenn die Bean
nicht aufgefunden werden kann und stellt einen schwerwiegenden Fehler dar. Dieser wird als RemoteException
an den Client weitergegeben. Die FinderException
hingegen tritt
auf, wenn eine Finder-Methode keinen Wert findet bzw. wenn bei Findern, die genau ein Objekt zurückerwarten,
mehr als ein Objekt zurückgeliefert wird. Diese Exception kann abgefangen werden, und dem Client kann
an dieser Stelle ein leeres Array bzw. ein leerer Wert zurückgeliefert werden.
Fügen Sie noch eine weitere Methode findEntries
hinzu, die ebenfalls ein Array EntryDTO[]
zurückliefert, diesmal aber keine Parameter erwartet, da alle Einträge des Buches zurückgegeben werden sollen.
/**
* @ejb.interface-method view-type = "remote"
*/
publicEntryDTO[]findEntries()throwsjava.rmi.RemoteException{
try{
EntryLocalHomeentryHome=lookupEntryLocalHome();
Collectionentries=entryHome.findAll();
EntryDTO[]entriesDTO=newEntryDTO[entries.size()];
Iteratoriterator=entries.iterator();
intarrayInc=0;
while(iterator.hasNext()){
EntryLocalentry=(EntryLocal)iterator.next ();
CalendardateOfBirth=newGregorianCalendar();
dateOfBirth.setTimeInMillis(entry.getDateOfBirth().longValue());
EntryDTOentryDTO=newEntryDTO(dateOfBirth,entry
.getLastname(),entry.getFirstname(),entry
.getPhoneNumber());
entriesDTO[arrayInc]=entryDTO;
arrayInc++;
}
returnentriesDTO;
}catch(e){
// Verbindungsfehler
thrownewRemoteException( e.getMessage());
}catch(e){
// falls keine Einträge gefunden wurden,
// gebe leeres Array zurück
returnnewEntryDTO[0];
}
}
Fügen Sie nun noch eine Methode zum Erzeugen eines neuen Eintrags hinzu, der als Parameter ein EntryDTO erwartet und keine Rückgabe liefert:
/**
* @ejb.interface-method view-type = "remote"
*/
publicvoidaddEntry(EntryDTOentry)thros/span>java.rmi.RemoteException{
try{
EntryLocalHomeentryHome=lookupEntryLocalHome();
entryHome.create(entry.getLastname(),entry.(),Long.valueOf(entry
.getDateOfBirth().getTimeInMillis()),entry.getTelephoneNumber());
}catch(e){
// Verbindungsfehler
thrownewRemoteException( e.getMessage());
}catch(e){
// Erzeugungsfehler
thrownewRemoteException( "Eintrag konnte nicht angelegt werden - "
+e.getMessage());
}
}
Damit ist auch diese Bean fertig. Rufen Sie nun wieder XDoclet auf, damit auch für die Session Bean die Deployment Deskriptoren, Interfaces und Hilfsklassen erzeugt werden.
Deployment
Nun sollen die Beans zum Deployment vorbereitet werden. Rufen Sie dazu die Projekteigenschaften ("Properties") auf und gehen Sie auf "Packaging Configurations". Wählen Sie "Enable Packaging" aus und klicken Sie auf "Add...". Geben Sie im nachfolgenden Dialog den Namen "phonebook.jar" an und wählen Sie als Ziel den Hauptordner des Projektes aus ("/EAI J2EE Example"). Klicken Sie dann auf OK.

Rechtsklicken Sie nun auf die soeben erstellte "phonebook.jar" und gehen Sie auf "Add Folder...". Wählen
Sie über den Button "Project Folder..." das Verzeichnis "bin" aus und geben Sie bei "Includes" den Wert
"**/*.class" ein (dies bedeutet, dass alle Dateien mit der Endung "class" in allen Unterverzeichnissen
unterhalb dieses Ordners) in das Archiv übernommen werden. Klicken Sie auf "OK"
Fügen Sie nun noch den Ordner "xml" hinzu, geben Sie diesmal aber für die "Includes" den Wert "**/*.xml"
und als Präfix den Wert "META-INF" ein. Klicken Sie zweimal auf "OK" und wählen Sie dann per Rechtsklick
auf Ihr Projekt "Run Packaging" aus. Nach Beendigung des Aufrufs sollten Sie eine Datei "phonebook.jar"
in Ihrem Projektordner finden.
Wählen Sie dann die Ansicht "Server Navigator" aus bzw. öffnen Sie diese über "Window" > "Show View" > "Other" > "JBoss-IDE/Server Navigator". Rechtsklicken Sie auf den Eintrag "JBoss 4.0.x" und wählen Sie, wenn er noch nicht laufen sollte, "Start" aus. In der Konsolenansicht sollte nun der JBoss Startprozess angezeigt werden. Sobald der Server gestartet ist (dies wird durch eine Meldung "JBoss (MX MicroKernel) [...] Started in XXs" angezeigt), rechtsklicken Sie bitte auf die "phonebook.jar" und wählen Sie dort "Deployment..." > "Deploy To..." aus. Wählen Sie dann den "Local JBoss 4.0 Main Deployer" und klicken Sie auf "OK".
Um auf die Bean zugreifen zu können, müssen Sie vom Client aus eine Verbindung zu dem Server anlegen. Wir haben einen grafischen Swing-Client bereitgestellt, mit dem Sie ihr Telefonbuch ausprobieren können. Das Kernstück des Zugriffs auf die Bean ist die Klasse ServerConnection.java. In dieser Klasse werden die Parameter für die Verbindung zum Server über Properties definiert, eine Verbindung zu dem Server aufgebaut und das Home-Interface der PhonebookBean zurückgegeben. Sie können diese Klasse auch als Basis für Verbindungen zu anderen Beans nehmen.
Um den Client zu benutzen, erzeugen Sie bitte über "File" > "New" > "Source Folder" ein neues
Quellverzeichnis "clientsrc". Rechtsklicken Sie dann auf den Ordner und wählen Sie "Import..." aus.
Geben Sie "Archive file" an und wählen Sie die Datei PhonebookClient.zip
aus. Klicken
Sie dann auf "Finish" - es sollten dann die Dateien des Clients erscheinen.
Gehen Sie nun auf die Projekteigenschaften, und wählen Sie dort "Java Build Path" aus. Gehen Sie dort
auf den Reiter "Libraries" und wählen Sie "Add External JARs..." aus. Suchen Sie das JBoss-Verzeichnis,
und fügen Sie aus dem Unterverzeichnis "client" die Datei jbossall-client.jar
hinzu.
Klicken Sie dann wieder auf "OK". Diese Datei beinhaltet alle Klassen, die benötigt werden, um auf einen
JBoss-Server zugreifen zu können.
Öffnen Sie die Klasse "PhonebookGUI" und führen Sie diese über "Run" > "Run As" > "Java Application" aus und experimentieren Sie ein wenig mit dem Client.
J2EE Web Services
Das Telefonbuch soll nun auch als Web Service angeboten werden. Die J2EE-Spezifikation sieht hierfür das
Erweitern einer Stateless Session Bean um ein sog. Service Endpoint Interface (SEI) vor, auf das man mit
einem beliebigen Web Service Client zugreifen kann.
Zusätzlich zu dem Service Endpoint Interface werden weitere Deployment Deskriptoren benötigt, die aber
mit Hilfe des Java Web Service Developer Packs und einem Ant-Buildfile generiert
werden können. Bitte stellen Sie sicher, dass Sie das Axis und das JWSDP installiert und, wie oben angegeben, auch die Umgebungsvariablen PATH
und JWSDP_HOME
angepasst bzw. erstellt haben. Fügen Sie weiterhin die folgenden Bibliotheken
zu dem "Build Path" des Projekts hinzu:
- Aus dem Unterverzeichnis
jaxrpc/libIhrer JWSDP-Installation:- jaxrpc-api.jar
- jaxrpc-impl.jar
- jaxrpc-spi.jar
- Aus dem Unterverzeichnis
jwsdp-shared/libvon JWSDP:- activation.jar
Importieren Sie nun das Archiv buildxml.zip
in das Hauptverzeichnis ihres Projekts,
um das Buildfile in ihr Projekt aufzunehmen.
Nun muss zuerst die PhonebookBean angepasst werden. Dies geschieht, indem Sie das Attribut view-type
der Tags @ejb.bean
(über der Klassendefinition) und der @interface-method
(über den veröffentlichten Methoden) von "remote" auf
"remote-service-endpoint" ändern (damit geben Sie an, dass Sie die Dienste der Bean auch
als Web Service anbieten möchten). Erzeugen Sie weiterhin ein Interface im Verzeichnis "src" mit dem Namen
"PhonebookSEI" und dem gleichen Package wie die Bean (z. B. lspi.eai.phonebook
) und lassen
Sie das Interface das Interface "java.rmi.Remote" erweitern. Übernehmen Sie nun die Signaturen der
drei Geschäftsmethoden der PhonebookBean in das Interface, so dass dieses wie folgt aussieht:
packagelspi.eai.phonebook;
importjava.rmi.Remote;
importjava.rmi.RemoteException;
publicinterfacePhonebookSEIpan class="java1-reservedword">extendsRemote{
publicEntryDTOfindEntry(java.lang.String lastname,
java.lang.Stringfirstname)throwsRemoteEcption;
publicEntryDTO[]findEntries()throwsRemoteException;
publicvoidaddEntry(EntryDTOentry)thros/span>RemoteException;
}
Dieses Interface definiert die Methoden, die über den Web Service zur Verfügung gestellt werden. Dadurch wird
es ermöglicht, dass Sie nur ausgewählte Methoden einer Stateless Session Bean als Web Service anbieten.
Öffnen Sie nun die Datei build.xml
. Dort finden Sie eine Liste von Properties, die Sie an ihr
Projekt anpassen müssen. Ändern Sie zuerst die Property "dir.jwsdp" auf das Verzeichnis, in das Sie das
JWSDP installiert haben. Passen Sie dann die "webservice."-Properties an (eine kurz Erklärung der einzelnen
Properties steht bei jeder Property). Diese sollten in etwa wie folgt aussehen:
<!-- Verzeichnis, in das das Java Web Service Developer Pack installiert wurde -->
<propertyname="dir.jwsdp "value="C:/Sun/jwsdp-."/>
<!-- Verzeichnis, in das Axis 1.3 installiert wurde -->
<propertyname="dir.axis"value="C:/Axis-1_3 "/>
<!-- Name des Web Service -->
<propertyname="webservice.name"value="Phonebokervice"/>
<!-- Das Package, in dem sich die Klassen für die Web Services befinden -->
<propertyname="webservice.package"value="lspiei.phonebook"/>
<!-- Der Zielnamensraum ihres Web Services. Es empfiehlt sich, den Packagenamen
zu verwenden und die "." dabei durch "/" zu ersetzen -->
<propertyname="webservice.targetnamespace"value=""/>
<!-- Der Name des Web Service Endpunkt Interfaces (ohne Package) -->
<propertyname="webservice.serviceendpoint"value=""/>
<!-- Der JNDI-Name der Session Bean, die als WS Endpunkt angeboten wird ("ohne ejb/")-->
<propertyname="webservice.beanname"value="Phoeook"/>
<!-- Der Klassenname des Remote-Interfaces der Bean, die als WS Endpunkt angeboten wird -->
<propertyname="webservice.remote"value="Phoneok"/>
Speichern Sie die Datei und führen Sie darauf einen XDoclet-Lauf durch. Rechtsklicken Sie dann auf die build.xml
und wählen Sie "Run As" > "Ant Build". Rechtsklicken Sie nach dem Durchlauf auf den
Projektnamen und wählen Sie "Refresh" aus. Es sollten nun einige zusätzliche Dateien in den Verzeichnissen
"src" und "xml" vorhanden sein. Weiterhin behebt die Ant-Datei einige Fehler in den Deployment Descriptoren,
die aufgrund von Bugs in XDoclet und wscompile auftreten können.
Fügen Sie der Packaging Konfiguration nun die Datei "xml/wsdl/PhonebookService.wsdl" hinzu und geben Sie als
Präfix META-INF/wsdl
an. Starten Sie nun einen weiteren Packaging-Vorgang und deployen Sie die phonebook.jar
auf dem Server.
Öffnen Sie nun ein Browserfenster und gehen Sie auf die Adresse http://localhost:8080/ws4ee. Wählen Sie dort den Punkt "View the list of deployed Web Services" aus. Es sollte die folgende Seite erscheinen:

Damit haben Sie ihr Telefonbuch auch als Web Service deployed.
Web Service Client erzeugen
Als letztes muss der Client nun noch auf die Nutzung von Web Services umgestellt werden. Die dazu benötigten Dateien werden aus der WSDL-Datei, die beim Deployment für die Session Bean generiert wurde, mithilfe von Apache Axis erzeugt werden.
Legen Sie hierzu eine neues Projekt - diesmal ein "Java Project" - an und importieren Sie das Archiv clientbuildxml.zip in das Hauptverzeichnis dieses Projekts sowie das
Archiv PhonebookWSClient.zip in das src
-Verzeichnis.
Fügen Sie dem "Build Path" des Projektes die JAR-Dateien im Axis-Unterverzeichnis lib
hinzu.
Öffnen Sie nun die build.xml und passen Sie die Properties am Anfang der Datei an. Kopieren Sie insbesondere
die Link-Adresse für die WSDL-Datei, die Sie unter der Liste der deployten Web Services finden, und
geben Sie diese als Wert für die Property "webservice.wsdl.url" an.
Führen Sie nun die build.xml
aus und aktualisieren Sie die Ansicht des Client-Projektes. Es sollten
nun einige weitere Klassen in dem src
-Verzeichnis befinden.
Axis' WSDL2Java besitzt ein Feature/einen Bug, den man beachten sollte: Die Parameter der Klassen, die aus
der WSDL-Datei generiert wurden, sind jetzt nicht in der ursprünglichen Reihenfolge, sondern alphabetisch
angeordnet. Das bedeutet z. B. für die Klasse EntryDTO
, dass die Parameter des Konstruktors nicht
als "dateOfBirth, lastname, firstname, telephoneNumber", sondern als "dateOfBirth, firstname, lastname,
telephoneNumber" übergeben werden müssen.
Führen und probieren Sie nun die Klasse PhonebookGUI des Client-Projektes aus. Weitere Informationen zum Thema Axis finden Sie in den zugehörigen Tutorials.
Tipps zu J2EE und Web Services mit J2EE
Hier finden Sie noch einige Hinweise, die Ihnen die Entwicklung eigener Web Services mit Java, JBoss-IDE und XDoclet erleichtern sollen.
Relationen mit XDoclet angeben
Für Beziehungen definieren Sie in der Outline-Ansicht über "J2EE" > "Add CMR Relationship". Wählen Sie
die Kardinalitäten und die Richtung der Beziehungen aus, geben Sie einen Bezeichner für das Attribut an,
über das die Beziehung realisiert wird und wählen Sie in Abhängigkeit von der Relation entweder den Typen
der Relation aus (bei 1:X-Relationen) oder eine Klasse des Collection-Frameworks (bei n:X-Relationen). Geben
Sie dann einen Namen für die Relation an (z. B. "Eintrag-hat-Telefonnummer"; dieser muss bei bidirektionalen
Relationen auf beiden Seiten identisch sein) und dann die den Namen für die Rolle der Relationen (z. B.
"Telefonnummer-eines-Eintrags").
Bei unidirektionalen Beziehungen müssen Sie den Namen der Bean angeben, auf die sie sich beziehen (dies ist
die Target-EJB) und eine Rolle, die dieser Gegenseite der Relation zukommt. Klicken Sie dann auf
"Finish".
Erweitern Sie die angelegte Relation um das XDoclet-Tag @jboss.relation
und geben Sie dort
den Namen des Schlüsselfeldes der Klassen an, auf die sich bezogen wird (Attribut "related-pk-field"), den
Namen, unter dem dieser Schlüssel als Fremdschlüssel in der Tabelle für die aktuellen Objekte abgelegt wird
(Attribut "fk-column") und ob dieser Schlüssel ein Constraint sein soll (Attribut "fk-constraint"; am besten
auf "true" setzen).
Wenn die Gegenseite einer unidirektionalen Relation Bestandteil einer X:n-Beziehung ist, fügen Sie noch
das Attribut "target-multiple" mit dem Wert "yes" zu dem XDoclet-Tag @ejb.relation
hinzu.
Den Rest der Arbeit übernimmt XDoclet dann.




