Inhaltsverzeichnis

1 Einleitung

2 Architekturmodell

2.1 Komponenten

2.1.1 File-System Version

2.1.2 Datenbank Version

2.2 Verteilung

3 Strukturmodell 3.1 Zentrale Objekte

3.2 Terminverwaltung

3.3 Systemverwaltung

3.4 Server

3.5 Design Patterns

3.5.1 Remote Proxy Pattern

3.5.2 Observer-Pattern

3.5.3 Persistence Broker - Pattern

4 Dynamisches Modell 4.1 Sequenzdiagramme

4.1.1 Termin erfassen

4.1.2 Termin speichern

4.2 Kollaborationsdiagramme

4.2.1 Serverstart

4.2.2 Login

4.2.3 Termin löschen


     
     
      1 Einleitung


    Ziel der vorliegenden Arbeit ist es, Verwendungsmöglichkeiten der Modellierungssprache UML (Unified Modelling Language) in der Design- und Entwurfsphase einer Applikationsentwicklung zu zeigen. Um dies möglichst plastisch zu gestalten, wird der Entwurf eines konkreten Programmes – die Anwendung "Calendarium" – beschrieben.

    Bei dieser Anwendung handelt es sich um einen Web-basierten Kalendermanager, der Termine für mehrere Benutzer verwalten soll. Ausgangsbasis für den Entwurf dieses Programmes sind diverse Analysemodelle, die in [Hitz99] erarbeitet wurden. In diesem Buch dient die Calendarium-Anwendung als veranschaulichendes Beispiel für die Benutzung der Sprache UML.

    In dieser Arbeit sollen nun die Modelle aus der Analysephase detailliert, erweitert und in ein Entwurfsmodell übergeführt werden. Die Vorgehensweise richtet sich dabei nach dem in [Hitz99] erstellten und verwendeten Vorgehensmodell ISP ("Irrational Separated Process"), auch wenn die Autoren betonen, daß es sich – wie auch der gewählte Name nahelegt – bei diesem Vorgehensmodell "keineswegs um einen Versuch handelt, die Welt der objektorientierten Softwareentwicklung »messianisch« mit einem neuen Verfahren zu beglücken" ([Hitz99], S. 171). Relevant für diese Arbeit ist das Vorgehen in der Entwurfsphase, die nach diesem Modell in den Systementwurf und Detailentwurf untergliedert wird.

    Aufgabe des Systementwurfes ist unter anderem die Auswahl der Programmiersprache, der zu verwendenden Persistenzmechanismen und die Art der Verteilung der Applikation auf unterschiedliche Rechner. Ergebnis dieses Systementwurfes ist das Architekturmodell (Abschnitt 2).

    Im Detailentwurf werden das Strukturmodell (Abschnitt 3) und das dynamische Modell (Abschnitt 4) verfeinert, "um die Entscheidungen des Systementwurfes umzusetzen" ([Hitz99], Seite 181). Die Änderungen und Erweiterungen, die in der Entwurfsphase im Rahmen der Verfeinerung vorgenommen werden, sollen durch den Vergleich der Strukturmodelle der Analyse- und Entwurfsphase aufgezeigt und erklärt werden. Im dynamischen Modell soll das Schema komplexer Abläufe exemplarisch dargestellt werden.

    An einigen Stellen dieser Arbeit – vor allem in Abschnitt 4 – werden Ausschnitte aus dem Quellcode der fertiggestellten Anwendung zur deutlicheren Darstellung des Entwurfes angeführt. Dieser Quellcode wurde der dem Buch [Hitz99] beigelegten CD-ROM entnommen.

    Die im Rahmen der vorliegenden Arbeit entwickelten Diagramme des Entwurfsmodelles wurden mit dem Tool Rational Rose 98™ erstellt. Die in diesem Programm verwendete Notation deckt sich leider nicht vollständig mit den Konventionen der UML-Version 1.3, die in [Hitz99] für die Diagramme des Analysemodelles verwendet wurde.
     
     
     

    2 Architekturmodell


    In diesem Kapitel wird die Zuordnung beziehungsweise Zusammenfassung der einzelnen, in den Diagrammen verwendeten Klassen zu Komponenten sowie deren Abhängigkeiten dargestellt (Abschnitt 2.1). Die für die Calendarium-Anwendung gewählte Client/Server-Architektur wird in der Verteilung der Komponenten dargestellt, wobei nicht alle angeführten Hardware-Knoten notwendigerweise auf unterschiedlichen Rechnern laufen müssen (Abschnitt 2.2).


      2.1 Komponenten

      Die gewählte Programmiersprache Java (Java Developement Kit 1.1.8) unterstützt die Einteilung der Klassen in einer besonders klaren Weise, indem diese in Packages zusammengefaßt werden. Wenn die Einteilung in Komponenten auch nicht unbedingt eins zu eins in der späteren Package-Struktur des Java-Codes abgebildet sein muß, so kann ein UML-Komponentendiagramm doch sehr gut als Anhaltspunkt für die Einteilung der Klassen in Packages dienen.

      Der Entwurf der Calendarium-Anwendung sieht für die Persistierung der Daten zwei Versionen vor:

      Diesen beiden Programm-Versionen entsprechend werden zwei unterschiedliche Komponentendiagramme entworfen, die im Folgenden beschrieben werden. Auf den Unterschied zwischen der File-System Version und der Datenbank Version wird an betreffenden Stellen auch in den weiteren Abschnitten einzugehen sein.


        2.1.1 File-System Version


        Abb. 2-1 Architekturmodell – Komponenten (File-System Version)

        Die Zentralen Objekte aus dem Analysemodell werden im Entwurf durch die Basisklassen repräsentiert, die in Abb. 2-1 eine eigene Komponente darstellen, die jedoch keinem speziellen Package zugeordnet werden kann. Auf die einzelnen Basisklassen wird in Abschnitt 3.1 noch näher eingegangen; als typische Vertreter seien an dieser Stelle die Klassen Person und Eintrag erwähnt: Vereinfacht gesagt wird jeder Benutzer durch eine Instanz der Klasse Person repräsentiert, der mehrere Instanzen der Klasse Eintrag zugeordnet werden können. Eine Instanz der Klasse Eintrag entspricht etwa einem Eintrag in einem Kalender.

        Entsprechend der Client/Server-Architektur werden unter anderem zwei Packages, client und server, modelliert. Auf die explizite Darstellung der zentralen Control-Klassen dieser beiden Packages, der Klassen Client und Server, wird verzichtet.

        Server

        Das Package server umfaßt alle Klassen auf der Server-Seite der Calendarium-Anwendung und enthält zwei wesentliche Komponenten: Die Komponente remote umfaßt für jede Basisklasse eine Remote Set Klasse, in der die Instanzen der jeweiligen Basisklasse zentral verwaltet werden. Dies wird im Diagramm durch die Abhängigkeit der Basisklassen von der remote-Komponente dargestellt. Die Funktion der Remote Set Klassen wird im Strukturmodell (Abschnitt 3) noch eingehender erläutert werden.

        Der zweiten Komponente messageServer kommt für die Kommunikation mit der Klienten-Seite zentrale Bedeutung zu (vgl. Abschnitt 3.5.2). Sie besteht nur aus der Klasse MessageServer, wird aber aufgrund ihrer speziellen Funktion und hohen Bedeutung als eigene Komponente dargestellt.

        Client

        Die Klienten-Seite der Calendarium-Anwendung wird durch das Package client repräsentiert. Für den Aufbau der Ansichten ist die Komponente ansicht zuständig. Da zum Erstellen oder Editieren eines Eintrages eigene Detail-Ansichten vorgesehen sind, wird für die Klassen, die für diese Funktionen herangezogen werden, eine eigene Komponente eintrag gebildet.

        Data

        Die Klassen des client-Packages arbeiten nicht direkt mit den Klassen des server-Packages zusammen, sondern bedienen sich hierfür der Komponenten des Packages data: Die Komponenten sets und message dienen als Schnittstelle zu den Komponenten remote und messageServer des server-Packages und kommunizieren mit diesen unter Verwendung der interface-Komponente, die als eine Art Kommunikationsprotokoll fungiert. Das Package data kapselt den Verbindungsaufbau von der Klienten-Seite zum Server und ist somit für die Verwaltung der Instanzen der Basisklassen auf der Klienten-Seite zuständig.

        File-System

        In der File-System Version ist das Package file-system für die Persisitierung der Daten in Form von Dateien zuständig. Für die Funktionalität des Speicherns ist die Komponente outputStream zuständig, die Komponente inputStream wird für das Laden der gespeicherten Daten verwendet. Persistiert werden die Mengen der remote-Komponente, in denen die Instanzen der Basisklassen verwaltet werden.


        2.1.2 Datenbank Version

        Abb. 2-2: Architekturmodell – Datenbank Version

        Durch einen Vergleich der Abb. 2-1 und Abb. 2-2 wird der Unterschied zwischen den beiden Persistenzmechanismen deutlich: Während bei der File-System Version bei einer Datenänderung einer einzelnen Basisklassen-Instanz die gesamte Menge aller Instanzen dieser Basisklasse als File erneut gespeichert werden muß, können Änderungen in einer Datenbank viel gezielter auf Ebene einzelner Basisklassen-Instanzen erfolgen.

        Neben den angeführten Komponenten ClassMappings und PersistenceBroker gibt es noch andere wichtige Klassen, die eine zentrale Rolle für die Datenbankanbindung spielen. Da der Aufbau dieser in Abschnitt 3.5.3 noch detaillierter erklärt wird, beschränkt sich das Komponentendiagramm in Abb. 2-2 auf die Darstellung der beiden genannten Komponenten.

        Die Komponente ClassMappings fungiert als Interpretor der verwendeten Klassennamen und Tabellennamen und stellt so für jede Basisklasse die Verwendung der korrespondierenden Datenbank-Tabelle sicher. Die Komponente PersistenceBroker ist für die Durchführung der SQL-Befehle in der Datenbank verantwortlich.


      2.2 Verteilung

      Abb. 2-3: Architekturmodell – Verteilung

      Die Verteilung der Komponenten auf einzelne Hardware-Knoten erfolgt der Client/Server-Architektur entsprechend getrennt nach Klienten- und Server-Seite, wobei die Verbindung dieser beiden Seiten über das Internet hergestellt wird.

      Die Datenbank wird lokal auf den Server-Rechner positioniert. Diese Lösung gilt auch für die File-System Version, bei der die verwendeten Dateien auf dem Server-Rechner liegen müssen.

      Eine spezielle Rolle nimmt die Klasse Monitor ein, die in erster Linie für den Systemadminstrator zur Wartung der Calendarium-Anwendung herangezogen wird. Sie ist ähnlich wie die Klasse Client aufgebaut und kann als Applikation gestartet werden, verfügt aber hauptsächlich über Wartungsfunktionen. Diese können nur durch lokalen Aufruf der Monitor-Komponente auf dem Server-Rechner durchgeführt werden. Auf die Funktionalität der Klasse Monitor wird in Abschnitt 3.3 noch näher eingegangen.


     
     
      3 Strukturmodell

    Nach dem Entwurf des Architekturmodelles müssen das Strukturmodell und das Dynamische Modell der Analysephase verfeinert werden.

    In diesem Kapitel erfolgt die Verfeinerung der Diagramme des Strukturmodelles

    aus der Analysephase. Neu hinzu kommen das Diagramm "Server" (Abschnitt 3.4), das den Aufbau der Server-Klassen beschreibt, sowie die Darstellung einiger verwendeter Design-Patterns (Abschnitt 3.5).

    Die Verfeinerung der Diagramme besteht unter anderem in der


      3.1 Zentrale Objekte

      Im Diagramm in Abb. 3-1 wird das statische Verhältnis der Zentralen Objekte zueinander dargestellt. Diese Zentralen Objekte werden im Entwurf mit der Bezeichnung "Basisklassen" in einem eigenen Package zusammengefaßt (siehe Abschnitt 2.1).

      Abb. 3-1: Strukturmodell - Basisklassen

      Das Strukturmodell der Zentralen Objekte aus der Analysephase ist in Abb. 3-2 dargestellt:

      Abb. 3-2: Analysemodell - Zentrale Objekte (Abb. 5-12 aus [Hitz99])

        Klasse Eintrag

        Die Klasse Eintrag wurde weitgehend unverändert in den Entwurf übernommen.

        Die Klasse Zusatzinfo aus der Analysephase, die die Angabe einer URL zu einem Eintrag ermöglicht, findet sich in Form des Attributes hyperlink in der Klasse Eintrag des Entwurfsmodelles als einfaches String-Objekt wieder.

        Hingegen wurde das Attribut zeitpunkt als eigene Klasse Datum, die auch für andere Zwecke Verwendung findet, gesondert dargestellt. Der Attributname wurde auf nfktRelevant ("notifikationsrelevant") geändert, um die Funktion dieses speziellen Zeitpunktes deutlicher auszudrücken.

        Jedem Eintrag sind bis zu 3 Instanzen der Klasse Notifikation zugeordnet, deren Versendung zentral durch die Klasse NfktQueue (siehe Abschnitt 3.4) verwaltet wird.

        Die Assoziation verantwortlichFür wird im Entwurfsmodell durch die Rolle owner eines Benutzers, der jeweils einem Eintrag zugeordnet ist, realisiert.

        Das Attribut beschreibung wird näher spezifiziert: einerseits durch eine Kurzbeschreibung (kurzText), die für alle Calendarium-Benutzer sichtbar ist, und andererseits durch eine Detailbeschreibung (langText), die nur Benutzer mit dem entsprechenden Detail-Leserecht sehen können.

        Das Attribut auszeichnung wird nicht in Klasse Eintrag, sondern in der Klasse EintragsTyp als Attribut farbe gespeichert. Wie in der Analyse vorgesehen, ist jedem Eintrag genau ein EintragsTyp zugeordnet, über den somit auch die farbliche Darstellung eines Eintrages bestimmt wird.

        Die mögliche Zuordnung eines Eintrages zu einer Serie ist neu gestaltet: Im Gegensatz zur Modellierung in der Analysephase ist nicht ein Termin oder ToDo-Eintrag Bestandteil einer Serie, sondern wird in der Klasse Eintrag selbst festgehalten, ob diese einer Serie zugeordnet ist. Eine detaillierte Beschreibung der Klasse Serie folgt weiter unten.

        Klasse ToDo

        Die Subklasse ToDoEintrag aus dem Analysemodell wird (mit dem verkürzten Namen ToDo) in das Entwurfsmodell übernommen. Das von Eintrag geerbte Attribut ntfktRelevant vom Typ Datum kennzeichnet den Bezugszeitpunkt für die zugeordneten Notifikationen.

        Ein zusätzliches Attribut erinnernAb (ebenfalls vom Typ Datum) übernimmt die Funktion des Attributes relevantAb der Analysephase und gibt an, ab welchem Tag der ToDo-Eintrag in einer Kalenderansicht sichtbar sein soll.

        Das Attribut erledigt (des Types Boolean) wird im Entwurf fallengelassen: Es gibt keine Möglichkeit, eine ToDo-Instanz als bereits erledigt zu kennzeichnen – für diesen Fall ist vorgesehen, den ToDo-Eintrag zu löschen.

        Klasse Termin

        Die zweite Subklasse Termin, die die Klasse Eintrag spezialisiert, besitzt zusätzlich zum Attribut ntfktRelevant, das für den Beginnzeitpunkt des Termines herangezogen wird, den Endzeitpunkt ende, der ebenfalls durch eine Instanz der Klasse Datum abgebildet wird. Im Unterschied zum Analysemodell werden somit nicht Beginnzeitpunkt und Dauer, sondern Beginn- und Endzeitpunkt gespeichert.

        Das Boolean-Attribut istVerschiebbar wird (mit dem verkürzten Namen verschiebbar) beibehalten.

        Die Selbst-Assoziation kollidiertMit wird durch die Assoziationsklasse Konflikt erweitert und näher spezifiziert: Jede auftretende Terminkollision wird durch eine Instanz der Klasse Konflikt dargestellt (siehe Beschreibung dieser Klasse weiter unten).

        Klasse Serie

        Die Klasse Serie wird – im Gegensatz zur Analysephase – nicht als souveränes Objekt, das die ihm zugeordneten Einträge verwaltet, gestaltet. Im Entwurf wird die Zuordnung einer Eintrag-Instanz zu einer Serie in der Klasse Eintrag gespeichert: Eine Navigation von einer Instanz der Klasse Serie zu den ihr zugeordneten Einträgen ist nicht mehr vorgesehen.

        Die im Analysemodell vorgesehene Subklasse Werktagsserie wird im Entwurf vereinfacht durch das Boolean-Attribut werktags umgesetzt.

        Anstelle des im Analysemodell vorgesehenen Attributes whDauer wird im Entwurf der Gültigkeitszeitraum einer Serie in zwei Attributen (der Klasse Datum) begin und end, die den Anfangs- und Endzeitpunkt der Serie definieren, festgehalten.

        Das Attribut whFrequenz wird im Entwurf verfeinert und durch die Integer-Attribute typ und frequenz verwirklicht. Die Verfeinerung besteht in der Art, daß zunächst zwischen fünf Serientypen unterschieden wird, die jeder für sich eine Frequenz aufweisen können. Das Zusammenspiel der Attribute typ und frequenz wird mit Hilfe ihrer Darstellung durch die Boundary-Klasse SerienPanel deutlicher. Die Beschreibung dieser Klasse erfolgt in Abschnitt 3.2.

        Klasse Konflikt

        Für jede (bei einem Neueintrag oder Änderung eines Termines) auftretende Terminkollision wird eine Instanz der Klasse Konflikt gebildet, in der neben der betroffenen Person auch der Zeitraum des kollidierenden Termin-Objektes durch die beiden Attribute von und bis (als Instanzen der Klasse Datum) gespeichert werden.

        In einem eigenen Boolean-Attribut verschiebbar wird festgehalten, ob der Termin, bei dem es (durch den Neueintrag oder die Änderung) zu einem Konflikt kommt, als "verschiebbar" gekennzeichnet wurde.

        Klasse Notifikation

        Jedem Eintrag sind bis zu drei Instanzen der Klasse Notifikation zugeordnet. Der Teilnehmer, an den die Notifikation ergehen soll, wird im Entwurf nicht mehr in der Notifikation selbst abgelegt, sondern im Eintrag-Objekt, von dem mit der Methode getAllPersonsWithNfkt alle Teilnehmer, die eine Notifikation erhalten sollen, abgefragt werden können (siehe Dynamisches Modell, Abschnitt 4.1.1).

        Der Zeitpunkt, zu dem die Notifizierung erfolgen soll, wird auf die zwei Attribute zeit und zeitEH aufgeteilt und in Form einfacher Integer-Variable gespeichert: Für die Angabe der Zeiteinheit (zeitEH) sind drei Möglichkeiten vorgesehen: Minuten, Stunden und Tage; im Attribut zeit wird die Anzahl für die gewählte Zeiteinheit festgehalten.

        Die Spezialisierung durch die Subklassen Signal, Fax und Email wird im Entwurf vereinfacht und durch das Attribut typ als Integer-Wert realisiert: Dieser Wert wird durch die Klasse MessageServer, die für die Versendung der Notifikationen zuständig ist, entsprechend interpretiert (siehe Abschnitt 3.4).

        Ebenfalls für die Abwicklung der Versendung wird im Entwurf ein Boolean-Attribut erledigt in die Klassen Notifikation aufgenommen, um der Klasse NfktQueue die Unterscheidung zwischen bereits versendeten und noch abzuarbeitenden Notifikationen zu ermöglichen. Die Klasse NfktQueue ist als Teil des Servers für die Verwaltung aller Notifikationen zuständig (siehe Abschnitt 3.4).

        Klasse Teilnehmer

        Die Klasse Teilnehmer erfährt im Entwurf dahingehend eine Änderung, daß sie nicht als Parent-Klasse der Klassen Person und Gruppe fungiert, sondern lediglich stellvertretend für eine Instanz einer dieser beiden Klassen auftritt. Diese Modellierung wurde gewählt, da – entgegen der Annahme in der Analysephase – die Klassen Person und Gruppe kaum Ähnlichkeiten aufweisen.

        Die Klasse Teilnehmer fungiert im Entwurfsmodell als Fassade für diese beiden Klassen: Instanzen der Klassen Termin oder ToDo können einheitliche Methoden für Gruppen und Personen verwenden, die richtige Interpretation erfolgt durch die Klasse Teilnehmer.

        Klasse Person

        Die Klasse Benutzer im Analysemodell wird im Entwurf durch die Klasse Person realisiert. Die Attributmenge wird verfeinert und erweitert: Das Attribut name wird auf vorname und nachname aufgeteilt. In den Attributen email_addr und faxnr werden auch im Entwurf die entsprechenden Adressen gespeichert. Über das Attribut kürzel kann eine Person eindeutig identifiziert werden, da sie mit diesem am Server registriert wird. Für die Anmeldung am System wird auch ein Passwort verwendet, das im Attribut passwd gespeichert wird.

        Analog zum Analysemodell wird jedem Benutzer - als Instanz der Klasse Person - über das Attribut vorzugsNtfkt ein bevorzugter Notifikationstyp zugeordnet.

        Da in Java das Konzept der Metaklasse ("powertype") nicht unterstützt wird, erfolgt dessen Umsetzung durch ein einfaches Integer-Attribut, dessen Wert stellvertretend für einen bestimmten Notifikationstyp gesetzt wird. In der Klasse MessageServer, die für die Versendung der Notifikation zuständig ist, muß sichergestellt werden, daß dieser Integer-Wert entsprechend interpretiert wird.

        Klasse Gruppe

        Im Entwurf wird auf die Verwendung des Composite-Pattern zur Darstellung verschachtelter Personengruppen verzichtet. Die Abbildung solcher Schachtelungen erfolgt in der Klasse Gruppe durch zwei mengenwertige Attribute gruppen und personen: Dem Namen entsprechend wird in diesen auf Instanzen der Klasse Person, die einer Gruppe zugeordnet sind, beziehungsweise auf weitere Instanzen der Klasse Gruppe referenziert.

        Wie bereits im Analysemodell vorgesehen, ist jeder Gruppe eine Instanz der Klasse Person als der Besitzer der Gruppe ihrem Attribut owner zugeordnet. Diese Modellierung ermöglicht es jedem Benutzer, eigene Gruppen zusammenzustellen. Während Gruppen, denen der Systemadministrator als owner zugeordnet ist, jedem Benutzer zur Verfügung stehen, können benutzerdefinierte Gruppen nur von derjenigen Person verwendet werden, die sie erstellt hat.

        Klasse Rechte

        Da in Java drei-wertige Assoziationen nicht unterstützt werden, muß die Beziehung zwischen zwei Benutzern (in den Rollen bewilliger und berechtigter) und der Klasse EintragsTyp, die im Analysemodell durch die Assoziationsklasse Berechtigung definiert werden soll, im Entwurf aufgelöst werden:

        In der neu eingeführten Klasse Rechte wird anhand des Integer-Attributes rechtsIndex die Art einer Berechtigung definiert. Es wird zwischen Leserecht, Detail-Leserecht und Eintragsrecht unterschieden, wobei jeder Benutzer das einfache Leserecht für alle Einträge aller anderen registrierten Benutzer besitzt. Detail-Leserechte und Eintragsrechte müssen hingegen explizit vergeben werden:

        Jeder Benutzer (als Instanz der Klasse Person) kann jeder anderen Person für einen bestimmten EintragsTyp eine Berechtigung erteilen. Im Entwurfsmodell wird zusätzlich auch die Erteilung von Rechten für ganze Gruppen vorgesehen. Jede Berechtigung wird als Instanz der Klasse Rechte mit den Attributen

        • sender für die Person, die die Berechtigung vergibt,
        • rechtsIndex für die Art der Berechtigung (Detail-Leserecht oder Eintragsrecht),
        • receiver_person oder receiver_gruppe für die Person oder Gruppe, der eine Berechtigung erteilt wird, und
        • eintragsTyp für den Eintragstyp, für den die Berechtigung gelten soll, gespeichert.

        Klasse EintragsTyp

        Die Klasse EintragsTyp wird aus dem Analysemodell übernommen und neben dem String-Attribut bezeichnung um das Attribut farbe erweitert: Jeder Eintrag ist einem bestimmten EintragsTyp zugeordnet und wird – abhängig von diesem – mit einem bestimmten Farbbalken dargestellt.

        Während die Menge verfügbarer Eintragstypen vom Systemadministrator vorgegeben wird, kann die Farbzuordnung von jedem Benutzer individuell geändert werden: Für jeden im System registrierten Benutzer wird eine eigene Menge von Eintragstypen mit dessen individueller Farbzuordnung gespeichert (siehe Server, Abschnitt 3.4).


      3.2 Terminverwaltung

      Im folgenden Diagramm Terminverwaltung (Abb. 3-3) werden einerseits die Klassen dargestellt, die für die Anzeige der einzelnen Kalenderansichten (Tages-, Wochen-, Monats- und Jahresansicht) entworfen werden, andererseits werden auch die Control- und Boundary-Klassen angeführt, die für die Erstellung oder Änderung der Eintragsobjekte benötigt werden.

      Abb. 3-3: Strukturmodell – Terminverwaltung

      Zur besseren Vergleichsmöglichkeit ist das Analysemodell in Abb. 3-4 dargestellt:

      Abb. 3-4: Terminverwaltung (Abb. 5-13 aus [Hitz99])

      Die Funktionalität der zentralen Calendarium-Klasse aus dem Analysemodell wird im Entwurf durch die Klasse Client konkretisiert. Diese enthält daher auch die Methode main, durch die das Calendarium-Programm auf der Klienten-Seite gestartet wird.

      Im Unterschied zur Klasse Calendarium steht die Client-Klasse mit den Instanzen der Basisklassen nicht direkt in Verbindung, sondern werden diese in der Klasse Data in einzelnen Sets gekapselt. Die Klasse Data stellt auch die Verbindung des Klienten zum Server her (siehe Remote Proxy Pattern, Abschnitt 3.5.1).

      Im Entwurf ergeben sich einige Änderungen in der Aufgabenverteilung bezüglich Boundary- und Control-Klassen: Der Aufbau der Oberflächenelemente wird durch verschiedene Boundary-Klasse verfeinert (siehe Beschreibung der Klassen EintragsObjekt, ListeObjekt, EditEintrag und deren Panels ), die Klasse Ansicht und deren Subklassen übernehmen einen Großteil der Control-Funktionalität (siehe Beschreibung der Klasse Ansicht).

        Client

        Wie bereits oben erwähnt stellt klientenseitig die Klasse Client die zentrale Control-Klasse der Calendarium-Anwendung dar. Als solche delegiert sie zeitweise die Kontrolle an weitere Control-Klassen. Die Trennung in Control- und Boundary-Klassen erfolgt in den Diagrammen jedoch nur nach der vorrangigen Funktionalität der jeweiligen Klasse: Die meisten Boundary-Klassen werden auch Control-Funktionalität und umgekehrt einige Control-Klassen auch Boundary-Funktionalität aufweisen.

        Neben der Verwaltung der Ansichten ist die Klasse Client vor allem für die Verarbeitung der Befehle, die ein Benutzer über die Auswahl eines Menüeintrages erteilt, verantwortlich.

        Ansicht

        Die abstrakte Klasse Ansicht übernimmt – im Gegensatz zum Analysemodell – vorrangig Control-Funktionen, indem sie die Verbindung zwischen den Daten der Termin- und ToDo-Einträge und deren graphische Darstellung durch die Boundary-Klassen bildet. Der Umfang des Zeitraumes, dessen Einträge angezeigt werden sollen, sowie die Art der Darstellung hängt dabei von der jeweiligen Unterklassen-Ausprägung ab. Vier Ansichten stehen zur Verfügung:

        • Tagesansicht,
        • Wochenansicht,
        • Monatsansicht und
        • Jahresansicht.

        In den Attributen bgnAnsicht und endAnsicht werden das Beginn- und das Enddatum als Instanzen der Klasse Datum gespeichert, die entsprechend der Ansicht-Subklasse den jeweiligen Zeitraum definieren, dessen Einträge angezeigt werden müssen.

        Die Boundary-Klasse Tag zur Darstellung eines Tages wird aufgrund der unterschiedlichen Darstellungsarten (je nach gewählter Ansicht) im Entwurf fallengelassen.

        Die jeweilige Unterklasse Tages-, Wochen-, Monats- oder Jahresansicht greift in ihrer Funktion als Control-Klasse auf die anzuzeigenden Daten zu. Analog zur Klasse Client erfolgt dies wieder über die Attribute termine (vom Typ TerminSet) und toDo (vom Typ ToDoSet) der Klasse Data.

        Besonders deutlich wird die Control-Funktionalität dadurch, daß die einzelnen Kalender, deren Einträge angezeigt werden sollen, nicht (wie im Analysemodell vorgesehen) als eigene Control-Klasse Kalender entworfen werden. Vielmehr besitzt die Klasse Ansicht ein Attribut openCal (als Instanz der Klasse OffeneKalender), in dem die Personen, deren Einträge angezeigt werden sollen, in einer Liste vermerkt werden. Die Auswahl dieser anzuzeigenden "Kalender" erfolgt über das Boundary-Element KalenderAuswahl, das als Attribut selKalender Bestandteil der Klasse Ansicht ist.

        EintragsObjekt

        Die abstrakte Klasse EintragsObjekt stellt die Parent-Klasse der beiden Boundary-Klassen TerminObjekt und ToDoObjekt dar: Das Parent-Child-Verhältnis der Klassen Eintrag, Termin und ToDo findet sich somit im Entwurf in "ihren" Boundary-Klassen wieder. Im Unterschied dazu werden die Control-Klassen EditTerminControl und EditToDoControl, die bei der Erstellung oder Änderung eines Eintrages die Control-Funktionalität übernehmen, nicht von einer gemeinsamen Parent-Klasse abgleitet, wie dies in der Analyse noch geplant war.

        Jeder Eintrag wird durch eine Instanz der entsprechenden Unterklasse TerminObjekt oder ToDoObjekt dargestellt.

        Abgesehen von dieser neuen Klassenstruktur wird im Entwurf die Gestaltung der Boundary-Ebene weiter verfeinert:

        Eine Referenz des Attributes openCal der Ansicht wird in einem eigenen Attribut (ebenfalls mit dem Namen openCal) gespeichert: Über dieses Attribut wird die farbliche Kennzeichnung eines Eintrages (in Abhängigkeit von dessen owner) bestimmt und die anzuzeigenden Kalender erfragt: Davon hängt nämlich ab, welche Kürzel (über das String-Attribut kürzel) eines Eintrages angeführt werden.

        ListeObjekt

        Die abstrakte Klasse ListeObjekt beziehungsweise ihre konkreten Subklassen TerminListeObjekt, ToDoListeObjekt und ToDoTerminListeObjekt dienen der Darstellung mehrerer EintragsObjekt-Instanzen. Diese Klassen übernehmen im Entwurf (gemeinsam mit der Klasse Eintragsobjekt und deren Subklassen) den Großteil der Boundary-Funktionalität der Ansicht-(Sub)Klassen des Analysemodelles.

        Je nach Ansicht-Unterklasse werden eine unterschiedliche Anzahl und Unterklassen-Ausprägung der Klasse ListeObjekt herangezogen:

        • Die Tagesansicht verwaltet zwei Instanzen: Ein TerminListeObjekt beinhaltet für jeden Termin des angezeigten Tages ein TerminObjekt, ein ToDoListeObjekt beinhaltet für jeden ToDo-Eintrag, dessen erinnernAb-Zeitpunkt kleiner oder gleich dem Tagesdatum ist, ein ToDoObjekt.
        • Die Wochenansicht besitzt für jeden der sieben Wochentage je ein TerminListeObjekt und ein ToDoListeObjekt mit den entsprechenden EintragsObjekt-Instanzen.
        • Die Monatsansicht besitzt für jeden Tag eines Monats (also zwischen 28 und 31) je ein ToDoTerminListeObjekt, in dem – wie der Name nahelegt – die Termine und anzuzeigenden ToDo-Einträge eines Tages zusammengefaßt angezeigt werden. Die einzelnen Einträge werden auch hier mit Hilfe von Instanzen der entsprechenden Unterklasse von EintragsObjekt dargestellt.
        • In der Jahresansicht werden keine einzelnen Einträge angezeigt, weshalb für diese Ansicht auch keine ListeObjekt-Unterklasse vorgesehen ist. In der Jahresansicht wird nur durch farbliche Kennzeichnung vermerkt, ob es für einen Tag einen Eintrag gibt.

        Abb. 3-5: Boundary-Klasse Jahresansicht

        EditTerminControl & EditToDoControl

        Wird ein neuer Eintrag erstellt oder werden die Detail-Daten eines bestehenden angezeigt, um sie eventuell zu editieren, übergibt der Client die Control-Funktionalität an die Control-Objekte EditTerminControl beziehungsweise EditToDoControl, die eigens für diesen Zweck instanziiert werden. Im Gegensatz zum Analysemodell gibt es jedoch im Entwurf keine gemeinsame Parent-Klasse C_Eintrag dieser beiden Control-Klassen.

        Diese beiden Klassen sind für die Instanziierung der entsprechenden Boundary-Subklassen von EditEintrag (EditTermin beziehungsweise EditToDo) zuständig. Der dynamische Aspekt des GUI-Aufbaues (durch die Boundary-Klassen) wird in Abschnitt 4.1.1 näher beschrieben.

        Als Control-Klassen bilden EditTerminControl und EditToDoControl die Schnittstelle zu den jeweils entsprechenden Entity-Klassen Termin beziehungsweise ToDo und sind nach Abschluß und Bestätigung der Benutzereingaben für die Erstellung oder Änderung derselben zuständig: Diese Aufgaben werden durch die Methoden createTermin (beziehungsweise createToDo) und updateTermin (beziehungsweise updateToDo) realisiert. Auch hierfür werden die dynamischen Aspekte im Dynamischen Modell (Abschnitt 4.1.1) näher beschrieben.

        Terminvorschlag

        Tritt beim Versuch, einen neuen Termin zu erstellen, ein Konflikt mit bereits bestehenden Terminen auf, so soll in einem bestimmten Zeitraum nach alternativen Möglichkeiten für diesen Termin gesucht werden können. Im Anlaysemodell sind dafür eine eigene Control-Klasse C_Vorschlag und zwei Boundary-Klassen SuchSpezifikation und Terminvorschlag vorgesehen. Im Entwurf stellt sich allerdings heraus, daß für die Modellierung einer Suche nach einem alternativen Termin eine einzige Boundary-Klasse, die gleichzeitig auch die Control-Funktion übernimmt, ausreicht. Die eigentliche Such-Funktionalität wandert in die Klasse TerminSetRemote, in der alle eingetragenen Termine gespeichert werden.

        Übrig bleibt lediglich die Klasse Terminvorschlag: Sie dient zunächst der Eingabe eines Suchzeitraumes. Mit diesen Daten wird die Methode getFreeOfKonflikte der Klasse TerminSet parametrisiert. Diese Assoziation mit der Klasse TerminSet ist im Diagramm aus Gründen der besseren Übersichtlichkeit nicht dargestellt. Auch die Anzeige des Suchergebnisses wird durch die Klasse Terminvorschlag realisiert.

        EditEintrag

        Während für die beiden Control-Klassen der Eintrag-Subklassen eine eigene Vererbungshierarchie im Entwurf wegfällt, wird eine solche für die Boundary-Klassen, die für die Darstellung eines Eintrages zuständig sind, eingeführt (vgl. Klasse EintragsObjekt oben). Diese Vererbungshierarchie der Klasse Eintrag wird auch für die Detail-Anzeige mit Hilfe der Klasse EditEintrag und ihren Subklassen EditTermin und EditToDo auf der Boundary-Ebene nachvollzogen. Die Beziehung der Entity-Klasse Eintrag zu den jeweiligen Boundary-Klassen für die normale Anzeige (EintragsObjekt) und für die Detail-Anzeige (EditEintrag) werden in Abb. 3-6 dargestellt:

        Abb. 3-6: Entity- und Boundary-Klassen für Eintrag

        Die Detail-Darstellung eines Eintrages durch Boundary-Klassen erfährt im Entwurf eine weitgehende Verfeinerung: Sie erfolgt durch die drei Panels

        • AllgemeinPanel, das ebenfalls durch zwei Subklassen TerminAllgPanel und ToDoAllgPanel spezialisiert wird,
        • TeilnehmerPanel und
        • Serienpanel.

        Die Klasse EditEintrag (beziehungsweise eine ihrer Subklassen) ist für die Erstellung und Verwaltung dieser drei Panels verantwortlich. Ihre Inhalte werden im Folgenden kurz beschrieben, der dynamische Aufbau hingegen wird in Abschnitt 4.1.1 dargestellt.

        AllgemeinPanel

        Die Boundary-Klasse AllgemeinPanel dient der Anzeige der Eckdaten eines Eintrages und ermöglicht deren Eingabe. Die Darstellung wird in erster Linie durch die Verwendung von Java Swing-Klassen realisiert. Die Klasse AllgemeinPanel weist folgende Attribute auf:

        • kurzText und langText sind Instanzen der Swing-Klasse JTextField und dienen der Eingabe und Anzeige der Kurz- und Detailbeschreibung eines Eintrages.
        • ort und hyperlink sind ebenfalls Instanzen der Swing-Klasse JTextField und werden zur Eingabe und Anzeige der entsprechenden (namensgleichen) Attribute eines Eintrages verwendet (vgl. Abschnitt 3.1).
        • Eine Instanz der Swing-Klasse JCheckBox mit dem Namen serienFlag dient der Kennzeichnung, ob ein Eintrag einer Serie zugeordnet ist.
        • Die verfügbaren Ausprägungen des EintragsTyp werden durch eine Swing-Klasse JComboBox zur Auswahl gestellt. Dieses Attribut trägt sinngemäß den Namen eintragsTyp.
        • Die Möglichkeit, für einen Eintrag bis zu drei Notifikationen festzulegen, wird dem Benutzer über drei Instanzen der Klasse NfktPanel zur Verfügung gestellt: Diese Boundary-Klasse kapselt Radio-Buttons (der Swing-Klasse JRadioButton), über die die Notifikationsart eingestellt werden kann (siehe Abb. 3-7). Ein JTextField dient der Eingabe der genauen Zeit und eine JComboBox der Definition der Zeiteinheit Minute, Stunde oder Tag.

        Abb. 3-7: Boundary-Klasse NfktPanel

        • Die Datum-Attribute ntfktRelevant und ende (bei einem Termin) beziehungsweise erinnernAb (bei einem ToDo-Eintrag) werden jeweils über Instanzen der Klasse DatePanel angezeigt beziehungsweise edititert. Diese Boundary-Klasse wird überall dort eingesetzt, wo ein Datum durch den Benutzer definiert werden kann.

        Während die Subklasse ToDoAllgPanel keine eigenen Attribute zu den geerbten hinzufügt, sind in der Klasse TerminAllgPanel zusätzlich folgende Felder definiert:

        • Da für einen Termin nicht nur das Datum, sondern auch die Zeit eingegeben werden kann, verfügt die Klasse TerminAllgPanel zusätzlich zu den beiden DatePanel über zwei Instanzen der Klasse TimePanel, die der Anzeige beziehungsweise Eingabe der Uhrzeit des Beginn- und Endzeitpunktes eines Termines dienen.
        • Aus den Feldern des Beginn- und Endzeitpunktes eines Termines wird das Feld dauer errechnet und in Stunden angezeigt, wobei Stundenbruchteile als Dezimalzahlen dargestellt werden.
        • Durch eine zweite JCheckBox verschiebbar kann ein Termin als Fixtermin oder verschiebbarer Termin spezifiziert werden.

        Die vollständige Ansicht AllgemeinPanel ist in Abb. 3-8 dargestellt. Da TerminAllgPanel alle Felder des ToDoAllgPanel inkludiert und darüberhinaus weitere Felder aufweist, wird nur ersteres dargestellt. Die Unterschiede zur Klasse ToDoAllgPanel können den oben angeführten Attribut-Listen entnommen werden.

        Abb. 3-8: Boundary-Klasse TerminAllgPanel

        TeilnehmerPanel

        Als zweites Panel dient das TeilnehmerPanel zur Anzeige oder Definition der Teilnehmer eines Eintrages: Ein Teilnehmer kann entweder eine Instanz der Klasse Person oder eine Instanz der Klasse Gruppe sein. Dementsprechend ermöglicht die Boundary-Klasse TeilnehmerPanel einerseits die Auswahl von Personen, andererseits die Auswahl von Gruppen. Diese Auswahl-Funktionalität wird von der (im Diagramm in Abb. 3-3 nicht dargestellten) Parent-Klasse BasicSelection zur Verfügung gestellt:

        Die Darstellung aller verfügbaren Personen erfolgt mit Hilfe der Boundary-Klasse SelectListEntry, alle verfügbaren Gruppen – das sind vom Systemadministrator vordefinierte Gruppen und vom Benutzer selbsterstellte Gruppen – werden mit Hilfe der Boundary-Klasse SelectTreeEntry dargestellt. Auch diese Hilfsklassen sind im Entwurfsmodell der Terminverwaltung (Abb. 3-3) nicht enthalten, da ansonsten das Diagramm zu komplex und unübersichtlich werden würde. Im Diagramm der Systemverwaltung in Abschnitt 3.3 erfolgt eine nähere Beschreibung der Parent-Klasse BasicSelection sowie der Klassen SelectListEntry und SelectTreeEntry.

        Im Entwurfsmodell der Terminverwaltung reicht es daher aus, das TeilnehmerPanel zur Auswahl aller Teilnehmer eines Eintrages – Personen und Gruppen – zu erwähnen, ohne näher auf die für diese Auswahl verwendeten Mechanismen einzugehen.

        Abb. 3-9: Boundary-Klasse TeilnehmerPanel

        SerienPanel

        Das dritte Panel, das zur Anzeige und Definition eines Eintrages verwendet wird, dient der Spezifizierung einer Serie. Entsprechend der Attribute der Klasse Serie begin, end, typ, frequenz und werktags (vgl. Zentrale Objekte, Abschnitt 3.1) werden folgende Boundary-Attribute zu deren Definition verwendet:

        • Die Attribute begin und end werden als Instanzen der Klasse Datum über das bereits von der Klasse AllgemeinPanel bekannte DatePanel eingegeben.
        • Jeder Serie wird ein bestimmter Typ zugeordnet. Grundsätzlich werden vier Typen unterschieden: täglich, wöchentlich, monatlich und jährlich. Die Darstellung (und Auswahl eines) dieser Typen erfolgt durch die Swing-Klasse JRadioButton. Bei Auswahl des Types "wöchentlich" besteht zusätzlich die Möglichkeit, die Wochentage anzugeben, für die der Serieneintrag erstellt werden soll. Beim Typ "monatlich" wird zwischen absolutem und realtiven Monatsdatum unterschieden: In Abb. 3-10 beginnt die Serie am Montag, dem 7. Februar 2000; für eine Serie mit Typ "monatlich" steht daher neben dem absoluten Monatstag 7 auch der erste Montag eines Monats als relatives Datum zur Verfügung.
        • Jeder Typ kann weiters durch die Angabe einer Frequenz näher bestimmt werden: So kann zum Beispiel mit Hilfe des Attributes frequenz festgelegt werden, ob ein Eintrag für jeden Tag, jeden zweiten oder etwa nur für jeden fünften Tag erstellt werden soll. Die Klasse SpinZahlenfeld stellt eine ansprechende Oberflächen-Darstellung zur Definition einer Integer-Zahl für die gewünschte Frequenz zur Verfügung.
        • Die Unterscheidung zwischen Werktagsserien und Serien, für die auch an Sonn- und Feiertagen Einträge erstellt werden sollen, erfolgt durch Auswahl eines JRadioButton.

        Abb. 3-10: Boundary-Klasse Serienpanel


      3.3 Systemverwaltung

      Im Entwurf wird die Funktionalität der Systemverwaltung getrennt nach der Art des Benutzers betrachtet:

      Der Systemadministrator kann systemweite Einstellungen, die alle Benutzer betreffen, vornehmen. Dafür steht ihm eine besondere Control-Klasse Monitor zur Verfügung. Diese ist ähnlich der Klasse Client konstruiert, verfügt aber über andere Menüpunkte und besitzt keine Ansichten zur Anzeige von Einträgen. Folgende Aufgaben werden über die Klasse Monitor durch den Systemadministrator bewerkstelligt:

      • Der Systemadministrator muß jeden Benutzer mit seinen Stammdaten als eine Instanz der Klasse Person registrieren. Dazu gehört auch die Vergabe eines Kürzels und des Passwortes.
      • Neben dieser Definition von Instanzen der Klasse Person kann der Administrator aber auch Instanzen der Klasse Gruppe definieren, die jedem Benutzer bei der Zuordnung von Teilnehmern zu Einträgen zur Verfügung stehen.
      • Auch die Definition der einzelnen Eintragstypen, die einem Eintrag zugeordnet werden können, erfolgt durch den Systemadministrator.
      • Feiertage müssen ebenfalls durch den Systemadministrator definiert werden.

      Jeder Benutzer der Calendarium-Anwendung kann mit Hilfe des Menüs "Verwalten" der Klasse Client seine eigenen Systemeinstellungen vornehmen: Diese umfassen folgende Möglichkeiten:

      • Für die Vergabe von Berechtigungen, die über das normale Leserecht hinausgehen, ist jeder Benutzer selbst verantwortlich. Dies betrifft also die Zuteilung von Detail-Lese- und Eintragsrechten für andere Benutzer oder Gruppen.
      • Jeder Benutzer kann seine eigenen, ihn betreffenden Stammdaten ändern.
      • Zusätzlich zu den global verfügbaren Gruppen kann jeder Benutzer seine eigenen Gruppen zusammenstellen. Diese stehen ausschließlich ihm zur Auswahl.
      • Die vom Administrator bestimmten Farben für einzelne Eintragstypen kann jeder Benutzer indivduell abändern und eine eigene Farbzuordnung erstellen. Die Definition eigener Eintragstypen hingegen ist nicht vorgesehen.

      Den Platz der steuernden Klasse Calendarium aus dem Analysemodell nimmt – wie bei der Terminverwaltung – die Control-Klasse Client ein. Für den Systemadministrator wird eine spezielle Klasse Monitor entworfen, die die Funktionalität für die Systemwartung zur Verfügung stellt. Wie bereits in Abschnitt 3.2 erwähnt, besteht zwischen der Klasse Client und den einzelnen Basisklassen keine direkte Verbindung, sondern wird diese über die Klasse Data hergestellt. Diese Modellierung wird auch für die Klasse Monitor gewählt.

      Die Teilgebiete der Systemverwaltung Eintragstyp, Benutzer, Personengruppe und Berechtigung werden (mit teilweise geänderten Namen) aus dem Analysemodell übernommen. Während die Systemverwaltung im Entwurfsmodell um die Definition der Feiertage erweitert wird, entfallen die Gebiete Druck und Export.

      In Abb. 3-11 sind die einzelnen Klassen der Systemverwaltung dargestellt:

      Abb. 3-11: Klassendiagramm – Systemverwaltung

      Abb. 3-12 zeigt das Strukturmodell für die Systemverwaltung aus der Analysephase.

      Abb. 3-12: Analysemodell – Systemverwaltung (Abb. 5-13 aus [Hitz99])

        EditUser

        Jeder Benutzer muß, um sich am System anmelden und mit dem Calendarium-Programm arbeiten zu können, als Instanz der Klasse Person im System registriert sein. Dies wird durch den Systemadministrator vorgenommen. Jeder Anwenders kann, sobald er durch den Systemadministrator als Instanz der Klasse Person registriert ist, seine eigenen Benutzerdaten selbst ändern.

        Die Control-Klasse für den Bereich der Personenverwaltung erhält im Entwurf den Namen EditUserControl und wird – wie im Analysemodell – durch die übergeordnete Control-Klasse instanziiert – im Entwurf sind das die Klassen Client oder Monitor.

        Im Fall eines "gewöhnlichen" Anwenders, das heißt ohne Administratoren-Berechtigung, werden die Daten der Entity-Klasse Person, nämlich die des Anwenders, in der Boundary-Klasse EditOneUser angezeigt und können editiert werden. Diese Klasse zeigt die Attribute der Klasse Person (kürzel, name, email etc.) mit Hilfe verschiedener Swing-Klassen (wie zum Beispiel JTextField oder JCombo-Box) an.

        Etwas komplizierter gestaltet sich dieser Vorgang beim Administrator: Diesem werden zunächst in der Boundary-Klasse EditAllUsers über eine Hilfsklasse SelectListEntry alle in der Calendarium-Anwendung registrierten Personen angezeigt.

        Die Klasse SelectListEntry kapselt die (Boundary-)Funktionalität zur Anzeige aller im System registrierten Personen in einer Liste und die (Control-)Funktionalität der Auswahl einer Person. Die ausgewählte Person wird der Klasse EditUserControl übergeben und ebenfalls durch die Boundary-Klasse EditOneUser angezeigt.

        Über einen speziellen Push-Button kann der Systemadministrator auch eine Instanz der Klasse EditOneUser öffnen, deren Attribute leer sind und auf diese Weise einen neuen Benutzer hinzufügen.

        EditTypen

        Entgegen dem Analysemodell werden die Terminfarben nicht in den Einstellungen (als eigene Basisklasse) gespeichert, sondern als eigenes Attribut farbe in der Klasse EintragsTyp definiert. Neben den vom Systemadministrator definierten Default-Eintragstypen, die in der Klasse TypSetRemote im Attribut defaultTyps gespeichert sind, gibt es auch für jeden registrierten Anwender eine eigene Menge von Eintragstypen (mit dem Namen userTypes), in denen über das Attribut farbe die anwenderspezifische Farbzuordnung abgelegt wird. Dieser Entwurf wurde deshalb gewählt, da es außer dieser Farbzuordnung keine weiteren anwenderspezifischen Einstellungen gibt, für die sich die Modellierung einer eigenen Basisklasse Einstellungen gelohnt hätte, wie dies im Analysemodell noch vorgesehen war. Andere Systemeinstellungen, wie zum Beispiel Gruppendefinitionen und Berechtigungen, werden direkt in den jeweiligen Basisklassen abgelegt.

        Bei der Verwaltung der Eintragstypen wird die Control-Funktionalität von der Klasse EditTypenControl übernommen. Die verfügbaren Eintragstypen werden durch die Boundary-Klasse EditTypen angezeigt. Wurde die Control-Klasse durch die Klasse Client instanziiert, so steht dem Benutzer nur ein Push-Button mit dem Label "Farbe ändern" zur Verfügung. Dem Administrator hingegen werden die Push-Buttons "Neuer Typ", "Typ ändern" und "Typ löschen" zur Auswahl angeboten. Diese Unterscheidung wird durch unterschiedliche Konstruktor-Methoden der Klassen EditTypenControl und EditTypen verwirklicht. Die Anzeige der vorhandenen Eintragstypen und der jeweils zugeordneten Farbe erfolgt durch die Swing-Klasse JList, für deren Befüllung die Daten der Klasse TypSet herangezogen wird.

        Die Attribute eines Eintragstypes, bezeichnung und farbe, werden in der Boundary-Klasse EditTyp editiert beziehungsweise definiert. Neben einem einfachen JTextField zur Eingabe der Bezeichnung wird eine Instanz der Swing-Klasse JColorChooser zur Definition der gewünschten Farbe benutzt. Dieser übernimmt die gesamte Funktionalität zur Definition einer individuellen Farbe.

        EditGruppen

        In der Calendarium-Anwendung gibt es einerseits Gruppen, die durch den Systemadministrator erstellt werden und von allen registrierten Benutzern verwendet werden können, und andererseits benutzerdefinierte Gruppen, die nur für den Benutzer zugänglich sind, der sie erzeugt hat. Dieser Eigentümer einer Gruppe wird in deren Attribut owner gespeichert.

        Für die Definition und Wartung einer Gruppe kommen jedoch für den Systemadministrator und alle anderen Benutzer die gleichen Klassen und Mechanismen zur Anwendung: Im Gegensatz zum Analysemodell wird auch für die Verwaltung der Gruppen eine eigene Control-Klasse eingeführt: Die Kontrolle wird vom Client an eine Instanz der Klasse EditGruppenControl delegiert. Die Anzeige erfolgt – analog zum Analysemodell - durch eine Boundary-Klasse EditGruppen. Der Aufbau dieser Anzeige wird im Entwurf aber weiter detailliert:

        Die Klasse SelectTreeEntry stellt – ausgehend von einer Basis-Gruppe – alle verfügbaren Gruppen in einer Baumstruktur dar. Jede neue Gruppe muß einer bereits bestehenden Gruppe zugeordnet werden. Die Basis-Gruppe bildet somit die Wurzel, an die alle weiteren Gruppen angehängt werden. Eine Gruppe kann Instanzen der Klasse Person, aber auch weitere Gruppen beinhalten.

        Dem Systemadministrator werden ausschließlich die vorhandenen Systemgruppen angezeigt. Diese bauen auf einer Basis-Gruppe mit dem Namen "ROOT" auf. Durch Zuordnung weiterer Gruppen zu dieser Basis-Gruppe kann die Menge allgemein verfügbarer Gruppen vom Systemadministrator beliebig erweitert werden. Die Basis-Gruppe "ROOT" wird nur dem Systemadministrator angezeigt, andere Benutzer sehen erst die weiteren durch den Systemadministrator definierten Gruppen-Ebenen.

        Zusätzlich zu den allgemein verfügbaren Gruppen wird jedem Benutzer eine zweite Basis-Gruppe mit dem Namen "Eigene Gruppen" angezeigt. Auf dieser aufbauend kann jeder Benutzer – analog zur oben beschriebenen Methodik der Gruppen-Definition – seinen eigenen Gruppen-Baum definieren. Für jeden registrierten Benutzer exisitiert eine eigene Basis-Gruppe, dessen owner eben dieser Benutzer ist. Diese Basis-Gruppe "Eigene Gruppen" steht ausschließlich seinem owner zur Verfügung.

        Da diese Konstruktion einerseits einen hohen Detaillierungsgrad aufweist, andererseits auch dynamische Aspekte beinhaltet, scheint sie im Strukturmodell der Systemverwaltung nur in Form der Klasse SelectTreeEntry auf. Diese kapselt die (Boundary-)Funktionalität zum Aufbau der Anzeige der Gruppen in Form eines Verzeichnisbaumes und übernimmt auch die (Control-)Funktionalität zur Rückgabe der selektierten Gruppe.

        Neben der Boundary-Klasse EditGruppen, die mit Hilfe der Klasse SelectTreeEntry für die Anzeige der verfügbaren Gruppen zuständig ist, gibt es im Entwurfsmodell eine weitere Boundary-Klasse EditGruppe, die für die Darstellung einer einzelnen Gruppe, nämlich der zuvor durch den Benutzer selektierten Gruppe, verwendet wird. Auch diese Klasse wird im Entwurfsmodell nicht näher detailliert dargestellt, da das Diagramm ansonsten zu komplex würde. Da eine Gruppe sowohl Personen als auch weitere Gruppen beinhalten kann, müssen einerseits alle registrierten Benutzer und andererseits alle verfügbaren Gruppen zur Auswahl stehen. Die eben beschriebene Klasse SelectTreeEntry stellt den Mechanismus für die Gruppenauswahl zur Verfügung. Für die Auswahl von Personen wurde bereits die Hilfsklasse SelectListEntry vorgestellt. Eine weitere Hilfsklasse BasicSelection kombiniert diese beiden Auswahlmechanismen. Sie kommt daher überall dort zur Anwendung, wo der Benutzer entweder Personen oder Gruppen auswählen kann. Für die Klasse EditGruppe wird eine Subklasse GroupContent von BasicSelection gebildet.

        EditRechte

        Die eben beschriebene Auswahlmöglichkeit von Personen und Gruppen wird auch für die Verwaltung der Berechtigungen verwendet: Während im Analysemodell nur die Vergabe von Rechten an Instanzen der Klasse Person vorgesehen war, wird das Berechtigungskonzept im Entwurf auf Instanzen der Klasse Gruppe erweitert. Nicht zuletzt diese Komplexitätssteigerung machen im Entwurfsmodell eine eigene Control-Klasse für die Verwaltung der Rechte notwendig: Die Kontrollfunktionen werden an die Klasse EditRechteControl delegiert.

        Abb. 3-13: Boundary-Klasse EditRechte

        Die Boundary-Klasse EditRechte verfügt als Subklasse von BasicSelection über die beiden Auswahl-Mechanismen SelectListEntry und SelectTreeEntry. Diese werden benötigt, um eine Person oder Gruppe auszuwählen, der dann für einen bestimmten Eintragstyp bestimmte Rechte zugeteilt werden können. Für jede Kombination aus Person oder Gruppe und EintragsTyp kann eine Instanz der Klasse Rechte (mit einem bestimmten Wert des Attributes rechtsIndex) erzeugt und in der Klasse RightSet abgelegt werden. Wie bereits in Abschnitt 3.1 erwähnt, ist jedem Benutzer das Leserecht für die Einträge aller anderen Benutzer eingeräumt. Lediglich das Detail-Leserecht oder Eintragsrecht für andere Benutzer muß explizit durch eine Instanz der Klasse Rechte zugeordnet werden. Die Rolle des sender der Klasse Rechte nimmt dabei immer der Benutzer ein, der anderen Personen oder Gruppen eine Berechtigung vergibt. Diese zusätzlichen Rechte für Einträge eines bestimmten Benutzers können also nur von diesem selbst erteilt werden. Dieser Teilbereich der Systemverwaltung ist für den Systemadministrator daher nicht relevant.

        EditFeiertage

        Dieser Bereich der Systemverwaltung kommt im Entwurfsmodell neu hinzu. Er dient der Definition der Feiertage, die in der Calendarium-Anwendung berücksichtigt werden sollen. Dieser Bereich ist daher nur für den Systemadministrator relevant und steht dem normalen Benutzer über das Menü der Klasse Client nicht zur Verfügung.

        Die Control-Klasse EditFeiertageControl wird durch die Klasse Monitor instanziiert und übernimmt die Kontrollfunktionalität. Die Boundary-Klasse EditFeiertage besteht im wesentlichen nur aus einer Liste bereits definierter Feiertage und wird mit den Daten der Klasse FeiertagSet gefüllt.

        Mithilfe einer zweiten Boundary-Klasse EditFeiertag können neue Instanzen der Klasse Feiertag hinzugefügt beziehungsweise bestehende geändert werden. Für die Durchführung dieser Änderungen ist die Control-Klasse EditFeiertageControl und in weiterer Folge die Klasse FeiertagSet zuständig.


      3.4 Server

      Bisher wurde die Calendarium-Anwendung in erster Linie aus der Sicht des Anwenders betrachtet und die Klassen beschrieben, mit denen ein Benutzer direkt oder indirekt interagiert. Dieses Kapitel hingegen beschäftigt sich mit dem Aufbau der Klassenstruktur des Servers.

      Abb. 3-14: Strukturmodell - Server

        Server

        Die Klasse Server stellt die zentrale Control-Klasse der Calendarium-Anwendung auf der Server-Seite dar. Analog zur Klasse Client besitzt sie die Methode main, mit der der Server gestartet werden kann.

        Die wichtigsten Komponenten des Servers sind

        • die Remote Sets, in denen die Instanzen der Basisklassen verwaltet werden,
        • der MessageServer, der für die Verwaltung und Versendung der Events an alle registrierten Benutzer zuständig ist und
        • die NfktQueue, die die Versendung der Notifikationen an die Teilnehmer eines Eintrages steuert.

        Remote Sets

        Alle Instanzen je einer Basisklasse werden auf dem Server in einem eigenen Attribut - dem jeweiligen Remote Set - verwaltet:

        • In personen der Klasse PersonRemoteSet werden alle registrierten Benutzer als Instanzen der Klasse Person verwaltet.
        • Im Attribut gruppen der Klasse GroupRemoteSet befinden sich alle Gruppen. Die Zugriffsmöglichkeit auf benutzerdefinierte Gruppen wird über das Attribut owner einer Gruppe gesteuert.
        • Das Attribut typen der Klasse TypSetRemote enthält zwei Mengen von Instanzen der Klasse EintragsTyp: Die vom Systemadministrator definierten Eintragstypen werden in der Menge defaultTyps verwaltet. In der Menge userTyps befinden sich – nach Benutzern getrennt – die Eintragstypen mit den individuellen Farbzuordnungen.
        • Die Menge der Feiertage wird im Attribut feiertage der Klasse FeiertagSetRemote gespeichert.
        • Das Attribut rechte der Klasse RightSetRemote verwaltet in zwei Mengen getrennt die Instanzen der Klasse Rechte, die einzelnen Personen erteilt wurden, und diejenigen Rechte-Instanzen, die Gruppen erteilt wurden.
        • Im Attribut termine der Klasse TerminSetRemote werden die Instanzen der Klasse Termin verwaltet. Im Attribut todo der Klasse ToDoSetRemote befinden sich die Instanzen der Klasse ToDo. Jeder dieser Eintrag-Instanzen sind neben einem bestimmten Eintragstyp die Teilnehmer und bis zu drei Notifikationen zugeordnet.

        Diese Remote Set Klassen werden von der Java RMI-Klasse UnicastRemoteObject abgeleitet und stellen unter Verwendung des RMI-Mechanismus die Verbindung zwischen Server und der Client-Instanzen her. In Abschnitt 3.5.1 folgt ein kurzer Überblick über die Gestaltung dieses Remote Proxy Patterns. Für eine detaillierte Beschreibung der Funktionsweise der RMI-Technologie – auch in Bezug auf die Calendarium-Anwendung – wird auf [StoC99] verwiesen.

        MessageServer

        Der Klasse MessageServer kommt serverseitig neben der Klasse Server die wichtigste Control-Funktion zu: Sie fungiert gleichsam als Kommunikationszentrale und leitet als solche sämtliche Nachrichten an alle Client-Instanzen weiter.

        Wird eine Instanz der Klasse Client gestartet, meldet sich diese durch Aufruf der Methode connect beim MessageServer an und wird in dessen Attribut loggedUsers, der Menge der angemeldeten Benutzer, gespeichert. Schließt ein Benutzer seine Calendarium-Anwendung, meldet sich die betroffene Client-Instanz mit Aufruf der Methode disconnect vom MessageServer ab.

        Informationen und Nachrichten jeglicher Art werden vom Server an Client-Instanzen als Instanzen einer Subklasse von CalendariumEvent erstellt: Ein CalendariumEvent besitzt einen Zeitstempel timeStamp und zwei Instanzen der Klasse Person, den sender und den empfänger der Nachricht. Die Klasse CalendariumEvent wird durch folgende Subklassen spezialisiert:

        • Bei Instanzen der Klasse AdminEvent werden keine Personen als Attribut empfänger angegeben, da diese Nachricht an alle angemeldeten Benutzer ergehen soll. Dieser Event-Typ wird zum Beispiel zur Ankündigung einer Abschaltung des Servers verwendet.
        • Die Klasse MessageEvent wird für Nachrichten an bestimmte Benutzer, die über eine Änderung unterrichtet werden sollen, verwendet. Dieser Event-Typ wird zum Beispiel bei Neuerstellung eines Termines an alle Teilnehmer dieses Eintrages versendet.
        • Die Klassen TerminEvent und ToDoEvent finden für die Aktualisierung geöffneter Ansichten Anwendung: Im Gegensatz zu den anderen Event-Typen besitzen sie keinen Nachrichtentext, der bei den Empfängern angezeigt werden könnte, sondern eine Referenz auf eine Instanz einer Subklasse von Eintrag. Diese Event-Typen dienen zum Beispiel der Anzeige neuer oder geänderter Einträge. Da nicht nur Teilnehmer eines Eintrages diesen sehen können, sondern alle Benutzer das (einfache) Leserecht auf alle Einträge besitzen, richtet sich dieser Event-Typ – so wie der Type AdminEvent – an keinen bestimmten Empfänger.
        • Für jede Notifikation wird eine Instanz der Klasse NtfktEvent erstellt, die neben dem Notifikationstext auch die bei der Erstellung des Eintrages angegebene Übertragungsart in einem eigenen Attribut delivery speichert.

        Durch Aufruf der Methode addEvent (der Klasse MessageServer) werden Instanzen dieser CalendariumEvent-Subklassen erzeugt und einer Liste, dem Attribut eventList, hinzugefügt. Die Elemente dieser Liste werden durch die Methode run sukzessive abgearbeitet und mit der Methode sendEvent dem Empfänger der CalendariumEvent-Instanz zugestellt. Dieser Mechanismus wird im Abschnitt 3.5.2 nochmals aufgegriffen und detaillierter dargestellt.

        NfktQueue

        Bei der Erstellung einer Instanz der Klasse Eintrag oder deren Änderung wird die Methode addNotifier der Klasse NfktQueue aufgerufen und die neue beziehungsweise geänderte Eintrag-Instanz als Parameter übergeben. Für jeden Teilnehmer des Eintrages wird für jede Notifikation eine Instanz der Klasse Notifier erstellt und diese in der NfktQueue gespeichert. Bei Erreichen des notifikationsrelevanten Zeitpunktes, der im Attribut nfktRelevant in der Klasse Eintrag gespeichert ist, wird eine Instanz der Klasse NfktEvent instanziiert, mit der Methode addEvent der Klasse MessageServer hinzugefügt und so die Versendung der Notifikation durchgeführt.


      3.5 Design Patterns

        3.5.1 Remote Proxy Pattern

        Durch die Verteilung der einzelnen Anwendungskomponenten auf verschiedene Rechner wird im Entwurfsmodell die Client/Server-Architektur realisiert. Die Gestaltung der Komponenten sowie deren physische Aufteilung wurde bereits im Architekturmodell (Abschnitt 2) dargestellt.

        In diesem Kapitel werden die Klassen, die die Kommunikation zwischen Client und Server ermöglichen, erläutert. Das dabei verwendete Remote Proxy Pattern wird in Java durch Klassen des Package java.rmi unterstützt. Abb. 3-15 zeigt abstrakt die Umsetzung des RMI-Konzeptes durch die Calendarium-Anwendung:

        Abb. 3-15: RMI-Klassen in JDK (Abb.6-1 aus [Hitz99])

        Dieses Konzept wird im Entwurf für jede Basisklasse gesondert verfolgt. Dabei hält und verwaltet die Server-Klasse - wie in Abschnitt 3.4 erwähnt - die Klassen FeiertagSetRemote, GroupSetRemote, RightSetRemote, PersonenSetRemote, TerminSetRemote, ToDoSetRemote und TypSetRemote, in denen jeweils die Instanzen einer Basisklassen zusammengefaßt sind. Diese Klassen, in der Folge kurz als Remote Set Klassen bezeichnet, werden von der Klasse UnicastRemoteObject des Packages java.rmi.server abgeleitet. Sie implementieren die jeweils zugehörigen Interface-Klassen FeiertagSetInterface, GroupSetInterface, RightSetInterface, PersonSetInterface, TerminSetInterface,ToDoSetInterface und TypSetInterface, die ihrerseits wiederum von der Interface-Klasse Remote des Packages java.rmi abgeleitet werden.

        In Abb. 3-16 ist das Entwurfsmodell dargestellt:

        Abb. 3-16: Strukturmodell – Remote Proxy Pattern

        Aus jeder UnicastRemoteObject-Subklasse – unter anderen die Remote Set Klassen – werden von einem speziellen Compiler, dem RMI-Compiler, die entsprechenden Stub- und Skeleton-Klassen gebildet, über die die Kommunikation zwischen Server und Klient abgewickelt wird.

        Das folgende Beispiel (aus [StoC99]) erläutert diesen Mechanismus:

        Abb. 3-17: Remote-Method-Invocation

          Die Grafik zeigt das Objekt K des Klienten, das auf das remote Objekt S des Servers zugreifen möchte. Da sich aber das Objekt S in einem anderen Adreßraum befindet, kann K die Methode nicht direkt aufrufen. Statt dessen greift K auf ein für das Objekt S stellvertretend agierendes Stub zu, das in seinem Adreßraum liegt.

          Das Stub arbeitet mit der Java-Virtual-Machine und dem RMI-System des Klienten zusammen. Es serialisiert die Argumente und leitete diese an den Server weiter. Zurückgelieferte Ergebnisse werden vom Stub an das Objekt K weitergeleitet.

          Serverseitig arbeitet das Skeleton. Es wandelt den Bytestream aus der Serialisierung wieder in Objekte um und ruft die entsprechende Methode vom Objekt S auf. Das Ergebnis wird serialisiert und an den Klienten zurückgeschickt.

          aus: [StoC99]

        Für jede Basisklasse existiert weiters klientenseitig eine "Set"-Klasse, in der über die entsprechende Interface-Klasse das Stub der jeweiligen Remote Set Klasse angesprochen wird. Die Set-Klassen werden in der Klasse Data zusammengefaßt, die bei Erstellung einer Client-Instanz erzeugt wird (siehe Abschnitt 4.2.2). Sie bildet für die Klasse Client eine Maske für die Kommunikation mit dem Stub der Remote Set Klassen: Die Control-Klasse Client operiert über Methoden der Set-Klassen, die ihrerseits über das jeweilige Interface die entsprechende Methode der Remote Set Klasse aufrufen. Für eine genaue Beschreibung des RMI-Mechanismus wird auf [StoC99] verwiesen.


        3.5.2 Observer-Pattern

        Veränderungen eines Objektes, zum Beispiel eines Eintrages, werden am Klienten vorgenommen: Die Control-Klassen Client beziehungsweise eine Unterklasse von Ansicht verarbeiten die Anwender-Interaktion und veranlassen die entsprechenden Änderungen in den Entity-Klassen: Dies wird durch Aufruf von Methoden der Remote Set Klassen veranlaßt, wobei das Remote Proxy Pattern zur Anwendung kommt.

        Umgekehrt aber müssen auch alle Clients durch den Server von Änderungen benachrichtigt werden:

        • Ein Eintrag wird für alle Teilnehmer, die diesem Eintrag zugeordnet wurden, erstellt. Jede Ansicht, die einen dieser Teilnehmer im Attribut openCal, in dem alle anzuzeigenden Kalender gespeichert werden, enthält und daher dessen Einträge anzeigt, muß von einem neu hinzugekommenen beziehungsweise geänderten Eintrag informiert und entsprechend aktualisiert werden. Dies wird durch Instanzen der Klassen TerminEvent und ToDoEvent realisiert.
        • Die Teilnehmer eines Eintrages werden zusätzlich durch eine Nachricht von neuen oder geänderten Einträgen verständigt. Diese MessageEvent-Instanzen werden vom Server an alle betroffenen Client-Instanzen versendet.
        • Auch Notifikationen werden zentral in der NfktQueue auf dem Server verwaltet und müssen bei Erreichen des notifikationsrelevanten Zeitpunktes an die entsprechenden Client-Instanzen weitergeleitet werden.

        Abb. 3-18: Klassendiagramm – Observer Pattern

        Wie schon in Abschnitt 3.4 gezeigt, werden Instanzen der Subklassen von CalendariumEvent für die Übermittlung von Informationen vom Server an Client-Instanzen verwendet.

        Die Verbindung zwischen Server und Client wird auch in dieser Richtung (vom Server zu den Client-Instanzen) über das Remote Proxy Pattern realisiert: Das Remote-Interface wird durch die Klasse CalendariumListener spezialisiert und auf der Klienten-Seite von der Klasse CalendariumListenerImpl implementiert. Diese Klasse wird von der Java RMI-Klasse UnicastRemotObject abgeleitet und ermöglicht die Erstellung der Stub- und Skeleton-Klasse; in diesem Fall liegt das Skeleton jedoch auf dem Klienten, der Stub auf dem Server. Die Umsetzung der Server-Nachrichten erfolgt auf den Client-Instanzen durch die Ausführung der Methode processCalendariumEvent der Klasse CalendariumListenerImpl, die als Attribut listener Bestandteil der Klasse Client ist.

        Diese Methode processCalendariumEvent wird von der Methode sendEvent des MessageServer bei Vorliegen neuer CalendariumEvents aufgerufen. Handelt es sich bei dem CalendariumEvent um eine Instanz der Klasse MessageEvent, NtfktEvent oder AdminEvent, wird die Anzeige der Nachricht oder Notifikation in einem eigenen Fenster veranlaßt.

        Neben dieser Funktion ist vor allem die Aktualisierung der Ansichten der Hauptzweck der Methode processCalendariumEvent: Die Klasse CalendariumListenerImpl besitzt das Attribut observed der Klasse BeingWatched, einer Subklasse der Java-Klasse Observable. Gemeinsam mit der Java-Interface Klasse Observer kann damit in einfacher Weise das Observer Pattern realisiert werden:

        Jede Klasse, die von Änderungen des Observable-Objektes unterrichtet werden soll, wird bei ihrer Instanziierung mit der Methode addObserver als Observer des Attributes observed hinzugefügt. Zum Beispiel fügt die Klasse Client, nachdem eine weitere Ansicht geöffnet wurde, diese als Observer hinzu:


        Tagesansicht tagesansicht = new Tagesansicht(this, statusLabel, new Date());
        gui = tagesansicht.getGUI();
        frames.put(c, gui);
         
        // Listener
        listener.addObserver(tagesansicht);

        Sobald nun die Methode notifyObservers der Klasse BeingWatched aufgerufen wird (zum Beispiel durch die Methode processCalendariumEvent der Klasse CalendariumListenerImpl), wird die Methode update der Observer-Klassen durchgeführt. (Für die genaue Beschreibung der Java-Klassen Observable und Observer wird auf [JDoc98] verwiesen.) Auf diese Weise werden Änderungen der Einträge, zum Beispiel ein neuer, verschobener oder gelöschter Termin, zunächst in den entsprechenden Remote Set Klassen des Server durchgeführt und danach über den MessageServer als CalendariumEvent dem CalendariumListenerImpl-Attribut listener aller Client-Instanzen gemeldet, wodurch in weiterer Folge alle Observers des listener informiert werden und die Methode update aufrufen: Die Eintrag-Änderung wird auf jeder betroffenen Benutzeroberfläche nachgezogen. Diese Dynamik wird im Dynamischen Modell in den Abschnitten 4.1.1 und 4.2.3 anhand zweier Beispiele dargestellt.

        Dieser Mechanismus wird auch genutzt, um die Nachrichten oder Notifikationen bei den betroffenen Benutzern zu speichern: Jeder Client besitzt ein Attribut notes der Boundary-Klasse Noticias, das ebenfalls als Observer dem CalendariumListenerImpl listener hinzugefügt wird. Es dient zur Anzeige der erhaltenen Notifikationen oder der Nachrichten, die bei der Erstellung, Änderung oder Löschens eines Eintrages an dessen Teilnehmer gesandt werden. Über das Observer-Pattern wird der Inhalt durch jede neue Nachricht oder Notifikation aktualisert, indem die update-Methode aufgerufen und der Textinhalt von notes mit dem Text dieser Nachricht beziehungsweise Notifikation erweitert wird.


        3.5.3 Persistence Broker - Pattern

        Die Calendarium-Anwendung enthält einige Klassen, deren Daten auch nach einer eventuellen Abschaltung des Servers, der über die Remote Set Klassen sämtliche Instanzen der Basisklassen verwaltet, zur Verfügung stehen müssen. Diese Persistenz kann in einfacher Form durch das Anlegen von Dateien erfolgen.

        Abb. 3-19: Strukturmodell: Datenbankanbindung unter Verwendung des Persistence Broker Patterns

        Etwas aufwendiger, dafür aber auch wesentlich eleganter und vor allem effizienter ist die Anbindung des Servers an eine Datenbank. Für diese kommt die Anwendung verschiedener Anbindungsmuster in Frage. Eine ausführliche Abhandlung verschiedener Datenbankanbindungsmuster und Aspekte ihrer Verwendbarkeit in Hinblick auf die Calendarium-Anwendung finden sich in [StoS99]. Dieses Kapitel gibt einen Überblick über das im Entwurf verwendete und in [StoS99] beschriebene "Persistence Broker Muster".

        PersistentObject

        Die Klassen, deren Instanzen persistent gespeichert werden sollen, werden von der abstrakten Klasse PersistentObject abgeleitet, die Methoden zur Verfügung stellt, die das Speichern (save), Laden (retrieve) und Löschen (delete) ihrer Instanzen in der Datenbank ermöglichen. Im Boolean-Attribut isPersistent wird festgehalten, ob eine Instanz bereits in der Datenbank abgelegt wurde. Ein eigenes Attribut objectIdentifier dient als Schlüssel-Attribut für die Datenbank und ist für die eindeutige Identifizierung der Objekte in der Datenbank notwendig. Die Organisation und Verwaltung der Schlüssel wird durch das Attribut oidMan vom Typ OIDManager übernommen.

        PersistenceBroker

        Die Klasse PersistenceBroker ist für die Durchführung der Befehle, die durch Methoden der Klasse PersistentObject in der Datenbank vorgenommen werden sollen, verantwortlich. Sie veranlaßt die Durchführung des SQL-Befehls, der (als Instanz der Klasse SqlStatement) mit der entsprechenden Methode saveObject, retrieveObject oder deleteObject als Parameter übergeben wird. Die Durchführung in der Datenbank übernimmt die Klasse ConnectionManager.

        ConnectionManager

        Die Klasse ConnectionManager stellt die Verbindung zur Datenbank her und kapselt die Datenbank-Funktionalität, die zum Sperren beziehungsweise Freigeben von Tabellen und zur Durchführung von SQL-Befehlen notwendig ist. Die Datenbank-Verbindung wird im Attribut connections gespeichert.

        ClassMap

        Die Abbildung der Basisklassen in den Tabellen der Datenbank wird für jede Basisklasse in einer Instanz der Klasse ClassMap gespeichert, um ein Mapping zwischen den Namen der Basisklassen und der verwendeten Tabellennamen zu ermöglichen. Die Instanzen der Klasse ClassMap werden in der Klasse ClassMappings zusammengefaßt.

        SqlStatement

        Die Klasse SqlStatement und ihre Subklassen SelectSqlStatement, InsertSqlStatement, UpdateSqlStatement und DeleteSqlStatement dienen der Konstruktion der entsprechenden Select-, Insert-, Update- und Delete-Befehle in SQL, die zur Manipulation einzelner Objekte verwendet werden.

        PersistentCriteria

        Auch die Klasse PersistenCriteria und ihre Subklassen RetrieveCriteria und DeleteCriteria werden zu Konstruktion von SQL-Befehlen herangezogen. Im Untschied zur SqlStatment-Klassenhierarchie dienen sie jedoch der Manipulation von Objektmengen. Zur näheren Spezifizierung beziehungsweise Einschränkung dieser Mengen werden Instanzen der Subklassen von SelectionCriteria gebildet, die die exakte Definition der Selektionsklausel in einem SQL-Befehl ermöglichen.


         
         
         

    4 Dynamisches Modell


    Im Entwurfsmodell werden Programm-Abläufe der Calendarium-Anwendung, deren Aufbau etwas komplexerer Natur ist, mit Hilfe einiger Sequenz- und Kollaborationsdiagramme konkretisiert und erläutert.

    In der Regel kann ein Verhaltensdiagramm einem Anwendungsfall (Use Case) aus der Anforderungsbeschreibung zugeordnet werden (vgl. [Hitz99], Kapitel 4.3). Dies ist auch in den meisten hier angeführten Fällen möglich. Es gibt aber auch Diagramme, die eine Interaktion verschiedener Klassen beschreiben, die in keinem Use Case beschrieben wird: Als Beispiele seien hier die Kollaborationsdiagramme "Serverstart" (Abschnitt 4.2.1) und "Login" (Abschnitt 4.2.2) erwähnt.


      4.1 Sequenzdiagramme

        4.1.1 Termin erfassen

        In den folgenden beiden Sequenzdiagrammen wird anhand des Use Case "Termin erfassen", also die Neuerstellung eines Termin-Eintrages, das Zusammenspiel der Control-, Entity- und Boundary-Klassen dargestellt.

        Diese Verhaltensdiagramme stehen in engem Zusammenhang mit der Klassenstruktur der Terminverwaltung (Abschnitt 3.2).

        Aufbau des GUI

        Abb. 4-1 zeigt den Aufbau der Benutzeroberfläche, die der Erfassung der Eckdaten des neuen Termines dient. Dieses Szenario geht von der Annahme aus, daß der Benutzer die Neuanlage eines Termines durch Auswahl eines Menüeintrages auslöst [1].

        Abb. 4-1: Dynamisches Modell – Termin erfassen (Aufbau des GUI)

        Die Klasse Client verarbeitet in ihrer Funktion als Control-Klasse das Benutzer-Kommando "Termin" [2], das vom Benutzer eigentlich nur mittelbar, und zwar über die Boundary-Klasse der Menüleiste, weitergeleitet wird und erzeugt eine neue Instanz der Control-Klasse EditTerminControl [3], an die auch die weitere Control-Funktionalität, die mit der Erstellung eines Termines zusammenhängt, delegiert wird.

        Zunächst wird eine Instanz der Boundary-Klasse EditTermin erzeugt [4], die wiederum drei Panels instanziiert:

        • Das TerminAllgPanel [5] dient der Eingabe beziehungsweise Anzeige der allgemeinen Termindaten (vgl. Abb. 3-8).
        • Das TeilnehmerPanel [6] dient der Eingabe oder Anzeige der einem Termin zugeordneten Teilnehmer. Dies können Personen und/oder Gruppen sein (vgl. Abb. 3-9).
        • Das SerienPanel [7] schließlich enthält, soll der Termin einer Serie zugeordnet werden, das Beginn- und Enddatum dieser Serie, sowie deren Typ und Frequenz (vgl. Abb. 3-10).

        Nach der Instanziierung der Boundary-Klasse EditTermin und ihrer Panel-Attribute fügt sich die Control-Klasse EditTerminControl mit der Methode addActionListener selbst zur Menge der ActionListener der Boundary-Klasse hinzu [8]: Benutzer-Interaktionen, die die Boundary-Klasse entgegennimmt, werden automatisch an alle registrierten ActionListener dieser Boundary-Klasse weitergeleitet.

        public EditTerminControl(JFrame f)
        { parentFrame = f;
         
        editTermin = new EditTermin(f);
        editTermin.addActionListener(this);

        termin = new Termin(Data.user);

        editTermin.start(termin);
        editTermin.setTitle("Termin eintragen");
        }

        Die Control-Klasse erstellt eine neue, leere Instanz der Klasse Termin [9] und übergibt sie als Parameter der Methode start der Boundary-Klasse EditTermin [10]. Da für die Anzeige eines bereits bestehenden Termin-Objektes die gleichen Methoden verwendet werden, wie für die Anzeige eines neuen und daher leeren Termin-Objektes, werden zunächst die eventuell gespeicherten Daten des Termines abgefragt und danach die entsprechenden GUI-Elemente befüllt [11]: Für den Aufbau des TeilnehmerPanel wird zuvor die Methode getTeilnehmer [12], für den Aufbau des SerienPanel die Methode getSerie [14] des Termin-Objektes ausgeführt, um dann die entsprechende fill-Methode der jeweiligen Boundary-Klasse mit den entsprechenden Parametern versorgen zu können [13, 15].

        Nachdem die Oberflächen-Elemente vollständig erstellt worden sind, veranlaßt die Control-Klasse Client mit der Methode getGUI in weiterer Folge die Anzeige derselben [16].

        Methode createTermin()

        Das Sequenzdiagramm in Abb. 4-2 beschreibt den dynamischen Ablauf der Erstellung einer neuen Instanz der Klasse Termin mit den vom Benutzer definierten Daten und deren Auswirkungen auf andere Objekte. Da mit der Erstellung auch die Persistierung der neuen Termin-Instanz einhergeht, werden in diesem Diagramm sowohl die File-System Lösung der Calendarium-Anwendung, als auch die Datenbank Version berücksichtigt. Der Ablauf der Persistierung in der Datenbank wird in Abschnitt 4.1.2 anhand eines eigenen Sequenzdiagrammes detailliert dargestellt.

        Abb. 4-2: Dynamisches Modell – Termin erfasse (Methode createTermin)

        Der Benutzer bestätigt seine Eingaben mit dem OK-Button [1], diese Aktion löst die Methode actionPerformed der Control-Klasse aus [2], die daraufhin mit Hilfe der Methode checkInput die Korrektheit der Benutzer-Eingaben überprüfen läßt [3]. Diese Überprüfung erfolgt durch die Boundary-Klasse EditTermin selbst. Geprüft werden unter anderem die syntaktische Korrektheit der Datum- und Zeitangaben und die Zuordnung mindestens eines Teilnehmers. Das Ergebnis dieser Prüfung wird als Booelan-Wert an die Control-Klasse zurückgegeben.

        Nach erfolgreicher Prüfung wird die Methode createTermin der Control-Klasse aufgerufen [4]. Zunächst werden potentielle Konflikte ermittelt, die ein neuer oder geänderter Termin mit anderen, bereits vorhandenen Terminen verursachen könnte. Diese Aufgabe wird mit der Methode getKonflikte [5] von der Klasse TerminSet an die Klasse TerminSetRemote weitergeleitet, indem die Interface-Methode getKonflikte mit der neuen Termin-Instanz als Parameter aufgerufen wird [6]. Die Klasse TerminSetRemote verfügt über sämtliche Termine aller registrierten Benutzer und ist somit in der Lage, potentielle Konflikte zu eruieren. Dazu ermittelt sie für alle Teilnehmer des neuen Eintrages alle Termine, die in den Zeitbereich des neuen Termines fallen. Für jede solche Termin-Instanz eines Teilnehmers, die eine zeitliche Überschneidung mit dem neuen Termin aufweist, wird eine Instanz der Klasse Konflikt erzeugt. Die Menge der Konflikt-Instanzen dient als Rückgabewert der Methode getKonflikte. Enthält diese Menge mindestens eine Konflikt-Instanz, so wird von der Control-Klasse EditTerminControl in weiterer Folge die Boundary-Klasse TerminVorschlag erzeugt, über die ein neuer, konfliktfreier Zeitraum für den Termin gesucht werden kann (vgl. Abschnitt 3.2).

        private boolean createTermin()
        { Vector konflikte = Data.termine.getKonflikte(termin);
        if(konflikte.size() == 0)
        { Data.termine.create(termin);
        return true;
        } else
        { // Konflikt
        Enumeration e = konflikte.elements();
        while(e.hasMoreElements())
        { Konflikt konflikt = (Konflikt) e.nextElement();
        msg += konflikt.getPerson().getNameLang()
        + konflikt.getBeginn()+ konflikt.getEnde();
        }
        showKonflikteDialog(msg))
         
        vorschlag = new TerminVorschlag(parentFrame);
        vorschlag.start(termin);
        }
        }

        Im Diagramm in Abb. 4-2 wird jedoch nur der Fall dargestellt, daß keine Konflikte auftreten und die Ergebnismenge der Methode getKonflikte daher leer ist: In diesem Fall kann die Methode createTermin der Klasse TerminSet aufgerufen werden [7]. Die Erstellung des Termines wird mit Aufruf der Interface-Methode createTermin an die Klasse TerminSetRemote delegiert [8].

        Mit der Methode sendNewMessages erfolgt zunächst die Erstellung einer Instanz der Klasse MessageEvent für alle Teilnehmer des Termines [9], um diese von dem neu eingetragenen Termin zu benachrichtigen: Mit der Methode getAllPersonsWithNfkt [10] werden nicht nur sämtliche Teilnehmer eines Termines ermittelt, sondern auch eventuell vorhandene Teilnehmer-Gruppen aufgelöst. Da auch Gruppen als Mitglieder einer Gruppe auftreten können, ist hierfür ein rekursiver Methoden-Aufruf notwendig.

        Der owner des Eintrages, der die Rolle des sender der MessageEvent-Instanz einnehmen soll, muß durch die Methode getOwner ermittelt werden [11]. Für jeden Teilnehmer wird nun eine Instanz der Klasse MessageEvent erzeugt [12] und diese mit der Methode addEvent dem MessageServer zur weiteren Verarbeitung übergeben [13]. Die durch den MessageServer ausgelösten Aktivitäten werden im Abschnitt 4.2.3 in einem Kollaborationsdiagramm näher betrachtet.

        Die Methode insertTermin [14] bewerkstelligt in der Datenbank Version der Calendarium-Anwendung die Persistierung der neuen Termin-Instanz. Für diesen Ablauf existiert ein eigenes Sequenzdiagramm, das den dynamischen Ablauf einer Datenbank-Interaktion erläutert und im Abschnitt 4.1.2 beschrieben wird. In der File-System Version der Calendarium-Anwendung wird in dieser Methode nur die interne Identifikationsnummer gesetzt und die neue Termin-Instanz dem Attribut termine, in dem alle Termine gespeichert werden, hinzugefügt.

        Die Klasse TerminSetRemote stellt fest, ob für die neue Termin-Instanz Notifikationen angegeben wurden. Ist dies der Fall, fügt sie mit der Methode addNotifier die Termin-Instanz der Klasse NfktQueue hinzu [15]. Dadurch werden von der NfktQueue bis zu drei Instanzen der Klasse Notifier erzeugt und von dieser verwaltet. Zum notifikationsrelevanten Zeitpunkt werden die entsprechenden Instanzen der Klasse NfktEvent erzeugt und dem MessageServer übergeben (vgl. Abschnitt 3.4).

        Abschließend wird die Aktualisierung der Ansichten vorgenommen, indem eine Instanz der Klasse TerminEvent erzeugt [16] und mit der Methode addEvent dem MessageServer übergeben wird [17]. Da alle Ansichten des vom Termin betroffenen Zeitraumes aktualisiert werden müssen, sofern sie Einträge mindestens eines Teilnehmers dieses Termines anzeigen, enthält dieser Subtyp der Klasse CalendariumEvent keine expliziten Empfänger, sondern wird an alle Client-Instanzen weitergeleitet.

        Im Sequenzdiagramm in Abb. 4-2 wurde die Zuordnung des Termines zu einer Serie nicht berücksichtigt, um die Komplexität nicht zu erhöhen. Es soll an dieser Stelle jedoch die Behandlung einer Serie kurz erläutert werden: Bei einem Serieneintrag werden in Abhängigkeit von Typ und Frequenz der Serie sämtlich zu erwartenden Einträge sofort erstellt. Diese Einträge werden im Prinzip genauso verarbeitet, wie eine einfache Termin-Instanz: Nachdem für die gesamte Serie eine Instanz der Klasse MessageEvent erzeugt und versendet wurde, wird für jeden einzelnen Eintrag eine Instanz der Klasse TerminEvent (beziehungsweise ToDoEvent) erzeugt und dem MessageServer übergeben sowie – bei Vorhandensein von Notifikationen – jeder Eintrag der NfktQueue hinzugefügt.


        4.1.2 Termin speichern

        Anhand eines Beispieles zeigt dieser Abschnitt das Zusammenspiel der Klassen, die die Datenbank-Anbindung herstellen und das Persistence Broker Muster (siehe Abschnitt 3.5.3) umsetzen. Als Beispiel wird die Methode insertTermin der Klasse TerminSetRemote herangezogen, die die Speicherung eines neu angelegten Termines vornimmt.

        Methode insertTermin()

        Abb. 4-3: Dynamisches Modell – Termin speichern (Methode insertTermin)

        Die Klasse TerminSetRemote ruft die Methode insertTermin auf, um die neu erstellte Termin-Instanz zu speichern [1]. Im Gegensatz zur File-System Version der Calendarium-Anwendung, in der die gesamte Remote Set Klasse mit der neuen Termin-Instanz erneut gespeichert wird und deshalb auch über die Methoden, die zum Speichern notwendig sind, verfügt, werden in der Datenbank Version neue Termin-Instanzen den bereits gespeicherten Daten hinzugefügt, weshalb auch die Klasse Termin selbst (als Subklasse von PersistentObject) über die notwendigen Methoden verfügt.

        Zunächst wird die Methode setKz der Parent-Klasse PersistentObject ausgeführt [2], um ihr Attribut kz mit dem Kürzel des owner des Termines zu belegen. Dies ist für die korrekte Einrichtung der OIDManager-Klasse notwendig (siehe unten). Danach wird die Methode save der Klasse PersistentObject aufgerufen [3]:

        Mit der Methode getMapping [4] wird dem Attribut map die der Klasse Termin entsprechende ClassMap-Instanz zugewiesen, die für den Zugriff auf die korrespondierende Datenbank-Tabelle notwendig ist. Die Methode getMapping ruft zu diesem Zweck die Methode getClass der Klasse ClassMappings mit dem Klassennamen, in diesem Fall "Termin", als Parameter auf [5].

        Da die Methode save nicht nur zum Speichern einer neu erstellten Instanz der Klasse PersistentObject verwendet wird, sondern auch bei der Durchführung eines Update (zum Beispiel bei der Änderung eines Termines), wird nun zwischen diesen beiden Fällen unterschieden: Die Tatsache, ob sich ein Objekt bereits in der Datenbank befindet, kann aus dem Wert des Boolean-Attributes isPersistent festgestellt werden.

        Existiert eine PersistentObject-Instanz noch nicht in der Datenbank und ist der Wert des isPersistent-Attributes daher "False", so muß mit der Methode getKey von der OIDManager-Klasse ein neuer Objekt-Identifier erstellt und dem Attribut objectIdentifier der PersistentObject-Instanz zugewiesen werden [7]. Für die Erstellung des korrekten Objekt-Identifiers benötigt die Klasse OIDManager das Attribut kz der PersistentObject-Instanz, das mit der Methode setKürzel übergeben wird [6].

        Die ClassMap-Instanz und die Termin-Instanz werden dem Konstruktor der Klasse InsertSqlStatement als Parameter übergeben [8]. Mit der Methode buildForObject, die den objectIdentifier als Parameter erhält, wird der entsprechende SQL-Insert-Befehl generiert [9].

        Ist der Wert des Attributes isPersistent "True", handelt es sich um ein Update einer bereits in der Datenbank vorhandenen Instanz der Klasse PersistentObject. In diesem Fall muß kein neuer Objekt-Identifier von der Klasse OIDManager erstellt werden. Anstelle des InsertSqlStatement wird eine Instanz der Klasse UpdateSqlStatement erzeugt.

        Mit dem erstellten SQL-Befehl kann nun die Methode saveObject der Klasse PersistenceBroker ausgeführt werden [12].

        Folgender Auszug aus dem Quellcode verdeutlicht den Aufbau der Methode save der Klasse PersistentObject:

        public void save()
        { map = getMapping();
        if (!isPersistent)
        { oidMan.setKürzel(kz);
        objectIdentifier = oidMan.getKey();
        isPersistent = true;
         
        sqlStmt = new InsertSqlStatement(map,this);
        sqlStmt.buildForObject("objectIdentifier",new Long(objectIdentifier));
        }
        else
        { sqlStmt = new UpdateSqlStatement(map,this);
        sqlStmt.buildForObject("objectIdentifier",new Long(objectIdentifier));
        }
        PersistenceBroker.saveObject(kz,map,sqlStmt.asString());
        }

        Die Methode saveObject der Klasse PersistenceBroker ruft über die Klasse ConnectionManager die entsprechenden Datenbank-Funktionen auf: Handelt es sich um einen Update-Befehl, so muß die entsprechende Tabelle zuvor mit der Methode lock gesperrt werden [13]. Die Durchführung des SQL-Befehles erfolgt durch die Methode processSql, der der zuvor erstellte Befehl als Parameter übergeben wird [14]. Eine erfolgreiche Durchführung wird mit der Methode commit bestätigt [15].


      4.2 Kollaborationsdiagramme

        4.2.1 Serverstart

        In Abschnitt 4.1.2 wurde die Datenbankanbindung am Fall der Manipulation einzelner Objekte dargestellt. Hierbei nicht verwendet wird die Klasse PersistentCriteria, die zur Erstellung von SQL-Befehlen, die auf Objekt-Mengen operieren, benötigt wird. Als Beispiel hierfür wird in diesem Abschnitt der Start der Calendarium-Anwendung auf der Server-Seite beschrieben: Bei diesem müssen sämtlich gespeicherte Instanzen der Basisklassen geladen und in den entsprechenden Remote Set Klassen abgelegt werden.

        Da der Ablauf für alle Remote Set Klassen gleich ist, wird im Kollaborationsdiagramm in Abb. 4-4 der Vorgang des Ladens beispielhaft nur für die Klasse PersonSetRemote gezeigt:

        Abb. 4-4: Dynamisches Modell – Start des Servers

        Bevor die RemoteSetKlassen erstellt und gefüllt werden können, muß nach der Instanziierung der Klasse Server die Methode fillMappings der Klasse ClassMappings ausgeführt werden [1]: Aus einer eigenen Datenbank-Tabelle werden alle Klassen- und Tabellennamen gelesen und jedes Paar als Instanz der Klasse ClassMap in ClassMappings gespeichert.

        Mit der Methode starten der Klasse Server [2] beginnt die Anlage der Remote Set Klassen [3]: Dieser Vorgang wird im Folgenden anhand der Klasse PersonSetRemote beispielhaft gezeigt. Zunächst müssen die Daten aus der Datenbank geladen werden [4]: Dazu wird von der Klasse ClassMappings mit der Methode getClass und dem Klassennamen der Basisklasse, dessen Instanzen geladen werden sollen - im Fall der Klasse PersonSetRemote sind das die Instanzen der Klasse Person -, die entsprechende ClassMap-Instanz erfragt [5], aus der später der Objekt-Identifier ermittelt wird.

        Mit "Person" als Klassennamen und "Admin" als Kürzel wird eine neue Instanz der Klasse RetrieveCriteria erzeugt [6], um alle Instanzen der Klasse Person aus der korrespondierenden Tabelle zu laden. Der Tabellenname wird durch die Klasse RetrieveCriteria selbständig über die Klasse ClassMappings ermitttelt. Mit der Methode buildForObject wird der SQL-Befehl aufgebaut [7], der dann in der Methode perform als Parameter verwendet wird [8].

        Die Klasse RetrieveCriteria ruft in der Methode perform mit dem SQL-Befehl als Parameter die Methode processSql der Klasse PersitenceBroker auf [9], in der die Durchführung des SQL-Befehles an die Klasse ConnectionManager, die die Verbindung zur Datenbank herstellt, delegiert wird [10]. Die Ergebnismenge wird an die Klasse PersonSetRemote zurückgereicht [11 - 13].

        Für jedes Element der Ergebnismenge wird eine Instanz der Klasse Person unter Verwendung des jeweiligen Objekt-Identifiers erstellt [14]. Die gespeicherten Attribut-Werte werden mit Hilfe der Methode swap auf die Attribute der neuen Instanz der Klasse Person übertragen [15].


        4.2.2 Login

        Das Kollaborationsdiagramm in Abb. 4-5 beschreibt die Methoden-Aufrufe, die bei Start einer Client-Instanz bis zur vollständigen Anzeige der Calendarium-Anwendung auf dem Client-Rechner erfolgen:

        Abb. 4-5: Dynamisches Modell – Anmeldung des Client am Server

        Die Calendarium-Anwendung wird klientenseitig mit der main-Methode der Klasse Client gestartet. Diese Methode erzeugt zunächst eine Instanz der Klasse ConnectionDialog [1], über die der Benutzer seine persönlichen Daten, die zur Anmeldung am Server notwendig sind, eingibt [2]. Neben dem Hostnamen und der Port-Nummer seines Rechners sind das sein Kürzel und Passwort.

        Nach der Eingabe der Login-Daten wird die Klasse Data initialisiert [3], wodurch alle Set-Klassen angelegt werden. Beispielhaft wird im Diagramm in Abb. 4-5 die Instanz der Klasse PersonenSet angeführt [5]. Diese stellt über das Registrierungssystem mit Hilfe des Hostnamen die Verbindung zur entsprechenden Stub-Klasse PersonenSetRemote her [6]. Diese wird in der Klasse PersonenSet durch das Attribut pi als Interface-Klasse PersonenSetInterface geführt. Neben der Klasse PersonenSet werden ebenso die Klassen GroupSet, RightSet, TypSet, TerminSet, ToDoSet und FeiertagSet initialisiert.

        Das analoge Verfahren wird für die Klasse Message und der über das Attribut mi (der Interface-Klasse MessageServerInterface) angesprochenen Klasse MessageServer angewandt [7, 8].

        Erst nachdem diese Verbindung zum Server aufgebaut worden ist, können mit der Methode connect das vom Benutzer angegebene Kürzel und Passwort überprüft werden [9]. Der Methodenaufruf wird an die Klasse MessageServer weitergeleitet [10]; diese kontrolliert in der Methode getPerson die Korrektheit des Passwortes [11], nachdem sie durch die Methode getByKürzel die dem Kürzel entsprechende Instanz der Klasse Person ermittelt hat [12]. Existiert eine Person mit der entsprechenden Kürzel-Passwort-Kombination, wird diese an die Klasse ConnectionDialog zurückgegeben, die durch Aufruf der Methode setUser das Attribut user der Klasse Data mit dieser Person-Instanz belegt [13].

        Nachdem die Client-Instanz erfolgreich am Server angemeldet werden konnte, wird das Attribut listener mit einer neuen Instanz der Klasse CalendariumListenerImpl belegt [14]. Wie bereits in Abschnitt 3.5.2 erwähnt, ist diese Klasse, die das Interface CalendariumListener implementiert, für die Verarbeitung der CalendariumEvent-Instanzen zuständig. Über das Attribut observed der Klasse BeingWatched wird über diese Klasse zudem das Observer Pattern umgesetzt. Die Observer-Klassen werden im Folgenden erzeugt und mit der Methode addObserver als Observer-Instanzen dem Attribut listener hinzugefügt: Zunächst wird eine Instanz der Klasse Noticias erzeugt [15] und der Methode addObserver der Klasse CalendariumListenerImpl als Parameter übergeben [16].

        public Client()
        {


        listener = new CalendariumListenerImpl(this);
        listener.addObserver(notes = new Noticias());
        }

        Das gleiche Verfahren wird auf die Instanz der Klasse Tagesansicht angewandt, die als Default-Ansicht angezeigt werden soll [17, 18]. Abschließend muß die neue CalendariumListener-Instanz dem MessageServer übergeben werden [19, 20]: Durch die Methode addCalendariumListenerTo wird der neue Benutzer im Attribut loggedUsers der Klasse MessageServer hinzugefügt.


        4.2.3 Termin löschen

        Anhand der folgenden Kollaborationsdiagramme soll das Zusammenspiel zwischen Control-, Entitiy- und Boundary-Klassen und die Funktionsweise des Observer-Patterns exemplarisch dargestellt werden. Als Beispiel wird das Löschen eines Termines herangezogen. Dieses Beispiel eignet sich auch gut zur Darstellung der Unterschiede zwischen der File-System Version und der Datenbank Version der Calendarium-Anwendung.

        File-System Version

        Zunächst soll der Vorgang des Löschens eines Termin-Eintrages anhand der File-System Lösung gezeigt werden.

        Abb. 4-6: Dynamisches Modell – Löschen eines Termines (File-System)

        Der Benutzer kann – in diesem Beispiel über das kontextabhängige Mausmenü – das Löschen eines Termines instruieren [1]. Die Boundary-Klasse TerminObjekt meldet der zuständigen Control-Klasse Tagesansicht die erfolgte Benutzer-Interaktion [2]. Die Control-Klasse erfragt daraufhin mit der Methode getEintrag die betroffene Termin-Instanz von der Boundary-Klasse [3]. Von der zurückgelieferten Termin-Instanz wird mit der Methode getOwner das Attribut owner ermittelt [4] und mit dem Benutzer, der als Attribut user von der Klasse Data ermittelt werden kann [5], verglichen: Nur dem owner eines Eintrages ist das Löschen desselben erlaubt. Referenzieren die Attribute owner und user auf dieselbe Instanz der Klasse Person, wird die Methode delete der Klasse TerminSet aufgerufen [6], die diese Aufgabe an die Klasse TerminSetRemote delegiert [7].

        public void actionPerformed(ActionEvent e)
        {
        else if(c.indexOf("löschen") >= 0)
        { // Termin löschen
        if(termin.getOwner().getID() == Data.user.getID())
        { Data.termine.delete(termin); } else
        { showDialog("Termin löschen",
        "Sie sind nicht berechtigt, diesen Termin zu löschen!");
        }
        }
        }

        Diese entfernt die betroffene Termin-Instanz zunächst mit der Methode removeTermin aus ihrem Attribut termine, in dem alle Termin-Instanzen gespeichert werden [8]. In der File-System Version wird an dieser Stelle die Klasse TerminSetRemote mit der Methode save neu gespeichert [9]: Die entsprechende Datei wird mit dem Befehl writeObject durch die neue Version überschrieben [10].

        Zur Anpassung der betroffenen Ansichten wird eine neue Instanz der Klasse TerminEvent mit dem owner-Attribut des gelöschten Termines als sender, keinem explizit angegebenen Empfänger (diese Nachricht wird daher an alle Clients gesendet) und der gelöschten Termin-Instanz als Parameter erzeugt [11]. Diese TerminEvent-Instanz wird dem MessageServer mit der Methode addEvent übergeben [12]. Dieser ruft in der Methode sendEvent [13] für alle angemeldeten Benutzer, die über das Attribut loggedUsers des MessageServers ermittelt werden, die Methode processCalendariumEvent der Interface-Klasse CalendariumListener auf [14]. Als implementierende Klasse des Interfaces CalendariumListener fungiert die Klasse CalendariumListenerImpl, die als Attribut listener der Klasse Client bei der Anmeldung des Client am Server dem MessageServer übergeben wurde (vgl. Abschnitt 4.2.2, Schritte [19] und [20]).

        Im Fall einer TerminEvent-Instanz, die als Parameter übergeben wird, ist die Aufgabe der Methode processCalendariumEvent lediglich der Aufruf der Methode notifyObservers [15] des Attributes observed (der Klasse BeingWatched). Diese Methode erbt die Klasse BeingWatched von ihrer Parent-Klasse Observable. Diese Methode ruft bei allen Klassen, die als Observer der Klasse BeingWatched registriert sind, die Methode update auf [17]. In diesem Beispiel sind das alle Ansichten, die den nunmehr gelöschten Termin anzeigen. Die Methode update der Klasse Tagesansicht überprüft zunächst, ob die Änderung in den angezeigten Zeitraum fällt. Ist dies – wie im Beispiel – der Fall, wird die Methode updateTermine aufgerufen [18]:

        public void update(Observable obj, Object arg)
        { CalendariumEvent evt = (CalendariumEvent) arg;
         
        if(evt.getEventID() == TERMIN_EVT) // Update Termin
        { Termin termin = ((TerminEvent) evt).getTermin();
         
        if(termin.getBeginn().isGreater(endAnsicht) <= 0 &&
          termin.getEnde().isGreater(bgnAnsicht) >= 0 &&
          termin.isRelevantFor(openCal.getPersonenListe()))
        { updateTermine(termin); }
        }
        }

        Die aktuellen Boundary-Klassen werden mit der Methode remove der verwendeten Swing-Klasse JSplitPane entfernt [19] und die Anzeige anschließend mit der Methode validate neu aufgebaut.

        public void updateTermine(Termin t)
        { splitPane.remove(terminListe.getGUI());
        Vector termine = Data.termine.getTermineVom(bgnAnsicht, openCal.getPersonenListe());
        terminListe = new TerminListeObjekt(this, bgnAnsicht, termine);
        splitPane.setBottomComponent(terminListe.getGUI());
        splitPane.validate();
        }

        Datenbank Version

        Im folgenden Kollaborationsdiagramm (Abb. 4-7) wird ebenfalls das Löschen eines Termines beschrieben. In den meisten Bereichen gleicht es daher dem Diagramm in Abb. 4-6. Die Beschreibung bezieht sich im Unterschied zu diesem aber auf die Datenbank Version der Calendarium-Anwendung. Die Unterschiede zwischen diesen beiden Versionen lassen sich somit gut durch den Vergleich der beiden Diagramme erkennen.

        Abb. 4-7: Dynamisches Modell – Löschen eines Termines (Datenbank Version)

        Der Ablauf erfolgt bis zum Aufruf der Methode removeTermin [8] in beiden Programm-Versionen ident. Während in der File-System Version die gelöschte Termin-Instanz in der Methode removeTermin aus der durch die Klasse TerminSetRemote verwalteten Menge der Termine entfernt wird und anschließend die gesamte Menge neu gespeichert werden muß, erfolgt die Entfernung des Termines in der Datenbank wesentlich eleganter: Nicht die Klasse TerminSetRemote ist für die Persistierung der Termin-Instanzen zuständig, vielmehr übernimmt die Klasse Termin als Subklasse von PersistentObject diese Aufgabe. In der Methode removeTermin werden die notwendigen Datenbank-Funktionen durch die Klasse Termin ausgeführt, bevor das Attribut termine der Klasse TerminSetRemote um die gelöschte Termin-Instanz verringert wird.

        Zunächst wird das Attribut kürzel des Benutzers herangezogen, um mit der Methode setKz das Attribut kz der Termin-Instanz zu setzen [9]. Dieses Attribut wird später von der Klasse PersistenceBroker zur Verwendung der richtigen Datenbankverbindung benötigt. Mit der Methode delete [10] nimmt die Klasse Termin die Durchführung der notwendigen Datenbank-Befehle vor:

        Die Methode getMapping [11] ermittelt durch Aufruf der Methode getClass anhand des Klassennamens "Termin" die relevante Instanz der Klasse ClassMap [12]. Diese dient neben einer Referenz auf die Termin-Instanz selbst als Parameter für die Instanziierung der Klasse DeleteSqlStatement [13]. Mit der Methode buildForObject wird die Erstellung des SQL-Befehles durchgeführt [14]. Der fertiggestellte SQL-Befehl wird zusammen mit dem Attribut kz und der ClassMap-Instanz als Parameter der Methode deleteObject der Klasse PersistenceBroker übergeben [15], die die endgültige Durchführung in der Datenbank vornimmt.

        public void delete()
        { map = getMapping();
        sqlStmt = new DeleteSqlStatement(map,this);
        if(isPersistent)
        { isPersistent = false;
        sqlStmt.buildForObject("objectIdentifier",new Long (objectIdentifier));
        PersistenceBroker.deleteObject(kz,map,sqlStmt.asString());
        }
        }

        Nachdem die Änderung in der Datenbank durchgeführt worden ist, kann die gelöschte Termin-Instanz auch aus dem Attribut termine der Klasse TerminSetRemote entfernt und mit der weiteren Verarbeitung – analog zur File-System Version – fortgefahren werden.
         
         
         

Literaturverzeichnis

[Hitz99] M. Hitz, G. Kappel: UML@Work – Von der Analyse zur Realisierung. dpunkt-Verlag 1999.

[JDoc98] Java Development Kit 1.1.8 API Documentation. URL: http://java.sun.com/docs

[StoC99] Chr. Stoiber: Client-Server-Programmierung im Internet – Eine Fallstudie mit Java und RMI. Wien 1999

[StoS99] S. Stoiber: Datenbankanbindungsmuster – Untersuchung alternativer Ansätze und Realisierung im Rahmen eines verteilten Kalender-Managers. Linz 1999