Technische Universität Berlin Fakultät IV: Elektrotechnik und Informatik INSTITUT FÜR WIRTSCHAFTSINFORMATIK FACHGEBIET SYSTEMANALYSE UND EDV Diplomarbeit Wintersemester 2004/2005 Konzeption und Implementierung des Sicherheitskonzepts in einem Net Business Tool Verfasser: MatrikelNr: Studiengang: email: letzte Änderung: Bassem El-Ahmad 181794 Technische Informatik [email protected] 3. Januar 2005 Gutachter: Betreuer: Prof. Dr. Krallman Dipl.-Kfm. Marian Scherz Die selbständige und eigenhändige Anfertigung versichere ich an Eides statt. Berlin,den 3. Januar 2005 Unterschrift Danksagung Ich möchte mich bei allen bedanken, die mich während meines Studiums unterstützt haben, besonders bei meinem Diplomarbeitsbetreuer Dipl.-Kfm. Marian Scherz, der viel Geduld aufbringen mußte. Mein größter Dank gilt meiner Mutter, die mir das Studium ermöglicht und mich auch zu jeder Zeit unterstützt hat. 3 Inhalt 1. Einleitung 1.1. Gliederung der Arbeit . . . . . . . . . . . . . . . . . . . . . . . . . . 10 11 2. Einleitung zum Net Business Tool 2.1. Hintergrund zum Sicherheitskonzept . . . . . . . . . . . . . . . . . . 2.2. Zweck des Sicherheitskonzepts . . . . . . . . . . . . . . . . . . . . . . 2.3. Anforderungen an Benutzer des Admin Tools . . . . . . . . . . . . . . 12 13 13 13 3. Modellierung und Entwurf 3.1. Einführung . . . . . . . . . . . . 3.2. UML Modellierung . . . . . . . . 3.2.1. Anwendungsfalldiagramm 3.2.2. Klassendiagramm . . . . 3.2.3. Sequenzdiagramm . . . . 3.2.4. Aktivitätsdiagramm . . . 3.3. Datenbank Schema . . . . . . . . . . . . . . . 15 15 16 17 18 20 21 21 . . . . . . . . 23 23 23 25 26 28 29 30 32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4. Angewandte Technologien 4.1. Enterprise JavaBeans . . . . . . . . . . . 4.1.1. Der EJB-Server . . . . . . . . . . 4.1.2. Der EJB-Container . . . . . . . . 4.1.3. Das EJBHome-Interface . . . . . 4.1.4. Das EJBObjekt-Interface . . . . . 4.1.5. Enterprise JavaBeans Typen . . . 4.1.6. Session Beans . . . . . . . . . . . 4.1.7. Der Zugriff auf eine Session-Bean 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2. 4.3. 4.4. 4.5. 4.1.8. Entity Beans . . . . . . . . . . . Java Servlets . . . . . . . . . . . . . . . Java Server Pages . . . . . . . . . . . . . 4.3.1. Elemente einer JSP-Seite . . . . . 4.3.2. Direktiven . . . . . . . . . . . . . 4.3.3. Aktionen . . . . . . . . . . . . . . 4.3.4. Scripting Elemente . . . . . . . . 4.3.5. Implizite Objekte einer JSP Seite 4.3.6. Gültigkeitsbereiche von Objekten 4.3.7. Beans . . . . . . . . . . . . . . . 4.3.8. Tag-Extensions . . . . . . . . . . 4.3.9. Zusammenfassung . . . . . . . . . Code Generation mit XDoclet . . . . . . 4.4.1. Was ist XDoclet? . . . . . . . . . 4.4.2. XDoclet und J2EE . . . . . . . . 4.4.3. Beispiel EJB mit XDoclet . . . . 4.4.4. Fazit . . . . . . . . . . . . . . . . JBoss Application Server . . . . . . . . . 4.5.1. Deployment unter JBoss . . . . . 5. Implementierung 5.1. UsersBean . . . . . . . . . . . 5.2. GroupsBean . . . . . . . . . . 5.3. PagesBean . . . . . . . . . . . 5.4. Anbindung an die Datenbank 5.5. Admin Tool Web-Interface . . 6. Admin Tool Benutzerhandbuch 6.1. Authentifizierung . . . . . . 6.2. User Verwaltung . . . . . . 6.2.1. List all user . . . . . 6.2.2. Create user . . . . . 6.2.3. Reset password . . . 6.2.4. Update User . . . . . 6.2.5. Delete user . . . . . 6.2.6. Show user groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 34 38 39 40 40 41 42 42 43 44 47 48 49 51 52 53 54 54 . . . . . 55 56 58 61 62 62 . . . . . . . . 65 65 66 66 68 68 68 69 70 6.3. Groups verwalten . . . . . . . 6.3.1. List all groups . . . . . 6.3.2. Create group . . . . . 6.3.3. Add user to group . . 6.3.4. Delete group . . . . . . 6.3.5. Delete user from group 6.3.6. Add sub-group . . . . 6.3.7. Delete sub-group . . . 6.4. PLP Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 70 71 71 71 72 72 72 73 A. Anhang 75 A.1. GroupsBean.Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 A.2. UsersBean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 A.3. PagesBean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Literaturverzeichnis 125 6 Abbildungsverzeichnis 3.1. Admin Tool UML Anwendungsfalldiagramm . . . . . . . . . . . . . . 3.2. Admin Tool Klassendiagramm . . . . . . . . . . . . . . . . . . . . . . 3.3. Die unterschiedlichen Datenbank Tabellen . . . . . . . . . . . . . . . 18 19 22 4.1. 4.2. 4.3. 4.4. 4.5. 4.6. 4.7. 4.8. Die EJB-Architektur . . . . . . . . . . . . . . . . . . . . . Die Beziehung Zwischen EJB-Server und EJB-Container . Client-Schnittstelle einer EJB . . . . . . . . . . . . . . . . EJB-Typen in EJB-Version 2.0 . . . . . . . . . . . . . . . Zugriff auf eine Session-Bean . . . . . . . . . . . . . . . . . Die verschiedenen Stufen bei der Ausführung eines Servlets Die verschiedenen Stufen bei der Ausführung eines Servlets XDoclet Ablauf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 25 26 30 33 36 39 50 5.1. 5.2. 5.3. 5.4. 5.5. Die Hauptpackages UsersBean . . . . . GroupsBean . . . . PagesBean . . . . . Web infterface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 56 59 61 64 6.1. 6.2. 6.3. 6.4. 6.5. 6.6. 6.7. 6.8. Authentifizierung Prozess . Login Page . . . . . . . . . Hauptansicht . . . . . . . . Liste aller verfuegbaren User Create new user . . . . . . . Update user . . . . . . . . . Delete user . . . . . . . . . List All Gropus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 66 67 67 68 69 69 70 . . . . . . . . . . . . . . . . . . . . 7 6.9. Add new group . . 6.10. Delete group . . . . 6.11. PLP Security Panel 6.12. PLP Managegroups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 72 73 74 Tabellenverzeichnis 4.1. Implizite Objekte einer JSP-Seite . . . . . . . . . . . . . . . . . . . . 4.2. Gültigkeitsbereiche von Objekten einer JSP-Seite . . . . . . . . . . . 9 41 43 1. Einleitung Diese Arbeit handelt von einem Admin Tool für die Realisierung eines Sicherheitskonzeptes für das NBT 1 . Zur Realisierung gehört unter anderem die Integration in das vorhandene PLP2 Tool. Weiterhin wird eine Webanwendung mit JSP’s und EJB ermöglicht. Mit Hilfe vom NBT Tool werden die Benutzer in Gruppen verwaltet und ihnen bestimmte Rechte zugewiesen. Es gibt drei Arten von Benutzern (Admin User, Group User und Guest User). Der Admin User verfügt über alle möglichen Rechte, wie z.B. anlegen, löschen und verändern von Usern und Gruppen. Dagegen hat der Super User weniger Rechte als der Admin User, er darf Veränderungen an den einzelnen Usern vornehmen, sie jedoch nicht löschen. Zuletzt hat der Guest User nur eingeschränkte Möglichkeiten und darf nur seine eigenen Informationen verändern. Das Sicherheitstool stellt bestimmten Usern HTML Elemente im LRP Tool zur Verfügung, dabei ist die Anzeige abhängig vom eingeloggten User in der zugehörigen Gruppe. Demnach werden Elemente angezeigt bzw. versteckt. Das Tool gliedert sich in die drei Hauptbereiche Usermanager, Groupmanager und Pagemanager. Im Usermanager werden alle Funktionalitäten für die Verwaltung der Benutzer implementiert. Der Gruppenmanager untergliedert sich in Groups und Subgroups, dabei werden im Bereich Groups die zugehörigen Rechte der Gruppe auf den User übertragen bzw. ihm entzogen, wenn der User aus der Gruppe ausscheidet, dagegen ist die Subgroup eine Untergruppe von Groups, d.h. sie erbt alle Rechte der Oberklasse Groups. Der Pagemanager wird für die Verwaltung der einzelnen Projekte und HTML Elemente benötigt. Zusätzlich wird eine PostgreSQL-Datenbank zur Verwaltung der Daten aufgebaut. Dafür werden Tabellen für die Bereiche Gruppen, Users und Elemente angelegt. 1 2 Net Business Tool:http://venus.cs.tu-berlin.de/ PRESENTATION LAYER PRODUCER:http://venus.cs.tu-berlin.de/ 10 1.1. Gliederung der Arbeit 11 1.1. Gliederung der Arbeit Als erstes werden die Sicherheitsanforderungen and das NBT Admin Tool im Kapitel 2 vorgestellt. Dabei werden die Sicherheitsaspekte für Webapplikationen in den Vordergrund gestellt, diskutiert und analysiert. Dazu werden Anforderungen und Strategien sowie der Ablauf dargestellt. In Kapitel 3 erfolgt eine Kurzbeschreibung der Modellierungssprache UML. Es werden bestimmte Modellierungsmethoden zur Konzeptbeschreibung verwendet. Die verschiedenen Usecases, Klassendiagramme sowie das Datenbankschema kommen durch spezifische Beschreibung zur Geltung. Die einzelnen Diagramme dienen als Bausteine zur Realisierung der Implementierungen. Die angewandten Technologien (EJB, Servlets, JSP, JBOSS und XDoclet) werden in Kapitel 4 beschrieben. Dazu gehört ein kleiner Exkurs in die Technologien, die wiederum auf die Problematik der Diplomarbeit abgestimmt werden. Zusätzlich werden Definitionen und Grundkenntnisse in den einzelnen Technologien erläutert. Weiterhin werden die Möglichkeiten und deren Anwendungsmöglichkeiten untereinander dargestellt und angewendet. Kapitel 5 beshreibt die vollständige Implemtentierung der einzelnen Funktionalitäten, wie z.B. UserBean, GroupBean und PagerBean. In UserBean werden Funktionalitäten zur Verwaltung und Erzeugung von Usern implementiert. Die GroupBean Klasse ermöglicht das Einbinden erzeugter User durch UserBean in verschiedene Gruppen zuzuordnen. Hingegen der Benutzerverwaltungen und Gruppenrechtzuweisungen beschreibt PagerBean die Verwaltung der einzelnen Projekte und HTML Elemente. Die Datenbankkonfiguration, die Interaktion zwischen Servlets und EJB’s, die Interaktion zwischen JSP und Servlets und die Realisierung des Web-Interfaces zum Admin Tool werden implementiert. Ein Benutzerhandbuch in Kapitel 6 dient für die Schritt für Schritt Benutzung des Admin Tools, das durch Grafikelemente die Navigation auf der Applikation erleichtert. Auszüge aus der User- und Gruppenverwaltung dienen als Demo. 2. Einleitung zum Net Business Tool Das Net Business Tool (NBT) ist ein generisches, komponentenbasiertes, plattformund geräteunabhängiges Framework auf Basis offener Quelltexte (Open Source). Dieses Framework übernimmt die Aufgaben der Komponentenverwaltung und -verteilung. Mit wenigen Operationen können mit dem NBT innerhalb weniger Minuten Webseiten erstellt werden. Der Webgenerator kann dabei an individuelle Anforderungen angepasst werden (Customizing). Ziel des NBT ist die Verschmelzung von Struktur- und Layout innerhalb des HTMLCodes so zu separieren, dass hierarchische Strukturen und logische Abhängigkeiten zwischen den verschiedenen HTML-Objekten definiert werden können. Das NBT verwaltet ein Projekt mit sämtlichen Bestandteilen einer zu erstellenden Website in einer Datenbank. Die Inhalte dieser Tabellen werden beim abschließenden Generierungslauf in XML Befehle übersetzt. Grundgedanke der Software ist die Trennung von Layout und eigentlichem Inhalt (Texte, Bilder, Multimedia, etc.). Das Erstellen von Stylesheets, die im NBT über Bausteine direkt oder als Datei eingebunden werden, gibt eine zusätzliche Möglichkeit, das Webdesign professionell zu gestalten1 . Das NBT soll in Anwendungsbereichen wie Internet- und Intranet-Applikationen, eBusiness sowie Geschäftsautomatisierung als plattformunabhängiges Werkzeug seine Funktionalität zur Verfügung stellen. Zur Umsetzung der Plattformunabhängigkeit ist das NBT in Java geschrieben und benötigt zur Ausführung lediglich eine installierte Java VM und einen Browser, der Java uneingeschränkt unterstützt. 1 Net Business Tool:http://venus.cs.tu-berlin.de/ 12 2.1. Hintergrund zum Sicherheitskonzept 13 2.1. Hintergrund zum Sicherheitskonzept Immer mehr Anwendungen laufen auf einer Internet basierenden Technik, so auch geschäftskritische Anwendungen. Das Internet stellt jedoch eine offene Plattform dar, so dass der Sicherheitsgedanke fest mit der Anwendung verankert werden muss. Spätestens beim Aufbau Internet basierender Geschäftsmodelle, zum Beispiel (eBusiness) oder beim Thema Fernwartung ist die Etablierung stabiler und verifizierbarer Sicherheitskonzepte unumgänglich. Nicht nur, dass der Gesetzgeber heute ein Management der Sicherheit (Risikomanagement) fordert, der Fortbestand einer Unternehmung hängt ganz entscheidend von der Wahrung der Unternehmensgeheimnisse ab. Dies betrifft sowohl Benutzerdaten mit ihren Rechten als auch den Entwicklungsstand von zukünftigen Produkten und Dienstleistungen. Je nach Tätigkeitsfeld sind verschiedene Informationen von unterschiedlicher Wichtigkeit und Bedeutung. Grundlage für eine Webanwendung bzw. Security-Lösung ist eine Risikoanalyse, in der mögliche Schwachstellen und Bedrohungen untersucht werden und wie mögliche Angriffe darauf gestartet werden können. Nach der Bewertung, welchen Schaden diese Angriffe anrichten können, werden geeignete Präventivmaßnahmen untersucht. Ausgehend von dieser Risikoanalyse, wird festgestellt, welche Sicherheitsmängel bestehen und welche Gefahren daraus entstehen können. 2.2. Zweck des Sicherheitskonzepts Sicherheitsaspekte spielen eine zentrale Rolle in Webapplikationen. Als Grundlage für eBusiness-Strategien muss sichergestellt werden, dass Informationen nicht jedem zugänglich sind, d.h. nicht jeder Benutzer ist befugt jede Information zu erhalten. Daher müssen Einschränkungen vorgenommen werden um die Sicherheit und Privatsphäre jedes einzelnen zu schützen und zu garantieren. 2.3. Anforderungen an Benutzer des Admin Tools Das Sicherheitskonzept innerhalb des Admin Tools muss die Verwaltung und Separierung der Informationen gewährleisten. Dabei werden bestimmte Zugriffsrechte den Usern durch Gliederung in bestimmten Gruppen auferlegt. Nicht jeder User hat 2.3. Anforderungen an Benutzer des Admin Tools 14 damit die Möglichkeiten bestimmte Elemente sowie Projekte zusehen, d.h. der bestimmte User sieht nur für ihn zugelassene Elemente. Die einzelnen User können ihre eigenen Daten ändern, aber sich nicht aus der Datenbank löschen. Hingegen diesen Bestimmungen darf der Admin alle Operationen für einen User vornehmen und den Benutzer somit aus der Datenbank löschen, verändern oder neu anlegen. Die Gliederung der User in einzelne Gruppen ermöglicht ihnen bestimmte Funktionalitäten auszuführen. Es gibt drei Arten von Usergruppen (Admingroup, Supergroup, Anonymousgroup). Die Admingruppe verfügt über alle Rechte im Tool. Dagegen verfügt ein Supergroup User über mehr Rechte als ein Guest bzw. Anonymous User. Jedoch können die User nur bestimmte Elemente bzw. Projekte sehen, die im Verbund mit ihnen stehen. 3. Modellierung und Entwurf 3.1. Einführung Die Unified Modelling Language (UML) hat sich in den letzten Jahren als standardisierte Modellierungssprache fuer Softwareprodukte durchgesetzt. Die Darstellung und Modellierung erfolgt mit verschiedenen Diagrammen, die unterschiedliche Aspekte des Systems beleuchten.1 Diese sind beispielsweise die gewuenschten Anwendungsfaelle (sogenannte use cases), Klassendiagramme (classdiagrams) oder Sequenzdiagramme (sequence diagrams). Letztere beschreiben die Kommunikation verschiedenener Klassen untereinander. Ein UML-Modell besteht aus mehreren dieser Diagramme, die in ihrer Gesamtheit ein Softwareprodukt möglichst vollständig modellieren sollen.2 Die Erstellung solcher Dokumente geschieht meist mit Softwareentwicklungswerkzeugen (engl. CASE-Tools). Bekannte Vertreter sind hier Rational Rose3 und Together ControlCenter4 . Diese Werkzeuge dienen meist nicht nur zur Erstellung der Diagramme, sondern helfen dem Softwareentwickler auch bei der Erstellung des Sourcecodes und der Dokumentation. Viele CASE-Tools bieten inzwischen das sogenannte Roundtrip-Engineering. Darunter versteht man die Möglichkeit, sowohl mit Diagrammen als auch mit dem eigentlichen Sourcecode zu arbeiten.5 Änderungen in einem der beiden Teile werden automatisch im anderen Teil nachgeführt. Dabei ist die Generierung von Diagrammen aus bereits bestehendem Sourcecode ebenfalls möglich. Diese Werkzeuge entwickeln sich zunehmend zu Werkzeugen für den gesamten Softwareentwicklungszyklus. So 1 Alhir (2003) Eine recht umfangreiche Übersicht findet sich unter http://www.jeckle.de/unified.htm 3 http://www.rational.com 4 http://www.togethersoft.com 5 Balzert (2001) 2 15 3.2. UML Modellierung 16 gibt es Programme, die automatisch Tests durchführen können und aus Testlaeufen Sequenzdiagramme erzeugen. Selten wird ein Softwareprodukt nur von einer Person entworfen und programmiert. Die Arbeit in Entwicklungsteams stellt somit den Regelfall dar. Softwareentwicklungswerkzeuge bieten auch hier einige Hilfswerkzeuge zur Teamarbeit an, die das gemeinsame Arbeiten an Dokumenten ermöglichen. Dazu gehört neben der Verwaltung von Revisionen und Varianten auch das Softwarekonfigurationsmanagement. Eine Vielzahl der Softwareentwicklungsprozesse arbeiten iterativ. Diese Iterationen können dabei verschiedene Formen annehmen, auch eine Ueberlappung ist moeglich. Es lässt sich jedoch allgemein feststellen, dass die ständige Überarbeitung und Korrektur der Entwurfsdokumente und Sourcecodes eine Vielzahl von verschiedenen Versionen produziert. Betrachtet man zusätzlich die Möglichkeit aus vorhandenen Programmen Diagramme generieren zu können (durch Reverse- Engineering oder Testläufe), so nimmt die Anzahl der Diagramme weiter zu. Diese Diagramme beinhalten viele wertvolle Informationen über den Entwicklungsprozess. 3.2. UML Modellierung 1. Was ist die UML? Unified Modelling Language ist eine Sprache zur Spezifikation, Visualisierung, Konstruktion und Dokumentation von Modellen für Softwaresysteme, Geschäftsmodelle und andere Nicht-Softwaresysteme. Sie bietet den Entwicklern die Möglichkeit, den Entwurf und die Entwicklung von Softwaremodellen auf einheitlicher Basis zu diskutieren. Die UML wird seit 1998 als Standard angesehen.6 Sie lag und liegt weiterhin bei der Object Management Group (OMG) zur Standardisierung vor. 2. Wer steht hinter der UML? Entwickelt wurde die UML von Grady Boch, Ivar Jacobsen und Jim Rumbaugh von RATIONAL ROSE SOFTWARE . Sie kombinierten die besten Ideen objektorientierter Entwicklungsmethoden und schufen daraus UML“. Vie” le führende Unternehmen der Computerbranche (Microsoft, Oracle, Hewlett6 Objektorientierter Softwareentwurf mit UML Bannert:1999 3.2. UML Modellierung 17 Packard,...) wirkten aktiv an der Entwicklung mit und unterstützen die UML. 3. Warum ist die UML keine Methode? Die UML ist keine Methode. Sie ist lediglich ein Satz von Notationen zur Formung einer allgemeinen Sprache zur Softwareentwicklung. Eine Methode beinhaltet Empfehlungen zur Vorgehensweise bei Entwicklungsprozessen. Um UML erfolgreich zu nutzen, ist es notwendig eine passende Methode zu entwickeln, die die UML unterstützt.7 Die Modellelemente der UML werden nach Diagrammtypen gegliedert: • Anwendungsfalldiagramm • Klassendiagramm • Aktivitätsdiagramm • Kollaborationsdiagramm • Sequenzdiagramm • Zustandsdiagramm • Komponentendiagramm • Einsatzdiagramm 3.2.1. Anwendungsfalldiagramm • Definition: Ein Anwendungsfalldiagramm besteht aus einer Menge von Anwendungsfällen und stellt die Beziehungen zwischen Akteuren und Anwendungsfällen dar. Es zeigt das äußerlich erkennbare Systemverhalten aus der Sicht eines Anwenders. 7 Alhir (2003) 3.2. UML Modellierung 18 Abbildung 3.1.: Admin Tool UML Anwendungsfalldiagramm • Beschreibung: Ein Anwendungsfalldiagramm beschreibt die Zusammenhänge zwischen verschiedenen Anwendungsfällen untereinander und zwischen Anwendungsfällen und den beteiligten Akteuren. Es zeigt die Struktur und Zusammenhänge von verschiedenen Geschäftsvorfällen und wie mit ihnen verfahren wird. Die drei wichtigsten Akteure im Admin Tool sind Guest, Super und Admin User. Abbildung 3.1 beschreibt die Interaktionen zwischen den verschiedenen Anwendungsfällen. 3.2.2. Klassendiagramm • Definition: Eine Klasse ist eine Menge von Objekten, in der die Eigenschaften (Attribute), Operationen und die Semantik der Objekte definiert werden. Alle Objekte einer Klasse entsprechen dieser Festlegung. 3.2. UML Modellierung Abbildung 3.2.: Admin Tool Klassendiagramm 19 3.2. UML Modellierung 20 • Beschreibung: Eine Klasse ist eine Zusammenfassung gleichartiger Objekte. Objekte sind die agierenden Grundelemente einer Anwendung. Die Gleichartigkeit bezieht sich auf Eigenschaften (Attribute) und/oder auf Fähigkeiten (Operationen/Methoden) der Objekte einer Klasse. Eine Klasse enthält gewissermaßen die Konstruktionsbeschreibung für Objekte die mit ihr erzeugt werden. Das Verhalten der Objekte wird durch die Möglichkeit eines Objektes Nachrichten zu empfangen und zu verstehen beschrieben. Dazu benötigt das Objekt bestimmte Operationen. Die Begriffe Operation und Nachricht sollten nicht synonym verwendet werden. Zusätzlich zu Eigenschaften und Fähigkeiten kann eine Klasse auch Definitionen von Zusicherungen, Merkmalen und Stereotypen enthalten. Das NBT Admin Tool besteht aus verschiedenen Klassen, die drei wichtigsten Klassen sind: UsersBean, GroupsBean und PagesBean. Abbilldung 3.2 beschreibt die Relatation zwischen den verschiedenen Klassen. 3.2.3. Sequenzdiagramm • Definition: Das Sequenzdiagramm beschreibt die zeitliche Abfolge von Interaktionen zwischen einer Menge von Objekten innerhalb eines zeitlich begrenzten Kontextes. • Beschreibung Mittels des Sequenzdiagrammes beschreibt man die Interaktionen zwischen den Modellelementen ähnlich wie bei einem Kollaborationsdiagramm, jedoch steht beim Sequenzdiagramm der zeitliche Verlauf des Nachrichtenaustausches im Vordergrund. Die Zeitlinie verläuft senkrecht von oben nach unten, die Objekte werden durch senkrechte Lebenslinien beschrieben und die gesendeten Nachrichten waagerecht entsprechend ihres zeitlichen Auftretens eingetragen. 3.3. Datenbank Schema 21 3.2.4. Aktivitätsdiagramm • Definition: In einem Aktivitätsdiagramm werden die Objekte eines Programmes mittels der Aktivitäten, die sie während des Programmablaufes vollführen, beschrieben. Eine Aktivität ist ein einzelner Schritt innerhalb eines Programmablaufes, d.h. ein spezieller Zustand eines Modellelementes, eine interne Aktion sowie eine oder mehrere von ihm ausgehende Transitionen enthält. Gehen mehrere Transitionen von der Aktivität aus, so müssen diese mittels Bedingungen voneinander zu entscheiden sein. Somit gilt ein Aktivitätsdiagramm als Sonderform eines Zustandsdiagrammes, dessen Zustände der Modellelemente in der Mehrzahl als Aktivitäten definiert sind.8 • Beschreibung: In einem Programmablauf durchläuft ein Modellelement eine Vielzahl von Aktivitäten, d.h. Zuständen, die eine interne Aktion und mindestens eine daraus resultierende Transition enthält. Die ausgehende Transition impliziert den Abschluss der Aktion und den Übergang des Modellelementes in einen neuen Zustand bzw. eine neue Aktivität. Diese Aktivitäten können in ein Zustandsdiagramm integriert werden oder besser aber in einem eigenen Aktivitätsdiagramm visualisiert werden. Ein Aktivitätsdiagramm ähnelt in gewisser Weise einem prozeduralem Flußdiagramm, jedoch sind alle Aktivitäten eindeutig Objekten zugeordnet, d.h. sie sind entweder einer Klasse, einer Operation oder einem Anwendungsfall eindeutig untergeordnet. 3.3. Datenbank Schema Eine logische Beschreibung von Daten, die in einer Datenbank gespeichert sind. Das Schema definiert nicht nur die Namen der einzelnen Daten, ihre Größe und andere Charakteristiken, sondern identifiziert auch die Beziehung zwischen den Daten.9 Das DB-Schema wurde so geplant, daß man neue Users, Groups, HTML Elemente und Pages hinzufügen kann, ohne daß man etwas am Quellcode der Komponenten 8 9 Sams (1999) Turner (March 27, 2002) 3.3. Datenbank Schema 22 Abbildung 3.3.: Die unterschiedlichen Datenbank Tabellen ändern muß. Die Datenbank wurde in einer Postgre-Datenbank10 realisiert. Dazu gibt es eine Tabelle Users mit den Attributen (Uid, Username, Realname, Email, Password), eine Tabelle Groups und deren Attributen (Gid, Groupname, Groupdescription, Groupowner), eine Tabelle sub-groups mit den Attributen (Users, Groups), eine Tabelle groups-user mit den Attributen (Gid, Uid), eine Tabelle elementgroups mit den Attributen (Gid, Pid, Eid). eine Tabelle projects-users mit den Attributen (Users, Projects). Für jeden Typ existiert dann eine eigene Tabelle mit den angegebenen Attributen, in der die eigentlichen Daten gespeichert werden. Abbildung 3.3 stellt Alle NBT Admin Tool benötigen Tabellen dar. 10 http://www.postgresql.org/ 4. Angewandte Technologien 4.1. Enterprise JavaBeans Um Enterprise JavaBeans (EJB) effektiv verwenden zu können, muss man die EJBArchitektur verstehen (siehe Abbildung 4.1). Die Enterprise JavaBeans sind Hauptbestandteil der Java2 Enterprise Edition (J2EE). Sie stellen die Komponententechnologie für Applikationsserver mit J2EE dar. Im 1997 wurde die Technologie EJB angekündigt. Im August 2001 wurde die Public Final-Version EJB 2.0 verfügbar, der viele signifikante Erweiterungen und Verbessrungen gegenüber den Vorgängerversionen enthält. Sun Microsystems definiert EJB wie folgt: Die Enterprise JavaBeans Architektur ist eine Komponenten-Architektur für die Entwicklung und Inbetriebnahme Komponenten-basierte Geschäftsanwendung. Applikationen, die unter Verwendung der EJB-Architektur geschrieben werden, sind skalierbar, transaktionsorientiert, und Mehr-benutzer-geeignet. Diese Applikation können einmal geschrieben und dann auf jeder Serverplattform in Betrieb genommen werden, die die EJB-Spezifikation unterstützen. 4.1.1. Der EJB-Server Eigentlich müsste man von einem J2EE-Server sprechen. Die Strategie von Sun aber bezieht EJB wesentlich stärker in das gesamte Portofolio von Java-basierten Programmierschnittstellen und Produkten ein, als es bislang der Fall war 1 .Der EJB-Server stellt Systemdienste für Enterprise-Beans zur Verfügung und verwaltet die Container, in denen die Beans ausgeführt werden. Also der EJB-Server ist eine Laufzeitumgebung für verschiedene Container. 1 (Denninger, 2000) 23 4.1. Enterprise JavaBeans 24 Abbildung 4.1.: Die EJB-Architektur Vom EJB-Server wird die Basisfunktionalität bereitgestellt. Dazu gehört: • das Thread- und Prozessmanagement, damit mehrere Container parallel auf dem Server ihre Dienste anbieten können • die Unterstützung von Clustering und Lastverteilung • die Ausfallsicherheit • ein Namens- und Verzeichnisdienst, um Komponenten auffinden zu können • eine Zugriffsmöglichkeit auf und das Pooling von Betriebssystemressourcen Die Schnittstelle zwischen dem Server und den Containern ist dabei vom Hersteller abhängig. Da ein Standard für dieses Protokoll fehlt, ist nicht sichergestellt, dass der EJB-Container des Herstellers A im Server des Herstellers B betrieben werden kann 2 . Der JBOSS- und der WebLogic Application Server sind ein Beispiel für einen solchen Server. 2 (Denninger, 2000) 4.1. Enterprise JavaBeans 25 Abbildung 4.2.: Die Beziehung Zwischen EJB-Server und EJB-Container 4.1.2. Der EJB-Container Der EJB-Container ist eine Laufzeitumgebung für Enterprise-Beans-Komponenten. Enterprise- Beans wird als Oberbegriff für Session-Beans, Entity-Beans und Messagedriven-Beans (EJB 2.0) verwendet. Eine Enterprise-Bean ist von ihrem EJB-Container abhängig. Genauso ist der EJB-Container auf den Server als Laufzeitumgebung und Dienstanbieter angewiesen. Ein EJB-Server kann viele Container haben, die alle eine oder mehrere Arten von Enterprise-Beans enthalten (siehe Abbildung 4.2). Ein EJB-Container kümmert sich um den gesamten Lebenszyklus der EnterpriseBeans. Dazu gehört das Erzeugen, Lesen, Ändern und Löschen von Enterprise-Beans. Das schließt nicht aus, Die Architektur von Enterprise JavaBeans dass möglichst oft ein Pooling verwendet wird, um die Operationen für Erzeugen und Löschen einzusparen 3 . Obwohl Container einen wesentlichen Bestandteil der Enterprise-JavaBeansArchitektur ausmachen, müssen sich Enterprise-Bean-Entwickler und Anwendungs3 (Zimmermann, 2000) 4.1. Enterprise JavaBeans 26 Abbildung 4.3.: Client-Schnittstelle einer EJB integratoren nicht mit den Containern befassen. In einem verteilten EJB-System sind sie im Hintergrund tätig. Die Spezifikation (EJB 2.0) verpflichtet den EJBContainer dazu, einer Bean zur Laufzeit mindestens folgende Programmierschnittstellen zugänglich zu machen: Java 2 APIs, EJB 2.0, JNDI 1.2, JTA 1.0.1, JMS 1.0.2, JDBC 2.0 und JavaMail 1.1. Dem Anbieter eines Java-Applikationsserver steht es frei zusätzliche Dienste über Standard- Schnittstellen anzubieten. 4.1.3. Das EJBHome-Interface Es steuert den Lebenszyklus der Bean. Im (EJB)Home-Interface sind die Methoden definiert, die ein Client zum Erstellen, Suchen und Löschen von Instanzen einer Bean verwendet (siehe Abbildung 4.3). Als Bean-Provider muss man das Home-Interface definieren, aber jedoch nicht implementieren. Dies übernimmt der EJB-Container, indem er ein Home-Objekt erstellt, das eine Referenz auf die Bean zurück gibt. Es ist üblich, dem Home-Interface denselben Namen zuzuweisen wie der Bean-Klasse und das Suffix Home anzuhängen. Hat die Bean beispielsweise den Namen Users, sollte der Name des Home-Interfaces 4.1. Enterprise JavaBeans 27 für Users UsersHome lauten. Alle Home-Interfaces erweitern das Interface javax.ejb.EJBHome, das wiederum das java.rmi.Remote erweitert. Deshalb können alle Methoden des Home-Interfaces die Ausnahme RemoteException auslösen 4 . Nachfolgend ist die vollständige Definition vom EJBHome-Interface dargestellt: package login.interfaces; /** * Home interface for Users. * @xdoclet-generated at ${TODAY} * @copyright The XDoclet Team * @author XDoclet * @version ${version} */ public interface UsersHome extends javax.ejb.EJBHome { public static final String COMP_NAME="java:comp/env/ejb/Users"; public static final String JNDI_NAME="ejbs/Users"; public login.interfaces.Users create() throws javax.ejb.CreateException,java.rmi.RemoteException; } EJBHome verfügt über zwei remove()-Methoden zum Entfernen von EnterpriseBean-Instanzen. Die erste remove()-Methode identifiziert die Instanz anhand eines Handles und die zweite anhand eines Primärschlüssels getEJBMetaData( ) gibt eine javax.ejb.EJBMetaData-Instanz zurück, die das Home-, das Remote- Interface, die Primärschlüsselklasse sowie die Information zurückgibt, ob es sich bei der Bean um eine Session- oder Entity-Bean handelt 5 . Mit getHomeHandle() kann ein Client sich einen so genannten HomeHandle zum Home-Interface geben 4 5 (Zimmermann, 2000) (Monson-Haefel, 2000) 4.1. Enterprise JavaBeans 28 lassen und diesen auf nicht flüchtigem Speicher ablegen, da HomeHandle von java.io.Serializable abgeleitet ist. Der Handle einer Enterprise-Bean kann zum einem übergeben werden, oder bei Entity-Beans auch deren Primärschlüssel, der in einem regulären Java-Objekt repräsentiert sein muss 6 . 4.1.4. Das EJBObjekt-Interface Der eigentliche Anwendungsdienst oder Persistenzdienst wird von den Methoden gebildet, die es außer Erstellen, Löschen und Auffinden noch gibt. Solche Methoden sind im Remote-Interface zusammengefasst. Wenn der Client sich eine Referenz auf das Remote-Interface holt, dann bekommt er in der Wirklichkeit eine Remote-Referenz auf ein EJB-Objekt. Das EJB-Objekt implementiert das Remote-Interface, in dem es Geschäftsmethoden an die Bean-Klasse weiter delegiert, es stellt seine eigenen Implementationen der in EJBObject definierten Methoden zur Verfügung 7 . Nachfolgend ist der Quelltext für EJBObject-Interface dargestellt: package login.interfaces; public interface Users extends javax.ejb.EJBObject { public boolean addUser( String username,String realname, String email,String password ) throws java.lang.Exception, java.rmi.RemoteException; ....... } Die Methode getEJBHome() gibt eine Remote-Referenz auf das Home-Objekt der Bean zurück. Die Remote-Referenz wird als javax.ejb.EJBHome-Objekt zurückgegeben, das dann auf das Home- Interface der jeweiligen Bean eingeengt werden kann. Diese Methode ist nützlich, wenn ein EJBObjekt den Sichtbarkeitsbereich des HomeObjekts, von dem es erzeugt wurde, verlassen hat. Wenn es sich bei der Bean um 6 7 (Zimmermann, 2000) (Monson-Haefel, 2000) 4.1. Enterprise JavaBeans 29 eine Entity-Bean handelt, gibt die getPrimaryKey()-Methode den Primärschlüssel für die Bean zurück. Die remove()-Methode löscht die Enterprise-Bean. Die Wirkung dieser Methode ist gleich wie bei der bereits besprochene Methode EJBHome.remove(). Die getHandle() Methode gibt einen beständigen Handle auf die Bean-Instanz zurück. Weiterhin gibt es die Methode isIdentical() zum Vergleichen von Enterprise-Beans 8 . Alle Methoden deklarieren das auslösen einer Ausnahme (Exception) von Typ java.rmi.RemoteException, was erforderlich ist, wenn eine Methode über RMI aufgerufen werden kann. Wie genau die oben genannten Methoden in der Realität verwendet werden, wird in den nächsten Kapiteln verdeutlicht. 4.1.5. Enterprise JavaBeans Typen Abbildung 3.3 veranschaulicht die drei EJB-Arten aus Version 2.0. In der ersten Version der EJBSpezifikation (1.0) war nur die Unterstützung von Session-Beans vorgeschrieben. Session-Beans sind EJBs, die genau einem Client zugeordnet sind. Diese Beans können einen internen Zustand haben, der über mehrere Aufrufe in der selben Bean hinweg existiert. Dieser Zustand wird allerdings in der Regel nur im Hauptspeicher gehalten oder temporär auf die Platte ausgelagert, überlebt einen Absturz oder Neustart des Servers nicht. Hingegen sind Entity-Beans, die erst seit der Version 1.1 verpflichtend sind, persistent in einem Backend-System gespeichert, das Methoden zum Erzeugen, Lesen, Schreiben und Löschen der Objekte bereitstellt. In der Regel übernehmen diese Aufgabe Relationale Datenbanken. Bei BeanmanagedPersistence (BMP) muss der Entwickler selbst die Zugriffmethoden implementieren. Der Entwickler kann bei der Container-managed-Persistence (CMP) aber abstrakt vorgeben, welche Felder persistent sein sollen und die Mapping-Tools der EJB-Server generieren die tatsächlichen Zugriffcode. Bislang wurden alle Methodenaufrufe von EJBs synchron durchgeführt, das heißt der Client wartet bis ein Rückgabewert vom Server geliefert wurde. Bei Message-driven-Beans (EJB 2.0) ist dies anders. Nun ist auch das asynchrone auslösen von Aktionen durch den Java-Messaging Service JMS9 möglich. Das bedeutet, die Clients stellen eine Nachricht in eine Warteschlange, für die der EJB-Server als Subscriber registriert ist. Jede EJB arbeitet nun anstehende Requests ab und ruft für jede Nachricht die onMessage()-Methode auf. Clients warten nicht auf das Ende des Methodenaufrufs, können jedoch eine Rückmeldung 8 9 http://info.borland.com/devsupport/appserver/faq/ejbcpp/ejb_cpp.html http://java.sun.com/products/jms/ 4.1. Enterprise JavaBeans 30 Abbildung 4.4.: EJB-Typen in EJB-Version 2.0 erhalten, wenn eine Message-driven-Bean den Auftragbearbeitet hat. Die Messagedriven-Beans ähneln vom Verhalten her zustandslosen (stateless) Session-Beans. Sie müssen allerdings nicht explizit mit create()-Methoden erzeugt werden und besitzen weder ein Home- noch ein Komponenten-Interface. Im Gegensatz zu bisherigen JMSServern können auch mehrere EJBs gleichzeitig aktiv sein und eine große Anzahl Requests parallel bearbeiten. 4.1.6. Session Beans Session Beans werden dazu verwendet Anwendungslogik auf dem Server auszuführen. Sie sind dafür geeignet, da für jeden Client durch den EJB- Server und den Container ein eigenes Bean konstruiert wird und auch nur dieser eine Client Zugriff darauf hat. Eine Session-Bean ist ein Geschäftsobjekt, weil sie die Logik eines Geschäftsprozesses realisiert. Der Geschäftsprozess beschreibt alle Schritte, die notwendig sind, um eine bestimmte Aufgabe zu erfüllen. Logisch wird eine Session-Bean als serverseitige Erweiterung des Clientprogramms betrachtet 10 . Sie kann Informationen für den Client behalten. Normalerweise wird die Bean entfernt, wenn der Client die Sitzung beendet. Im Gegensatz zu Entity-Beans repräsentieren Session-Beans keine gemeinsamen genutzten Daten in der Datenbank 11 und somit sind sie nicht persistent. Session-Beans können die Interaktionen zwischen Entity-Beans verwalten und beschreiben, wie diese zu10 11 Denninger (2000) (Monson-Haefel, 2000) 4.1. Enterprise JavaBeans 31 sammenarbeiten, um eine bestimmte Aufgabe zu erfüllen. Das Bean ist nicht persistent, d.h. sobald die Kommunikation zwischen Client und Server beendet ist wird auch die Instanz durch den Container zerstört. Dies hat den Nachteil, daß bei einer eventuellen Verbindungsunterbrechung, wie z.B. durch einen Server- oder Client- Absturz sämtliche Informationen verloren gehen. Daten, wie z.B. eine Transaktion die von dem Session Bean an eine Datenbank oder an ein Entity Bean delegiert werden, bleiben natürlich bestehen (es wird aber keine Rollback Funktionalität angeboten), es geht lediglich die Instanz des Session Beans verloren. Man kann die Session Beans weiter in statefull (zustandsbehaftete) und stateless (zustandslose) Beans unterteilen. Zustandslose Beans bekommen eine Anfrage und geben eine Antwort, d.h. sie können sich nicht den Status einer vorausgegangenen Anfrage merken und dementsprechend reagieren. Sie werden also bei einfachen Methodenaufrufen verwendet, wie z.B. bei Zahlungsvorgängen, bei denen eine Kreditkartennummer und der Betrag übermittelt wird, das Bean die Zahlungsfähigkeit überprüft und dann eine positive oder negative Bestätigung zurückschickt. Ein weiterer Anwendungsfall wäre eine komplexere Berechnung. Dem Bean werden mehrere Eingangsdaten übergeben, die sie dann auswertet und ein Ergebnis zurückschickt. Bei zustandbehafteten Session Beans handelt es sich um kompliziertere Konstrukte, bei denen der Zustand der Konversation mit den Clients gespeichert wird. Session Beans haben zusammenfassend folgende Eigenschaften: • Repräsentiert nicht Daten in einer Datenbank. • Kann gemeinsamgenutzte Daten in einer Datenbank verändern. • Ein SessionBean ist immer nur einem Client zugeordnet (d.h. ein gleichzeitiger Zugriff von mehreren Clients ist nicht möglich) • Ist relativ kurzlebig. • Unterstützt Transaktionen, ist jedoch kein Erfordernis. • Verschwindet falls der EJB Server abstürzt (d.h. die Session Bean kann nicht wiederhergestellt werden). 4.1. Enterprise JavaBeans 32 4.1.7. Der Zugriff auf eine Session-Bean Der Client bekommt mit Hilfe des JNDI eine Referenz auf das Home-Interface. Beim Aufruf der Methode lookup von Context wird ein Objekt vom Typ java.lang.Object zurückgegeben. Ihr Code muss das zurückgegebene Objekt in den erwarteten Typ umwandeln (siehe Abbildung 4.5). Für die richtige Konvertierung der Datentyp gibt es die Methode narrow() von der Klasse javax.rmi.PortableRemoteObject. Nachdem das Home-Interface bekannt ist, kann der Client eine neue Bean erzeugen (create) und bekommt als Rückgabewert das Remote-Interface der Bean. Wenn es sich um zustandslose Session-Bean handelt, verfügt das Home-Interface nur über eine create()-Methode, so dass diese vom Client aufgerufen werden muss, um die RemoteSchnittstelle zu beziehen. Die vorgegebene create()-Methode einer zustandslosen Sesssion-Bean hat keine Parameter 12 . Mit dem Remote-Interface kann der Client mit der Bean fast so arbeiten, als wäre sie ein lokales Objekt. Eine Java-Client-Applikation verwendet JNDI, um eine Verbindung zu einem EJB-Server aufzubauen und ein bestimmtes Home-Objekt zu finden. Nach dem Gebrauch der Bean muss der Client die Bean wieder löschen. Der folgende Codefragment zeigt, wie das JNDI-API verwendet werden könnte, um eine Referenz auf das Home-Objekt zu finden und zu bekommen: //mit dem Naming-Service einen JNDI-Kontext abrufen public void init() throws ServletException { try { Context context = new InitialContext(); value = (String) context.lookup("java:/comp/env/Title"); Object ref = context.lookup("java:/comp/env/ejbs/Users"); usershome = (UsersHome) PortableRemoteObject.narrow(ref, UsersHome.class); } catch (Exception e) { throw new ServletException("Lookup of java:/comp/env/ failed"); } } 12 http://info.borland.com/devsupport/appserver/faq/ejbcpp/ejb_cpp.html 4.1. Enterprise JavaBeans 33 Abbildung 4.5.: Zugriff auf eine Session-Bean 4.1.8. Entity Beans Die Unterstützung von Entity-Beans wurde erst in EJB-1.1 als obligatorisch für Server- und Container-Provider definiert. Im Gegensatz zu Session-Beans waren die Entity-Beans in der EJB.1.0-Spezifikation noch optional 13 . Entity-Beans modellieren Geschäftskonzepte, die als Substantive ausgedrückt werden können. Geschäftskonzepte repräsentieren Daten, die kontrolliert und möglicherweise auch manipuliert werden müssen. Entity-Beans repräsentieren also Objekte der realen Welt. Sie beschreiben sowohl den Zustand als auch das Verhalten von diesen Objekten und ermöglichen es Entwicklern, die Daten und die Geschäftsregeln, die zu bestimmten Konzepten gehören, einzukapseln. Entity-Beans repräsentieren Daten in der Datenbank. Es hat viele Vorteile, EntityBeans zu verwenden, anstatt direkt auf die Datenbank zuzugreifen. Die Verwendung von Entity-Beans, um Daten in Objekt-Form zu bringen, stellt den Entwicklern einen einfachen Mechanismus für den Zugriff auf die Daten und die Veränderung derselben zu Verfügung. Es ist beispielsweise viel einfacher, den Titel eines Buch durch Aufruf von buch.setTitel() zu ändern, als einen SQL-Befehl an die Datenbank zu schicken. 13 (Denninger, 2000) 4.2. Java Servlets 34 Außerdem erhöht man so die Chancen 14 , wiederverwendbare Software zu schreiben. Eine einmal definierte Buch-Entity-Bean kann in vielen Bibliotheken auf konsistente Weise wiederverwendet werden. Weil Entity-Beans es mehreren Clients ermöglichen, parallel auf die gleichen Daten zuzugreifen, werden sie als zentrale Ressource betrachtet. jeder Client einer Entity-Bean arbeitet fast so, als würde er exklusiv auf sie zugreifen. Die Datenbankprobleme, die durch den parallelen Zugriff entstehen, vermeidet der EJB-Container durch die teilweise Serialisierung der Zugriffe und durch den Einsatz von Transaktionen (siehe Kapitel Transaktionen) 15 . Der paralleler Zugriff mehrere Clients auf eine Entity-Bean ist ein wesentlicher Unterschied zu SessionBeans, die jeweils für genau einen Client existieren. Folgende Eigenschaften kennzeichnen Entity Beans: • Repräsentiert Daten in einer Datenbank. • Erlaubt gleichzeitigen (shared ) Datenzugriff von mehreren Clients auf ein Entity Bean • Unterliegt immer der Transaktionsverwaltung. • Kann langlebig sein (d. h. lebt solange die Daten in der Datenbank bleiben) • Uberlebt EJB Serverabstürze (d. h. das Entity Bean kann nach einem Absturz wiederhergestellt werden) 4.2. Java Servlets Java Servlets bieten eine sehr mächtige Möglichkeit, Web-Applikationen zu bauen, die leicht auf bereits vorhandene Systeme (z.B.: Datenbanken) zugreifen können. Was ist nun ein Servlet? Die Java Servlet Specication Version 2.416 definiert Servlets wie folgt: 14 (Monson-Haefel, 2000) (Denninger, 2000) 16 Java Servlet Specification Version 2.4: http://java.sun.com/products/servlet/ 15 4.2. Java Servlets 35 Ein Servlet ist eine Web-Komponente (im Engl.: web component), die über einen ” Kontainer (Servlet Engine17 ) gemanagt, dynamische Inhalte generiert. Servlets sind kleine“ plattform-unabhängige Java Klassen, die nachdem sie in neutralen Byteco” de kompiliert wurden, von einem Web-Server dynamisch geladen und zum Laufen gebracht werden können. Servlets interagieren mit Web-Clients über ein RequestResponse-Schema (basierend auf das Hypertext Transfer Protocol), das vom Servlet Kontainer implementiert wird“ Vereinfachend kann ein Servlet auch mit einem Applet 18 verglichen werden, das auf der Server-Seite läuft und daher keine grafische Oberfläche besitzt. Folgende Vorteile bietet der Einsatz von Java Servlets: 1. Plattform-Unabhängigkeit 2. Zugang zu allen Java API’s 3. Schneller als CGI Scripts19 4. Session-Management Zur Ausführung des Servlets ist eine sogenannte Servlet Engine notwendig, die Anfragen an bestimmte Servlets weiterleitet. Die Servlet Engine ist entweder direkt im Web-Server integriert (JavaSoft Java Web Server) oder eine eigene Server-Applikation wie JBOSS20 oder Tomact Abbildung 2.8 zeigt, welche Methoden von der Servlet Engine während des Servlet’s Life Cycle aufgerufen werden. Die init() Methode dient zur Initialisierung des Servlets, das geschieht entweder bei der ersten Verwendung des Servlets oder schon vorher. Die service() Methode kann mit der main() Methode einer Java Applikation verglichen werden. Für das abschließende Aufräumen der besetzten Resourcen wird die destroy() Methode verwendet. Bei der Programmierung eines Servlets müssen diese Methoden entsprechend angepaßt werden, weiters muss das Servlet direkt oder indirekt das javax.servlet.Servlet Interface implementieren. Der einfachere Weg ist das überschreiben von Methoden einer vorhandenen Servlet-Klasse, wie zum Beispiel der javax.servlet.http.HttpServlet 17 Eine Servlet Engine ist eine Server-Applikation, die Servlets ausführt. Java Servlet Programming 19 Gewöhnliche CGI’s spalten beim Request jeweils einen neuen Prozess ab, Servlets hingegen sind nur einfache Threads. 20 http://www.jboss.org 18 4.2. Java Servlets 36 Abbildung 4.6.: Die verschiedenen Stufen bei der Ausführung eines Servlets Klasse. Listing 3.1 zeigt ein vom HttpServlet abgeleites Servlet, das nur die doGet() Methode überschreibt, die dem HTTP Get entspricht. Listing 3.1 Hello, ein vom HttpServlet abgeleitetes Java Servlet: 1 import javax.servlet.http.*; 2 import javax.servlet.*; 3 import java.io.*; 4 public class Hello extends HttpServlet { 5 public void doGet (HttpServletRequest request, 6 HttpServletResponse response) 7 throws ServletException, IOException { 8 // get an PrintWriter from the response object 9 PrintWriter out = response.getWriter(); 10 // prepare the response’s content type 11 response.setContentType("text/html"); 12 // get the IP address of the client 4.2. Java Servlets 13 14 15 16 17 37 String remoteAddress = request.getRemoteAddr(); // print to the output stream! out.println("Hi Man from <b>" + remoteAddress + "</b>"); } } Vorausgesetzt der Server unterstützt Java Servlets, kann nun das Servlet via WebBrowser21 aufgerufen werden, dass im Gegenzug ein simples in unserem Fall sogar unvollständiges HTML-Dokument an den Client sendet. Akzeptiert das Servlet den Aufruf, werden der doGet Methode zwei Objekte übergeben, eines der Klasse HttpServletRequest und eines der Klasse HttpServletResponse. Das erste Objekt (request) kapselt die ,eingehende‘ , das andere Objekt (response) die ,ausgehende‘ Kommunikation und vereinfacht das Handling mit dem Hypertext Transfer Protokoll. Zum Beispiel wird in Listing 2.7, Zeile 11 der MIME22 -Type der HTTP-Antwort festgelegt. Ein etwas sinnvollerer Anwendungsbereich von Servlets ist deren Einsatz zur Kommunikation mit Datenbanken. Die benötigte Schnittstelle zu Datenbanken wird durch das JDBC23 API realisiert, dass für alle bekannten Datenbanken Treiber zur Verfügung stellt. Ein Verbindungsaufbau mit einer Postgresql Datenbank zeigt das folgende Java Code-Stück aus der init() Methode eines Datenbank Servlets: ""); Class.forName("org.postgresql.Driver").newInstance(); dbc = DriverManager.getConnection("jdbc:postgresql://localhost:5432/myDB?" +"user=name&password=paswd"); Das erste Statement erzeugt ein Connection-Objekt, das die Verbindung zur Datenbank kapselt. Dazu wird ein passender JDBC-Driver für die Datenbank benötigt, in unserem Fall heißt dieser örg.gjt.mm.mysql.DriverḊas zweite Statement stellt nun eine konkrete Verbindung zur Datenbank her und diese bleibt während der Lebenszeit des Servlets aufrecht. Das Auslesen aller Datensätze einer Tabelle Produkte kann zum Beispiel so geschehen: 21 Aufruf über URL, zum Beispiel http://localhost/servlets/Hello. Multipurpose Internet Mail Extensions, siehe [Freed Borenstein, 1996]. 23 Java DataBase Connectivity. Ein Java API zum Ausführen von SQL-Statements, stellt eine Reihe von Klassen und Interfaces zur Manipulation von Datenbanken zur Verfügung. 22 4.3. Java Server Pages 38 1 Statement stmt = dbc.createStatement(); 2 stmt.execute("select * from Produkte"); 3 ResultSet rs = stmt.getResultSet(); Mit der Möglichkeit, SQL-Statements zu exekutieren (siehe Zeile 2), steht nun die gesamte Funktionalität der Datenbank zur Verfügung. Der Einsatz von RMI24 würde den Code des Servlets noch zusätzlich vereinfachen und die Komplexität des DatenbankHandlings auf eine zusätzliche Schicht verlagern. Die Servlets-Technologie wird von einer immer größer werdenden Anzahl von Servern unterstützt und bietet eine gute Alternative zu CGI basierenden zeitkritischen Applikationen.25 Zusätzliches Session-Management erlaubt nun endgültig die Interaktivität zu erhöhen. Mit Java hat man zudem den Vorteil, auf einer großen Menge von Plattformen ohne Probleme lauffähige Applikationen erzeugen zu können und von der Mächtigkeit der vorhandenen APIs zu profitieren. Zur Zeit ist die Java Servlet API Specification Version 2.4 unter http://java.sun.com/products/servlet verfügbar. 4.3. Java Server Pages JSP ist eine Technologie, die von der Firma SUN entwickelt wurde und die auf der Java- Servlet-API basiert.Java Server Pages sind den Active Server Pages (ASP) von Microsoft sehr ähnlich. Mit beiden Techniken kann man in eine normale HTMLSeite spezielle Kommandos integrieren, und damit zum Beispiel dynamische Inhalte in die Seite integrieren26 . Ziel von Java Server Pages ist es, eine Trennung von Design und Applikationslogik zu erreichen. Da JSP auf Java basiert, ist außerdem eine hohe Portabilität gewährleistet. Ein großer Vorteil von JSP ist, daß man innerhalb einer JSP-Seite auf beliebige Java APIs zugreifen kann.Während Active Server Pages nur mir den Microsoft Webservern verfügbar sind, kann man die Java Server Pages mittlerweile mit allen gängigen Webservern benutzen27 . Ein Webserver kann aber normalerweise nicht selbst die JSP-Seiten korrekt behandeln. Dazu benötigt man eine JSP-Engine. Diese wird dann gewöhnlich über 24 Remote Method Invocation, siehe http://java.sun.com/docs/books/tutorial/rmi Kurniawann (2002) 26 Pure JSP – Java Server Pages: A Code-Intensive Premium Reference 27 Perry (2004) 25 4.3. Java Server Pages 39 Abbildung 4.7.: Die verschiedenen Stufen bei der Ausführung eines Servlets ein zusätzliches Modul mit dem Webserver verbunden. Spezielle Anfragen an den Webserver (meist alle Anfragen auf Dateien mit der Endung .JSP) werden dann an die JSP-Engine weitergegeben. Die JSPEngine ist normalerweise als Servlet implementiert, welches in einer Servlet-Engine läuft. Die JSP-Engine sorgt dafür, daß aus einer JSP-Seite ein Servlet generiert und dann kompiliert wird. Ist dann eine aktuelle Klasse vorhanden, so wird die Anfrage an das entsprechende Servlet weitergegeben. Treten beim generieren oder kompilieren des Servlets Fehler auf, so werden diese als HTML-Seite zurückgegeben. 4.3.1. Elemente einer JSP-Seite Die Basis einer JSP-Seite ist normalerweise eine beliebige HTML-Seite. In diese Templatedaten werden JSP-spezifische Elemente eingefügt.Es gibt drei unterschiedliche Typen von Elementen:28 Direktiven, Aktionen und Scripting Elemente. Ein zusätzliches Element ist der ’versteckte’ Kommentar. Da es eigentlich keine Funktion für die Seite hat, fällt dieses Element allerdings etwas aus der Reihe. Es wird dazu benutzt um Kommentar in einer JSP-Seite einzufügen, der nicht in die Ausgabe geschrieben wird. Der gleiche Effekt ist allerdings auch mit Scripting Elementen 28 KOLB (2000) 4.3. Java Server Pages 40 möglich. Dieser Text wird in den Ausgabestrom geschrieben. <!-- Auch dieser HTML-Kommentar wird ausgegeben ! --> <%-- Dieser Kommentar wird nicht ausgegeben ! --%> <% /* Dieser Kommentar ist in der Programmiersprache der Seite und wird auch nicht ausgegeben ! */ %> Grundsätzlich können alle speziellen Elemente einer JSP-Seite auch XML-konform geschrieben werden. Aktionen werden sowieso schon in einer an XML angelehnten Form geschrieben. Für Direktiven und Scripting Elemente gibt es entsprechende Alternativen. Im folgenden wird noch einmal auf die Funktionen und Schreibweise der drei Hauptelemente eingegangen.29 4.3.2. Direktiven Mit einer Direktiven lassen sich bestimmte Informationen an die JSP-Engine übergeben. Es gibt die drei Direktiven include, taglib und page. Mit der include-Direktiven lassen sich statische Seiten einbinden. Die taglib-Direktive definiert die Zuordnung von Präfixen zu Benutzertags. Auf diese Direktive und die Benutzertags wird später noch genauer eingegangen. Die page-Direktive wird benutzt, um Eigenschaften der gesamten JSP-Seite zu setzten. So kann man zum Beispiel den Typ des Inhaltes definieren oder die Puffergröße setzten. Theoretisch kann man damit auch die Programmiersprache der Seite bestimmen. Diese muß dann natürlich auch von der JSP-Engine unterstützt werden. <%@ page language="java" import="package.*" buffer="none" %> <%@ taglib uri="tagLibURI" prefix="myTagLib" %> <%@ include file="header.html" %> 4.3.3. Aktionen Aktionen dienen zur Beeinflussung des Ausgabestromes. Es gibt eine Reihe von Standardaktionen, die in einer JSP Seite benutzt werden können. Dazu zählen zum Bei29 Goodwill (2000) 4.3. Java Server Pages Name request response pageContext session application out config page exception Klasse javax.servlet.ServletRequest javax.servlet.ServletResponse javax.servlet.jsp.PageContext javax.servlet.http.HttpSession javax.servlet.ServletContext javax.servlet.jsp.JspWriter javax.servlet.ServletConfig java.lang.Object java.lang.Throwable 41 Zweck Repräsentation der Anfrage der Antwort auf die Anfrage Der Kontext der Seite Das Session Objekt Der Kontext der Anwendung Ausgabestrom für die Antwort Enthält Einstellungen der Seite Instanz der Seitenimplementierung Die aufgetretene Ausnahme, Tabelle 4.1.: Implizite Objekte einer JSP-Seite spiel das Einbinden von dynamischen Seiten oder der Zugriff auf Beans30 . Auch benutzerdefinierte Tags zählen zu den Aktionen. Einbinden eines Beans und setzen von Eigenschaften. <jsp:useBean id="myBean" class="package.class"/> <jsp:setProperty name="myBean" property="property1" value="propertyValue"/> Ausgabe einer Eigenschaft... <jsp:getProperty name="myBean" property="property2"> 4.3.4. Scripting Elemente Scripting Elemente sind Codefragmente, die direkt in die JSP-Seite geschrieben werden. Es gibt drei verschiedene Typen von Scripting Elementen: Deklarationen, Scriptlets und Ausdrücke. Alle müssen in speziellen Zeichen verschachtelt sein. <%! Deklaration %> <% Scriptlet %> <%= Ausdruck %> Mit Deklarationen kann man für eine Seite Variablen und Methoden definieren. Diese können dann später in Scriptlets oder Ausdrücken verwendet werden. Ein Scriptlet 30 Beans sind wiederverwertbare Java-Komponenten. Sie bestehen aus einer Java-Klasse, die einen parameterlosen Konstruktor enthält und meist Daten durch get- und set-Methoden zugänglich macht. 4.3. Java Server Pages 42 besteht aus beliebigem Code. Dieser wird ausgeführt, sobald die Ausgabe der Seite die Position des Tags erreicht hat. Ausdrücke werden zur Laufzeit ausgewertet und das Ergebnis wird direkt in den Ausgabestrom geschrieben. Sie können auch als dynamische Parameter für Attribute von Aktionen benutzt werden, sofern dies für das Attribut erlaubt ist. Beispiel: <html> <body> <%! String color="black"; %> Wir produzieren eine Zufallsfarbe !<br> <% color="#"; while (color.length() < 7) { int i = (int) (Math.random() * 15); if (i > 9) color += (char) (i + 87); else color += (char) (i + 48); %> <font color="<%= color %>">Der Text ist farbig !</font><br> </body> </html> 4.3.5. Implizite Objekte einer JSP Seite Es gibt eine Reihe von Objektinstanzen, die innerhalb einer JSP-Seite verfügbar sind. Man kann direkt auf sie zugreifen, da sie von der JSP-Engine erzeugt werden. In Tabelle 4.1 sind alle Impliziten Objekte einer JSP-Seite aufgelistet. Einige dieser Objekte, wie zum Beispiel exception sind allerdings nur in speziellen Fällen verfügbar. 4.3.6. Gültigkeitsbereiche von Objekten Alle impliziten Objekte einer JSP-Seite sind Gültigkeitsbereichen zugeordnet. Das bedeutet eine Instanz ist nur in einem bestimmten Bereich verfügbar. Auch eigene Objekte können solchen Bereichen zugeordnet, und so zum Beispiel auf mehreren Seiten genutzt werden. Eine Übersicht aller Gültigkeitsbereiche ist in Tabelle 4.2 verfügbar. 4.3. Java Server Pages page request session application 43 Objekte die dieses Gültigkeitsbereiches sind nur von der Seite aus erreichbar, auf der sie erzeugt wurden. Sie sind im pageContext Objekt abgelegt. (Implizite Objekte: page, response, pageContext, out, config) Objekte die dieses Gültigkeitsbereiches sind im request Objekt abgelegt. Sie sind auf allen Seiten verfügbar, die bei der Bearbeitung der selben Anfrage beteiligt sind. Das kann zum Beispiel bei einem forward der Fall sein. (Implizite Objekte: request) Objekte, dieses Gültigkeitsbereiches sind nur verfügbar, wenn die Seite eine Session benötigt. Nur in diesem Fall ist das session Objekt vorhanden, in dem die Objekte abgelegt werden. (Implizite Objekte: session) Alle Objekte, die im ServletContext (application Objekt) verfügbar sind, besitzen diesen Gültigkeitsbereich. Sie sind von allen Seiten aus erreichbar.(Implizite Objekte: application) Tabelle 4.2.: Gültigkeitsbereiche von Objekten einer JSP-Seite 4.3.7. Beans Man kann in einer JSP-Seite auch Java-Beans einbinden. Dies geschieht über eine spezielle Aktion: jsp:useBean <jsp:useBean id="instanceName" class="package.class" scope="scope"/> Mit anderen Aktionen kann man nun entweder Eigenschaften des Beans setzten oder auslesen. Setzten eines konstanten Wertes, oder eines Ausdrucks: <jsp:setProperty name="instanceName" property="property" value="value"/> Setzen von Werten aus den Request-Parametern: <jsp:setProperty name="instanceName" property="property" param="param"/> Ausgabe einer Eigenschaft: <jsp:getProperty name="instanceName" property="property" /> Beans sind eine gute Möglichkeit, um dynamische Inhalte in eine JSP-Seite einzubinden. Dabei bleibt der Code vollständig getrennt vom Layout. In vielen Fällen können Beans deshalb auch komplett wiederverwertet werden. Beans sind meist recht einfach zu erstellen, da keine großen Anforderungen an sie gestellt werden. Der Nachteil von 4.3. Java Server Pages 44 Beans ist, daß man nicht ohne weiteres Zugriff auf die impliziten Objekte der JSPSeite hat. Alle Einstellungen und Objekte, die dem Bean verfügbar gemacht werden sollen, müssen über die Aktion setProperty übergeben werden. 4.3.8. Tag-Extensions Da die Bibliothek, die während der Diplomarbeit entwickelt wurde, auf Tag-Extensions basiert, wird hier etwas ausführlicher darauf eingegangen. Mit JSP-Tag-Extensions ist es möglich eigene Tags zu definieren. Diese können dann mit spezieller Logik versehen werden. Es besteht aber auch die Möglichkeit, Tags aus fertigen Bibliotheken zu benutzen. Zu jeder Bibliothek gehört eine Beschreibungsdatei, der Tag-LibraryDescriptor (TLD). Dort stehen Informationen zur Bibliothek und deren Tags. Unter anderem geschieht dort auch die Zuordnung von Tagname zu Tagklasse. Aber nicht nur ein Tag einer Bibliothek, sondern auch jedes andere, eigene Tag muß in einem TLD deklariert werden, um es in einer JSP-Seite nutzen zu können. Zusätzlich muß man noch mit einer Direktive den entsprechenden Tag-Library-Descriptor bekannt machen:Einbinden einer Tag-Library <%@ taglib uri="tagLibURI" prefix="myTagLib" %> In einer JSP-Seite kann man dann auf alle Tags in einer solchen Bibliothek mit dem gleichen, in der Direktive angegebenen Präfix zugreifen. Die Tags können theoretisch beliebig verschachtelt werden. Auch ist es möglich innerhalb der Tags beliebigen HTML-Code, Scripting-Elemente oder Aktionen zu verwenden: <myTagLib:myTag1 attribute1="value"> Hallo Welt !<br><jsp: <myTagLib:myTag2 attribute1="value1"/> </myTagLib:myTag1> Tags in einer JSP-Seite Ob solche Verschachtelungen allerdings sinnvoll, bzw. überhaupt möglich sind, hängt von der Logik der Tags ab. Es kann zum Beispiel sein, daß ein bestimmtes Tag immer im Body31 eines anderen Tags stehen muß. Genauso kann es aber auch sein, daß ein 31 Der Body eines Tags ist das, was zwischen Start-Tag und End-Tag steht. 4.3. Java Server Pages 45 Tag den Inhalt des Bodys stets ignoriert. Wenn eine JSP-Seite von der JSP-Engine kompiliert wird, dann prüft diese, ob die Tags syntaktisch korrekt verwendet wurden. Die Informationen, die dazu nötig sind, bekommt sie aus dem Tag-Library-Descriptor. Dieser ist in einem XML-Dialekt geschrieben. Die Datei endet üblicherweise mit ’.tld’. Die Struktur ist wie folgt: <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"> <taglib> <tlibversion>1.0</tlibversion> <jspversion>1.1</jspversion> <shortname>aShortName</shortname> <uri>theTaglibURI</uri> <info>Infos zu der Bibliothek</info> <tag> <name>myTag</name> <tagclass>package.class</tagclass> <info>Infos zu dem Tag</info> <attribute> <name>attribute1</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>attribute2</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib> 4.3. Java Server Pages 46 Die Klasse, die zu einem Tag gehört ist der sogenannte Tag-Handler. Jeder TagHandler muß das Interface Tag32 implementieren. Tags die Attribute definieren, müssen zusätzlich für jedes Attribut eine get- und eine set- Methode implementieren. Es gibt zwei Verschiedene Arten von Tag-Handlern. Einmal Tag-Handler, die auf den Inhalt des Bodys zugreifen müssen, und dann solche, die es nicht müssen. Das Interface BodyTag erweitert das Tag Interface, um den Zugriff auf den Body zu ermöglichen. Mit den Tag-Extensions hat man gegenüber Beans ein paar Vorteile. Diverse Methoden der Tag-Handler Interfaces bieten die Möglichkeit, durch Rückgabewerte die Ausgabe zu beeinflussen. Dadurch kann man zum Beispiel den Body des Tags mehrfach, oder auch gar nicht ausgeben. Auch ist es möglich, den gesamten Inhalt zu bearbeiten, bevor er in den Ausgabestrom geschrieben wird. Damit lassen sich mit Tags Schleifenkonstrukte (for, while,...) oder bedingte Ausgaben (if, switch,...) erzeugen. Die Tag-Handler werden von der JSP-Engine erzeugt. Diese ruft dann die Methoden des Tag- Handler Interfaces und die set-Methoden der belegten Attribute nach einem bestimmten Schema auf. Der Ablauf bei einem normalen Tag ist wie folgt: ATag t = new ATag(); t.setPageContext(...); t.setParent(...); t.setAttribute1(value1); t.setAttribute2(value2); t.doStartTag(); t.doEndTag(); t.release(); Für Tags, die BodyTag implementieren ist der Ablauf folgendermaßen: // bis hier wie bei einem normalen Tag t.doStartTag(); 32 Die Klassen, auf die sich im folgenden bezogen wird gehören zum Package javax.servlet.jsp.tagext der Servlet API. http://java.sun.com/products/servlet/2.2/javadoc/index.html 4.3. Java Server Pages 47 out = pageContext.pushBody(); t.setBodyContent(out); t.doInitBody(); t.doAfterBody(); // solange wie doAfterBody den Status EVAL_BODY_TAG zurückgibt // wird die Body Auswertung erneut ausgeführt. ... t.doAfterBody(); t.doEndTag(); t.pageContext.popBody(); t.release(); Die Klassen TagSupport und BodyTagSupport sind zwei Basisimplementierungen für Tag-Handler. Diese erleichtern die Programmierung, da man nur noch die Methoden implementieren muß, in denen spezielle Logik nötig ist. In allen Tags sind auch die impliziten Objekte der JSP-Seite zugänglich. Allerdings ist nur das pageContext Objekt direkt verfügbar. Alle anderen lassen sich über Methoden dieses Objekts erreichen. Tags, die in einem anderen verschachtelt sind, haben Zugriff auf die entsprechende Tag- Handler-Instanz des übergeordneten Tags - dem Parent. 4.3.9. Zusammenfassung Da als Basis für JSP die Programmiersprache Java verwendet wird, ist eine hohe Portabilität gewährleistet. Für die gängigsten Betriebssysteme bekommt man schon eine Java Runtime Engine (JRE). Da es auch verschiedene JSP-Engines gibt, die komplett unter Java geschrieben sind kann somit auch JSP mit diesen Systemen verwendet werden.Java ist außerdem einfach zu erlernen und bietet eine umfangreiche Klassenbibliothek, auf die uneingeschränkt zugegriffen werden kann. Java Anwendungen mit grafischer Oberfläche sind oft recht langsam. Dies liegt häufig an den AWT33 Klassen. Da die JSP-Engine komplett serverseitig läuft, benötigt man diese Klassen im Normalfall nicht. Somit sind JSP-Seiten meist auch ausreichend schnell.Nutzt man Beans und Taglibraries, so bekommt man eine Trennung von Design und Logik. 33 Abstract Window Toolkit. Eine API, die für das erstellen von Oberflächen gedacht ist. In den Java Version bis 1.2 recht langsam. 4.4. Code Generation mit XDoclet 48 So kann man gut änderbare und skalierbare Webanwendungen erstellen. Außerdem wird der Code besser wiederverwendbar. Ein Nachteil von JSP ist allerdings, daß man viele Daten in der Session ablegen muß und die Logik stellenweise ungünstig verteilt ist. Als Beispiel könnte man eine JSP-Seite nehmen, die Daten lädt und in einem Formular bereitstellt. Nach dem Abschicken der Formulardaten müssen diese auf der Folgeseite ausgewertet werden. Jegliche Zustände müssen dabei in der Session abgelegt werden, damit sie auf beiden Seiten verfügbar sind. Die Logik ist auf mindestens Zwei Seiten verteilt. Sinnvoller wäre, daß standardmäßig die Logikkomponente der ersten Seite auch die Formulardaten auswertet. Die Komponente hätte alle Zustände zur Verfügung und könnte dann, je nach Daten entscheiden, welche Seite dem Benutzer angezeigt wird. Alles in allem ist JSP aber eine gute Möglichkeit dynamische Webseiten zu erzeugen. Das zeigt sich auch daran, daß diverse Application-Server und auch Content-Management- Systeme auf JSP aufsetzten, oder es zumindest unterstützen. 4.4. Code Generation mit XDoclet Wenn man einmal die ganzen Klassen und Deployment Descriptoren für eine EJB von Hand geschrieben hat, wird man ermüdet feststellen, dass es hier eigentlich unheimlich viele redundante Vorgänge gibt. Da wäre doch ein Tool nützlich, das diesen Code nach den gewünschten Vorgaben generiert. Solche Tools gibt es natürlich: Viele kosten Geld und sind Bestandteil von Entwicklungsumgebungen basieren auf Eigenentwicklungen und Datenbanken. Es gibt aber auch ein kostenloses XML-basierendes Tool: XDoclet. XDoclet braucht normalerweise nur eine Datei, nämlich die EJB Implementation, und generiert daraus alle weiteren notewendigen Dateien, um eine EJB zu deployen, also: 1. EJB und produktspezifische (JBoss, WebLogic, WebSphere, Orion) deployment descriptoren 2. Home und Remote Interfaces 3. Primary Key Class für die Entity Bean 4. Bulk Data Object (Value Object) 4.4. Code Generation mit XDoclet 49 5. EJB Wrapper Klassen Wo nötig, kann man auch die Erstellung einiger dieser Datein unterbinden, man kann den generierten Code verändern lassen und zusätzlichen Code/Descriptions als vordefinierte ’Merge Points’ hinzufügen. XDoclet ist eine Weiterentwicklung des Original-EJBDoclet-Tools von Rickard Oberg. Die Idee dahinter ist simpel: Statt mit allen EJB-Dateien zu jonglieren, wird die ganze Komponente aus der Bean-Klasse erzeugt. Wie das geht? In Java gibt es doch die Javadoc-Tags. Wir setzten nun einfach spezielle @ -Tags in die normalem JavadocKommentare hinein, und lassen das Doclet-Tool diese Tags auslesen. Das Tool generiert die entsprechenden XML-Descriptoren und Interfaces für diese EJB. XDoclet führt die Idee von EJBDoclet noch weiter, indem das Framework noch viel mehr als nur EJB unterstützt: Man kann auch Web Services, Web Application Descriptoren und sogar Eigenentwicklungen damit schreiben lassen. 4.4.1. Was ist XDoclet? XDoclet ist eine OpenSource Engine für die Generierung von Code. Sie liest Java Quelldateien ein und wertet die darin enthaltenen Metainformationen aus. Die XDoclet Informationen werden in Form von JavaDoc Tags direkt im Quellcode angegeben. Mit Hilfe von Code-Schablonen und den ermittelten Metainformationen erzeugt die Engine die gewünschten Ausgabedateien. Mögliche Zielformate sind unter anderem XML (z. B. Deployment Deskriptoren), Java Quelldateien oder Implementierungen der J2EE Patterns.34 Der Funktionsumfang von XDoclet kann über Module erweitert werden. Module enthalten die entsprechenden Generierungs-Schablonen für eine bestimmte Technologie. Folgende Module sind bereits in XDoclet integriert: • EJB • Web 34 Sullins und Whipple (May 2003) 4.4. Code Generation mit XDoclet Abbildung 4.8.: XDoclet Ablauf 50 4.4. Code Generation mit XDoclet 51 • Hibernate • Mockobjects • JDO • JMX Die Anpassung bzw. die Erstellung eigener Module ist m?glich. Für die Ausführung der Engine wird in der aktuellen Version das ebenfalls als OpenSource verfügbare Buildwerkzeug Apache Ant benötigt. Die einzelnen Module stellen Ant Tasks zur Verfügung, mit deren Hilfe der eigentliche Generierungslauf ausgeführt wird. 4.4.2. XDoclet und J2EE XDoclet bietet eine gute Unterstützung der J2EE Technologien. Neben der manuellen Erstellung bzw. Erweiterung der Deployment Deskriptoren entfallen z. B. auch zeitaufwendige T?tigkeiten wie die Erstellung von Home- und Remoteinterfaces bei der EJB Entwicklung. Es genügt, die Bean Klasse um Metainformationen in Form von Tags zu erweitern.35 Dies hat zum einen zur Folge, daß innerhalb des Projektes keine veralteten Deployment Deskriptoren mehr enthalten sind, zum anderen erhält der Entwickler eine bessere Ubersicht über das Projekt, da die Informationen nicht mehr auf mehrere Dateien verteilt, sondern zentral in einer Datei gepflegt werden. Mit Hilfe von XDoclet lassen sich neben Standard Deployment Deskriptoren auch containerabhängige Deskriptoren erzeugen. Unterstützt werden zur Zeit unter anderem: • JBoss • Tomcat • Bea Weblogic • IBM Websphere 35 Sullins und Whipple (May 2003) 4.4. Code Generation mit XDoclet 52 Zusätzlich wird die Implementierung einiger J2EE Patterns erleichtert. Durch Deklaration in der Java Datei sowie in der Build Datei lassen sich so z. B. ValueObjects, Struts Formulare oder Schlüsselgenerierungs Session Beans erzeugen. 4.4.3. Beispiel EJB mit XDoclet Abschließend wird anhand eines Beispiels der Einsatz von XDoclet während der EJB Entwicklung gezeigt. Ausgangspunkt der Entwicklung ist die Bean Klasse, die wir, entgegen der eigentlichen Vorgehensweise bei Session Beans, als abstrakte Klasse implementieren. XDoclet erstellt bei der Generierung eine abgeleitete Klasse, die die fehlenden Callback Methoden (setSessionContext, etc.) implentiert. Diese Klasse wird dann auch automatisch in den Deployment Deskriptor eingetragen. In die Dokumentation der Klassen werden allgemeine Metainformationen wie BeanName und Typ des Beans aufgenommen. In der Dokumentation der Business-Methode wird definiert, daß die Methode auch im Interface aufgeführt sein soll. Damit ist die Bean Implementierung abgeschlossen. Zur Generierung wird das XDoclet Modul ejbdoclet benutzt. Dieses muß hierzu in der Build Datei angegeben und aufgerufen werden. Im Beispiel enthält der Aufruf von ejbdoclet die Anweisung, daß sämtliche auf Bean.java endende Dateien im Source Verzeichnis in die Verarbeitung eingeschlossen werden sollen. Durch das session Element wird ejbdoclet angewiesen, Session Beans zu berücksichtigen und die entsprechenden Tags auszuwerten. Das Attribut acceptAbstractClasses erlaubt den oben bereits erwähnten Einsatz von abstrakten Klassen bei Session Beans. Im weiteren wird definiert, daß Remote- und Homeinterfaces erstellt werden sollen. Zusätzlich wird ein Standard Deployment Deskriptor sowie ein JBoss Deployment Deskriptor erzeugt. Die so erstellten Java Klassen können nun compiliert und in ein JAR Archiv gepackt werden. Dieses kann anschlieäend in den Server deployed werden. <target name="xdoclet-generate" depends="init"> <taskdef name="ejbdoclet" 4.4. Code Generation mit XDoclet 53 classname="xdoclet.modules.ejb.EjbDocletTask"> <classpath refid="compile.classpath"/> </taskdef> <ejbdoclet destdir="${build.src.dir}" excludedtags="@version,@author" mergedir="${meta.dir}/ejb-jar/META-INF/"> <fileset dir="${src.dir}"> <include name="**/*Bean.java"/> </fileset> <session acceptAbstractClasses="true"/> <remoteinterface/> <homeinterface/> <deploymentdescriptor destdir="${build.dir}/META-INF"/> <jboss version="3.0" xmlencoding="UTF-8" destdir="${build.dir}/META-INF" validateXml="false"/> </ejbdoclet> </target> 4.4.4. Fazit XDoclet ist sicher kein Model Driven Architecure (MDA) Tool im eigentlichen Sinne, es handelt sich vielmehr um ein hilfreiches Werkzeug, mit dessen Hilfe die Entwicklung besonders von J2EE Anwendungen vereinfacht wird. Der Entwickler kann sich wieder auf die eigentlichen Aufgaben konzentrieren und muß sich nicht mehr mit aufwendiger, fehleranfälliger Cut- and Paste Arbeit aufhalten.36 36 http://www.xdoclet.org 4.5. JBoss Application Server 54 4.5. JBoss Application Server JBoss ist ein vollständig in Java implementierter Open-Source Application Server. Er unterstützt Enterprise JavaBeans 2.0 und arbeitet sowohl mit Jetty als auch mit Tomcat als Web Engine zusammen.37 4.5.1. Deployment unter JBoss JBoss unterstützt das sogenannte Hot Deployment. Um eine EJB einzusetzen,genügt es, die dazugehörige Jar-Datei in das Deploy-Verzeichnis des Application Servers zu kopieren (JBOSS/server/default/deploy). JBoss erkennt, dass die Datei verändert wurde und lädt die enthaltenen EJBs. Im Verzeichnis JBOSS/server befnden sich mehrere Profile des Application Servers. Beim Start wird durch den Parameter -configure=X das zu ladende Profile X festgelegt. Das Standardprofile ist JBOSS/server/default. Ein neues Profile lässt sich einfach durch das Kopieren eines bestehenden erstellen. Jedes Profile besitzt ein eigenes Deploy-Verzeichnis. Bevor die Komponente Usermanagement eingesetzt werden kann, muss sie zunächst kompiliert, zusammen mit den Deployment-Deskriptoren in ein Jar-Archiv gepackt und in das Deployment-Verzeichnis des JBoss kopiert werden Neben deploy werden die Funktionen compile und clean zur Verfügung gestellt. Deploy erstellt aus den kompilierten Klassen ein Jar-Archiv und kopiert es in das standard Deploy-Verzeichnis des Application Servers. Der Pfad zu einer Installation von JBoss wird dem Skript mit der Variablen jboss.path übergeben. Falls es notwendig ist, wird von deploy die Funktion compile aufgerufen, wodurch zunächst sämtliche Klassen kompiliert werden. Mit clean können alle erzeugten Klassen und Archive wieder gelöscht werden. 37 http://www.jboss.org 5. Implementierung In diesem Kapital werden die Hauptpackages dargestellt. Weiter wird die JSP Webinferface erklärt. Die Implementierung der Hauptpackages untergliedert sich in drei Unterpackages (package login.ejb, package login.interfaces, package login.web), die im Zusammenhang miteinander stehen. Im Package login.ejb befinden sich alle EJB’s zur Realisierung der einzelnen Klassen usersBean, pagesBean und groupsBean. Das Interface wird durch XDoclet automatisch erzeugt. Das Interface beinhaltet somit das remote- und home-interface. Hingegen benutzt das login.web package die anderen beiden Packages (Abbildung 5.1). In diesem Package befinden sich unter anderem die Servlets und die JSP-Seiten. Hinweis: Die vollständige Implementierung der einzelnen Methoden, Klassen und Packages sind im Anhang zu finden. Abbildung 5.1.: Die Hauptpackages 55 5.1. UsersBean 56 5.1. UsersBean Alle notwendigen Funktionalitäten über den User werden in der Klasse UsersBean implementiert. Darin befinden sich die Methoden wie die Auflistung aller User, Löschen einzelner User, Hinzufügen einzelner User in eine bestehende Gruppe und Verändern der Daten eines Users. Abbildung 5.2.: UsersBean Login: Die folgende Methode login erhält den Namen und das Passwort des Users. Ist der User vorhanden und tippt der User sein Passwort korrekt ein, so wird er durch diese Methode erkannt und die Zulassung zum System erfolgt. Sollte der User falsche Eingaben vornehmen, wird eine Exception ausgelöst. public Integer login(String userName, String password) { ... } 5.1. UsersBean 57 Add User: Durch die addUser Methode wird der User in die Datenbank hinzugefügt. Existiert der User bereits, wird eine Exception ausgelöst und die Daten des Users werden verworfen. Diese Aktion wird durch eine Hilfsmethode isUserFree durchgeführt. Der User erfährt somit, dass jemand mit dem von ihm gewählten Usernamen schon existiert. Existieren jedoch die Daten nicht, so wird der User in die Datenbank aufgenommen. public boolean addUser(String username, String realname, String email, String password) throws Exception { .... } Delete User: In der folgenden Methode wird ein User aus der vorhandenen Liste der User gelöscht. Dafür wird die ID des Users benötigt. Ist der User zum Löschen markiert, wird er nach Ausführen aus der Datenbank sowie aus den Gruppen, in denen er sich befindet, gelöscht. Als Ausgabe erhält man, dass der User erfolgreich vom System entfernt wurde. public String deleteUser(Integer uid) throws Exception { ... } Update User: Mit Hilfe dieser Methode updateUser können die Daten der jeweiligen User aktualisiert werden. Die Methode braucht zur Aktualisierung der Daten das Usernamen, Realnamen, Emailadresse und den Passwort des Users. public void updateUser(String username, String realname, String email, String password) throws Exception, RemoteException { ... } 5.2. GroupsBean 58 Get Users: Durch die Implementierung der Methode getUser wird das Auslesen eines bzw. mehrerer User ermöglicht. Die Ausgabe erfolgt durch ein String Array pro User. Dieses Array liefert als Ausgabe den UserId, Usernamen, Realnamen und Emailadresse. public String[] getUser(String username) throws Exception, RemoteException { ... } Reset Password: Durch diese Option hat der User die Möglichkeit, eine Anfrage beim Admin für die Zurücksetzung seines Passwortes zu machen. Dafür benötigt der Admin lediglich den Usernamen des Users, um das Passwort auf einen Defaultwert zurückzusetzen. public void setpassword(String username, String admin, String password) throws Exception, RemoteException { ... } 5.2. GroupsBean In der Klasse GroupsBean befinden sich alle Methoden für die Verwaltung von Gruppen. Darunter befinden sich Methoden, wie z.B. Erstellen einer neuen Gruppe, Löschen einer vorhanden Gruppe sowie das Hinzufügen bzw. Löschen eines User in bzw. aus eine(r) vorhandene(n) Gruppe. Zusätzlich werden in dieser Klasse den Gruppen auch Elemente und Projekte zu geordnet. Add Group: Durch diese Methode wird eine neue Gruppe im System erstellt. Als Pflichteingabefelder sind Gruppenname und Gruppenbeschreibung einzugeben. Falls die Gruppe vorhanden ist, wird eine Exception ausgelöst und die eingegebenen Daten werden verworfen. 5.2. GroupsBean 59 Abbildung 5.3.: GroupsBean public boolean addGroup(String groupname, String gdescription) throws Exception { ... } Delete Group: Die Methode deleteGroup löscht eine vorhandene Gruppe aus dem System. Durch das Entfernen der Gruppe verliert die Gruppe ihre Gültigkeit, jedoch existieren die User weiterhin im System. Zum Löschen der Gruppe benötigt man lediglich die ID der Gruppe. Als Rückgabewert wird ein String ausgegeben. public String deleteGroup(Integer gid) throws Exception { ... } Add user to group: Es besteht die Möglichkeit einer vorhandenen Gruppe einen bzw. mehrere User hinzuzufügen. Dies wird durch die Methode addUserToGroup 5.2. GroupsBean 60 ermöglicht. Als Eingabewerte für die Methode sind Gruppenname und Username erforderlich. public boolean addUserToGroup(String gname, String uname) throws Exception { ... } Deletes a user from a group: Das Löschen eines Users aus einer vorhandenen Gruppe bedeutet nicht das Löschen des Users aus dem System. In der Methode removeUserFromGroup wird der User aus der Gruppe entfernt, somit verliert der User seine Gruppenangehörigkeit. Die Gruppe und der User existieren weiterhin. public void removeUserFromGroup(String gname, String uname) throws Exception{ ... } Add element to group: Die Methode addElementToGroup weist einer vorhandenen Gruppe ein neues HTML-Element bzw. ein neue Page zu. Dazu benötigt die Methode die Page ID bzw. Element ID und den Gruppennamen. Ist ein neues Element bzw. Page einer Gruppe hinzugefügt worden, so erhalten alle User die Berechtigungen zu den Elementen. public void addElementToGroup(Integer eid, Integer pid,String gname) throws Exception{ ... } Delete element from group: Im Gegensatz zu addElementToGroup besteht auch eine Methode removeElementFromGroup, um Elemente und Pages aus einer Gruppe wieder zu Entfernen. Nach dem entfernen der Elemente verlieren die Gruppenmitglieder die Berechtigung gegenüber den Elementen. public void removeElementFromGroup(Integer eid, 5.3. PagesBean 61 Integer pid, String gname) throws Exception { ... } Get Group: Die Darstellung und Anzeige aller vorhandenen Gruppen wird durch eine Vektormethode getAllGroups realisiert. Diese Methode ist eine einfache Implementierung zur Darstellung der Gruppen mit Ihren ID’s, Gruppenname sowie der Gruppenbeschreibung. public Vector getAllGroups() throws Exception ,RemoteException { ... } 5.3. PagesBean Die Klasse PagesBean besteht aus einigen private-Methoden und der isVisible-Methode. Dabei wird in den private-Methoden, die nach aussen nicht sichtbar sind, ein Vergleich zwischen den Gruppen und den Usern durchgeführt, ob sie im Zusammenhang miteinander stehen. Ist dies der Fall, so hat die Gruppe, und somit auch der User, die Berechtigung dieses HTML-Element zu sehen. Abbildung 5.4.: PagesBean isVisible: Die größte Bedeutung kommt der isVisible Methode in der Klasse PagerBean zu. Diese Methode , gibt die Berechtigung bzw. Zusammengehörigkeit der Gruppen und User frei, und ermöglicht somit Elemente oder Pages zusehen. 5.4. Anbindung an die Datenbank 62 public Boolean isVisible(Integer userID, Integer projectID,Integer pageID, Integer elementID,Vector pageHierarchy) { ... } 5.4. Anbindung an die Datenbank Die Anbindung der PostgreSQL-Datenbank wird durch ein EJB configBean“ rea” lisiert. Dabei wird eine Verbindung durch die Methode getDBConnectionData aufgebaut. Diese Methode braucht zusätzlich den Treiber für die Datenbank. Als Parameter benötigt die Methode noch zusätzliche Angaben wie den Usernamen für die Datenbank, Passwort, Port und den Datenbanknamen. public Map getDBConnectionData (){ Map map = new HashMap(); map.put("driver", "org.postgresql.Driver"); map.put("url", "jdbc:postgresql://localhost:5432/admin"); map.put("username", "test"); map.put("password", "test"); return map; } 5.5. Admin Tool Web-Interface Das Web-Interface dient zur Realisierung der Funktionalitäten und ist eine JSPSeite, die eine Verbindung zwischen dem Benutzer und dem System darstellt. Im Package web.root werden die einzelnen Servlets und JSP-Seiten abgelegt. Weitere Klassen innerhalb des Web-Interfaces sind validateUser, validateGroup und treeTag. Um auf die EJB’s zugreifen zu können, benötigen wir die Klasse validateUser, um eine 5.5. Admin Tool Web-Interface 63 Verbindung zwischen den EJB’s und den JSP-Seiten zu ermöglichen. Äquivalent gilt diese Implementierung für den validateGroup, der dann die Verbindung zwischen den EJB’s und den JSP-Seiten gruppenseitig herstellt. Ein Auszug aus dem validateUsers sieht wie folgt aus: public boolean addUser(String username, String realname, String email, String password) { try { Context context = new InitialContext(); String value = (String) context.lookup("java:/comp/env/Title"); Object ref = context.lookup("java:/comp/env/ejbs/Users"); UsersHome usershome = (UsersHome) PortableRemoteObject.narrow(ref, UsersHome.class); Users bean = usershome.create(); bean.addUser(username, realname, email, password); return true; } catch (Exception exp) { return false; } } Die Klasse treeTag dient zur dynamischen Darstellung der einzelnen Links. Durch Ausführen dieser Klasse wird die Navigation der einzelnen Funktionalitäten sichergestellt. Diese Klasse ermöglicht weiterhin das Expandieren bzw. den Kollaps der Baumstruktur. 5.5. Admin Tool Web-Interface Abbildung 5.5.: Web infterface 64 6. Admin Tool Benutzerhandbuch Das folgende Kapitel beschreibt eine Schritt für Schritt Einführung in das Admin Tool. In diesem Abschnitt werden die einzelnen Schritte über das Einloggen bis hin zum Benutzen des Admin Tools im Detail beschrieben.1 6.1. Authentifizierung Abbildung 6.1.: Authentifizierung Prozess Der Zugang zum Admin Tool erfolgt über eine Authentifizierungsseite. Nur authentifizierten Usern ist der Zugang in das Admin Tool gestattet. Jeder User muss sich mit seinem gültigen Usernamen und Passwort auf der Authentifizieungsseite anmelden (siehe Abbildung 6.2). Versucht ein nichtauthentifizierter User den Zugang zu erzwingen, wird er vom Admin Tool abgewiesen und erhält eine Fehlermeldung anhand einer HTML Error Page (siehe Abbildung 6.1). Nach dem Einloggen gliedert sich die Webapplikation in drei Bereiche. Im linken Bereich der Applikation sieht man die User und Gruppen Verwaltung. Im rechten 1 NBT Admin Tool: http://www.venus.cs.tu-berlin.de/adminproject/login.jsp 65 6.2. User Verwaltung 66 Abbildung 6.2.: Login Page Frame, dem so genannten Hauptframe, sieht man die Übersicht der einzelnen Funktionen bzw. Darstellung der einzelnen Links (Abbildung 6.3). 6.2. User Verwaltung Im Untermenü User Verwaltung befinden sich weitere Untermenüs, die alle Benutzer anzeigen, neue User anlegen, Passwörter zurücksetzen bzw. Anzeigen von vorhandenen Gruppen. 6.2.1. List all user In der User List erhält man alle im System vorhandenen User mit ihren Usernamen, Realnamen und Emailadressen sowie eine Checkbox, um bestimmte Operationen auf den User auszuführen, wie z.B. Einfügen eines ausgewählten Users in eine Gruppe. 6.2. User Verwaltung 67 Abbildung 6.3.: Hauptansicht Abbildung 6.4.: Liste aller verfuegbaren User 6.2. User Verwaltung 68 6.2.2. Create user Unter diesem Link besteht die Möglichkeit einen neuen User anzulegen. Dafür werden Informationen über den Usernamen, Realnamen und die Emailadresse benötigt. Falls der User sich in der Datenbank befindet bzw. sein Username schon existiert, wird der User vom System abgewiesen und die eingegebenen Daten werden verworfen. Abbildung 6.5.: Create new user 6.2.3. Reset password Falls der User sein Passwort vergisst, kann der Admin dessen Passwort auf Wunsch wieder zurücksetzen. Dieses Feature wird in Zukunft dann über eine Email abgewickelt und der User erhält sein zurückgesetztes Passwort per Mail. 6.2.4. Update User Diese Funktionalität ist für den Admin sowie für den Anonymous User verfügbar, d.h. sogar der Anonymous User hat das Recht, Veränderungen an seinen eigenen Daten vorzunehmen. 6.2. User Verwaltung 69 Abbildung 6.6.: Update user 6.2.5. Delete user Delete user dient zum Löschen eines Users aus der Datenbank. Ist der User aus der Datenbank verworfen, gehen alle Daten über den User verloren sowie dessen Zugehörigkeiten in den einzelnen Gruppen. Abbildung 6.7.: Delete user 6.3. Groups verwalten 70 6.2.6. Show user groups Mit dieser Funktion wird eine detaillierte Ausgabe über einen ausgewählten User in den eingetragenen Gruppen dargestellt. Es werden nur die Gruppen dargestellt, in denen sich der User auch tatsächlich befindet. 6.3. Groups verwalten Unter dem Hauptmenüpunkt Gruppenverwaltungen verbergen sich Funktionalitäten wie Anzeigen aller Gruppen, Erzeugen von neuen Gruppen sowie das Hinzufügen,Löschen und die Verwaltung der Subgruppen, die eine Teilmenge der Hauptgruppen sind. 6.3.1. List all groups Im ersten Untermenü von der Gruppenverwaltung befindet sich list all groups. Hierbei werden alle Gruppen aufgelistet, die im System vorhanden sind. Diese Gruppen sind eindeutig über ID’s gekennzeichnet. Weiterhin erhält man eine Kurzbeschreibung zu den einzelnen Gruppen. Abbildung 6.8.: List All Gropus 6.3. Groups verwalten 71 6.3.2. Create group Create Group dient zur Erzeugung einer neuen Gruppe. Dabei wird ein eindeutiger ID der Gruppe durch das System zugeordnet. Weiterhin hat der Eintragende die Möglichkeit den Gruppennamen sowie eine Kurzbeschreibung über den erfolgten Eintrag einzugeben. Abbildung 6.9.: Add new group 6.3.3. Add user to group Ein neuer bzw. bestehender User wird zu einer vorhandenen Gruppe hinzugefügt, somit erhält der User bestimmte Rechte, die der Gruppe auferlegt sind, und erhält dadurch die Verknüpfung zu den Projekten und den HTML Elementen. 6.3.4. Delete group Mit ein paar klick’s löscht man eine vorhandene Gruppe aus der Liste. Dafür wählt man die Checkbox und geht dann auf Delete. Hierbei wird die Gruppe gelöscht, jedoch nicht die einzelnen User, d.h. die User verlieren nur die Zugehörigkeiten zur gelöschten Gruppe und nicht mehr. 6.3. Groups verwalten 72 Abbildung 6.10.: Delete group 6.3.5. Delete user from group Löscht man einen User aus der Gruppe, so verliert der User die Verknüpfung zu den einzelnen Projekten sowie zu den HTML Elementen, da der User nicht mehr in der Gruppe existiert. Dies bedeutet jedoch nicht, dass der User komplett vom System gelöscht wurde, sondern nur den Verlost der Berechtigung für die jeweilige Gruppe. 6.3.6. Add sub-group In dem Untermenü add sub-group wird eine neue Gruppe einer bestehenden Maingruppe erzeugt. In diesem Falle erbt die Untergruppe alle Berechtigungen und Funktionalitäten der Maingruppe. Zu dem werden auch alle User aus der Maingruppe ein Teil der neuen Subgruppe. 6.3.7. Delete sub-group Mit dem Entfernen einer Subgruppe werden alle Funktionalitäten und Berechtigungen sowie die eingetragenen User gelöscht. Durch diesen Löschvorgang wird die Maingruppe, aus der die Subgruppe einmal erzeugt wurde, nicht angetastet, d.h. die Maingruppe verliert weder ihre Berechtigungen, Funktionalitäten noch die eingetragenen 6.4. PLP Security 73 User. 6.4. PLP Security Abbildung 6.11.: PLP Security Panel Das Admin Tool wird durch ein Gui-Interface in PLP ergänzt. Damit man die Security (Abbildung 6.11) Funktionalitäten ausnutzen kann muss, man sich auf dem PLP als Administrator einloggen. Daher ist ein zusätzliches Login Fenster für das PLP vorhanden, um bestimmte Elemente den verschiedenen Usern, die in unterschiedlichen Gruppen zugeordnet sind, anzuzeigen. 6.4. PLP Security 74 Die einzelnen Elemente und Pages werden durch den Menüpunt Managegroups (Abbildung 6.12) behandelt. Dabei werden bestimmte Projekte und HTML Elemente zu bestimmten Gruppen hinzugefügt. Jede Gruppe erhält somit eine bestimmte Auswahl an HTML Elementen und Projekten, die ihr zugewiesen werden. Abbildung 6.12.: PLP Managegroups A. Anhang A.1. GroupsBean.Java /* $Id: GroupsBean.java,v 1.10 2004/12/13 06:42:15 bassem Exp $ */ package login.ejb; import import import import import import import import import import java.lang.reflect.InvocationTargetException; java.lang.reflect.Method; java.rmi.RemoteException; java.sql.Connection; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.util.Map; java.util.Vector; import import import import import import import import import javax.ejb.CreateException; javax.ejb.EJBException; javax.ejb.EJBHome; javax.ejb.EJBObject; javax.ejb.SessionBean; javax.ejb.SessionContext; javax.naming.Context; javax.naming.InitialContext; javax.naming.NamingException; 75 A.1. GroupsBean.Java 76 import javax.rmi.PortableRemoteObject; /** * @author <a href="mailto:[email protected]">Bassem El-Ahmad</a> * * @ejb.bean name = "Groups" * display-name = "GroupsBean EJB" * description = "EJB inorder to manged the Groups" * view-type = "remote" * jndi-name = "ejbs/Groups" */ public class GroupsBean implements SessionBean { /** * The Java-Mysql Connection * * @uml.property name="stmt" * @uml.associationEnd multiplicity="(0 -1)" elementType="java.lang.String" */ private Statement stmt = null; /** * database connection result * * @uml.property name="rs" * @uml.associationEnd multiplicity="(0 -1)" elementType="java.lang.String" */ private ResultSet rs = null; private String admin; private String all; private String page; A.1. GroupsBean.Java 77 private boolean loggedIn; public GroupsBean() { super(); } public Connection getConnection() { Map map = null; try { Context context = new InitialContext(); Object ref = context.lookup("funcEJBS/Config"); EJBHome homeObject = (EJBHome) PortableRemoteObject.narrow(ref, EJBHome.class); Method createMethod = homeObject.getEJBMetaData() .getHomeInterfaceClass() .getMethod("create", new Class[] {}); EJBObject ejbObject = (EJBObject) createMethod.invoke(homeObject, new Object[] {}); Method method = homeObject.getEJBMetaData() .getRemoteInterfaceClass().getMethod("getDBConnectionData", new Class[] {}); map = (Map) method.invoke(ejbObject, new Object[] {}); Class.forName((String) map.get("driver")).newInstance(); Connection conn = DriverManager.getConnection((String) map .get("url"), (String) map.get("username"), (String) map .get("password")); return conn; } catch (NamingException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); A.1. GroupsBean.Java 78 } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return null; } /** * Adds a group * * @param group The Group * @param user The user who wants to add the Group * @throws Exception if group allready exists * @ejb.interface-method view-type = "remote" */ public boolean addGroup(String groupname,String gdescription) throws Exception String insert; try { Connection conn = getConnection(); if (this.groupFree(groupname)) { stmt = conn.createStatement(); A.1. GroupsBean.Java 79 insert = ("insert into groups(gname,gdescription) values(’" + groupname.trim() + "’,’" + gdescription.trim() + "’)"); stmt.execute(insert); return true; } else{ throw new Exception(groupname + " is allready in Database!"); } } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } A.1. GroupsBean.Java 80 return false; } /** * Deletes a group * @param gid The Group ID * @ejb.interface-method view-type = "remote" */ public String deleteGroup(Integer gid)throws Exception { String result = "Group Deleted"; Connection conn = null; try { conn = getConnection(); stmt = conn.createStatement(); stmt.executeUpdate("DELETE FROM groups_users WHERE gid = "+gid.intVal stmt = conn.createStatement(); stmt.executeUpdate("delete from groups where gid ="+gid.intValue()); }catch(Exception e){ e.printStackTrace(); result = e.getMessage(); } finally{ try{ conn.close(); A.1. GroupsBean.Java 81 }catch(SQLException ex){ } } return result; } /** * Adds a user to a group * * @param group The name of the Group * @param user The user who is to be added to the group * @param admin The admin who wants to add to the group * @throws Exception If user is not allowed to add users to the group * @ejb.interface-method view-type = "remote" */ public boolean addUserToGroup(String gname, String uname) throws Exception { String insert = null; try { Connection conn = getConnection(); if (!(groupFree(gname))) { if (this.getUid(uname) == 0) throw new Exception("Failed to add " + uname + " to group " + gname + "!"); stmt = conn.createStatement(); insert = ("insert into groups_users values (’" + this.getGid(gname) + "’,’" + this.getUid(uname) + "’)"); stmt.executeUpdate(insert); return true; A.1. GroupsBean.Java 82 } else throw new Exception("Group " + gname + " does not exist"); } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * Deletes a user from a group * * @param group The name of the Group * @param user The user who is to be deleted from the group * @param admin The admin who wants to delete the user * @throws Exception If user is not allowed to delete users from group * @ejb.interface-method view-type = "remote" */ public void removeUserFromGroup(String gname, String uname) throws Exception{ try { Connection conn = getConnection(); String delete = null; stmt = conn.createStatement(); delete = ("DELETE FROM groups_users WHERE uid = ’" + this.getUid(uname) + " and gid = ’" + this.getGid(gname) + "’)"); A.1. GroupsBean.Java 83 stmt.executeUpdate(delete); } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * Adds a element to a group * * @param gname The name of the Group * @param eid Element ID * @param pid The Project ID * @throws Exception If user is not allowed to add users to the group * @ejb.interface-method view-type = "remote" */ public void addElementToGroup(Integer eid,Integer pid, String gname) throws Ex String insert; try { Connection conn = getConnection(); stmt = conn.createStatement(); insert = ("insert into element_groups values(" + this.getGid(gname) + "," + eid.intValue()+"," + pid.intValue()+ ")"); stmt.executeUpdate(insert); } catch (Exception e) { throw new Exception(e.getMessage()); } } A.1. GroupsBean.Java 84 /** * Delete element from group * * @param gname The name of the Group * @param eid Element ID * @param pid The Project ID * @throws Exception If user is not allowed to add users to the group * @ejb.interface-method view-type = "remote" */ public void removeElementFromGroup(Integer eid,Integer pid, String gname) throws E String insert; try { Connection conn = getConnection(); stmt = conn.createStatement(); insert = ("delete from element_groups where eid="+eid.intValue()+" AND pid="+ pid.intValue()+" AND gid="+getGid(gname)); stmt.executeUpdate(insert); } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * Returns a Vector of all groups * * @return A vector of groups (only with Groupnames) A.1. GroupsBean.Java 85 * @throws Exception If user is not allowed to * @ejb.interface-method view-type = "remote" */ public Vector getAllGroups() throws Exception ,RemoteException { String select = null; Vector allgroups = new Vector(); Connection conn = getConnection(); if (conn != null) { try { stmt = conn.createStatement(); select = "select gid,gname,gdescription,gowner from groups"; rs = stmt.executeQuery(select); row[0] row[1] row[2] row[3] = = = = while (rs.next()) { String[] row = new String[4]; rs.getString("gid"); rs.getString("gname"); rs.getString("gdescription"); rs.getString("gowner"); allgroups.add(row); } } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } A.1. GroupsBean.Java finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } } return allgroups; } /** * Returns a Vector of all group names * * @return A vector of groups (only with Groupnames) * @throws Exception If user is not allowed to * @ejb.interface-method view-type = "remote" */ public Vector getGroupName() { Vector groups = new Vector(); Connection conn = getConnection(); if (conn != null) { try { 86 A.1. GroupsBean.Java 87 stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT gname FROM groups"); while (rs.next()){ String group = new String(rs.getString("gname")); groups.add(group); } } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } } return groups; } /** A.1. GroupsBean.Java * Returns a Vector of all group names * * @return A vector of groups (only with Groupnames) * @throws Exception If user is not allowed to * @ejb.interface-method view-type = "remote" */ public Vector getGroupNamesOfElement(Integer eid, Integer pid) { Vector groups = new Vector(); Vector gids = new Vector(); Connection conn = getConnection(); if (conn != null) { try { stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT gid FROM element_groups where eid="+ eid.intValue()+" AND pid="+pid.intValue()); while (rs.next()){ gids.add(new Integer(rs.getInt("gid"))); } rs = stmt.executeQuery("SELECT gid, gname FROM groups"); while(rs.next()){ Integer i = new Integer(rs.getInt("gid")); if(gids.contains(i)) groups.add(rs.getString("gname")); } } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { 88 A.1. GroupsBean.Java if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } } return groups; } /** * * @param gid * @param uid * @return * @ejb.interface-method view-type = "remote" */ public Vector getGroupNamesOfUser(Integer gid, Integer uid) { Vector usergroups = new Vector(); Vector gids = new Vector(); Connection conn = getConnection(); if (conn != null) { try { stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT gid FROM groups_users where uid="+ uid.intValue()+" AND gid="+gid.intValue()); 89 A.1. GroupsBean.Java while (rs.next()){ gids.add(new Integer(rs.getInt("gid"))); } rs = stmt.executeQuery("SELECT gid, gname FROM groups"); while(rs.next()){ Integer i = new Integer(rs.getInt("gid")); if(gids.contains(i)) usergroups.add(rs.getString("gname")); } } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } } 90 A.1. GroupsBean.Java 91 return usergroups; } /** * Returns the Id of a given group * * @param user * The groups name * @return The Group id */ private int getGid(String group) throws Exception, RemoteException { String select; try { Connection conn = getConnection(); stmt = conn.createStatement(); select = ("select gid from groups where gname =’" + group.trim() + "’" rs = stmt.executeQuery(select); if (rs.next()) { return rs.getInt("gid"); } else return 0; } catch (Exception e) { System.out.println(e.getMessage()); return 0; } } /** * Returns the Id of a given User A.1. GroupsBean.Java 92 * @param user The user * @return The Uid */ public int getUid(String uname) throws Exception, RemoteException { String select; try { Connection conn = getConnection(); stmt = conn.createStatement(); select = ("select uid from users where username = ’" + uname + "’"); rs = stmt.executeQuery(select); if (rs.next()) { return rs.getInt("uid"); } else return 0; } catch (Exception e) { System.out.println(e.getMessage()); return 0; } } /** * Checks if the Groupname is already used * @param gName The name of the Group) * @return true, if Groupname is unused, false else * */ private boolean groupFree(String gname) throws Exception, RemoteException { String select; try { Connection conn = getConnection(); A.1. GroupsBean.Java 93 stmt = conn.createStatement(); select = ("select * from groups where gname = ’" + gname.trim() + "’") rs = stmt.executeQuery(select); if (rs.next()) return false; else return true; } catch (Exception e) { System.out.println(e.getMessage()); return false; } } /** * Default create method * * @throws CreateException * @ejb.create-method */ public void ejbCreate() throws CreateException { } /* (non-Javadoc) * @see javax.ejb.SessionBean#ejbActivate() */ public void ejbActivate() throws EJBException, RemoteException { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.ejb.SessionBean#ejbPassivate() */ A.2. UsersBean 94 public void ejbPassivate() throws EJBException, RemoteException { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.ejb.SessionBean#ejbRemove() */ public void ejbRemove() throws EJBException, RemoteException { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext) */ public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException { // TODO Auto-generated method stub } } A.2. UsersBean /* $Id: UsersBean.java,v 1.10 2004/12/05 18:41:19 bassem Exp $ */ package login.ejb; import import import import java.lang.reflect.InvocationTargetException; java.lang.reflect.Method; java.rmi.RemoteException; java.sql.Connection; A.2. UsersBean import import import import import import java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.util.Map; java.util.Vector; import import import import import import import import import import javax.ejb.CreateException; javax.ejb.EJBException; javax.ejb.EJBHome; javax.ejb.EJBObject; javax.ejb.SessionBean; javax.ejb.SessionContext; javax.naming.Context; javax.naming.InitialContext; javax.naming.NamingException; javax.rmi.PortableRemoteObject; 95 /** * @author <a href="mailto:[email protected]">Bassem El-Ahmad </a> * * @ejb.bean name = "Users" * display-name = "UsersBean EJB" * description = "EJB inorder to manged the Users" * view-type = "remote" * jndi-name = "ejbs/Users" */ public class UsersBean implements SessionBean { /** A commando for the database */ private Statement stmt = null; A.2. UsersBean 96 /** database connection result */ private ResultSet rs = null; /** * Is the user logged in * * @uml.property name="loggedIn" */ private boolean loggedIn; /** * * @uml.property name="pass" */ private String pass; public UsersBean() { super(); } public Connection getConnection() { Map map = null; try { Context context = new InitialContext(); Object ref = context.lookup("funcEJBS/Config"); EJBHome homeObject = (EJBHome) PortableRemoteObject.narrow(ref, EJBHome.class); Method createMethod = homeObject.getEJBMetaData() .getHomeInterfaceClass() .getMethod("create", new Class[] {}); EJBObject ejbObject = (EJBObject) createMethod.invoke(homeObject, A.2. UsersBean } } } } } } } } } } new Object[] {}); Method method = homeObject.getEJBMetaData() .getRemoteInterfaceClass().getMethod("getDBConnectionData", new Class[] {}); map = (Map) method.invoke(ejbObject, new Object[] {}); Class.forName((String) map.get("driver")).newInstance(); Connection conn = DriverManager.getConnection((String) map .get("url"), (String) map.get("username"), (String) map .get("password")); return conn; catch (NamingException e) { e.printStackTrace(); catch (SecurityException e) { e.printStackTrace(); catch (RemoteException e) { e.printStackTrace(); catch (NoSuchMethodException e) { e.printStackTrace(); catch (IllegalArgumentException e) { e.printStackTrace(); catch (IllegalAccessException e) { e.printStackTrace(); catch (InvocationTargetException e) { e.printStackTrace(); catch (InstantiationException e) { e.printStackTrace(); catch (ClassNotFoundException e) { e.printStackTrace(); catch (SQLException e) { e.printStackTrace(); } return null; } 97 A.2. UsersBean 98 /** * Adds a user * * @param user The username * @param admin The admin who adds the user * @throws Exception If user is not allowed to add an user or if user allready exi * * @ejb.interface-method view-type = "remote" */ public boolean addUser(String username, String realname, String email, String String insert; try { Connection conn = getConnection(); if (this.userFree(username)) { stmt = conn.createStatement(); insert = ("insert into users(username, realname, email, password) + username.trim() + "’,’" + realname.trim() + "’,’" + email.trim() + "’,’" + password.trim() + "’)"); stmt.execute(insert); return true; } else{ throw new Exception(username + " is allready in Database!"); } } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); A.2. UsersBean 99 System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } return false; } /** Deletes a User @param user the user who is going to be deleted @param admin the admin who wants to delete the user (can be the user) @throws Exception If user is not allowed to delete user * * * * * * @ejb.interface-method view-type = "remote" */ public String deleteUser(Integer uid) throws Exception { String result = "User Deleted"; A.2. UsersBean 100 String delete = null; String select = null; String toDelete = null; try { Connection conn = getConnection(); //User is deleted from the groups_users table stmt = conn.createStatement(); stmt.executeUpdate("delete from groups_users where uid ="+uid.intV //User is deleted from the user table stmt = conn.createStatement(); stmt.executeUpdate("delete from users where uid ="+uid.intValue()) //All groups owned by user are deleted stmt = conn.createStatement(); stmt.executeUpdate("select * from groups where gowner ="+uid.intVa } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { A.2. UsersBean 101 stmt = null; } } } return result; } /** * Updates data of a specific user * * @param username The (old) name of the user who is going to be updated * @param user The new user data * @param admin The admin who wants to change the Data * @throws Exception If user is not allowed to update user or if new username allr * * @ejb.interface-method view-type = "remote" */ public void updateUser(String username, String realname, String email, String password) throws Exception, RemoteException { String update = null; try { Connection conn = getConnection(); if ((!(username.equals(username))) && this.userFree(username)) { stmt = conn.createStatement(); update = "update users set username = ’" + username + "’, realname = ’" + realname + "’, email = ’" + email + "’ where uid = " + this.getUid(username) + ";"; stmt.executeUpdate(update); } else throw new Exception("Username " + username + " is allready in use"); } catch (SQLException ex) { A.2. UsersBean 102 System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } } /** Returns a User @param user The user @param admin The admin who wants the userdata (can be the user) @return A User @throws Exception If user is not allowed to get the user * * * * * * * @ejb.interface-method view-type = "remote" */ public String[] getUser(String username) throws Exception, RemoteException { String select = null; A.2. UsersBean 103 String[] returnUser = null; String str = null; Statement stmt = null; ResultSet rs = null; String iproj = null; try { Connection conn = getConnection(); stmt = conn.createStatement(); select = "select username,realname,email from users where username = ’ rs = stmt.executeQuery(select); if (rs.next()) { returnUser = new String[3]; returnUser[0] = rs.getString("username"); returnUser[1] = rs.getString("realname"); returnUser[2] = rs.getString("email"); } else throw new Exception("User " + username + "does not exist"); } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } A.2. UsersBean } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } return returnUser; } /** * Returns a Vector of Users * * @return A vector of User objects (but only with the Usernames) * @throws Exception If user is not allowed to * * @ejb.interface-method view-type = "remote" */ public Vector getAllUser() throws Exception, RemoteException { String select = null; Vector allUser = new Vector(); Connection conn = getConnection(); if (conn != null) { try { Statement stmt = conn.createStatement(); select = "select uid,username,realname,email from users"; 104 A.2. UsersBean rs = stmt.executeQuery(select); while (rs.next()) { String[] row = new String[4]; row[0] = rs.getString("uid"); row[1] = rs.getString("username"); row[2] = rs.getString("realname"); row[3] = rs.getString("email"); allUser.add(row); } } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } } return allUser; 105 A.2. UsersBean 106 } /** Checks if a user exists and uses the correct password @param User The user @param password The password @return The user who is logged in @throws Exception If user is unable to login * * * * * * * @ejb.interface-method view-type = "remote" */ public Integer login(String userName, String password) { Integer result = new Integer(-1); try { Connection conn = getConnection(); stmt = conn.createStatement(); String sql = "SELECT uid FROM users" + " WHERE username=’" + userName + "’" + " AND password=’" + password + "’"; ResultSet rs = stmt.executeQuery(sql); if (rs.next()) { result = new Integer(rs.getInt("uid")); rs.close(); stmt.close(); conn.close(); } } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); A.2. UsersBean 107 System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } return result; } /** Checks if a user exists in group @param Username The user @param Groupname The groupname @return True if the user part the group else return false @throws Exception If user is unable to login * * * * * * * @ejb.interface-method view-type = "remote" */ public boolean isUserinGroup(String username, String groupname) throws Excepti A.2. UsersBean 108 RemoteException { String sql = "select * from gruops, users where gruops.gname = \’" + groupname + "\’ "; sql += " and group.gid = users.gid "; sql += ("and user.username = \’" + username + "\’ "); System.out.println(sql); boolean ret = false; ResultSet r = null; try { Connection conn = getConnection(); Statement select = conn.createStatement(); sql = new String(sql.getBytes("SJIS"), "8859_1"); r = select.executeQuery(sql); ret = r.first(); select.close(); conn.close(); } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); A.2. UsersBean 109 } catch (SQLException sqlEx) { stmt = null; } } } return ret; } /** Allows the admin to change an Users password @param user The user whos password is going to be change @param admin the admin @param password The new password @throws Exception If user is not allowed to set password * * * * * * * @ejb.interface-method view-type = "remote" */ public void setpassword(String username, String admin, String password) throws Exception, RemoteException { String update = null; try { Connection conn = getConnection(); if (this.checkAdmin(admin)) { stmt = conn.createStatement(); update = "update users set password = sha1(\"" + password + "\") where uid = " + this.getUid(username) + ";"; stmt.executeUpdate(update); } else throw new Exception( "You are not allowed to change the password of user " + username); } catch (SQLException ex) { A.2. UsersBean 110 System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } } /** Allows an user to change his password @param user The user who wants to change his passwod @param password The new password @throws Exception If user is not allowed to set password * * * * * * * @ejb.interface-method view-type = "remote" */ public void userSetpassword(String username, String password) throws Exception RemoteException { String update = null; try { Connection conn = getConnection(); A.2. UsersBean 111 stmt = conn.createStatement(); update = "update users set password = sha1(\"" + password + "\") where uid = " + this.getUid(username) + ";"; stmt.executeUpdate(update); } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } finally { if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { rs = null; } } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { stmt = null; } } } } /** * Returns the Id of a given User * @param user The user * @return The Uid * A.2. UsersBean 112 * @ejb.interface-method view-type = "remote" */ public int getUid(String username) { int re_uid = -1; try { Connection conn = getConnection(); Statement select = conn.createStatement(); String sql = "select * from users where " + "users.username = \"" + username + "\""; sql = new String(sql.getBytes("SJIS"), "8859_1"); ResultSet res = select.executeQuery(sql); if (res.first()) { re_uid = res.getInt("uid"); } select.close(); conn.close(); } catch (Exception err) { System.out.println("Error in SQL query: " + err); } return re_uid; } /** * Checks if the username is already used * @param username The name of the User * @return true, if username is unused, false else */ private boolean userFree(String username) throws Exception, RemoteException { A.2. UsersBean 113 String select; try { Connection conn = getConnection(); stmt = conn.createStatement(); select = ("select * from users where username = ’" + username.trim() + rs = stmt.executeQuery(select); if (rs.next()) return false; else return true; } catch (Exception e) { System.out.println(e.getMessage()); return false; } } /** * * @uml.property name="loggedIn" */ public void setLoggedIn(boolean status) { this.loggedIn = status; } /** * * @uml.property name="pass" */ public void setPassword(String pass) { final String password = null; this.pass = password; A.2. UsersBean 114 } /** * Allows an Admin to set a new admin * * @param newAdmin The new admin * @param admin The admin who wants to set the new Admin * @throws Exception If user is not allowed to set a new admin */ public void setAdmin(String newAdmin, String admin) throws Exception, RemoteException { String update = null; try { Connection conn = getConnection(); if ( checkAdmin(admin)) { if (!(this.userFree(newAdmin))) { stmt = conn.createStatement(); update = "update users set adminflag = \"" + ’a’ + "\" where uid = " + this.getUid(newAdmin) + ";"; stmt.executeUpdate(update); } else throw new Exception(newAdmin + " does not exist!"); } else throw new Exception("You are not allowed to set new Admins"); } catch (Exception e) { System.out.println(e.getMessage()); } } /** * Checks if a user is a global admin * @param admin The (potential) admin A.2. UsersBean 115 * @return true if user is admin, false else */ private boolean checkAdmin(String admin) throws Exception, RemoteException { String select = null; try { Connection conn = getConnection(); stmt = conn.createStatement(); select = ("SELECT * FROM users WHERE username = ’" + admin + "’ AND adminflag = rs = stmt.executeQuery(select); if (rs.next()) return true; else return false; } catch (Exception e) { System.out.println(e.getMessage()); return false; } } /** * Default create method * * @throws CreateException * @ejb.create-method * */ public void ejbCreate() throws CreateException { A.2. UsersBean 116 } /** * (non-Javadoc) * * @see javax.ejb.SessionBean.ejbActivate() */ public void ejbActivate() throws EJBException, RemoteException { // TODO Auto-generated method stub } /** * (non-Javadoc) * * @see javax.ejb.SessionBean.ejbPassivate() */ public void ejbPassivate() throws EJBException, RemoteException { // TODO Auto-generated method stub } /** * (non-Javadoc) * * @see javax.ejb.SessionBean.ejbRemove() */ public void ejbRemove() throws EJBException, RemoteException { // TODO Auto-generated method stub } A.3. PagesBean 117 /** * (non-Javadoc) * * @see javax.ejb.SessionBean.setSessionContext(javax.ejb.SessionContext) */ public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException { // TODO Auto-generated method stub } } A.3. PagesBean /* $Id: PagesBean.java,v 1.10 2004/12/10 20:02:35 bassem Exp $ */ package login.ejb; import import import import import import import import import import import import java.io.UnsupportedEncodingException; java.lang.reflect.InvocationTargetException; java.lang.reflect.Method; java.rmi.RemoteException; java.sql.Connection; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.util.Iterator; java.util.Map; java.util.Vector; import javax.ejb.CreateException; A.3. PagesBean import import import import import import import import import 118 javax.ejb.EJBException; javax.ejb.EJBHome; javax.ejb.EJBObject; javax.ejb.SessionBean; javax.ejb.SessionContext; javax.naming.Context; javax.naming.InitialContext; javax.naming.NamingException; javax.rmi.PortableRemoteObject; /** * @author <a href="mailto:[email protected]">Bassem El-Ahmad</a> * * @ejb.bean name = "Pages" * display-name = "PagesBean EJB" * description = "EJB in order to managed the Page" * view-type = "remote" * jndi-name = "ejbs/Pages" */ public class PagesBean implements SessionBean { private Connection conn; public PagesBean() { super(); // TODO Auto-generated constructor stub } public Connection getConnection() { Map map = null; try { Context context = new InitialContext(); Object ref = context.lookup("funcEJBS/Config"); A.3. PagesBean EJBHome homeObject = (EJBHome) PortableRemoteObject.narrow(ref, EJBHome.class); Method createMethod = homeObject.getEJBMetaData() .getHomeInterfaceClass() .getMethod("create", new Class[] {}); EJBObject ejbObject = (EJBObject) createMethod.invoke(homeObject, new Object[] {}); Method method = homeObject.getEJBMetaData() .getRemoteInterfaceClass().getMethod("getDBConnectionData", new Class[] {}); map = (Map) method.invoke(ejbObject, new Object[] {}); Class.forName((String) map.get("driver")).newInstance(); Connection conn = DriverManager.getConnection((String) map .get("url"), (String) map.get("username"), (String) map .get("password")); return conn; } catch (NamingException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { 119 A.3. PagesBean 120 e.printStackTrace(); } return null; } /** * detects whether a HTML element is visible or not * @throws Exception if group allready exists * @ejb.interface-method view-type = "remote" */ public Boolean isVisible(Integer userID,Integer projectID,Integer pageID, Integer elementID,Vector pageHierarchy){ Vector userGroups = getGroupsForUser(userID.intValue()); Vector elementGroups = getGroupNamesOfElement(elementID,projectID); if(elementGroups.size() == 0){ elementGroups = getGroupNamesOfElement(pageID,projectID); } if(elementGroups.size() == 0){ for(int i = 0; i < pageHierarchy.size() && elementGroups.size() == 0 ; i++){ Integer tmp = (Integer)pageHierarchy.elementAt(i); elementGroups = getGroupNamesOfElement(tmp,projectID); } } return new Boolean(compareGroups(userGroups,elementGroups)); } private boolean compareGroups(Vector userGroups ,Vector elementGroups){ A.3. PagesBean 121 boolean result = false; for (int i = 0; i < userGroups.size(); i++) { if(elementGroups.contains(userGroups.get(i))){ result = true; break; } } return result; } private Vector getGroupsForUser(int uid){ Vector result = new Vector(); String sql = "select gid from groups_users where uid="+uid; boolean ret = false; ResultSet rs = null; //Connection conn = null; try { //conn = getConnection(); Statement select = conn.createStatement(); rs = select.executeQuery(sql); while(rs.next()){ result.add(new Integer(rs.getInt("gid"))); } //iyad:temp loesung jeder User ist auch in der Gruppe //anonymous mitglied, bessere Loesung mit SubGruppen rs = select.executeQuery("select gid from groups where gname=’anonymous’"); if(rs.next()){ result.add(new Integer(rs.getInt("gid"))); } } catch (Exception err) { System.out.println(err.getMessage()); } finally{ //try{ // conn.close(); A.3. PagesBean 122 //}catch(SQLException ex){ //} } return result; } private Vector getGroupNamesOfElement(Integer eid, Integer pid) { Vector groups = new Vector(); Vector gids = new Vector(); ResultSet rs = null; //Connection conn = getConnection(); if (conn != null) { try { Statement stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT gid FROM element_groups where eid="+ eid.intValue()+" AND pid="+pid.intValue()); while (rs.next()){ gids.add(new Integer(rs.getInt("gid"))); } rs = stmt.executeQuery("SELECT gid, gname FROM groups"); while(rs.next()){ Integer i = new Integer(rs.getInt("gid")); if(gids.contains(i)) groups.add(i); } } catch (SQLException ex) { System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } A.3. PagesBean 123 finally { //try { //conn.close(); //} catch (SQLException sqlEx) { //} } } return groups; } /** * Default create method * * @throws CreateException * @ejb.create-method */ public void ejbCreate() throws CreateException { conn = getConnection(); System.out.println("dbConection created for PagesBean"); } /* (non-Javadoc) * @see javax.ejb.SessionBean.ejbActivate() */ public void ejbActivate() throws EJBException, RemoteException { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.ejb.SessionBean.ejbPassivate() */ A.3. PagesBean 124 public void ejbPassivate() throws EJBException, RemoteException { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.ejb.SessionBean.ejbRemove() */ public void ejbRemove() throws EJBException, RemoteException { if(conn != null){ try{ conn.close(); }catch(Exception ex){ System.out.println("Exception while closing the database connection in " +"PagesBean"); System.out.println(ex.getMessage()); } } System.out.println("PagesBean removed"); } /* (non-Javadoc) * @see javax.ejb.SessionBean.setSessionContext(javax.ejb.SessionContext) */ public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException { // TODO Auto-generated method stub } } Literaturverzeichnis [JMS ] : Java Message Service. http://java.sun.com/products/jms/ [RMI ] : The Java Remote Method Invocation. http://java.sun.com/docs/books/tutorial/rmi [JBoss ] : JBoss Developer Zone. http://www.jboss.org [NBT ] : Net Business Tool. http://venus.cs.tu-berlin.de/ [PostgreSQL ] : PostgreSQL. http://www.postgresql.org/ [Rational ] : Rational. http://www.rational.com [UML-Uebersicht ] : Eine recht umfangreiche UML Uebersicht. http://www.jeckle.de/unified.htm [Servlet-Specification ] : 125 Literaturverzeichnis Servlet Specification Version 2.4. http://java.sun.com/products/servlet/ [Together-Technologies ] : Together technologies. http://www.togethersoft.com [XDoclet ] : What is XDoclet. http://xdoclet.sourceforge.net/xdoclet/index.html [Alhir 2003] Alhir, Sinan S. (Hrsg.): Learning UML. O’Reilly, 2003 [Balzert 2001] Balzert, Heide (Hrsg.): UML kompakt. Spektrum, 2001 [Bannert 1999] Bannert, Weitzel M. (Hrsg.): Objektorientierter Softwareentwurf mit UML. Addison-Wesley, 1999 [Denninger 2000] Denninger, S. (Hrsg.): Enterprise JavaBeans. Addison-Wesley, 2000 [Goodwill 2000] Goodwill, James (Hrsg.): Pure JSP – Java Server Pages: A Code-Intensive Premium Reference. Sams, 2000 [Jason Hunter 1998] Jason Hunter, William C. (Hrsg.): Java Servlet Programming. OReilly, 1998 [KOLB 2000] KOLB, DUANE K. FIELDS MARK A. (Hrsg.): Web Development with JavaServer Pages. Manning Publications Co, 2000 126 Literaturverzeichnis 127 [Kurniawann 2002] Kurniawann, Budi (Hrsg.): Java for the Web with Servlets, JSP, and EJB: A Developer’s Guide to J2EE Solutions. New Riders Publishing, 2002 [Monson-Haefel 2000] Monson-Haefel, R. (Hrsg.): 2. Auflage Enterprise JavaBeans. OReilly, 2000 [Perry 2004] Perry, Bruce W. (Hrsg.): Java Servlet and JSP Cookbook. O’Reilly, 2004 [Sams 1999] Sams (Hrsg.): Sams Teach Yourself UML in 24 Hours. Macmillan Computer Publishing, 1999 [Sullins und Whipple May 2003] Sullins, Benjamin G. (Hrsg.) ; Whipple, Mark B. (Hrsg.): EJB Cookbook. Manning Publications Co, May 2003 [Turner March 27, 2002] Turner, James (Hrsg.): MySQL and JSP Web Applications:ata-Driven Programming Using Tomcat and MySQL. Sams Publishing, March 27, 2002 [Zimmermann 2000] Zimmermann, J. (Hrsg.): Verteilte Komponenten und Datenbankanbindung. Addison-Wesley, 2000