Portierung ausgewählter Dienste einer proprietären Serveranwendung auf einen J2EE-Applikationsserver Studienarbeit bei der SAP AG, Sankt-Leo Rot/Baden Von cand. Inform. Lan Liu Betreuer: Prof. Dr. J. Calmet Dipl.-Inform. Regine Endsuleit Tag der Anmeldung: 01. October 2003 Tag der Abgabe: 31.Dezember 2003 0 Ich erkläre hiermit, dass ich die vorliegende Arbeit selbständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel verwendet habe. Karlsruhe den 31. Dezember 2003 1 Inhaltsverzeichnis 1 2 Einleitung ......................................................................................... 3 1.1 Problemstellung ......................................................................................... 3 1.2 Zielsetzung ................................................................................................ 3 1.3 Gliederung der Arbeit ................................................................................ 4 Grundlagen ....................................................................................... 5 2.1 Remote Method Invocation (RMI) ............................................................. 5 2.2 Java Naming and Directory Interface (JNDI) ............................................. 6 2.3 JDBC......................................................................................................... 7 2.4 Die Datenbank MaxDB.............................................................................. 7 2.5 Enterprise JavaBeans [4] [8] ...................................................................... 8 2.5.1 Einführung [3]........................................................................................................... 8 2.5.1.1 EJB Container....................................................................................................... 9 2.5.2 Session Beans.......................................................................................................... 12 2.5.3 Entity Beans ............................................................................................................ 13 2.6 2.6.1 2.6.2 2.6.3 2.6.4 2.7 3 JBoss IDE mit Eclipse [5] [6]....................................................................15 Installation von JBoss IDE ..................................................................................... 16 Konfiguration in JBoss IDE ................................................................................... 16 XDoclet ................................................................................................................... 17 Packaging ................................................................................................................ 17 HotSpot VM .............................................................................................18 Implementierung ............................................................................ 20 3.1 Struktur der Implementierung ...................................................................20 3.2 RMI ..........................................................................................................20 3.3 3.4 3.5 3.5.1 3.5.2 3.6 3.7 JNDI ........................................................................................................................ 21 Datenbank................................................................................................................ 22 EJB .......................................................................................................................... 23 Session Beans.......................................................................................................... 24 Entity Beans ............................................................................................................ 25 Anwendung der Entwurfsmuster............................................................................ 27 Effektives Java..........................................................................................27 4 Evaluation und Dokumentation der Ergebnisse ............................... 28 5 Zusammenfassung und Ausblick..................................................... 38 6 Appendix ........................................................................................ 40 7 Literatur .......................................................................................... 49 2 1 Einleitung 1.1 Problemstellung Innerhalb der SAP existiert eine Vielzahl von Entwicklungsabteilungen, die hochkomplexe und oft vielgestaltige Softwarekomponenten produzieren. Dabei kommen unterschiedliche Programmiersprachen und Technologien (hauptsächlich ABAP, C, C++ sowie Java) zum Einsatz. Mit Softwarelogistik wird die Disziplin bezeichnet, die von der Entwicklung angelieferten Softwarekomponenten in lauffähige Systeme auf Kundenseite zu überführen. Dies erfordert eine Vielzahl eng miteinander verzahnter und grösstenteils zeitkritischer Prozesse. Zu diesen Prozessen gehören: • Die Übernahme der Komponenten (in Form von Datenbankexporten, Quell- oder Binärdateien) von den Entwicklungsabteilungen • Das Zusammenstellen dieser Komponenten zu Produkten und Lösungen • Die Validierung der Komponenten, Produkte und Lösungen • Die Bereitstellung zur Auslieferung auf Datenträgern (CD, DVD) oder zum Download • Die Entwicklung von Werkzeugen, mit denen Installation und Wartung komplexer, heterogener Systemlandschaften auf Kundenseite erst möglich werden Das sogenannte Kit-Konzept sorgt durch die Einführung sogenannter Kits und Data Units für eine neue vereinheitlichte Sichtweise auf diese Komponenten. Insbesondere wird die bisherige Bindung von Komponenten an konkrete physikalische Medien aufgebrochen und die Auslieferung redundanter Komponenten drastisch reduziert. Das MID-Projekt zielt darauf ab, durch flächendeckende Einführung des Kit-Konzepts sowie eine Redefinition oben genannter Prozesse die Softwarelogistik flexibler, kostengünstiger und kundenfreundlicher zu gestalten. Die vorliegende Studienarbeit soll nun bei der Klärung der Frage mithelfen, ob für die Produktversion des MID Servers ein standardkonformer J2EE-Server oder ein entsprechend modifizierter J2EE-Server zum Einsatz kommen soll. 1.2 Zielsetzung Dazu sollen folgende Aufgaben durchgeführt werden: 1. Einarbeitung in das Kit-Konzept und die verfügbaren Entwicklungswerkzeuge. 2. Erstellung eines Frameworks, mit dem ausgewählte Dienste des MID Servers auf funktionelle Korrektheit und Performance geprüft werden können. 3. Ein quantitativer Vergleich alternativer Implementierungen in Bezug auf deren Datendurchsätze und Antwortzeiten unter mehreren Laststufen. 4. Daneben soll auch beleuchtet werden, inwiefern sich ein Paradigmawechsel hin zu J2EE auf die Produktivität und Codequalität bei der Entwicklung auswirkt. 3 1.3 Gliederung der Arbeit Zuerst werden im Kapitel 2 die Grundlagen für diese Studienarbeit erläutert. Dabei werden auf die Grundprinzipien von Remote Method Invocation (RMI), Java Naming and Directory Interface (JNDI), JDBC und Enterprise Beans eingegangen und die Datenbank, MaxDB, der J2EE Server JBoss und die Entwicklungsumgebung (IDE) für EJBs, Eclipse, beschrieben. Das Kapitel 3 beinhaltet die Implementierungsbeschreibung eines Frameworks, mit dem ausgewählte Dienste des MID Servers auf die funktionelle Korrektheit und Performance geprüft werden können. Weiter werden die benötigten Entwurfsmuster und effektive Java Programmierungstechniken kurz erwähnt, die auch für die Performance eine Rolle spielen. Die Auswertung und die Dokumentation der Ergebnisse werden anschließend im Kapitel 4 erklärt. Dort werden die Ergebnisse bei verschiedenen Profilen präsentiert und ausgewertet. Im Kapitel 5 wird schließlich diese Studienarbeit kurz zusammengefasst und ein Ausblick auf zukünftige Arbeit gegeben. 4 2 Grundlagen 2.1 Remote Method Invocation (RMI) Auf Daten wird nicht nur lokal sondern auch von entfernten Rechnern zugegriffen. Entfernte Datenaufrufe sehen typischerweise bei Client/Server Architekturen. Die Client/Server Architektur wurde entwickelt, um Daten von mehreren Computer gemeinsam zu benutzen. Entfernte Prozedur Aufrufen (RPCs, Remote Procedure Calls) oder eine normale Abfrage Sprache, SQL, werden zwischen Client und Server angewendet. [3] Ein Remote-Methodenaufruf (Remote Method Invocation, RMI) ist einer der Eckpfeiler der Enterprise JavaBeans und eine ausgesprochen praktische Möglichkeit, verteilte Java Anwendungen zu erstellen. Durch RMI wird nicht eine Methode auf einem anderen Java-Objekt aufgerufen, das in derselben Java Virtual Machine (JVM, d.h. in demselben ausführenden Programm) läuft, sondern wird eine Methode auf einem Java-Objekt aufgerufen, das in einer anderen JVM auf demselben oder einen anderen Rechner existiert. Man braucht nicht viel zu machen, um eine Klasse RMI-fähig azu machen, und auch das Aufrufen von Methoden ist einfach. Der Angelpunkt von RMI sind die RemoteInterfaces, also, Interfaces, die alle Remote-Methoden für ein Objekt defineiern. Wenn man mit einem Remote-Interface eine Remote-Methode aufrufen, verbirgt RMI mit Hilfe des Interfaces die Tatsche, dass man eine Methode remote, also aus der Ferne, aufrufen. Dahinter steht der Gedanke, dass RMI ein Stub Objekt erzeugt, das das Remote-Interface implementiert und in der JVM des Clients ausführt. Wenn der Client eine Remote-Methode aufruft, übermittelt die Methodenimplementierung des Stubs den Methodenaufruf über die JVM des Servers, wo ein weiteres besonderes Objekt namens Skeleton die Anfrage interpretiert und die richtige Methode aufruft. Wenn die Methode des Servers einen Wert zurückgibt oder eine Exception auslöst, verpackt der Skeleton die Ergebnisinformationen und sendet sie an den Stub zurück. Der Stub gibt die Informationen dann an den Client zurück. Der Stub und das Skeleton kommunizieren über ein Protokoll namens Java Remote Method Protocol (JRMP). Wir schauen noch mal die ganze Architektur von RMI. RMI besteht aus drei Schichten: Stub-/Skeletonschicht, entfernte Referenzschicht und Transportschicht. Jede Schicht ist unabhängig von den anderen Schichten und beeinflusst die anderen Schichten nicht. (Abbildung 2.1) 5 Abbildung 2.1: RMI-Architektur Die entfernte Referenzschicht verwaltet alle Operationen der entfernten Objekte, beispielsweise ist das Kopieren der Objekte. Die Transportschicht hört die Anfrage des Methodeaufrufs ab und baut die Verbindung für diesen Aufruf auf. Außerdem verwaltet und kontrolliert sie diese Verbindungen. Um den entfernten Aufruf zu lokalisieren verwaltet Transportschicht auch eine Datentabelle entfernter Objekte, die in diesem Adressraum existieren. Bei den Objekttypen, die man in Remote-Methodenaufrufen (als Paratmeter oder Ergebniswerte) verwenden können, erzwingt RMI eine bedeutende Restriktion, dass alle Objekte, die man übergibt, java.io.Serializable oder java.rmi.Remote implementieren. Objekt-Serialisierung ist ein Prozess, mit dem der Zustand eines Objekts in einer Abfolge von Bytes gespeichert wird und diese Bytes in der Zukunft wieder in ein Objekt zusammengefasst werden kann. Wenn man ein serialisierbares (lokales) Objekt als RMI-Parameter übergibt, wird dieses Objekt als Nicht-Remote-Objekt bezeichnet. Dies bedeutet, dass das ganze Objekt vom Client zum Server oder vom Server zum Client geht. Wenn man hingegen ein Remote-Objekt als RMI–Parameter übergibt, dann übergibt RMI nur eine Remote-Referenz. Dies bedeutet, dass RMI nicht das ganze Objekt übergibt, sondern nur etwas, was letztlich zu einem Stub wird. Das Remote-Objekt selbst bewegt sich nicht von der Stelle. 2.2 Java Naming and Directory Interface (JNDI) Ein Namensdienst verbindet einen einzelnen Namen mit einer bestimmten Ressource. Eine RMI-Registrierungsdatenbank ist beispielsweise ein Namensdienst, der nur RMI-Objekte speichert. Ein Verzeichnisdienst verbindet einen Namen mit einer Gruppe von Attributen und Ressourcen. Wenn man einen Namensdienst durchsucht, kann man nur nach einem speziellen Namen suchen. Wenn man ein Verzeichnis durchsucht, kann man nach Elementen suchen, die einer bestimmten Gruppe von Attributen entsprechen. Das Java Naming and Directory Interface (JNDI) unterscheidet sich zwischen Namens- und Verzeichnisdiensten. JNDI ist eine Menge von Schnittstellen, mit denen man auf Namensdienst und Verzeichnisdienst innerhalb von Java Applikationen zugreifen kann. Die Klasse Context ist der Kern des JNDI-APIs. Man benutzt Context, wenn man etwas nachschlagen oder neue Name-/Wert-Verbindungen hinzufügen möchte. [4][8] 6 2.3 JDBC Auf die Datenbank wird mittels des API Java Database Connectivity (JDBC) zugegriffen. Ein JDBC-System besteht aus vier Teilen: Applikationsprogramm, Treiberprogrammverwaltung, Treiberprogramm und Datenquelle. Mit JDBC hat man ein Standard-API, das zum Großteil Datenbank unabhängig ist. Das heißt, mit JDBC kann man auf die Datenbank wie zum Beispiel Oracle, Sybase, oder Informix zugreifen. Es braucht nicht für jede Datenbank eine eigene Schnittstelle. Eine Datenbank erlaubt normalerweise eine bestimmte Anzahl geöffneter Anweisungen. Wenn der Speicherbereiniger die alten Verbindungen nicht löscht, welche bereits abgeschlossen sind, kann es geschehen, dass die Anzahl der maximal zulässigen Anweisungen überschritten wird. Dies führt zur eine Kollision bei dem Zugreifen der Datenbankressourcen. Hierdurch kommt es zu einer Leistungssinken der Anwendung. Um eine Anweisung zu schließen, ruft man die Methode close() dieser Anweisung auf. 2.4 Die Datenbank MaxDB Eine Datenbank ist in einem konsistenten Zustand, wenn die Daten korrekt gespeichert sind und alle Bedingungen für die Daten und zwischen den korrelierten Daten erfüllt sind. Hierzu zählen z.B. korrekte Zugriffspfade, Wertebereiche für Attribute, Übereinstimmung von Fremdschlüsseln in einer anderen Relation, Eindeutigkeit des Primärschlüssels. Die Konsistenzprüfung wird zum Teil von der Datenbank selbstständig vorgenommen, teils muss dies z. B. durch eine CheckKlausel vom Programmierer explizit angestoßen werden. Die hier benutzte Datenbank ist die SAP Datenbank Max DB. Die MaxDB ist eine erweiterte Version von SAP DB, eine open Source Datenbank. Diese Datenbank ist für große mySAP ERP (Enterprise Resource Planning ) Umgebungen geeignet. Die MaxDB bietet schnelle Erreichbarkeit des Zugriffs auf die Datenbank und hohe Skalierbarkeit, was für diese Studienarbeit geeignet ist. Die MaxDB-Administration bietet unterschiedliche Programmierungsschnittstellen und passende Terms für Java, Perl, PHP, Python usw. Datenbank Manager ist ein Tool für Verwaltung der MaxDB Datenbank. Es wird benutzt, um die Datenbank Instanz auf lokalen und entfernten Rechnern zu erstellen, sie zu kontrollieren, zu überwachen, im Bedarfsfall zurückzusetzen und wiederherzustellen. Für jede Datenbank erstellt die MaxDB eine LogDatei und eine Konfigurationsdatei. Das Datenbanksystem unterstützt verschiedene Applikationsbereiche. Die Instanz jedes Bereiches hat einen eigenen Typ. Hier wird OLTP (online transaction Processing) benutzt. OLTP wird optimiert, um individuelle Transaktionen in einer Datenbank mit hoher Anzahl an Benutzern schneller verarbeiten zu können. Es gibt außerdem OLTP, den live Cache Typ, der nur für SAP-Applikationen zur Verfügung steht, sowie den Archive Typ. Die Auswahl des geeigneten Typs erhöht die Performance. Die MaxDB hat viele Datenbankparameter, die man vor dem Zugreifen auf eine Datenbank eingegeben werden können. Diese Parameter können zum Beispiel die Cache-Größe, der Instanztyp, die Log-Segmentgröße, die MaxCPU usw. sein. 7 Um mit einer Datenbankinstanz zu arbeiten, eine Datenbankanfrage zu schicken oder eine Datenbankinstanz zu verwalten, muss der Benutzter anhand eines Benutzernamens und eines Passworts mit einer Datenbankinstanz kommunizieren. Solange noch Transaktionen offen sind, zeigt die Verwaltung der Datenbank, dass es noch Sessions gibt, die noch nicht abgeschlossen sind. Ein wichtiger Punkt in einer Client-Server-Programmierung ist die Skalierbarkeit. Um die Skalierbarkeit zu erreichen, sollen folgende Hinweise beachtet werden: • Angemessenen Index für die Daten erstellen, um die Anzahl der gesuchten Datensätze zu reduzieren. • Kein gleiches Selektieren, um mehrere Zugriffe derselben Daten innerhalb einer Transaktion zu vermeiden. • Vollständige WHERE Klauseln, um die Anzahl der transferierten Datensätze zu reduzieren. • Parallele Abläufe einhalten • Lineare Abhängigkeit von Datensätzen, um die interne Datentabelle optimal zu benutzen [1] 2.5 2.5.1 Enterprise JavaBeans [4] [8] Einführung [3] Die Grundstruktur einer Drei-Schicht-Datenbankanwendungen besteht aus einer Präsentationsebene, einer Geschäftslogikebene und einer Datenebene. In einer typischen Geschäftsanwendung wird für die Datenbankebene eine Datenbank und für die Präsentationsebene entweder ein GUI-Font-End oder ein Webserver verwendet. Am schwierigsten ist die Geschäftslogikebene zu entwerfen. Hier muss herausgefunden werden, wie die Zusammenarbeit zwischen die Datenbank und die Clients ist, und wie die Clients auf die Remote-Geschäftsobjekte zugreifen. Außerdem muss es sich entschieden werden, ob ein Objekt zwischen Methodenaufrufen die Zustandsdaten speichern soll. Enterprise Beans sind Server Seite Java. Es gibt 2 Arten von Server Seite Java, die sind Folgendes: 1. .Java, die als Server arbeiten sind zum Beispiel EJB Container, Servlet Container, http Server, Applikation Server usw. 2. Java, die als Komponenten in einer Java-Applikation arbeiten. Sie sind zum Beispiel EJB, JavaBeans, JSP, Servlet. Die Nutzung liegt häufig am Aufbau einer Web-Applikation durch die Komponenten. [2] In einer typischen Datenbankanwendung gibt es oft Änderungen an der Datenbank, die ein Teil einer Transaktion darstellen. In der Vergangenheit war es schwierig, Operationen auf verteilten Objekten zu einer einzigen Transaktion zusammenzufassen. Java unterstützt durch die Server-seitige Programmierung und seine hervorragenden Netzwerkfähigkeit einen Mechanismus für die Durchführung verteilter Transaktionen. Die J2EE-Spezifikation bietet für verteilte Objekte, verteilte Transaktionen an. Die Transaktionen sind normalerweise mit Datenbankoperationen verbunden, obwohl das Konzept auch allgemein auf Objekte angewandt werden kann. 8 Um Transaktionen auf Java-Objekte auszudehnen, hat Sun eine Klasse von JavaObjekten, die so genannten Enterprise JavaBeans, definiert. Dabei handelt es sich um Beans, die in einer Datenbank gespeichert sein können, aber nicht müssen, und an Transaktionen teilnehmen können. Enterprise Java Beans können Daten darstellen, die in einer Datenbank gespeichert sind. Sie können sogar mit einem EJB-Server Datenbankdaten automatisch in einer Bean einlesen und Bean-Daten automatisch in die Datenbank zurückschreiben. Bei einer großen Datenbankanwendung kann dies enorm viel Zeit sparen. Eine Enterprise JavaBean ist eine besondere Art von JavaBean, die der Durchführung Server-seitiger Geschäftslogikoperationen dient. Hier werden Session-Bean und Entity Bean angewendet. EJBs sind J2EE Komponente. Sie sind Komponenten für die Erstellung von verteilten Anwendungen. Die mit Enterprise JavaBeans erstellten Anwendungen sind multiuserfähig, skalierbar, verteilbar und plattformunabhängig. Eine Enterprise JavaBean hat mindestens drei obligatorische Java-Klassen: • Das Interface Remote definiert die Methoden, die ein Client-Programm auf der Bean geschäftliche Methode aufrufen kann. • Das Interface Home definiert Methoden, um eine Bean zu erstellen und zu suchen. • Eine Java-Klasse implementiert sowohl das Interface Home als auch das Interface Remote. Diese Klasse ist das, was man normalerweise mit der EJB meint. Ein Client erstellt eine EJB mit dem Interface Home und kommuniziert über das Interface Remote die EJB Klasse. Die Abbildung 2.3 zeigt, wie ein Client mit den Interfaces Home und Remote einer EJB integrieren. erzeuge und finde Interface Home Client BeanMethoden EJBBehälter BeanImplementierung Interface Remote Abbildung 2.3 Enterprise JavaBean 2.5.1.1 EJB Container Enterprise Beans sind Software-Komponenten, die in einem spezial Behälter, Enterprise Container, existieren. In J2EE ist ein Behälter fast so etwas wie ein Mini- 9 Server: Er stellt die Laufzeitunterstützung für die Elemente bereit, die er enthält. Ein EJB-Behälter erhält Enterprise JavaBeans und sorgt für die Bildung eines Verbindungs-Pools wie zum Beispiel entferntes Zugreifen nach einem Bean, die Sicherheit vom Zugreifen, Persistenz der Daten und die Transaktionsverarbeitung. So kann der Bean Entwickler sich nur auf die geschäftliche Logik konzentrieren. Hierdurch ermöglicht der EJB Container den schnellen Aufbau und Einsatz von Enterprise Beans. Enterprise Bean ist von seinem Container abhängig. Durch den Container kann ein Bean eine JDBC-Verbindung bekommen oder mit anderen Beans kommuniziern. Ein Container kann gleichzeitig mehrere Beans verwalten. Container verwaltet die Ressourcen und den Lebenszyklus von allen Beans sehr sorgfältig, um Speicherverbrauch und Verarbeitung zu reduzieren. Zum Beispiel wird der Container ein Bean in ein Pool verschieben, wenn dieses Bean gerade nicht benutzt wird. Beans werden aus dem Speicher geräumt und wieder zurück verlagert, wenn die Beans wieder gebraucht werden. Der Client weiß von diesem Prozess nichts, weil er nicht direkt auf die Beans zugreifen kann. Abbildung 2.4: EJB Container Enterprise Bean kommuniziert mit seinem Container durch drei Mechanismen: Callback Methoden, EJBContext Interface oder JNDI (Java Naming and Directory Interface): • Jede Bean Klasse implementiert die Callback Methoden, die durch das entsprechende Interface definiert werden. Jede Callback Methode alarmiert die Bean bei unterschiedlichem Ereignis in seinem Lebenszyklus. Der Container ruft dabei die Methoden auf um sich bei der Bean zu melden, wenn der Container diese Bean aktiviert, wenn die Datenzustände in der Datenbank gespeichert 10 • • werden sollen, wenn eine Transaktion beendet oder wenn diese Bean aus dem Speicher geräumt werden soll. Jeder Bean erhält ein EJBContext-Objekt, was den Container direkt referenziert. EJBContext Interface bietet Methoden, durch die die Bean mit dem Container kommunizieren kann, damit die Bean Informationen aus seiner Umgebung abfragen kann. JNDI wurde bereits in Kapitel 2.2 erklärt. Jede Bean hat automatisch einen Zugang zu einem spezialen Naming System—ENC (Enviroment Naming Context). Der ENC wird vom Container verwaltet und von Beans durch JNDI zugegriffen. JNDI ENC ermöglicht das Zugreifen der Bean auf die Ressourcen wie zu Beispiel eine JDBC Verbindung und die Kommunikation zwischen mehreren Beans. 2.5.1.2 EJB-Transaktionen Es muss darauf geachtet werden, dass eine Transaktion auf dem Server schnell bearbeitet wird und es zu keinen gegenseitigen Beeinflussungen der einzelnen Transaktionen kommt. Mit einer Transaktion kann man mehrere Operationen zu einer einzigen alle-oder-keine Operation zusammenfassen. Anders ausgedrückt: Entweder verlaufen alle Operationen erfolgreich oder alle scheitern. Transaktionsoperation werden normalerweise danach beurteilt, ob sie den ACID-Test bestehen. Dabei handelt es sich um vier Kriterien, die die gewünschten Eigenschaften einer Transaktion beschreiben: Unteilbarkeit, Konsistenz, Isoliertheit und Dauerhaftigkeit. Im Bereich der EJBs ist eine Transaktion normalerweise nicht mit einer Eintity-, sondern mit einer Session-Bean verbunden. 2.5.1.2 wichitge Verfahren Enterprise JavaBeans verwenden mehrere wichtige Verfahren. Die drei wichtigsten Verfahren bei der EJB-Entwicklung sind RMI, JDBC und JNDI. Jede Remote-Methode muss so deklariert werden, dass sie eine java.rmi.RemoteException auslöst. Remote-Methoden müssen Ergebnistypen und Parameter haben, die mit RMI kompatibel sind. Vor allem müssen die Objekte, die mit RMI hin und her gegeben werden, das Interface java.io.Serializable implementieren. Wenn man BMP verwendet, wird JDBC benötigt. Mit dem Namensdienst JNDI werden Objekte im System gesucht. Die Abbildung 2.5 zeigt, wie sich die verschiedene Kernverfahren in EJB-Anwendung einfügen. 11 finde Dienste JNDI Clients RMI finde Dienste EJBKomponente JDBC Datenquelle Datenbank k Abbidung 2.5 JDBC, RMI und JNDI sind die wichtigsten Verfahren bei der Implementierung und Verwendung von EJBs 2.5.1.3 Die Bean bereitstellen Ein weiterer Aspekt der Enterprise JavaBeans, der etwas gewöhnungsbedürftig sein kann ist, dass EJBs nicht ausgeführt, sondern bereitgestellt werden. Hier wird durch das JBoss anbietende Packwerkzeug eine JAR-Datei angelegt, welche die EJBKlassen, einige XML-Dateien für den Bereitstellungs-Deskriptor und einige Helferklassen enthält, welche nur für den gerade verwendeten Behälter bestimmt sind. Das Bereitstellungswerkzeug das von JBoss mitgeliefert wird, ist einfach anzuwenden. Bereitstellungswerkzeuge sollen garantieren, dass alle Ressourcen die in der Datei des Werkzeugs (Deployment Deskriptor) deklariert werden, benutzbar sind. Das Bereitstellungswerkzeug erstellt einige Klassen und Schnittstellen anhand der Werkzeuge von EJB Container, damit der EJB Container später EJB anhand der Klassen und Schnittstellen in der Ausführungszeit verwalten kann. Der EJB-Server sollte laufen, bevor etwas bereitstellt wird. 2.5.2 Session Beans Session Beans repräsentieren normalerweise Geschäftslogikfunktionen und werden nicht in der Datenbank gespeichert. Eine Session-Bean kann dennoch auf die Datenbankzugreifen. Session-Beans arbeiten bei der Durchführung einer Operation häufig mit mehreren Entity-Beans zusammen, müssen aber nicht unbedingt eine 12 Entity-Bean verwenden. In dieser Arbeit wird zuerst eine zustandslose Session-Bean implementiert, dann arbeitet diese Session-Bean durch die Fassade Methode mit einer Entity-Bean zusammen, was in 2.5.3 genauer erklärt wird. Eine Session-Bean kann zwischen Methodenaufrufen Daten speichern, wenn es nötig ist, muss dies aber nicht machen. Eine Session-Bean wird als zustandshältige? Session-Bean bezeichnet, wenn sie Daten speichert. Wenn sie keine Daten speichert, wird sie als zustandslose Session-Bean bezeichnet. Jeder Client hat seine eigene Session-Bean. Aber wenn die Session-Bean zustandslos sind, kann es geschehen, dass eine Session-Bean für mehrere Clients arbeitet, da sie sich zwischen Methodenaufrufen nichts merkt. Deswegen besitzt auch eine zustandslose Session-Bean wenige Ressourcen vom Server. Die gemeinsame Benutzung von Ressourcen von Stateless Session reduziert die Belastung des Servers und erhöht so die Skalierbarkeit der Anwendung. Eine Datenbank ist eine typische, gemeinsam benutzte Ressource. Um Dienste der Datenbank in Anspruch zu nehmen, braucht man zuerst die Verbindungen mit der Datenbank zu bauen. Diese Verbindungen sind beschränkt. Deswegen ist es wichtig für die Datenbankverwaltung, die Verbindungen zu koordinieren. Es ist wichtig, dass jeder Client schnell die Verbindung freilässt. Die Verbindung wird nur während einer Transaktion benutzt. Um die Verbindungen gut zu nutzen, sollte einerseits die Transaktion so schnell wie möglich bearbeitet werden, andererseits sollten so wenige Verbindungen wie möglich benutzt werden. [1] [4] 2.5.3 Entity Beans Entity-Beans sind Enterprise JavaBeans, die Datenobjekte in ihrer Anwendung darstellen und normalerweise in einer Datenbank gespeichert werden. Ein Client kann Methoden auf einer Entity-Bean ebenso wie auf einer Session-Bean aufrufen. Allerdings können in manchen Anwendungen nur Session-Beans auf Entity-Beans zugreifen. Im Gegensatz zu Session-Beans sind Entity-Beans zustandshaltig. Außerdem unterscheiden sich Entity- von Session-Beans dadurch, dass mehrere Clients gleichzeitig auf dieselbe Entity-Bean zugreifen können. Der EJB-Behälter kümmert sich um alle Transaktionskonflikte, die eventuell aus der gemeinsamen Nutzung der Entity-Beans entsehen. Der EJB-Behälter entscheidet, wann er Entity-Beans lädt und speichert. Je nach dem Persistenzmodell der Bean kann der Behälter sogar die Aufgabe übernehmen, die Bean in die Datenbank zu schreiben. Entity-Beans können das Laden oder Speichern der Daten in die Datenbank entweder selbst verwalten oder sie vom Behälter verwalten lassen. Wenn eine Bean ihre Persistenz selbst verwaltet, verwendet sie die Bean-Verwaltete Persistenz (Bean Managed Persistence, BMP). Wenn der Behälter die Persistenz der Bean verwaltet, verwendet die Bean die Behälter-verwaltete Persistenz (Container Managed Persistence, CMP). 13 Es ist manchmal nicht gut, eine Transaktion nur durch ein Entity Bean auszuführen. Ein Beispiel dafür ist eine Transaktion wie Geldabheben. Bei dieser Transaktion sollte die Eingabe geprüft und Geld dem Kunde gegeben werden. Wenn alle Aktionen nur in einem Entity ausgeführt werden, kann es passieren, dass die Eingabe nicht richtig ist, aber Geld schon dem Kunde gegeben wird. Wenn die Aktionen in mehreren Entity Beans beschrieben werden, dauert die Ausführung der Aktionen sehr lang. Die Nachteile, wenn man nur Entity-Beans verwendet, sind folgende. 1. hohe Kosten des Internets, was der Performance schadet. Es sei denn, dass wir lokale Interface benutzen. 2. schlechtes Parallel laufen. 3. hohe Abhängigkeit zwischen Client und Entity Bean. Falls sich die Anforderungen des Clients ändern, ändern die Funktionen in einer Entity Bean auch sehr stark. 4. schlechte Wiederverwendbarkeit von Entity Bean. 5. schlechte Trennung der Arbeit zwischen Programmier, Entwicklung und Business Logik. [1] [4] 2.5.3.2 Session Fassade Aus der Gründe der Nachteile der nur Entity-Bean benutzde Anwendung wird eine Session Fassade zwischen Client und Entity-Bean gebraucht. Session Fassade dient als Vermittler und Puffer für Entity-Bean. Sie kann als ein Vermittler zwischen Clients und Entity-Beans sein, durch sie Clients die Entity-Beans über Session-Beans zugreifen können. Die Clients können auch nur auf Session-Bean zugreifen. Session Fassade verwendet das Entwurfsmuster der Fassade. Das Entwurfsmuster der Fassade bietet eine einheitliche Schnittstelle zu einer Menge von Schnittstellen eines Subsystems. Die Fassadeklasse definiert eine abstrakte Schnittstelle, welche die Benutzung des Subsystems vereinfacht. Diese einheitliche Schnittstelle ist hier diese Session Fassade, die Entity Beans verbirgt. Die Clients können nur auf die Session Fassade zugreifen, wobei sich die Session Fassade überlegt, auf welche entsprechende Entity Bean zugegriffen werden sollte. Diese Session Fassade sind Stateless Session. Gelegentlich werden eine große Klasse an Session Bean geschrieben, wobei alle Aktivitäten in die Session-Fassade geschrieben werden. Sie ist jedoch nicht effektiv für das Programm. Diese Session Beans sollten in unterschiedlichen Gruppen aufgeteilt werden. Fassade hat mehrere Vorteile: 1. Niedrige Netzverbindungskosten: Der Client benutzt nur ein Mal die Netzverbindung vom Server bei einer Methodeaufruf. Die übliche Aufrufe spielen sich zwischen Session Bean und Entity Bean in einer lokalen Interface ab, was keine Netzverbindungen braucht. Die meisten Server können auch die Kommunikation zwischen Session Bean und Entity Bean optimieren. 2. Klare und saubere Trennung zwischen geschäftlicher Logik und präsentierender Schicht: Alle geschäftliche Logik ist hinter der Fassade verborgen. 3. Zusammenfassung der Aktivitäten in Session Bean: Aktivitäten werden in Session Bean abgegrenzt und durch deployment descriptor (Dislozieren Deskriptor) eingesetzt. 14 4. Lockere Abhängigkeit zwischen Client und Entity Bean: Falls die Entity Bean geändert wird, muss der Client nicht entsprechend geändert werden. 5. Gute Wiederverwendbarkeit: Das in Session Bean zusammengefasste Session Bean kann von jedem Typ Client (JSP, Servlet, Applikation oder Applet) verwendet werden. 6. Die deployment discriptor deklariert die Aktivitäten klar in Session Bean, dadurch hat man eine klare Trennung von Session Bean und Aktivitäten. Dies verringert Fehlern. Die Methoden die mit „find“ anfangen, heißen find-Methoden. Clients sucht die Entitiy-Beans nach Suchkriterien mittels der find-Methoden. Es gibt keine StandardAbfragesprache für die find-Methoden. In CMP werden die find-Methoden nicht in der Bean Klasse implementiert. Container implementieren diese find-Methoden, wenn das Bean eingesetzt wird. Deployer benutzt Anbieter spezifischer Tools, um dem Container mitzuteilen, wie sich eine bestimmte find-Methode verhält. 2.5.3.2 CallBack Methoden Ein Entity Bean verfügt über Callback Methoden, durch die der Container den Ereignisse die Bean benachrichtigt. Die Callback Methoden für Entity Bean sind in der Interface: javax.ejb.EntityBean definiert und in allen Entity Klasse implementiert. Es gibt folgende Definitionen der Callbackmethoden: SetEntityContext(), unsetEntityContext(), ejbLoad(), ejbStore(), ejbActivate(), ejbPassivate(), ejbRemove(). Die setEntityContext() Methode bietet dem Bean eine Interface, durch die das Bean mit dem Container kommuniziert, es ist also ein EntityContext. Die Entitycontext Interface verfügt über eine Methode, durch die Informationen über den Kontext übermittelt werden. Durch die EntityContext Interface kann man die Sicherheitsinformation über die Aufrufer bekommen, den Stand der aktuellen Transaktion überprüfen, eine Transaktion zurücksetzen und eine Referenz von Bean selber bzw. seine Home-Interface oder seinen Hauptschlüssel bekommen. Der EntityContext wird nur ein Mal im Lebenszyklus einer Beaninstanz aufgesetzt. Die unsetEntityContext() Methode wird am Ende eines Lebenszykluses von Bean benutzt, um die Referenz eines EntityContextes zu löschen und aufzuräumen. Die ejbLoad() und ejbStore() Methoden in einem CMP Entity werden aufgerufen, wenn der Entity Bean mit der Datenbank synchronisiert wird. Die ejbLoad() Methode wird aufgerufen, bevor der Container die Daten von der Datenbank holt. Die ejbStore() Methode wird aufgerufen, bevor der Container die von ihm verwalteten Felder in die Datenbank schreibt. Diese Methoden werden benutzt, um zum Beispiel die Daten zu komprimieren, bevor sie in die Datenbank eingelagert werden, und um die Daten zu dekomprimieren, wenn die Daten von der Datenbank abgerufen werden. Die ejbPassivate() und ejbActivate() Methoden werden vom Container aufgerufen, bevor das Bean deaktiviert und nach dem das Bean aktiviert wird. 2.6 JBoss IDE mit Eclipse [5] [6] 15 Die Eclipse Platform ist entwicklet, um IDE (integrated development environment) zu bauen. Mittels des Eclipse IDEs können Applikationen in Java, C++ und Enterprise JavaBeans erstellt werden. Diese Studienarbeit wird in der Programmierumgebung JBoss IDE mit Eclipse entwickelt. JBoss–IDE bietet eine Menge von IDE Klasse für JBoss und basiert auf einer Integration mit Eclipse. JBoss-DIE bietet die folgende Funktionen: • Debugging und Kontrollieren des JBoss Servers und Kontrollieren des Lebenszykluses des Servers • Funktionen von Xdoclet • Eine einfach Methode der Konfiguration des Packetarichivierens JBoss EJB Container ist der Kern von JBoss Server. JBoss EJB-Container erstellt einerseits automatisch Stub und Skeleton Dateien während des Programmablaufs, andererseits unterstützt er die Technik Hot Deplyment. Mittels der Technik Hot Deployment können neue Komponente wie zum Beispiel Enterprise Beans, kleine Service Programme und JSP Dateien während des Server Ablaufprozesses hinzugefügt werden, ohne die Server Applikation zu beenden und neu zu starten. 2.6.1 Installation von JBoss IDE Weitere Software wird benötigt um JBoss zu installieren: • Sun`s SDK (Software Development Kit). SDK ist eine Schnittstelle, mit der eigene Softwaren an bestehende Programmen, Applikationen und Betriebssysteme angehängt werden können. • org.jboss.ide.ecllipse_1.2.1.bin.dist.zip. Diese Datei sollte in Eclipse Hauptverzeichnis verpackt werden. • Ant. Es ist ein Java Build Tool. Ant basiert auf XML und wird benutzt, um Jakartas Tomcat Web Server aufzubauen. 2.6.2 Konfiguration in JBoss IDE Bevor das Projekt entwickelt wird, sollte die folgende JAR Dateien von JBoss Bibliothek ins Projekt importiert werden: • jnp-client.jar, • jbossall-client.jar, • jboss-j2ee.jar, • jboss-transaction-client.jar, • log4j.jar. (Diese Datei findet man unter dem Verzeichnis Client in der JBoss Package) • jboss-j2ee.jar (Diese Datei findet man unter server\default\lib in der JBoss Package) Konfigurieren von Client Um das Projekt in JBoss zu starten, müssen einige Konfigurationsinformation eingetragen werden: -Djava.naming.factory.initial=org.jnp.interfaces.NamingContextFactory (siehe 3.3) -Djava.naming.provider.url=jnp://localhost:1099 -Djava.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces 16 2.6.3 XDoclet Eine wichtige Eigenschaft des JBoss-IDEs ist die Benutzung des Xdoclet Plug-ins. Ein Plug-in ist ein Programm, das sich als Teil eines anderen Programms einfügt, um zusätzliche Funktionen zur Verfügung zu stellen. Diese zusätzliche Funktionen, die Xdoclet anbietet, sind: • Ein Kode Assistent mit Kontext-abhängigen Angeboten • Ermöglichung der vom Benutzter definierten Schablone • Anbieten einer graphischen Oberfläche, um XDoclet Konfigurationen zu machen • Ermöglichung der Aktivieren/Deaktivieren Konfigurationen In der Implementierung schreibt der Entwickler die Informationen über die Bean Klasse, diese Beschreibung nennt man hier tags. Diese tags werden in Xdoclet konfiguriert und dadurch werden die Informationsdateien erstellt. Wenn Xdoclet fehlerfrei ausgeführt wurde, bekommt man folgende Information (Beispiel für die Session Bean). Buildfile: C:\lan\eclipse-SDK-2.1win32\eclipse\workspace\mid\xdoclet-build.xml N400004: [ejbdoclet] Generating EJB deployment descriptor (ejb-jar.xml). [ejbdoclet] Generating jboss.xml. [ejbdoclet] Generating Remote interface for 'mid.ejb.MIDSampleServicesSessionBean'. [ejbdoclet] Generating Home interface for 'mid.ejb.MIDSampleServicesSessionBean'. _generation_: BUILD SUCCESSFUL Total time: 14 seconds Durch die Information sieht man, dass Xdoclet automatisch für das Projekt die Information von deployment descriptor ejb-jar.xml, die jboss.xml Datei und die Interfaces für die Session Bean Klasse erzeugt. 2.6.4 Packaging In JBoss-IDE lässt sich einfach und schnell das Archiv von Package erstellen. In dieser Arbeit gibt es drei Packages: • Eine für EJB.Jar. Sie beinhaltet EJB Klassen, Interface und Deployment Deskriptor. • Eine für EJB Client Jar. Sie hat EJB Schnittstellen. • Eine für die J2EE Applikation Ear. Sie besteht aus EJB Jar und die Deployment Deskriptor. Klick “Ok” in der Konfigurationsoberfläche, um die Konfiguration von Verpackungen zu speichern. Diese erzeugt das Ant Build File „packaging-build.xml” für dieses Projekt. Somit hat die ganze J2EE Applikation eine Verpackungkonfiguration. 17 Wenn eine Packaging erfolgreich in JBoss ausgeführt ist, wird die folgende Information geliefert: Buildfile: C:\lan\eclipse-SDK-2.1win32\eclipse\workspace\mid\packaging-build.xml N400004: [jar] Building jar: C:\lan\eclipse-SDK-2.1win32\eclipse\workspace\mid\MIDSampleServicesEJB.jar N400024: [jar] Building jar: C:\lan\eclipse-SDK-2.1win32\eclipse\workspace\mid\MIDSampleServicesEJB-client.jar N400034: [jar] Building jar: C:\lan\eclipse-SDK-2.1win32\eclipse\workspace\mid\MIDSampleServicesApp.ear _generation_: BUILD SUCCESSFUL Total time: 1 second Aus der Information sieht man, dass die packaging-build.xml Datei automatisch erstellt wird. Außerdem sind die JAR Package, Client Package und EAR Package, die für dieses Projekt erforderlich sind, automatisch erzeugt wurden. Die application.xml Datei erzeugt man manuell (Diese Datei siehe bitte in Appendix B). 2.7 HotSpot VM HotSpot VM ist eine Java virtuelle Maschine, in dem der Java Code dynamisch kompiliert wird. Im Gegensatz zu der klassischen JVM (Java virtuelle Maschine) werden die Programme effektiver in HotSpot VM ausgeführt, indem die Müllsammlung und Thread Bearbeitung in HotSpot VM erweitert werden. Außerdem hat HotSpot einen Profil Monitor, der während des Ausführens eines Programms erkennt, welche Teile öfter benutzt werden, die dann als hot spot bezeichnet werden. Darüber hinaus wird erkannt, wie die Performanz beeinflusst wird. Es wird die entsprechende Optimierungstechnologie angewendet, um die Programme schneller arbeiten zu lassen. Der klassische JVM fällt es schwer zu erkennen, welche Teile am meisten optimiert werden sollen. HotSpot VM kann durch die Erkennung von hot spot das Programm besser optimieren. Die unnötigen Kode, die für das Ausführen von Daten nicht benötigt werden, werden nicht kompiliert. Es gibt zwei HotSpot VM: Java HotSpot Client VM und Java HotSpot Server VM. Die Beide sind zueinander kompatibel. Die Gründe warum es zwei HotSpot VM gibt sind, dass die auf der Client Seite und auf der Server Seite laufende Applikationen unterschiedlich sind. Auf der Client Seite konzentrieren sich die Benutzter auf die Oberfläche, ob sie bedienerfreundlich und übersichtlich ist. Auf der Server Seite sind die Effektivität und die Ressourcen Benutzbarkeit wichtiger als die Oberflächen. Deswegen sind die Optimierungsstrategien auf der Client Seite und auf der Server Seite unterschiedlich. 18 19 3 Implementierung 3.1 Struktur der Implementierung Es gibt drei Basis Klassen in der Implementierung: mid.ejb.MIDSampleKey, mid.ejb.MIDSampleObject und mid.ejb.MIDSampleServeices. In der Klasse MIDSampleServices werden die fünf Methoden: add(), get(), update(), delete() und find(), definiert, die den Datentyp MIDSampleObject haben. In dieser Arbeit werden anhand der genannten Methoden die Aufrufantwortzeit gemessen. Ablauf des Projekts Zuerst wird in dieser Arbeit getestet, ob alle diese Methode richtig funktionieren. Dann werden die Antwortzeit für lokale und entfernte Methodenaufrufe ohne persistente Daten, die in J2SE implementiert sind, gestestet. Danach werden die lokale und entfernte Methodeaufrufe, die die persistente Daten bearbeiten, getestet. Am Ende sollte die Antwortzeit für lokale und entfernte Methodeaufrufe, die in J2EE implementiert sind, getestet werden. 3.2 RMI Um die Antwortzeit von Remote-Methodenaufrufe zu testen, müssen wir RMI realisieren. Die wichtigsten Sachen in RMI sind Stub und Skeleton. Diese braucht man in RMI nicht zu schreiben. Stub und Skeleton werden in dem Remote-Interface mit einem Programm namens rmic (dem RMI-Kompilierer) erzeugt. Bevor ein Stub und ein Skeleton generieren werden kann, muss man das Interface Remote zuerst definieren. Ein Remote-Interface erweitert einerseits java.rmi.Remote und enthält andererseits einige Methoden, die eine java.rmi.RemoteException auslösen können. Diese Eigenschaften werden in der Klasse mid.ejb.MIDSampleServices.java realisiert. Das Interface Remote kennzeichnet ein Objekt als Remote-Objekt, d.h. wenn ein Objekt das Interface mid.ejb.MIDSampleServices.java implementiert, weiss RMI dass das Objekt ein remote Objekt ist. Diese Implementierung ist in der Klasse mid.impl.basic.MIDSampleServicesImpl.java geschrieben. In Remote Interface kann man auch die Methode definieren, die nicht Remote-Methoden sind. Diese Methoden werden aber nicht remote sein, auch wenn das Interface remote ist. Deswegen kann das Interface mid.ejb.MIDSampleServices.java für lokale Methodenaufrufe benutzt werden. Wenn man eine Methode remote aufrufen möchten, dann muss die Methode in der Lage sein, eine RemoteException auszulösen. Stub und Skeleton können jetzt noch nicht generiert werden. Das Programm rmic generiert Stub und Skeleton mit der Implementierung des Remote-Objekts, nicht mit dem Interface Remote. 20 Um ein Remote-Objekt zu implementieren, muss die Klasse mid.impl.basic.MIDSampleServicesImpl.java erstellt werden, die eine Unterklasse von java.rmi.server.UnicastRemoteObjekt ist und die Remote-Methoden add(), get() usw. implementiert. Weil RMI Klassen dynamisch geladen werden, benötigt man hier einen besonderen Sicherheitsmanager, um zu gewährleisten, dass keine falsche Klassendatei geschickt wird. Dieser Sicherheitsmanager ist in der main-Methode eingerichtet. if (System.getSecurityManager() == null){ System.setSecurityManager(new RMISecurityManager()); } Um Remote-Objekte zu aktivieren (d.h. zur Verwendung bereitzustellen), sollten die Remote-Objekte durch die Klasse java.rmi.Naming in der RMIRegistrierungsdatenbank registriert werden. Naming.rebind("rmi://localhost:1099/MIDSampleServices", m); Nun kann man mit dem rmic Programm den Stub und das Skeleton generieren. rmic mid.ejb. MIDSampleServicesImpl Bevor man mid.impl.basic.MIDSampleServicesImpl.java ausführt, muss man die RAI-Registrierungsdatenbank mit dem Befehl rmiregistry starten. Die RMI-Registrierungsdatenbank ist ein Verzeichnisdienst, mit dem Clients RemoteObjekte durch ihre Namen finden können. Diese wird in der Klasse test.MIDSampleClient.java implementiert. System.out.println("basic remote:"); multi_clientTest((MIDSampleServices)Naming.lookup("rmi://localh ost/MIDSampleServices"), "remote client", clientCount, readCount, writeCount, addTimeSet, getTimeSet, updateTimeSet, deleteTimeSet, findTimeSet); Die Objekte, die übergegeben werden, sind Objekte von der Klasse MIDSampleKey.java und MIDSampleObjekt.java. Die beiden Klassen implementieren die Klasse java.io.Serializable. Dann werden die Objekte, die bei den Remtoe-Methodenaufrufen übergegeben werden, als Nicht-Remote-Objekt bezeichnet. 3.3 JNDI Bei der Anwendung von JNDI sollte zuerst ein Initial Context-Objekt erzeugt werden. Hier wird das InitialContext–Objekt als Eingangspunkt für JNDI verwendet. Dies wird in der folgenden Implementierung des Clients einer Session-Bean gezeigt: Properties props = System.getProperties(); Context context = new InitialContext(props); 21 Der Konstruktor InitialContext sucht nach der Systemeigenschaft java.naming.factory.initial, die den Namen der Klasse enthält, die InititalContext erzeugt. Manchmal muss man diesen Wert selbst bereitstellen. Bei einigen EJBBehältern wie z.B. den von Suns J2EE SDK ist diese Eigenschaft bereits gesetzt. Wenn das Programm ausgeführt werden soll, wird mit der Option –D die Factory für den Anfangskontext angegeben. Diese macht man in der JBoss Konfiguration folgendermaßen: -Djava.naming.factory.initial=org.jnp.interfaces.NamingContextFactory Nachdem nun ein Context-Objekt erzeugt ist, kann man mit der Methode lookup das Objekt, MIDSampleServices suchen. Mit der folgenden Aufruf wird die EJB gesucht: Object objref = context.lookup("ejb/mid/MIDSampleServices"); Es gibt mehrere Namensdienste von JNDI. Durch den Namen rmi in der Bean Implementierungsklasse weiß man, dass der hier benutzende Namensdienst eine RMIRegistrierungsdatenbank ist: multi_clientTest((MIDSampleServices)Naming.lookup("rmi://localh ost/MIDSampleServices"), "remote client", clientCount, readCount, writeCount, addTimeSet, getTimeSet, updateTimeSet, deleteTimeSet, findTimeSet); 3.4 Datenbank Vier Datenbanktabellen werden in dieser Arbeit erstellt: Midsampleobject, object_attribute, sub_object, objectKey. Man sollte bei der Erstellung der Datenbank darauf achten, ob das Datenschema vollständig und korrekt ist. Durch die Implementierung von mid.impl.basic.MIDSampleServicesJDBC.java greift man auf die Objekte zu, die in Datenbank gespeichert sind. In dieser Implementierung wird der Datenbanktreiber "com.sap.dbtech.jdbc.DriverSapDB" für die Datenbankverbindung benutzt. Die hier benutzte Datenbank wird durch unteren Code angezeigt: url = "jdbc:sapdb:///db1"; Um die erlaubte Anzahl offener Datenverbindung nicht zu überschreiten, sollte man abgeschlossene Datentransaktionen schließen. Diese erreicht man durch die Methode releaseConnection() in dieser Klasse. In der Klasse wird die Methode getNextKey() implementiert, die die Datentabelle objectKey benutzt. Diese Tabelle enthält nur einen einzigen Spalte: object_key. Dieses Datenfeld erzeugt für jede neue Objekt einen neuen Schlüssel. Die Add() Methode ruft dann diese Methode auf, wenn neue Objekte in der Datenbank angelegt werden sollen. Durch getNextKey() hat jedes neuerstellte Objekt automatisch einen neuen Schlüssel, der durch Inkrementieren erzeugt wird. Die neuen Objekte haben dann den Schlüssel in allen Datentabellen. Die Daten in der Datenbank sind dadurch eindeutlich identifiziert. 22 Beispielsweise durch Aufbauen der Datenbankverbindung, Schlüssel Erstellung usw. sind die Antwortzeiten der Methodenaufrufe mit persistenten Daten im allgemeinen langsamer als die Methodenaufrufe ohne persistente Daten. 3.5 EJB Jede Bean Klasse hat zwei Interfaces: Ein entferntes (Remote) Interface und ein Heim (Home) Interface. Home Interface definiert die Lebenszyklus-Methode einer Bean (Erzeugen, Löschen und Finden einer Bean) und hat die Eigenschaften des Interfaces javax.ejb.EJBObject, das java.rmi.Remote erweitert. Remote Interface definiert die Geschäfts Methode einer Bean und erbt das Interface javax.ejb.EJBHome, das auch das Interface java.rmi.Remote erweitert. EJB Container remote Client Session Bean Entity Bean home Abbildung 3.1: Konzeptionell Ansicht von EJB Architektur Die Abbildung 3.1 zeigt die Struktur von EJBs. Ein Client bekommt einen Referenz von dem Remote Interface durch ein Home Interface. In dieser Arbeit wird der Client in der Klasse test.SessionBeanTester implementiert. Durch folgenden Code eines Session-Bean Clients wird die Arbeit eines EJB-Cleints besser verstanden: //bekommt eine Referenz, die home Interface implementiert MIDSampleServicesSessionHome home = (MIDSampleServicesSessionHome) PortableRemoteObject.narrow(objref, MIDSampleServicesSessionHome.class); //benutzen home Interface, um ein neue Instanz von //MIDSampleServicesSession Bean zu erzeugen MIDSampleServicesSession session = MIDSampleServicesSession)home.create(); //benutzen die geschäftliche Methode von //MIDSampleServicesSession multi_clientTestSession(session, "session test", clientCount, readCount, writeCount, addTimeSet, getTimeSet, updateTimeSet, deleteTimeSet, findTimeSet); 23 Remote Interface und Home Interface werden jeweils in den Kapiteln Session Beans und Entity Beans genauer erklärt. 3.5.1 Session Beans Das Remote und Home Interface der Session Bean sind mid.interfaces.MIDSampleServicesSession und mid.interfaces.MIDSampleServicesSessionHome. Im Remote Interface werden die geschäftlichen Methoden: add(), get(), update(), delete(), find() definiert. Die Bean Klasse ist in der Klasse MIDSampleServicesSessionBean.java implementiert. Diese Session Bean hat keine direkte Verbindung mit der Datenbank. Die Verbindung von Bean zu einer Datenbank wird in der Entity-Bean implementiert. Diese Session-Bean ist eine zustandslose Session-Bean. In der Bean werden die Callback Methoden: ejbCreate(), ejbActivate(), ejbPassivate(), ejbRemove() implementiert. Durch diese Methoden alarmiert der Bean-Container die Bean bei Ereignissen. Durch SessionContext kann die Bean mit anderen Beans und dem Container kommunizieren. JBoss bietet XDoclet an, durch das man leichter Beans bereitstellen kann. Die tags in Beans, die man für die Bereitstellung benötigt, sehen Folgendes aus: /** * * @ejb.bean name = "MIDSampleServicesSession" * display-name = "MIDSampleServicesSession EJB" * description = "MIDSample Session Bean" * view-type = "remote" * jndi-name = "ejb/mid/MIDSampleServicesSession" * */ Die ejb-jar.xml Datei mit diesen Informationen wird durch das Ausführen des Xdoclets erzeugt. Der Client der Session-Bean wird in der Klasse test.SessionBeanTester.java implementiert. 3.5.1.1 Bereitstellung der Session-Bean Zuerst wird eine EJB-JAR Datei erstellt. Diese Datei enthält die Bean Klasse MIDSampleServicesBean.java und deren Interfaces: MIDSampleServices.java und MIDSampleServicesHome.java. Außerdem hat diese JAR-Datei auch die Beschreibung von deploytool (deployment descriptor), die den Typ der Bean und andere Konfigurationsinformationen enthält. Dann wird eine EJB-JAR Datei erstellt, die EJB Interfaces enthält. Danach wird eine EAR-Datei (Enterprise Archive) erstellt. Eine EAR-Datei ist eine JAR-Datei für J2EE-Anwendungen. Diese Datei enthält EJB-JAR und deployment descriptor. 24 Schließlich muss man deploytool noch mitteilen, welcher JNDI-Name für die EJB gegeben wird. Die Bereitstellung der Bean wird in dieser Arbeit anhand des JBoss Tools „Packing Configurations“ erstellt. 3.5.2 Entity Beans 3.5.2.1 CMP Der EJB-Behälter bei Container-verwalteter Persistenz (CMP) weiß, wie die Bean sowie die verbundenen Datenelemente geladen und gespeichert werden. Die CMP behandelt die Datenbankoperationen für den Entwickler und befreit ihn von einem Großteil der unangenehmen Seiten von Entity-Beans. Hier wird keinen SQL-Code mehr gebraucht. Diese CMP Klasse verwendet die Version 2.0 der EJB-Spezifikation, die grundlegend die Version 1.1 geändert hat. Eine gute CMP-Implementierung kann bewirken, dass die Anwendung mit weniger Arbeit wesentlich schneller läuft als eine typische BMP-Anwendung. Eine der größten Verbesserungen, die man bei einer CMP-Implementierung sehen kann, ist der neu hinzu gekommene Cache. Wenn eine CMP-Maschine Objekte im Cache zwischenspeichern kann, kann sie bewirken, dass man wesentlich seltener mit der Datenbank zusammenarbeiten muss. Bei Objekten, die häufig gelesen werden, bringt das Zwischenspeichern im Cache noch weitere Vorteile. Dies gilt besonders bei Objekten, die regelmäßig verwendet werden. Die Klasse MIDSampleServices02Bean.java ist eine CMP Klasse der Version EJB 2.0. Diese Klasse einer CMP-Bean muss abstrakt sein. Darüber hinaus deklariert sie für keines der persistenten Felder Elementvariablen, sondern nur die abstrakten Zugriffsmethoden. Die verschiedenen Feldwerte sind auch nicht vorhanden. Die Home und Remote Interfaces der CMP Bean sind: MIDSampleServices02.java und MIDSampleServices02Home.java. Im Home Interface der Entity Bean Klasse ist außer der create Methode noch eine Suchmethode vorhanden: public mid.interfaces.MIDSampleServices02 findByPrimaryKey(mid.interfaces.MIDSampleServices02PK pk) throws javax.ejb.FinderException,java.rmi.RemoteException; Anhand der finder-Methoden, die mit dem Wort find beginnen und die FinderException auslöst, wird CMP nach den Suchkriterien--den Hauptschlüsseln des MIDSampleObjekts--gesucht. Um eine neue Instanz von CMP Entity Bean zu erzeugen und folglich Daten in die Datenbank hinzuzufügen, wird die create() Methode des Home Interfaces aufgerufen. Ein Home Interface kann keine, eine oder mehrere create() Methoden deklarieren. create() Methode hat auch eine entsprechende ejbCreate() und ejbPostCreate() Methode in der Bean Klasse. Wenn eine create() Methode im Home Interface aufgerufen wird, delegiert der Container die create() Methode eine Instanz zu der entsprechenden ejbCreate() Methode. Die create Methode und die entsprechende ejbPostCreate() in dieser CMP Klasse sehen folgendermaßen aus: public MIDSampleKey ejbCreate()throws CreateException{ 25 try{ setMIDKey(getNextKey()); return null; } catch(SQLException exc){ throw new CreateException("Unable to access database" + exc.toString()); } } public void ejbPostCreate() throws CreateException{ } Die Bean Implementierungsklasse besteht außerdem aus den Callback Methoden ejbActivate(),ejbPassivate(),ejbLoad(),ejbStore(),ejbRemove(). Die Geschäfts Methoden dieser CMP sind wie in Session-Bean. Die abstrakten Zugriffsmethoden in CMP sind: public abstract int getVersion(); public abstract void setVersion(int aVersion); public abstract String getName(); public abstract void setName(String aName); Die CMP Klasse muss für alle Felder, die in der Datenbank gespeichert sind Zugriffsmethoden enthalten, obwohl einige Zugriffsmethoden dem Client über das Remote Interface verfügbar gemacht werden können. Die Bereitstellung der CMP ist ähnlich wie bei Session-Bean. Die Anfragen für die finder-Methoden in der EJB 2.0-Version der CMP sind nicht mehr in der herstellerspezifischen Konfigurationsdatei enthalten, sondern in der Datei ejb-jar.xml. Um das besser zu verstehen, sehen wir die Beschreibung von CMP in der Datei ejbjar.xml. <entity > <description><![CDATA[MIDSample CMP]]></description> <display-name>MIDSampleServices02 EJB</display-name> <ejb-name>MIDSampleServices02</ejb-name> <home>mid.interfaces.MIDSampleServices02Home</home> <remote>mid.interfaces.MIDSampleServices02</remote> <ejb-class>mid.ejb.MIDSampleServices02CMP</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>mid.interfaces.MIDSampleServices02PK</primkey-class> <reentrant>False</reentrant> <cmp-version>2.x</cmp-version> <abstract-schema-name>MIDSampleServices02</abstract-schemaname> </entity > 26 3.6 Anwendung der Entwurfsmuster Das Entwurfsmuster--Fabrik Methode--wird häufig in der Klasse MIDSampleClient.java verwendet. Die Methode multi_clientTest() ist ein Beispiel. Diese Methode wird auf unterschiedliche Weise genutzt: lokale Aufrufe, entfernte Aufrufe, Methodenaufrufe ohne Datenbank oder mit Datenbank usw.. Man übergibt dieser Methode bei unterschiedlicher Anwendung nur verschiedene Parameter. 3.7 Effektives Java Durch effiziente Java Programmierung wird ein Performancezuwachs erreicht und dadurch die Antwortzeit der Ergebnisse reduziert. Effektiv Java Programmierung ist zum Beispiel Minimieren der Zugreifbarkeit von Klassen und Attributen, durch die das Programm schnell ausgeführt wird. [9] 27 4 Evaluation und Dokumentation der Ergebnisse Diese Arbeit ergibt die Antwortzeiten der Messungsergebnisse der Methodenaufrufe. Die Antwortzeiten werden als normale durchschnittliche Zeit, maximale Antwortzeit und Durchschnitt durch Standardabweichung der Methodenaufrufen dargestellt. Dabei ist für einen Single Client die durchschnittliche Zeitmessungen nicht interessant. Die Standardabweichung für einen Single Client hat als Ergebnis in NaN. In dieser Studienarbeit beschreibt jedes Profil eine Laststufe, da sich die Antwortzeiten in den Profilen dramatisch unterscheiden. profil 0: • • • Please 0 number number number number 1000 lesende Transaktionen (zum Beispiel get- und find-Methoden) 500 schreibende Transaktionen (zum Beispiel add-, update- und deleteMethoden) Single Client give the number of the group(from 0 to 5): of of of of objects for single client = 1000 multiple clients = 1 objects to read = 1000 objects to write = 500 basic local: The average, maximum and deviation time of the call for 1 local client multiple clients (in msec) add: 0.01, 0.01, NaN get: 0.0, 0.0, NaN update: 0.04, 0.04, NaN delete: 0.02, 0.02, NaN find: 0.03, 0.03, NaN basic remote: The average, maximum and deviation time of the call for 1 remote client multiple clients (in msec) add: 4.236, 4.236, NaN get: 3.575, 3.575, NaN update: 5.208, 5.208, NaN delete: 3.104, 3.104, NaN find: 1.573, 1.573, NaN JDBC local: The average, maximum and deviation time of the call for 1 local client of JDBC multiple clients (in msec) add: 86.134, 86.134, NaN get: 48.279, 48.279, NaN update: 93.574, 93.574, NaN delete: 104.07, 104.07, NaN find: 33.478, 33.478, NaN JDBC remote The average, maximum and deviation time of the call for 1 remote client of JDBC multiple clients (in msec) add: 81.397, 81.397, NaN 28 get: update: delete: 45.145, 83.22, 90.51, find: 43.833, 45.145, 83.22, 90.51, NaN NaN NaN 43.833, NaN session bean: The average, maximum and deviation time of the call for 1 session test multiple clients (in msec) add: 11.417, 11.417, NaN get: 11.426, 11.426, NaN update: 13.38, 13.38, NaN delete: 10.292, 10.292, NaN find: 5.278, 5.278, NaN Add () Find() Lokal Aufruf Remote Aufruf 0.01 ms 4.23 ms 0.03 ms 1.573 ms JDBC Lokal 86.134 ms 33.478 Session Bean 11.417 ms 5.278 Tabelle 1 Ergebnisse-Vergleich von Profile 0 Aus den obigen Ergebnissen kann man erkennen, dass entfernte schreibende Methodenaufrufe wie zum Beispiel der add Methode 4.23 ms dauern, was wesentlich langsamer ist als ein lokaler Methodeaufruf. Der Methodenaufruf der find Methode braucht auch viel mehr Zeit bei einem entfernten Methodenaufruf als bei einem lokalen Methodenaufruf. Die Methodenaufrufe durch JDBC erfordern zusätzliche Zeit. Zum Beispiel braucht die add Methode bei einem JDBC lokal Aufruf ca. 84 ms länger als bei einem entfernten Methodenaufruf, dessen Daten nicht persistent sind. Außerdem zeigen uns die Ergebnisse von Profil 0, dass die Methodenaufrufe durch eine Session-Bean länger als bei einem entfernten Methodenaufruf dauern. Diese kann man auch deutlich in der Tabelle 1 erkennen. Nach dem direkten hintereinander Ausführen eines gleichen Profils unterscheiden sich die Ergebnisse manchmal. Die Antwortzeiten im zweiten Ablauf sind meistens kürzer als die des ersten Ablaufs. Der Grund dafür ist, dass die Daten beim ersten Zugreifen in den Hauptspeicher gelagert werden. 29 Die unteren Ergebnisse sind vom zweiten Ablauf des Profil 0. Profil 0 Please 0 number number number number give the number of the group(from 0 to 5): of of of of objects for single client = 1000 multiple clients = 1 objects to read = 1000 objects to write = 500 basic local: The average, maximum and deviation time of the call for 1 local client multiple clients (in msec) add: 0.0, 0.0, NaN get: 0.0, 0.0, NaN update: 0.02, 0.02, NaN delete: 0.02, 0.02, NaN find: 0.01, 0.01, NaN basic remote: The average, maximum and deviation time of the call for 1 remote client multiple clients (in msec) add: 4.036, 4.036, NaN get: 3.485, 3.485, NaN update: 4.726, 4.726, NaN delete: 2.864, 2.864, NaN find: 1.523, 1.523, NaN JDBC local: The average, maximum and deviation time of the call for 1 local client of JDBC multiple clients (in msec) add: 78.824, 78.824, NaN get: 41.95, 41.95, NaN update: 75.93, 75.93, NaN delete: 84.0, 84.0, NaN find: 33.078, 33.078, NaN JDBC remote The average, maximum and deviation time of the call for 1 remote client of JDBC multiple clients (in msec) add: 77.572, 77.572, NaN get: 44.404, 44.404, NaN update: 89.75, 89.75, NaN delete: 90.328, 90.328, NaN find: 36.773, 36.773, NaN session bean: The average, maximum and deviation time of the call for 1 session test multiple clients (in msec) add: 10.596, 10.596, NaN get: 10.555, 10.555, NaN update: 13.038, 13.038, NaN delete: 10.336, 10.336, NaN find: 4.807, 4.807, NaN 30 Profil 1: • • • Please 1 number number number number 1000 lesende Transaktionen 1000 schreibende Transaktionen (Unterschied zu Profil 0) Single Client give the number of the group(from 0 to 5): of of of of objects for single client = 1000 multiple clients = 1 objects to read = 1000 objects to write = 1000 basic local: The average, maximum and deviation time of the call for 1 local client multiple clients (in msec) add: 0.01, 0.01, NaN get: 0.02, 0.02, NaN update: 0.01, 0.01, NaN delete: 0.01, 0.01, NaN find: 0.04, 0.04, NaN basic remote: The average, maximum and deviation time of the call for 1 remote client multiple clients (in msec) add: 4.787, 4.787, NaN get: 4.426, 4.426, NaN update: 2.684, 2.684, NaN delete: 1.492, 1.492, NaN find: 1.623, 1.623, NaN JDBC local: The average, maximum and deviation time of the call for 1 local client of JDBC multiple clients (in msec) add: 89.358, 89.358, NaN get: 49.742, 49.742, NaN update: 38.355, 38.355, NaN delete: 41.68, 41.68, NaN find: 32.997, 32.997, NaN JDBC remote The average, maximum and deviation time of the call for 1 remote client of JDBC multiple clients (in msec) add: 77.852, 77.852, NaN get: 44.544, 44.544, NaN update: 44.444, 44.444, NaN delete: 46.237, 46.237, NaN find: 36.813, 36.813, NaN session bean: The average, maximum and deviation time of the call for 1 session test multiple clients (in msec) add: 12.548, 12.548, NaN get: 8.352, 8.352, NaN update: 6.058, 6.058, NaN delete: 5.928, 5.928, NaN find: 5.799, 5.799, NaN In diesem Profil werden mehr schreibende Transaktionen als in Profil 0 bearbeitet. Aber man sieht, dass die benötigte Zeit von einem Methodeaufruf nicht deutlich 31 länger als in Profil 0 ist. Der Grund ist die Skalierbarkeit. Skalierbarkeit heißt, dass die Leistung mit wachsender Datenbank-Größe und wachsender Nutzungsintensität (Anzahl und Komplexität der Transaktionen) nicht kollabiert und stetes Wachstum ohne Einbußen der Funktionalität und Leistung möglich ist. Maßzahlen für Leistung sind Durchsatz und Antwortzeit. Als Durchsatz bezeichnet man die Anzahl abgeschlossener Transaktionen pro Sekunde, als Antwortzeit die Zeitspanne zwischen Beginn und Ende einer Transaktion. Gemessen werden hier die Antwortzeiten der Methodenaufrufen. Es gibt enge Wechselwirkung zwischen Skalierbarkeit und Leistung und es gibt normalerweise die gemeinsame Betrachtung: Performanz. [1] Andererseits sieht man hier, dass manche Methodeaufrufe im Profil 1 sogar schneller sind als in Profil 0. Gründe dafür sind schnellerer Zugriff auf Daten im Hauptspeicher und die Verwendung der Technologie Hotspot JVM (siehe 2.7). Profil 2 Please 2 number number number number give the number of the group(from 0 to 6): of of of of objects for single client = 1000 multiple clients = 10 objects to read = 1000 objects to write = 500 basic local: The average, maximum and deviation time of the call for 10 local client multiple clients (in msec) add: 0.7667999999999999, 0.931, 0.1969505296035304 get: 0.293, 0.781, 0.17216271373325875 update: 0.36419999999999997, 0.462, 0.036859492972940125 delete: 0.5519999999999999, 0.6, 0.057503623074260844 find: 0.3128, 0.351, 0.02527537229091679 basic remote: The average, maximum and deviation time of the call for 10 remote client multiple clients (in msec) add: 35.0133, 37.204, 1.5290277993258041 get: 36.4233, 39.037, 2.015076896795753 update: 62.233999999999995, 69.58, 7.434630679013809 delete: 31.901799999999998, 39.036, 7.775446736719091 find: 14.693099999999998, 17.015, 1.80937063643688 JDBC local: The average, maximum and deviation time of the call for 10 local client of JDBC multiple clients (in msec) add: 944.3681, 944.929, 0.31595057630374423 get: 0.6679999999999999, 1.001, 0.17537578700227308 update: 0.609, 0.782, 0.11415875885021796 delete: 0.7709999999999999, 0.94, 0.10848963084092415 find: 359.32789999999994, 359.477, 0.07960520641822923 JDBC remote The average, maximum and deviation time of the call for 10 remote client of JDBC multiple clients (in msec) add: 1172.9918999999998, 1177.734, 3.262470789992724 get: 0.5919, 0.761, 0.14452716007726712 update: 1.2396, 1.862, 0.4516771708495645 delete: 0.9408, 1.762, 0.42384609366031806 32 find: 489.22079999999994, 491.127, 1.3495303545225519 session bean: The average, maximum and deviation time of the call for 10 session test multiple clients (in msec) add: 86.743, 89.509, 2.0075877177459627 get: 49.956700000000005, 52.755, 2.5867217369567306 update: 100.52279999999999, 113.464, 7.049060407048745 delete: 96.84880000000001, 112.822, 13.17962745047573 find: 65.33489999999999, 69.42, 4.063168097542715 In diesem Profil laufen gleichzeitig 10 Clients. Aus den Ergebnissen kann man erkennen, dass ein Methodenaufruf bei gleicher Anzahl von Objekten länger als bei Single Client dauert (hier im Vergleich mit Profile 0). Zum Beispiel: in Profil 2 braucht das Ausführen einer Add Methode bei lokalem Aufruf durch JDBC 944.3681 ms und in Profil 0 dauert es nur 86.134 ms (durchschnittlichen Antwortzeit). Die Messergebnisse zeigen uns, dass die Skalierbarkeit bis zu einer Grenze funktioniert (Hier ist die Anzahl von Clients von 1 bis 10 gestiegen, unter der Bedienungen, dass 1000 Objekten, 1000 lesende Transaktionen und 500 schreibende Transaktionen bearbeitet werden). Den Unterschied zwischen den Antwortzeiten von Profil 3 und Profil 4 kann man in den unteren Ergebnissen sehen. Zum Beispiel dauert ein entfernter Add Aufrufe 9.92ms im Profil 3 und im Profil 4 347.49 ms (Im Profil 4 laufen gleichzeitig 10 Clients und im Profil 3 gibt es nur einen Client). Profil 3 Please 3 number number number number give the number of the group(from 0 to 6): of of of of objects for single client = 100 multiple clients = 1 objects to read = 100 objects to write = 50 basic local: The average, maximum and deviation time of the call for 1 local client multiple clients (in msec) add: 0.0, 0.0, NaN get: 0.1, 0.1, NaN update: 0.0, 0.0, NaN delete: 0.2, 0.2, NaN find: 0.0, 0.0, NaN basic remote: The average, maximum and deviation time of the call for 1 remote client multiple clients (in msec) add: 9.92, 9.92, NaN get: 6.41, 6.41, NaN update: 12.42, 12.42, NaN delete: 5.6, 5.6, NaN find: 2.5, 2.5, NaN JDBC local: The average, maximum and deviation time of the call for 1 local client of JDBC multiple clients (in msec) add: 435.03, 435.03, NaN 33 get: update: delete: find: 4.5, 8.02, 4.4, 89.33, 4.5, 8.02, 4.4, 89.33, NaN NaN NaN NaN JDBC remote The average, maximum and deviation time of the call for 1 remote client of JDBC multiple clients (in msec) add: 482.89, 482.89, NaN get: 0.6, 0.6, NaN update: 3.42, 3.42, NaN delete: 5.8, 5.8, NaN find: 34.35, 34.35, NaN session bean: The average, maximum and deviation time of the call for 1 session test multiple clients (in msec) add: 28.34, 28.34, NaN get: 14.72, 14.72, NaN update: 23.44, 23.44, NaN delete: 16.42, 16.42, NaN find: 12.02, 12.02, NaN Aus Profil 4 und Profil 5 sieht man, dass die Antwortzeit deutlich länger dauert, wenn die Anzahl der gleichzeitig laufenden Clients von 10 bis 100 gestiegen ist. Ein lokale Add Methodenaufruf dauert 10.61 ms in Profil 5 und 0.56 ms im Profil 4. Die Anzahl der gleichzeitig laufende Clients im Profil 4 ist 10 und im Profil 5 100. Profil 4 Please 4 number number number number give the number of the group(from 0 to 5): of of of of objects for single client = 100 multiple clients = 10 objects to read = 100 objects to write = 50 basic local: The average, maximum and deviation time of the call for 10 local client multiple clients (in msec) add: 0.56, 0.8, 0.32386554137309653 get: 0.37, 0.5, 0.2002775851439974 update: 0.38, 0.6, 0.19888578520235062 delete: 0.69999999, 1.2, 0.21602468994692864 find: 0.81, 1.0, 0.2330951164939612 basic remote: The average, maximum and deviation time of the call for 10 remote client multiple clients (in msec) add: 47.49000000000001, 50.27, 1.583105246729422 get: 47.236000000000004, 52.57, 6.415930174183631 update: 45.20800000000001, 69.1, 22.574385090677936 delete: 11.798, 25.04, 7.515763877788249 find: 9.564, 20.63, 6.008603461334052 JDBC local: The average, maximum and deviation time of the call for 10 local client of JDBC multiple clients (in msec) add: 959.741, 1017.06, 88.07398108030166 34 get: update: delete: find: 515.463, 926.3699999999999, 913.4540000000001, 359.606, 526.16, 975.4, 920.74, 441.33, 13.34176570856429 18.07150796142923 8.241569699463277 34.276528477143714 JDBC remote The average, maximum and deviation time of the call for 10 remote client of JDBC multiple clients (in msec) add: 936.6379999999999, 958.28, 10.792798833790355 get: 448.87299999999993, 450.05, 1.0372725989075622 update: 908.99, 912.72, 3.834953280897525 delete: 885.3700000000001, 890.08, 2.281544311304196 find: 362.062, 367.93, 6.399525676867688 session bean: The average, maximum and deviation time of the call for 10 session test multiple clients (in msec) add: 135.044, 147.21, 8.894472940477 get: 105.42, 109.96, 3.045535895189692 update: 146.612, 164.62, 8.3152935406194 delete: 119.588, 177.66, 26.180194719588233 find: 75.251, 94.14, 14.095711286297924 Profil 5 Please 5 number number number number give the number of the group(from 0 to 6): of of of of objects for single client = 100 multiple clients = 100 objects to read = 100 objects to write = 50 basic local: The average, maximum and deviation time of the call for 100 local client multiple clients (in msec) add: 10.618099999999995, 18.63, 5.008228924439852 get: 9.071900000000012, 11.72, 0.9998340215791177 update: 6.437600000000012, 18.42, 1.722723425277549 delete: 6.892599999999999, 7.0, 0.15201422049375268 find: 4.302500000000003, 4.41, 0.1609590888481553 basic remote: The average, maximum and deviation time of the call for 100 remote client multiple clients (in msec) add: 393.3281000000001, 445.85, 42.10712026678642 get: 434.8442999999997, 521.45, 111.0262990344307 update: 614.5062, 692.58, 104.85652657422959 delete: 193.12580000000014, 273.18, 33.9286503043482 find: 58.137699999999995, 110.16, 35.04229674726291 JDBC local: The average, maximum and deviation time of the call for 100 local client of JDBC multiple clients (in msec) add: 11789.593300000006, 11854.35, 24.89338628871515 get: 45.350300000000004, 57.08, 3.1338801763302886 update: 104.83519999999999, 116.36, 9.453150880831087 delete: 85.50200000000001, 98.34, 4.862697011147438 find: 3782.4773000000014, 3794.16, 16.97253936912106 DBC remote 35 The average, maximum and deviation time of the call for 100 remote client of JDBC multiple clients (in msec) add: 12510.748699999998, 12563.77, 27.493341440164365 get: 73.3544, 83.12, 3.858274937708496 update: 139.96359999999996, 151.42, 7.758900463599231 delete: 114.57819999999994, 148.02, 10.805423664329709 find: 5633.636299999997, 5640.11, 4.342817512801481 session bean: The average, maximum and deviation time of the call for 100 session test multiple clients (in msec) add: 1091.2162000000003, 1127.52, 40.94591016391643 get: 587.2357, 670.76, 35.118357634425244 update: 1396.6582000000008, 1542.22, 61.17624567682388 delete: 1227.6518, 1387.8, 93.59879529981012 find: 806.5785, 911.21, 61.09476420184292 Es können auch weitere Laststufen getestet werden, die noch mehr bearbeitende Objekten oder Transaktionen haben. Die Antwortzeiten werden noch länger dauern, da die Skalierbarkeit bis zu einer Grenze nicht mehr stabil bleibt. Es können keine Messergebnisse mit Entity Bean aufgezeigt werden. Da JBoss die Beschreibung von Entity Bean in Namensumgebung nicht findet. Die Antwortzeiten der Methodenaufrufe sind sehr abhängig von der Anzahl der gleichzeitig laufenden Clients, von der Anzahl der bearbeitenden Clients, von der Anzahl der bearbeitenden Lese und Schreibe-Transaktionen. Dies stellt hohe Anforderungen an die Ressourcen wie Prozessor, Speicherplatz, Netzverbindung usw.. Um die Performanz zu erreichen gibt es einige Methoden. In der Implementierung kann man PreparedStatement statt Statement benutzten, wenn eine Funktion mehrmals in der Datenverarbeitung ausgeführt wird, da die SQL-Anfrage mittels PreparedStatement vor-kompiliert werden. PreparedStatement ist auch besser zu benutzen, wenn man viele Argumenten für einen besonderen SQL Befehl spezifiziert. Um Skalierbarkeit zu erreichen, benutzt man auch Objekt Pool und Thread Pool im Java System. Einige Objekten werden initialisiert, wenn das System gestartet ist. So wird ein Objekt aus dem Objekt Pool abgeholt, wenn ein Objekt gebraucht wird. Ein Objekt wird auch in den Objekt Pool zurückgegeben, wenn das Objekt nicht mehr gebraucht wird. Ein Thread wird benutzt, um Objekte parallel aus dem Objekt Pool zu holen und Objekte können dann gleichzeitig laufen. Um einen Thread zu erstellen oder zu löschen, wird viel Zeit gebraucht. Wenn es einen Thread Pool gibt, wird ein Thread vom Thread Pool abgeholt, wenn dieser gebraucht wird. Ein Thread bekommt dann die benötigte Objekte direkt vom Objekt Pool. Nach der Benutzung eines Threads, wird der Thread auch in den Thread Pool zurückgegeben. Durch Objekt Pool und Thread Pool wird die Anwendung schneller ausgeführt. Instance Pool ist ein Objekt Pool. Objekt Pool hat Lebenszyklus. Kurzgefasst ist EJB = Objekt Pool + Entfernte Objekt Pool. Die Abbildung 4.1 erklärt die Realisierung des Objekt- und Thread-Pools. 36 EJB Container/Server Instanz Pool 1. druch home Interface entfernte Objekt erstellen Home Objekt Client 3.durch entfernte Interface Bean aufrufen Entfernte Objekt Abbildung 4.1: EJB Architektur 37 EJB 4.Bean vom Pool abrufen 5 Zusammenfassung und Ausblick Aus den Ergebnissen kann folgendes zusammengefasst werden. Ein entfernter Methodenaufruf dauert länger als ein lokaler Methodenaufruf. Das Zugreifen von persistenten Daten, dauert länger als das Zugreifen von Daten, die nicht persistent sind. Bei der Programmierung von Datenbankanwendung und Serveranwendung sollte die Skalierbarkeit eingehalten werden. Die Skalierbarkeit wird jedoch durch die Anzahl der bearbeitenden Objekte und die Anzahl der gleichzeitig laufenden Clients beeinflusst. Zum Beispiel ist die Antwortzeit der Add Methodenaufrufe durch JDBC im Profil 2 944.3681 ms und im Profil 0 86.134 ms. In Profil 0 und Profil 2 sind gleich Anzahl von Objekten bearbeitet. Nur gibt es im Profil 2 10 gleichzeitig laufende Clients und im Profil 0 nur Single Client. Jede Profil ist eine Laststufe, unter der Datendurchsätze und Antwortzeiten von Methodeaufrufen stabil bleiben. Um die Skalierbarkeit zu erreichen, gibt es mehrere Ansätze (und Hinweise). Erster Ansatz: Die gemeinsame Nutzung der Ressourcen durch Stateless Session reduziert die Belastung des Servers. Hierdurch können die Funktionen von gleichzeitig laufenden Clients schneller auf dem selben Server bearbeitet werden. Zweiter Ansatz: die EJB-Leistung verbessern Einige der wichtigen Verfahren zur Verbesserung der EJB-Leistung Wenn die Anzahl der Interaktionen zwischen dem Client und dem Server reduziert wird, sollte Anwendungen wesentlich schneller werden. Darüber hinaus gibt es noch einige weitere Bereiche, in denen man die Leistung verbessern kann. Unter anderem sind dies die folgenden Bereiche: • Mehr Caching von Eintity-Beans und Session-Beans • Bildung von Datenbankverbindungs-Pools • Schnellere Datenbank-Interface-Bibliotheken • Optimiertes Verfahren für lokale Aufrufe • Caching von Daten Die meisten dieser Eigenschaften sollten in einem kommerziellen EJB-Behälter zur Verfügung stehen. Eine der wichtigsten Eigenschaften, das Caching von Daten, ist in EJB-Behälter nicht sehr verbreitet. Manche Produkte von CMP-Maschine speichern die Daten im Cache, damit der Behälter dann, wenn man erneut auf eine Bean zugreifen muss, die Bean-Daten nicht 38 von der Datenbank zu holen braucht, da sie bereits geladen sind. Diese Art von Caching kann die Leistung erheblich verbessern, weil es einen Großteil der Zeit nicht mit dem Schreiben, sondern dem Lesen von Daten verbringt. Dritter Ansatz: Datenbankverbesserung Auf der Datenbank Seite ist es notwendig, dass angemessene Indexe erstellt werden, um die Anzahl gesuchter Datensätze zu reduzieren. Weitere Optimierungen der Datenbank können durch die Vermeidung von gleichen Selektieren, Optimierung von dem Datenbank Puffer, Vervollständigung von WHERE Klauseln, Ermöglichung paralleler Abläufe, Erhöhung Linearer Abhängigkeit von Datensätze usw. erreicht werden. EJB ist für eine Client / Server Anwendung geeignet, wenn es kein Zugangsschutzsystem zwischen Client und Server gibt. RMI ermöglicht nicht nur die einfache Realisierung für verteilte Programmierung sondern erhöht die Anwendungsleistung durch seine spezielle Struktur. Session Fassade von J2EE Struktur ist wie eine Klammern in EJB. Sie hat mehrere Vorteile: Wenige Netzverbindungskosten, Zusammenfassung der Aktivitäten in Session Bean, Lockere Abhängigkeit zwischen Client und Entity Bean, Klare und saubere Trennung zwischen Geschäftslogik und präsentierende Schicht, deswegen erhöht Session Fassade die Effektivität der Anwendung, verstärkt die Wiederverwendbarkeit und trennt Client, Geschäftslogik (Session Fassade) und Daten Logik (Entity-Bean usw.). J2EE ist nach den Ergebnissen und der passenden Theorie dieser Arbeit für eine Client und Server Programmierung geeignet. 39 6 Appendix A. Teil von der JAVA Implementierung A.1 MIDSampleServices.java package mid.ejb; import java.rmi.RemoteException; import java.rmi.Remote; public interface MIDSampleServices extends Remote { /** * Adds a single MIDSampleObject. * * @param object the specified MIDSampleObject * @return a generated key for the sepcified object */ public MIDSampleKey add(MIDSampleObject object) throws RemoteException; /** * Retrieves the MIDSampleObject with the given key. * * @param key the given key * @return the MIDSampleObject with the given key */ public MIDSampleObject get(MIDSampleKey key) throws RemoteException; /** * Updates the MIDSampleObject with the given key. * * @param key the given key * @param object the specified object */ public void update(MIDSampleKey key, MIDSampleObject object) throws RemoteException; /** * Deletes the MIDSampleObject with the given key. * * @param key the given key */ public void delete(MIDSampleKey key) throws RemoteException; /** * Retrieves all MIDSampleObjects with the given attribute. * * Each attribute is a key-value pair of strings. All MIDSampleObjects having an * attribute entry matching the given attribute are returned. * * @param attributeKey the given attribute key * @param attributeValue the given attribute value 40 * @return an array of all MIDSampleObjects with the given attribute */ public MIDSampleKey[] find(String attributeKey, String attributeValue) throws RemoteException; } A.2 MIDSampleServicesImpl.java package mid.impl.basic; import java.util.*; import java.rmi.*; import java.rmi.server.UnicastRemoteObject; import mid.ejb.*; /** * @author d042318 */ public class MIDSampleServicesImpl extends UnicastRemoteObject implements MIDSampleServices/*, Remote*/ { public MIDSampleServicesImpl()throws RemoteException { super(); } private private private private Map l = new HashMap(5000, 0.1f); Set s = l.keySet(); Iterator keyIterator = s.iterator(); long currentId = 1; /** * Adds a single MIDSampleObject. * * @param object the specified MIDSampleObject * @return a generated key for the sepcified object */ public synchronized MIDSampleKey add(MIDSampleObject object) { MIDSampleKey key = MIDSampleKey.createMIDKey(currentId++); l.put(key, object); return key; } /** * Retrieves the MIDSampleObject with the given key. * * @param key the given key * @return the MIDSampleObject with the given key */ public synchronized MIDSampleObject get(MIDSampleKey key) { Object o = new Object(); o = l.get(key); 41 return (MIDSampleObject)o; } /** * Updates the MIDSampleObject with the given key. * * @param key the given key * @param object the specified object */ public synchronized void update(MIDSampleKey key, MIDSampleObject object) { l.remove(key); l.put(key, object); } /** * Deletes the MIDSampleObject with the given key. * * @param key the given key */ public synchronized void delete(MIDSampleKey key) { Set subObjectsKey = new HashSet(); MIDSampleObject mo = null; MIDSampleKey supKey, subKey; while(keyIterator.hasNext()){ supKey = (MIDSampleKey)keyIterator.next(); mo = (MIDSampleObject)l.get(supKey); int subZahl = mo.getSubObjectCount(); if( subZahl!= 0){ for(int i =0; i<subZahl; i++){ subKey = mo.getSubObject(i); subObjectsKey.add(subKey); } } } if(!subObjectsKey.contains(key)){ l.remove(key); } } /** * Retrieves all MIDSampleObjects with the given attribute. * * Each attribute is a key-value pair of strings. All MIDSampleObjects having an * attribute entry matching the given attribute are returned. * * @param attributeKey the given attribute key * @param attributeValue the given attribute value * @return an array of all MIDSampleObjects with the given attribute */ public synchronized MIDSampleKey[] find(String attributeKey, String attributeValue) { 42 List keys = new ArrayList(); MIDSampleObject mo = null; MIDSampleKey key = null; while(keyIterator.hasNext()){ key = (MIDSampleKey)keyIterator.next(); mo = (MIDSampleObject)l.get(key); String moValue = mo.getAttributeValue(attributeKey); if(moValue == null){ /*"do nothing";*/ } else if(moValue.equals(attributeValue)){ keys.add(key); } } MIDSampleKey[] keysArray = new MIDSampleKey[keys.size()]; keys.toArray(keysArray); return keysArray; } public static void main(String[] args){ System.out.println("do nothing"); String motdService = System.getProperty("service", "MIDSampleServicesImpl"); if (System.getSecurityManager() == null){ System.setSecurityManager(new RMISecurityManager()); } try { MIDSampleServicesImpl impl = new MIDSampleServicesImpl(); Naming.rebind("//localhost/"+motdService, impl); } catch (Exception exc){ exc.printStackTrace(); } } } A.3 MIDSampleServicesSessionBean.java /* * Created on 23.10.2003 */ package mid.ejb; import import import import import import import import java.rmi.RemoteException; java.util.ArrayList; java.util.HashMap; java.util.HashSet; java.util.Iterator; java.util.List; java.util.Map; java.util.Set; import javax.ejb.CreateException; 43 import javax.ejb.EJBException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; /** * @author d042318 * * @ejb.bean name = "MIDSampleServicesSession" * display-name = "MIDSampleServicesSession EJB" * description = "MIDSample Session Bean" * view-type = "remote" * jndi-name = "ejb/mid/MIDSampleServicesSession" * */ public class MIDSampleServicesSessionBean implements MIDSampleServices, SessionBean { private SessionContext context; public MIDSampleServicesSessionBean() throws RemoteException { super(); } /** * @throws CreateException * @ejb.create-method */ public void ejbCreate() throws CreateException { } public void ejbActivate() throws EJBException, RemoteException { } public void ejbPassivate() throws EJBException, RemoteException { } public void ejbRemove() throws EJBException, RemoteException { } public void setSessionContext(SessionContext aContext) throws EJBException, RemoteException { context = aContext; } private Map l = new HashMap(); private long currentId = 1; /** * Adds a single MIDSampleObject. * * @param object the specified MIDSampleObject * @return a generated key for the sepcified object * * @ejb.interface-method view-type = "remote" */ public MIDSampleKey add(MIDSampleObject object) { 44 MIDSampleKey key = MIDSampleKey.createMIDKey(currentId++); l.put(key, object); return key; } /** * Retrieves the MIDSampleObject with the given key. * * @param key the given key * @return the MIDSampleObject with the given key * * @ejb.interface-method view-type = "remote" */ public MIDSampleObject get(MIDSampleKey key) { Object o = new Object(); o = l.get(key); return (MIDSampleObject)o; } /** * Updates the MIDSampleObject with the given key. * * @param key the given key * @param object the specified object * * @ejb.interface-method view-type = "remote" */ public void update(MIDSampleKey key, MIDSampleObject object) { l.remove(key); l.put(key, object); } /** * Deletes the MIDSampleObject with the given key. * * @param key the given key * * @ejb.interface-method view-type = "remote" */ public void delete(MIDSampleKey key) { Set subObjectsKey = new HashSet(); Set s = l.keySet(); MIDSampleObject mo = null; MIDSampleKey supKey, subKey; Iterator keyIterator = s.iterator(); while(keyIterator.hasNext()){ supKey = (MIDSampleKey)keyIterator.next(); mo = (MIDSampleObject)l.get(supKey); int subZahl = mo.getSubObjectCount(); if( subZahl!= 0){ for(int i =0; i<subZahl; i++){ subKey = mo.getSubObject(i); 45 subObjectsKey.add(subKey); } } } if(!subObjectsKey.contains(key)) l.remove(key); else{ } } /** * Retrieves all MIDSampleObjects with the given attribute. * * Each attribute is a key-value pair of strings. All MIDSampleObjects having an * attribute entry matching the given attribute are returned. * * @param attributeKey the given attribute key * @param attributeValue the given attribute value * @return an array of all MIDSampleObjects with the given attribute * * @ejb.interface-method view-type = "remote" */ public MIDSampleKey[] find(String attributeKey, String attributeValue) { List keys = new ArrayList(); Set s = l.keySet(); MIDSampleObject mo = null; MIDSampleKey key = null; Iterator keyIterator = s.iterator(); while(keyIterator.hasNext()){ key = (MIDSampleKey)keyIterator.next(); mo = (MIDSampleObject)l.get(key); String moValue = mo.getAttributeValue(attributeKey); if(moValue == null){ } else if(moValue.equals(attributeValue)){ keys.add(key); } } MIDSampleKey[] keysArray = new MIDSampleKey[keys.size()]; keys.toArray(keysArray); return keysArray; } } 46 B. META-INF(von Session Bean) B.1. Ejb-jar.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd"> <ejb-jar > <description><![CDATA[No Description.]]></description> <display-name>Generated by XDoclet</display-name> <enterprise-beans> <!-- Session Beans --> <session > <description><![CDATA[MIDSample Session ean]]></description> <display-name>MIDSampleServicesSession EJB</display-name> <ejb-name>MIDSampleServicesSession</ejb-name> <home>mid.interfaces.MIDSampleServicesSessionHome</home> <remote>mid.interfaces.MIDSampleServicesSession</remote> <ejb-class>mid.ejb.MIDSampleServicesSessionBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> <!-- To add session beans that you have deployment descriptor info for, add a file to your XDoclet merge directory called session-beans.xml that contains the <session></session> markup for those beans.--> <!-- Entity Beans --> <!-- To add entity beans that you have deployment descriptor info for, add a file to your XDoclet merge directory called entity-beans.xml that contains the <entity></entity> markup for those beans.--> <!-- Message Driven Beans --> <!-- To add message driven beans that you have deployment descriptor info for, add a file to your XDoclet merge directory called message-driven-beans.xml that contains the <messagedriven></message-driven> markup for those beans.--> </enterprise-beans> <!-- Relationships --> <!-- Assembly Descriptor --> <assembly-descriptor > <!--To add additional assembly descriptor info here, add a file to your XDoclet merge directory called assembly-descriptor.xml that contains he <assembly-descriptor></assembly-descriptor> markup.--> <!-- finder permissions --> <!-- transactions --> <!-- finder transactions --> </assembly-descriptor> </ejb-jar> 47 B.2 jboss.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 3.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_3_0.dtd"> <jboss> <enterprise-beans> <!-To add beans that you have deployment descriptor info for, add a file to your XDoclet merge directory called jboss-beans.xml that contains the <session></session>, <entity></entity> and <messagedriven></message-driven> markup for those beans. --> <session> <ejb-name>MIDSampleServicesSession</ejb-name> <jndi-name>ejb/mid/MIDSampleServicesSession</jndi-name> </session> </enterprise-beans> <resource-managers> </resource-managers> </jboss> B.3 application.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd"> <application> <display-name>mid</display-name> <module> <ejb>MIDSampleServicesEJB.jar</ejb> </module> </application> 48 7 Literatur [1] Datenbanksysteme Konzepte und Techniken der Implementierung Theo Härder Erhard Rahm Springer-Verlag 1999 [2] Über JBoss http://www.jboss.org [3]Über Client/Server http://www.sei.cmu.edu/str/descriptions/clientserver_body.html [4] Über J2EE Developer’s Guide JSP, Servletes, EJB 2.0, JNDI, JMS, JDBC, Corba, XML, RMI .Mark Wutka Markt + Technik Verlag 2002 [5] über JBoss IDE JBoss-DIE 1.2.2- Tutorial Guide (http://www.jboss.org) [6] Über JBoss JBoss-DIE 1.2.2 Tutorial Guide [7] Enterprise JavaBeans Technology Fundmentals http://developer.java.sun.com/developer/onlineTraining/EJBIntro/EJBIntro.html [8] Java Enterprise in a nutshell Farley, Jim O’Reilly, 2003 [9] Effective Java Programming Language Guide Joshua Bloch [10] Entwurfsmuster-- Elemente wiederverwendbarer objektorientierter Software Erich Gamma 1. Aufl., 5., korrig 49