Technischer Überblick

Aus Salespoint

Wechseln zu: Navigation, Suche

Inhaltsverzeichnis

Übersicht

Dieses Dokument befasst sich mit der technischen Realisierung des Frameworks "SalesPoint". Die Strukur, umgesetzte Konzepte und wichtige Begriffe werden erläutert. Ziel des Technischen Überblicks ist es, ein generelles Verständnis für das Framework zu schaffen.

Mit SalesPoint ist es möglich, kleinere Geschäftsanwendungen zu programmieren. Insbesondere können verteilte Anwendungen erstellt werden, die aber mit Simulationscharakter lokal auf einem PC ablaufen oder auch verteilt auf mehreren PCs. Interaktion zwischen mehreren Rechnern kann durch mehrere Fenster auf dem Bildschirm nachgestellt werden. Im Abschnitt #Applikationsverwaltung wird genauer auf dieses Konzept eingegangen.

Das Framework gliedert sich physisch in die Hauptpakete sale, data, users, log und util.

Ausgewählte Pakete und Unterpakete seien im Folgenden vorgestellt:

sale zentrale Klassen des Frameworks, u.a. Shop, Gate und Transition.
sale.stdforms Standardoberflächen, wie z.B. FormSheets, die in allen Anwendungen benutzt werden können.
data wichtige Klassen zur Verwaltung der Daten. Catalog und Stock sind dort ebenso zu finden wie DataBasket, MoneyBag und Currency.
data.stdforms vielfältige Möglichkeiten, die eben genannten Dinge in Tabellen u.a. darzustellen.
data.swing wichtige Klassen zur Datenvisualisierung in swing (Modelle, Tabellen, Listen,...)
users wichtige Klassen für die Nutzerverwaltung. Mit deren Hilfe können Rechte (Capabilities) vergeben werden.
log Klassen für das Mitprotokollieren der Aktionen einer Anwendung. Sehr leicht können Handlungsabläufe der Nutzer aufgezeichnet und wenn notwendig abgerufen werden.
util Hilfsklassen, z.B. für das Anzeigen von Icons.

Das folgende statische UML-Diagramm gibt einen Überblick über die wichtigsten Elemente des Frameworks. Es zeigt vor allem, wie die Klassen aus den unterschiedlichen Paketen untereinander zusammenarbeiten:

Übersichtsdiagramm

Das Framework besteht also aus zwei großen, verhältnismäßig unabhängigen Bereichen: der Datenverwaltung (data) und der dynamischen Applikationsverwaltung (sale). Außerdem gibt es noch die kleineren Bereiche Benutzermanagement (user), Protokollverwaltung (log) und GUI. Im Folgenden werden wichtige Begriffe und Konzepte aus diesen Bereichen erläutert.

Applikationsverwaltung

Beginnen wir mit dem Bereich der Applikationsverwaltung. Ein wichtiger Begriff ist hier der des Ladens (Shop). Jede Anwendung auf Basis des Frameworks "SalesPoint" basiert auf diesem Shop. Umgekehrt gibt es in einer Anwendung auch nur genau einen Laden. Ein Laden kann beliebig viele Verkaufsstände (SalesPoint) haben.

Ein Programm, das mit SalesPoint geschrieben wird, soll oft eine verteilte Anwendung sein, zum Beispiel eine Firma mit Filialen, ein Markt mit Theken und Kassen oder eine Bibliothek mit Bibliothekaren und Onlinebestellsystem. In solchen Fällen nimmt das Projekt einen simulativen Charakter an. Wie bereits erwähnt, werden verteilte Anwendungen durch mehrere gleichzeitig geöffnete Fenster, die SalesPoints, dargestellt. So könnte ein SalesPoint die Fleischtheke im Supermarkt sein, ein anderer wäre die Kasse. Der Laden ist dann die übergeordnete Instanz, die die Zusammenarbeit der Verkaufsstände koordiniert.

Andererseits kann eine SalesPoint Anwendung ein voll funktionsfähiges Programm zur Benutzung auf einem einzelnen Rechner sein. In dem Fall repräsentiert der Shop das laufende Programm an sich, die SalesPoints sind gewöhnliche Fenster, die Daten anzeigen oder aufnehmen können.

Alle Interaktionen mit dem Laden finden im Rahmen von Prozessen (SaleProcess) statt. Ein Prozess ist eine abwechselnde Folge von Kommunikation mit dem Anwender (z.B. eine Kundin an der Fleischtheke will 250g "Hackepeter") und internen Bearbeitungsvorgängen (z.B. Entnahme, Abwiegen und Abpacken der gewünschten 250g "Hackepeter" durch den Verkäufer). Diese wechselnden Vorgänge können als endliche Automaten interpretiert werden. Die Kommunikation mit dem Anwender findet in den Zuständen (Gate) des Prozesses statt, die interne Bearbeitung wird in Zustandsübergängen (Transition) erledigt. Nachstehendes Diagramm verdeutlicht diesen Zusammenhang nochmals grafisch.

Prozesse bestehen aus Zuständen und Übergängen

Die vom Framework vordefinierten Gates und die Reihenfolge, in der von einem Gate zum nächsten gegangen wird, zeigt die folgende Abbildung. Die im Diagramm erkennbare Lücke zwischen dem InitialGate und den restlichen Zuständen, welche zum Ende des Prozesses führen, muss vom Anwendungsprogrammierer geschlossen werden.

Zusammenhänge zwischen den einzelnen Gates

Diese explizite Darstellung des Prozessaufbaus im Objektmodell hat den zusätzlichen Vorteil, dass Prozesse von der Anwendung quasi "angefasst" werden können. Das heißt, Prozesse können jederzeit in einem definierten Zustand (nämlich an einem Gate) unterbrochen und später an der selben Stelle wieder fortgesetzt werden. Befindet sich der Prozess zum Zeitpunkt der Unterbrechung gerade in einer Transition, so wird er einfach am nächsten Gate unterbrochen. Damit dies aber überhaupt machbar ist, müssen ein paar Bedingungen eingehalten werden: Da das Unterbrechen von Prozessen nicht zur Systemblockade führen soll, fordert man, dass Transitionen verhältnismäßig kurz sind. Insbesondere dürfen sie keine "potentiell unendlich" andauernden Aktivitäten umfassen. "Potentiell unendlich" sind dabei alle Aktivitäten, deren Endtermin nicht vorherbestimmbar ist, zum Beispiel eine Benutzerkommunikation. Dabei könnte es vorkommen, dass der Benutzer einfach "vergisst", einen Knopf zu wählen, was dazu führen würde, dass die Transition nicht verlassen und damit der Prozess nicht unterbrochen werden kann. Deshalb sind Benutzerkommunikation und ähnliche langandauernde Vorgänge nur an einem Gate erlaubt. Ein Prozess darf sich beliebig lange an einem Gate aufhalten, muss aber sicherstellen, dass er zu jedem Zeitpunkt unterbrochen werden kann. Im Gegenzug wird einem Prozess zugesichert, dass er nie während einer Transition unterbrochen wird.

Prozesse können auf verschiedene Arten ausgeführt werden. Zu jedem Zeitpunkt kann an jedem Verkaufsstand höchstens ein Prozess aktiv sein. Wenn beim Starten eines Prozesses bereits ein anderer läuft, so wird dieser angehalten und erst nach Beendigung des neuen Prozesses fortgesetzt. Prozesse können aber auch als Hintergrundprozesse wichtige Arbeit erledigen. Um den Prozess davon abzukoppeln, ob er gerade als Hintergrundprozess (background process) oder als Prozess an einem Verkaufsstand abläuft (SalesPoint process), gibt es das Konzept der Prozessumgebung (ProcessContext). Ein Prozess kommuniziert nicht direkt mit dem Verkaufsstand, auf dem er abläuft, oder mit dem Laden, in dem er stattfindet. Aus Prozesssicht handelt es sich bei all dem um Prozessumgebungen, über welche er die von ihm benötigten Ressourcen erhält bzw. ansteuern kann. In der folgenden Abbildung wird der Zusammenhang zwischen ProcessContext und Prozess noch einmal verdeutlicht.

Prozesse können an Verkaufsständen und als Hintergrundprozesse laufen

Wenn man mit dem Anwender kommunizieren will, so braucht man ein gemeinsames Medium. Im Framework gibt es dafür die Anzeige (Display). Über die Anzeige kommunizieren Anwender und Anwendung miteinander. Für jeden Verkaufsstand, der im Laden angelegt wird, gibt es eine solche Anzeige. Die Anzeigen von Verkaufsständen können verschiedene Formen annehmen. Sie können als unabhängige Fenster dargestellt werden, in Form von Tabs im Shopfenster oder als Fenster innerhalb des Shopfensters. Es ist möglich, zwischen diesen Anzeigeformen zu wechseln. Wird der Verkaufsstand aus dem Laden entfernt, so wird auch seine Anzeige vernichtet. Auf seiner Anzeige kann ein Verkaufsstand beispielsweise Menüpunkte und Knöpfe unterbringen, mit denen der Anwender Prozesse auslösen kann. Wird ein Prozess gestartet, so erhält er die Verantwortung für den Inhalt der Anzeige. Er kann jetzt alle Formulare und Menüs anzeigen, die für ihn wichtig sind.

Auf einer Anzeige können Formulare (FormSheet) und Menüs (MenuSheet) angezeigt werden. Menüs sehen aus, wie Menüs in anderen Anwendungen auch. Formulare bestehen aus zwei Bereichen: dem Komponentenbereich und der Knopfleiste. Im Komponentenbereich, der gewöhnlich den oberen Teil der Anzeige einnimmt, kann jede beliebige Swing-Komponente untergebracht werden. Hier hat der Anwender die Möglichkeit, Eingaben zu machen, aus Listen auszuwählen, etc. Gewöhnlich im unteren Bereich der Anzeige befindet sich die Knopfleiste, eine Zeile, die nur Schaltflächen enthält. Diese dienen dazu, Aktionen auszulösen. Obwohl die Formulare beliebige Swing-Komponenten enthalten können, sind Formulare und Menüs doch im Wesentlichen eine Abstraktion von der konkreten Darstellungsform und werden deshalb vom Framework unterstützt. Wenn man als Anwendungsentwickler ein Menü oder Formular verwendet, braucht man sich nicht darum zu kümmern, wie dieses letztendlich konkret dargestellt wird.

Ein weiterer Begriff, der des Formular-Inhaltserzeugers (FormSheetContentCreator / FSCC),wurde in früheren Salespoint versionen (< 4.0) für das Persistenzmanagement eingeführt. Beim Speichern der Anwendung wurden alle benötigten Klassen serialisiert und als Datenstrom in eine Datei geschrieben. Da die Swing-Klassen zum Teil erhebliche Schwächen bezüglich der Serialisierung aufweisen, musste es vermieden werden, Swing-Klassen zu serialisieren. Dies wurde erreicht, indem man nur die Information speichert, die nötig ist, um das GUI wieder zu regenerieren. Bei Menüs ist diese Information einfach das MenuSheet selbst (Achtung: nicht die Swing-Komponente! Das MenuSheet enthält nur die Information welche Menüknöpfe in welchem Menü zur Verfügung stehen.), bei Formularen ist es ein gesondertes Objekt, das die Strategie zur Erzeugung des Formular-Inhalts kapselt (vgl. Strategy-Muster). Beim Neuladen wird diese Strategie angewandt und die Benutzeroberfläche damit neu erstellt. Da ab Salespoint 4.0 das Persistenzmanagement generell umgestellt wurde sind diese Klasse nicht mehr notwendig, d.h. die GUI kann woanders generiert werden, wie jedoch erwähnt eignen sich diese FSCCs gut zu Kapselung und werder daher auch weiterhin empfohlen.

Formulare müssen gelegentlich mit ihrer Anzeige kommunizieren, um sicherzustellen, dass sich Änderungen am Formular auch sofort auf die Anzeige auswirken. Wenn beispielsweise ein Formular einen "Löschen"-Knopf hat, der ein gerade in einer Tabelle ausgewähltes Element löscht, so soll dieser vielleicht nicht selektierbar sein, solange kein Eintrag in der Tabelle ausgewählt ist. Die Anwendung würde also den Zustand des Knopfes immer dann ändern, wenn sich die Auswahl in der Tabelle ändert. Um den Zustand des Knopfes zu verändern, verwendet die Anwendung eine entsprechende logische Operation, ohne sich um die Umsetzung auf der Ebene von Swing zu kümmern. Das Formular hat dann die Aufgabe, dafür zu sorgen, dass die Änderungen auch auf der Oberfläche sichtbar werden. Um auch hier die bisherige enge Kopplung zwischen Formular und Oberfläche aufzuheben, wurde der Formularcontainer (FormSheetContainer) eingeführt. Er stellt ein Objekt dar, das ein Formular anzeigt. Das Formular erhält die Möglichkeit, den Formularcontainer über Änderungen im Zustand des Formulars zu informieren. Der Formularcontainer stellt gewissermaßen die Sicht des Formulars auf eine Anzeige dar.

Mit den Knöpfen in der Knopfleiste eines Formulars oder in einem Menü werden Aktionen (Action) verknüpft, die ausgeführt werden, wenn der entsprechende Knopf gedrückt wurde.

Jedes Formular oder Menü wird immer in einem gewissen Kontext dargestellt. Dieser besteht aus dem Laden, in dem es dargestellt wird, gegebenenfalls dem Verkaufsstand, an dem es dargestellt wird und/oder gegebenenfalls dem Prozess, der es darstellt. Nur der Laden ist immer vorhanden. Aktionen, die mit Knöpfen eines Formulars oder Menüs verbunden sind, werden immer im Kontext dieses Formulars oder Menüs aufgerufen. Dies geschieht, indem der entsprechenden auszuführenden Methode der Verkaufsstand und der Prozess als Parameter übergeben werden. Der Laden ist ohnehin bekannt, da es nur einen pro Anwendung gibt. Die folgende Abbildung verdeutlicht diese Zusammenhänge noch einmal grafisch.

Formulare und Menüs verwenden Aktionen, um Benutzereingaben mit Programmreaktionen zu verknüpfen

Zum Abschluss dieses Abschnitts muss noch einmal der Laden erwähnt werden. Zusätzlich zu den hier geschilderten, eher konzeptionell begründeten Verantwortungen hat der Laden auch noch einige andere, die eher technischer Natur sind. Auf abstraktester Ebene ließen sich diese etwa wie folgt motivieren: "Der Laden bildet das zentrale Element der Anwendung. Er bündelt sämtliche Aktivitäten und Verantwortungen." Das heißt, der Laden ist zentraler Ansprechpartner für alle Teile der Applikation. Er "kennt", entweder durch direkte Assoziation oder weil sie ein "Singleton" implementieren, sämtliche Elemente der Anwendung. Deshalb ist es nur natürlich, dem Laden auch die Verantwortung für das Persistenzmanagement (siehe #Datenverwaltung) zuzuordnen.

Datenverwaltung

Ein weiterer wichtiger und großer Bereich des Frameworks ist die Datenverwaltung. Diese stellt für Verkaufsanwendungen typische Datenstrukturen zur Verfügung. Außerdem bietet sie Mechanismen, die zur Implementation von Transaktionseigenschaften wie Isolation und Atomizität verwendet werden können.

Ein Geschäft stellt die angebotenen Waren oder Dienstleistungen in Form eines oder mehrerer Kataloge (Catalog) dar. Ein Katalog ist im Wesentlichen eine Liste von Katalogeinträgen (CatalogItem). Jeder Katalogeintrag beschreibt eine Ware oder Dienstleistung durch ihre Eigenschaften. Alle Katalogeinträge haben mindestens einen Namen bzw. Schlüssel und einen Wert. Werte (Value) sind Objekte, die Zahlen repräsentieren. Für die Werte stellt das Framework wichtige Operationen wie Addition und Subtraktion zur Verfügung. Wertobjekte können beispielsweise verwendet werden, um Zahlen in Tabellen anzuzeigen. Den Tabellen müssen Objekte übergeben werden, welche sie anzeigen sollen. Einfache Datentypen wie int oder double lassen sich nicht ohne Weiteres darstellen.

Kataloge sind Listen von potentiell verfügbaren Objekten. Um tatsächlich verfügbare Objekte darzustellen, benötigt man Bestände. Ein Bestand (Stock) bezieht sich auf einen Katalog und verzeichnet zu jedem Katalogeintrag die Anzahl tatsächlich verfügbarer Objekte dieses Typs. "Tatsächlich verfügbar" heißt dabei nicht, dass Bestände nur verwendet werden können, um Lager- oder Kassenbestände darzustellen. Auch Bestellungen lassen sich als Bestände auffassen, wobei "tatsächlich verfügbar" dann heißt "auf der Bestellung aufgeführt".

Es gibt zwei grundlegende Typen von Beständen: "zählende Bestände" (CountingStock) und "beschreibende Bestände" (StoringStock). Abzählende Bestände speichern zu jedem Katalogeintrag tatsächlich nur die Anzahl verfügbarer Objekte, während speichernde Bestände für jedes tatsächlich verfügbare Objekt einen Bestandseintrag (StockItem) speichern. Dadurch ergibt sich die Möglichkeit, die einzelnen Objekte zu unterscheiden und ggf. mit individuellen Merkmalen anzureichern. Zum Beispiel könnte es für einen Fahrzeughändler interessant sein, für jeden der 50 roten Pordi 911 Boxter, die er auf dem Hof stehen hat, zusätzlich noch die Fahrgestellnummer, die PIN der Wegfahrsperre etc. zu speichern. Dennoch sind alle 50 Autos rote "Pordi 911 Boxter" und er möchte vermutlich nicht für jedes einen neuen Katalogeintrag anlegen. Deshalb kann er die zusätzlichen Informationen in einem Bestandseintrag speichern.

Die folgende Abbildung stellt diese Zusammenhänge noch einmal grafisch dar. Sie zeigt außerdem zwei interessante zusätzliche Elemente: Kataloge bzw. Bestände sind selbst wieder Katalog- bzw. Bestandseinträge. Dadurch wird es möglich, auch mit geschachtelten Katalogen bzw. Beständen zu arbeiten. Diese können in manchen Anwendungen, die mit sehr komplexen Beständen arbeiten müssen, recht nützlich sein. Beispielsweise könnte eine Wechselstube die Währungen, zwischen denen sie wechseln kann, in einem Katalog anbieten. Die Währungen selbst sind wieder Kataloge, die die einzelnen Münzen und Banknoten beschreiben. In Analogie dazu gäbe es auch einen geschachtelten Bestand, der für jede Währung die Momentanbestände jedes einzelnen Schalters verwalten könnte. Werden geschachtelte Bestände verwendet, so müssen immer auch geschachtelte Kataloge benutzt werden. Der Katalog C, auf den sich ein Bestand B bezieht, muss sich auf der gleichen Schachtelungsebene befinden wie B selbst. Er muss außerdem im Katalog des Bestandes enthalten sein, der B enthält. Das heißt, Bestände und ihre zugehörigen Kataloge müssen identisch geschachtelt sein. Eine bestehende Einschränkung der Schachtelung ist, dass persistente Kataloge bzw. Bestände keine weiteren Kataloge bzw. Bestände aufnehmen können. Nicht-peristente hingegen können persistente als auch nicht-persistente aufnehmen.

Zweitens sieht man in dieser Abbildung, dass die gemeinsame Eigenschaft einen Namen zu haben in dem Begriff des Benennbaren (Nameable) zusammengefasst wurde. Benennbare Objekte haben einen Namen, welcher sie identifiziert. Dieser Name muss gewissen Regeln genügen. Die Regeln werden von einem Namenskontext (NameContext) festgelegt, welcher dem benennbaren Objekt zugeordnet werden kann. Vor jeder Namensänderung wird geprüft, ob der neue Name den Regeln des Namenskontextes genügt. Ist dem nicht so, wird der Name nicht geändert. Namen sollten allerdings mehr als eine ID aufgefasst werden und sollten daher nicht geändert werden, da dies mögliche Inkonsistenzen zur Folge haben kann.

Die folgende Abbildung zeigt noch einmal die Zusammenhänge zwischen Catalog und Stock.

Kataloge und Bestände

Es sei darauf hingewiesen, dass die Aggregation von StockItem zu Stock lediglich eine symbolische ist, verdeutlicht durch den Schrägstrich bei /ist Element von. Wie bereits erwähnt, werden in CountingStocks nicht tatsächlich StockItems gespeichert, sondern nur deren Name und Anzahl. Im StoringStock dagegen werden wirklich StockItem-Objekte abgelegt, deshalb enthält das Diagramm die explizite Aggregation von StockItem zu Stock.

Um Transaktionseigenschaften wie Isolation und Atomizität zu implementieren, existiert der Datenkorb (DataBasket). Datenkörbe arbeiten eng mit Datencontainern wie Katalogen und Beständen zusammen, um transaktionale Eigenschaften zu implementieren. Man kann sich einen Datenkorb auf zweierlei Weise vorstellen. Abstrakt ist er eine Art Transaktionshandle, also eine Struktur, die die aktuelle Transaktion kennzeichnet, und die an jede Routine, die Datencontainer manipuliert, übergeben werden muss, damit diese Manipulationen im Datenkorb aufgezeichnet werden können. Etwas konkreter kann man sich einen Datenkorb durchaus als eine Art Warenkorb vorstellen, in den beliebige Datenelemente hineingelegt werden können. Diese Transaktionen werden Softwareseitig abgebildet und sind für die Datenbank nicht als solche erkennbar.

Wenn aus einem Bestand ein Element entnommen wird, so wird das es zunächst in den Datenkorb eingeordnet. Solange das Element im Datenkorb ist, wird es vom Bestand als "vorläufig gelöscht" markiert. Erst wenn dem Datenkorb die Löschung des Elements bestätigt wurde (siehe unten), wird es endgültig aus dem Bestand gelöscht. Analog verhält es sich mit dem Hinzufügen von Elementen. Wird ein Element zum Bestand hinzugefügt, so wird es gleichzeitig in den entsprechenden Datenkorb eingefügt. Im Bestand wird es zunächst als "vorläufig hinzugefügt" markiert und steht anderen Nutzern zunächst nicht zur Verfügung. Erst wenn das Hinzufügen dem Datenkorb bestätigt wurde, wird das Element als "tatsächlich enthalten" markiert und wird für andere Nutzer sichtbar. Die Benutzung von Katalogen verläuft ganz analog.

Entsprechend der zwei möglichen Sichtweisen auf Datenkörbe gibt es auch zwei getrennte Untermengen der Datenkorbschnittstelle. Die eine bietet Funktionen, mit denen Transaktionseigenschaften implementiert werden können (Transaktionsschnittstelle), die andere bietet Funktionen, um den Inhalt des Datenkorbs zu inspizieren und auszuwerten (Inspektionsschnittstelle).

Die Transaktionsschnittstelle bietet verschiedene Variationen der Operationen commit und rollback. Diese werden verwendet, um Änderungen, die im Datenkorb registriert sind, zu bestätigen und endgültig festzuschreiben (commit) oder um sie rückgängig zu machen (rollback). Commit bzw. rollback können unbedingt, also für alle vom Datenkorb beschriebenen Aktionen, oder nur für eine Auswahl dieser Aktionen durchgeführt werden. Außerdem ist es möglich, Unterbereiche des Datenkorbs zu definieren, die ähnlich wie Checkpoints in Transaktionen verwendet werden können.

Die Inspektionsschnittstelle umfasst Operationen zum Iterieren über den Inhalt des Datenkorbs sowie eine Funktion, um den Wert aller Elemente aufzusummieren, die einer bestimmten Bedingung genügen. Außerdem können Datenkörbe gespeichert werden, was bedeutet, dass auch der Zustand von Transaktionen gesichert werden kann, so dass diese später wieder fortgesetzt werden können. Fasst man die Aufgaben des Datenkorbs nochmals zusammen, so ergeben sich folgende Verantwortlichkeiten:

Transaktionsschnittstelle Ermöglichen von Atomizität durch commit- und rollback- Operationen.
Isolation Ermöglichen von Isolation (bis zu einem gewissen Grad (im Prinzip SQL Level 2: read committed)) durch Einschränkung der Sichtbarkeit von Datenelementen.
Inspektionsschnittstelle Verwendung des Datenkorbs als "Einkaufskorb".

Bestände, Kataloge und Datenkörbe können Ereignisse auslösen, wenn ihr Inhalt geändert wird.

Benutzerverwaltung

Das Framework umfasst auch einen kleineren Bereich, der sich mit der Verwaltung von Benutzern beschäftigt. Hier sind die folgenden Begriffe wichtig.

Benutzer (User) haben einen Namen, ein Passwort, ein Bild sowie eine Liste von Rechten bzw. Fähigkeiten (Capability). Die Fähigkeiten bestimmen, welche Operationen ein Benutzer ausführen darf und welche ihm nicht gestattet sind. Der Name und das Passwort dienen der Identifikation des Benutzers beim Login.

Benutzer werden von einer Benutzerverwaltung (UserManager) verwaltet. Diese kann Benutzer aufnehmen, löschen und mit Hilfe einer Benutzerfabrik (UserCreator) sogar neue Benutzer erzeugen. Neu erzeugte Benutzer erhalten zunächst einen Satz von Standardfähigkeiten (default capabilities).

Benutzer haben Fähigkeiten und werden von Benutzermanagern verwaltet.

Protokollverwaltung

Ein kleiner Bereich des Frameworks beschäftigt sich mit dem Erstellen und Auswerten von Protokollen.

Protokolle (Log) sind Ströme von Protokolleinträgen (LogEntry). Ein Protokolleintrag verzeichnet den Zeitpunkt der Protokollierung sowie einen Text, der den zu protokollierenden Sachverhalt beschreibt. Er kann jedoch darüber hinaus noch weitere Informationen enthalten, die für den speziellen Typ von Protokolleintrag wichtig sind. Protokollierbare Objekte oder Vorgänge (Loggable) liefern auf Anfrage einen Protokolleintrag zurück, der dann ins Protokoll geschrieben wird.

Auf Protokolle besteht nur Schreibzugriff, um Protokollströme zu lesen und auszuwerten benötigt man einen Protokolleingabestrom (LogInputStream). Dieser kann mittels eines Protokollfilters (LogEntryFilter) gefiltert werden.

GUI

Das Framework enthält bereits einige Standard-GUI-Komponenten. Neben einfachen Formularen wie MessageForm, TextInputForm, etc. gibt es auch Formulare zum Anzeigen und Editieren des Inhalts von Datencontainern (siehe Abschnitt #Datenverwaltung) und Protokoll-Eingabeströmen (siehe Abschnitt #Protokollverwaltung).

Bei den meisten dieser Formulare werden zunächst die entsprechenden Swing-Komponenten angeboten, die anschließend zu Standard-Formularen zusammengesetzt werden. Das erlaubt einerseits die flexible Verwendung dieser Komponenten in eigenen Formularen, bietet aber andererseits auch den Komfort bereits vorgefertigter Formulare für besondere Anwendungsfälle.

Ein wichtiges Element des Framework-GUI sind Tabellen. Dafür gibt es ein paar spezielle Komponenten, deren genauere Betrachtung sich an dieser Stelle lohnt. Die meisten im Rahmen des Frameworks darzustellenden Daten sind Listen von komplexen, aber gleichartigen Elementen. Um diese darzustellen, ist im Prinzip nur zu entscheiden, welche Attribute man darstellen möchte und in welcher Form. Man kann dann jedem darzustellenden Element eine Zeile und jedem darzustellenden Attribut eine Spalte einer Tabelle zuordnen.

In Anlehnung an das von Swing verwendete Model-View-Controller (MVC) Muster, welches die Trennung von Daten und deren Sichten behandelt, gibt es für jede Tabelle einen Datensatzbeschreiber (TableEntryDescriptor). Dieser hat die Aufgabe, die anzuzeigenden Attribute auszuwählen und ihnen eine Spalte und eine Formatierung zuzuordnen. Die Aufgabe der tatsächlichen Darstellung der Datensätze in Form einer Tabelle wird dann intern vom Framework an Swing delegiert.

Ein weiteres wichtiges Element sind ListViews. Sie eigenen sich im Vergleich zu Tabellen besser zur Produktpräsentation, da sie komplexe Möglichkeit zur Visualisierung bieten. Sie sind frei skalierbar, können komfortabel durchsucht werden und unterstützen die Produktkategorisierung. Es stehen konkrete Ableitungen für die Datenelemente (Kataloge, Stocks, ...) mit entsprechenden Standardrenderern zur verfügung.

Persönliche Werkzeuge