wissen.leben | WWU Münster 


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 das table-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 Umgebungsvariable JAVA_HOME sollte 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 Unterverzeichnis jaxrpc\bin dem Suchpfad (PATH ) hinzu und setzen Sie eine neue Umgebungsvariable JWSDP_HOME auf 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.

Auswählen eines Arbeitsbereichs

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".

Quell- und Ausgabeordner auswählen

XDoclet-Module aktualisieren

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".

Erzeugen einer neuen EntityBean

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" .

Anlegen eines neuen persistenten Feldes

Anlegen eines neuen persistenten Feldes

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;
}

Anlegen einer Create-Methode

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.

Erzeugen eines Data Transfer Objects

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".

Erzeugen einer Session Bean

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".

Erzeugen einer Geschäftsmethode

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.

Anlegen eines JAR-Archivs

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/lib Ihrer JWSDP-Installation:
    • jaxrpc-api.jar
    • jaxrpc-impl.jar
    • jaxrpc-spi.jar
  • Aus dem Unterverzeichnis jwsdp-shared/lib von 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:

Web Service Auflistung im JBoss

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.

Beziehung definieren



Impressum | © Praktische Informatik