• Keine Ergebnisse gefunden

3.1 Aufgabenstellung

Wie schon im letzten Kapitel kurz dargestellt, soll der Benutzeragent (User Agent) einen Be-nutzer gegenüber dem System repräsentieren. Es wäre zwar prinzipiell möglich, auf die Kom-ponente des Benutzeragenten zu verzichten und den Benutzer (beziehungsweise die Oberflä-che, mit der der Benutzer am Bildschirm arbeitet) direkt anzusprechen; allerdings wirft dieser Ansatz einige Probleme auf:

• Da ein Benutzer nicht ständig anwesend ist, etwa nachts oder an Wochenenden, können in dieser Zeit Aufträge (Tasks), die für diesen Benutzer bestimmt sind, nicht ausgeliefert wer-den. Es ist also eine Komponente notwendig, die sich in etwa mit einer Mailbox vergleichen läßt: Während der Abwesenheit des Benutzers werden die Aufträge durch diese Kompo-nente gesammelt und sind danach auf Wunsch des Benutzers abrufbar. Diese Funktionalität muß folglich durch den Benutzeragenten abgedeckt werden.

• Der Benutzeragent (User Agent) muß fehlertolerant sein: Hat er einen Auftrag (Task) von der ConTract Engine erhalten, so muß dieser an den Benutzer übermittelt werden, und zwar unabhängig davon, ob der Benutzeragent zwischen dem Empfang und der Weiterleitung der Aufgabe wegen eines System- oder Programmfehlers neu gestartet werden muß. Um diese Fehlertoleranz zu erreichen, nimmt der Benutzeragent die Dienste des Storage Agents in Anspruch, der für die persistente Speicherung der an ihn übermittelten Daten verantwortlich ist. Wird der Benutzeragent neu gestartet, ist er mit Hilfe dieser Daten in der Lage, eine Re-covery durchzuführen und seinen alten Zustand wiederherzustellen.

• Die Aufgaben, die der Benutzer zur Bearbeitung von der ConTract Engine erhält, laufen in der Regel unter dem Schutz einer Transaktion ab. Es ist daher notwendig, den Benutzer in diese Transaktion einzubinden. Dies ist jedoch nur möglich, wenn der Benutzeragent, als die den Benutzer repräsentierende Komponente, am Transaktions-Protokoll teilnimmt.

3.2 Struktur des Benutzeragenten

Wie schon im einführenden Kapitel kurz dargelegt, gliedert sich der Benutzeragent (User Agent) in zwei Teile: Zum einen in den Monitoring-Teil, der es dem Benutzer ermöglicht, Ein-fluß auf den Ablauf von ConTracts zu nehmen (der Benutzer kann ConTracts starten, anhalten, weiterlaufen lassen, abbrechen und ähnliches) und zum anderen in den Tasklist-Teil, der in die-ser Arbeit beschrieben wird.

Benutzeragent

Abbildung 6: Der Benutzeragent gliedert sich in einen Monitoring- und einen Tasklist-Teil.

Auf der Seite des Monitoring-Teils des Benutzeragenten wird für jeden ConTract eine Instanz des Schnittstellenobjektes angelegt, das für die Übermittlung der Benutzerbefehle an die jewei-lige ConTract Engine und deren Meldungen an den Benutzer zuständig ist. Der Tasklist-Teil beschränkt sich auf nur ein Schnittstellenobjekt, das die durch den Benutzer zu erledigenden Aufgaben von allen ConTract Engines entgegennimmt.

Die beiden Teile des Benutzeragenten konzentrieren sich auf völlig unterschiedliche Dienste und sind deshalb weitgehend unabhängig voneinander. Einzig in der Initialisierungsphase beim Programmstart ergeben sich gemeinsam zu erledigende Aufgaben, wie etwa das Binden an den Storage Agent. Danach erfolgt zwischen den beiden Teilen keinerlei Interaktion mehr. Der Tasklist-Teil interessiert sich beispielsweise in keinster Weise dafür, welche ConTracts vom Benutzer gestartet wurden und in welchem Zustand sich diese befinden; der Monitoring-Teil zeigt sich hingegen von der Frage unberührt, welche Aufgaben der Benutzer zu bearbeiten hat.

Aus diesem Grunde bietet es sich an, den Tasklist-Teil und den Monitoring-Teil des

Benutzer-stellen. Die Vorteile dieses Ansatzes liegen in der größeren Sicherheit wegen der Trennung der Adreßräume. In der gegenwärtigen Implementierung ist es für jeden Teil möglich, auf den Speicherbereich des jeweils anderen zuzugreifen und dadurch einen Programmfehler zu verur-sachen. Auch falls aufgrund eines internen Fehlers ein Teil des Benutzeragenten abstürzt, zieht er den anderen mit ins Verderben, obwohl dieser eigentlich hätte weiterarbeiten können.

Der Grund, warum die beiden Komponenten des Benutzeragenten trotzdem in einem ausführ-baren Programm zusammengeführt werden, liegt in der Aufgabenstellung begründet, die eine Aufteilung nicht vorsieht. Für jeden Benutzer soll nur ein Agent zuständig sein.

3.3 Threads innerhalb des Benutzeragenten

Was geschieht nun, wenn die ConTract Engine und das Tasklist Interface gleichzeitig versu-chen, den Benutzeragenten (User Agent) anzusprechen? In diesem Fall ist es ohne die Verwen-dung mehrerer Threads nur möglich, eine der beiden Anfragen zu bearbeiten. Der andere Auf-ruf wird mit einer System Exception abgelehnt; im Falle von Threads lassen sich die Aufträge in einer Warteschlange halten.

Obwohl sowohl die Auslieferung neuer Aufgaben durch die ConTract Engine als auch die In-teraktion mit dem Tasklist Interface dadurch, daß es sich um die einen menschlichen Benutzer betreffende Kommunikation handelt, nur relativ selten stattfindet, ist eine solche Kollision doch möglich und sollte verhindert werden. Verschärft wird diese Problematik noch dadurch, daß sich nicht nur die Komponenten des Tasklist-Teils des Benutzeragenten, sondern auch die des Monitoring-Teils im selben ausführbaren Programm befinden und beim Eintreffen von Nach-richten um die Zuteilung des Prozessors konkurrieren.

Verarbeitung der

Abbildung 7: Der Tasklist-Teil des Benutzeragenten mit mehreren Threads

Aus diesem Grunde lassen die Implementierer des Benutzeragenten die jeweiligen Schnittstel-lenobjekte in verschiedenen Threads laufen. Der Nachteil einer Verwendung von Threads ist allerdings der erhöhte Aufwand für die Synchronisation des Zugriffs auf Datenstrukturen, auf die von verschiedenen Threads aus zugegriffen wird.