Es sind nun alle Voraussetzungen erfüllt, um den Anmeldeprozess zu implementieren. Bei diesem initialen Prozess muss der Anwender sich zunächst durch Namen und Passwort identifizieren. Nach erfolgreicher Anmeldung werden die weiteren möglichen Aktivitäten angezeigt. Wurde kein Nutzername ausgewählt oder ein falsches Passwort eingetippt, erscheint eine Fehlermeldung. Meldet man sich ab, terminiert der Prozess. Wählt man eine der möglichen Aktivitäten, wie z.B. Video leihen, so wird ein neuer, auf die Aktivität ausgerichteter Prozess gestartet. Der Anmeldeprozess "schläft" dann solange, bis der neugestartete Prozess terminiert. Abbildung 6.1 verdeutlicht den Ablauf des Prozesses anhand eines Zustandsdiagramms.

Abbildung 6.1: Der Anmeldeprozess als Zustandsdiagramm
Deklaration eines SaleProcess
Wie im Technischen Überblick erläutert wird, ist ein SaleProcess
eine Folge von Zuständen und Zustandsübergängen. Die Zustände werden dabei durch das Interface Gate
, die Zustandsübergänge durch das Interface Transition
beschrieben. Während eine Transition
atomar behandelt, d.h. nicht unterbrochen wird, kann an einem Gate
der Kontrollfluss unterbrochen werden und somit auch Interaktion mit dem Anwender stattfinden. Eine Implementation von Gate
, die die Kommunikation über ein FormSheet
und/oder MenuSheet
ermöglicht, ist die Klasse UIGate
.
Im Folgenden wird die Klasse SaleProcessLogOn
als Spezialisierung von SaleProcess
implementiert. Es werden zunächst die in
Abbildung
6.1 aufgeführten Zustände deklariert mit Ausnahme des Stop-Gate
, welches bereits in der Klasse SaleProcess
selbst definiert ist. Für die Repräsentation der Zustände werden UIGate
-Instanzen benutzt. Deren Konstruktor wird, sowohl für das erwartete FormSheet
als auch für das MenuSheet
, vorläufig null
übergeben. Außerdem muss die abstrakte Methode getInitialGate()
implementiert werden, welche das Gate
zurückliefert, an das zu Prozessbeginn gesprungen wird. Die anderen Methoden dienen der Rückgabe der restlichen UIGate
-Instanzen.
package videoautomat; public class SaleProcessLogOn extends SaleProcess { private UIGate uig_log_on = new UIGate(null, null); private UIGate uig_fault = new UIGate(null, null); private UIGate uig_main = new UIGate(null, null); public SaleProcessLogOn() { super("SaleProcessLogOn"); } protected Gate getInitialGate() { return uig_log_on; } private Gate getFaultGate() { return uig_fault; } private Gate getMainGate() { return uig_main; } }
Um den Prozess zur Ausführung bringen zu können, wird der Button des Automatenformulars (siehe Abschnitt Der FormSheetContentCreator) mit einer Aktion verknüpft. Die Methode getDefaultFormSheet()
in der Klasse VideoAutomat
wird dafür wie folgt erweitert.
protected FormSheet getDefaultFormSheet() { FormSheet fs = Global.getStartFormSheet( VideoShop.getVideoStock(), null, false, new TEDVideoStock()); fs.addContentCreator(new FormSheetContentCreator() { protected void createFormSheetContent(FormSheet sheet) { sheet.getButton(Global.FB_LOGON).setAction(new Action() { public void doAction(SaleProcess process, SalesPoint point) throws Throwable { point.runProcess(new SaleProcessLogOn()); } }); } }); return fs; }
Es ist darauf zu achten, dass die Kopplung der Aktion an den Button zwecks Serialisierbarkeit innerhalb einer FormSheetContentCreator
-Instanz erfolgt, die dem Formular hinzugefügt werden muss. Den Button erhält man vom Formular über die in Global
zuvor definierte Integer-ID. Mittels der Methode setAction(Action a)
wird der Button mit der gewünschten Action
-Instanz verbunden.
Die Methode doAction(SaleProcess process, SalesPoint point)
des Interface Action
definiert was innerhalb der Aktion geschieht. Die übergebenen Parameter liefern den Kontext in welchem die Aktion stattfindet. In diesem Fall wird beispielsweise der VideoAutomat
übergeben, in dem die Aktion selbst definiert ist. Mittels runProcess(SaleProcess process)
wird ein Anmeldeprozess instantiiert und auf dem Automaten gestartet.
Wird die Anwendung zum jetzigen Zeitpunkt übersetzt und ausgeführt und der betreffende Button betätigt, so startet der Prozess, jedoch mit leerer Anzeigefläche. Darüberhinaus kann das Fenster vom Videoautomaten nicht wie gewohnt geschlossen werden. Das liegt daran, dass ein SalesPoint
mit darauf laufendem Prozess nicht ohne weiteres geschlossen werden kann.
Anmeldung durch das LogOnForm
Im initialen Zustand des Anmeldeprozesses soll der Anwender zwecks Authentifikation den Namen und das Kennwort eintragen. Das Framework bietet dafür eine eigene FormSheet
-Ableitung, die Klasse LogOnForm
. Dieses spezielle Formular zeigt anhand einer übergebenen Instanz von UserManager
eine Auswahlliste aller registrierten Namen an, sowie ein Textfeld zur Eingabe des Kennworts. Optional kann mit Hilfe einer Implementation des Interface UserFilter
die Menge der dargestellten Nutzer eingeschränkt werden.
Es wird eine neue Klasse im Paket videoautomat.gui
erstellt, die die Oberflächenelemente des Anmeldeprozesses beinhalten soll. In dieser Klasse wird eine statische Methode definiert, die die gewünschte Instanz von LogOnForm
für den Startzustand zurückliefert. Die Standardbuttons (Ok und Cancel) werden beibehalten.
package videoautomat.gui; public class LogOn { public static LogOnForm getLogOnForm( boolean ask_password, UserManager um, Comparator cmp, UserFilter uf) { LogOnForm lof = new LogOnForm( "Are you a registered user?", "Select your user name!", "Enter your passphrase!", ask_password, um, cmp, uf); return lof; } }
Der boolesche Parameter im Konstruktoraufruf entscheidet darüber, ob eine Passwortabfrage erfolgt oder nicht. Der übergebene java.util.Comparator
bestimmt, wie die Nutzernamen in der Liste sortiert werden.
Wo die einzelnen String-Objekte, die ebenfalls dem Konstruktor von LogOnForm
übergeben werden in dem Formular auftauchen, kann zur Ausführungszeit betrachtet werden. Dazu muss zuvor das Formular dem initialen UIGate
des Anmeldeprozesses hinzugefügt werden. In der Klasse SaleProcessLogOn
wird dafür eine Variable vom Typ LogOnForm
definiert und in der Methode getInitialGate()
instantiiert. Das Formular wird durch den Aufruf setFormSheet(FormSheet fs)
an den Startzustand gebunden.
public class SaleProcessLogOn extends SaleProcess { . . . private LogOnForm lof_initial = null; protected Gate getInitialGate() { lof_initial = LogOn.getLogOnForm(true, UserManager.getGlobalUM(), null, null); uig_log_on.setFormSheet(lof_initial); return uig_log_on; }
Bei einem erneuten Test des Programms wird das Formular korrekt angezeigt, vorausgesetzt es wurde zuvor neu übersetzt.
Einen Zustandsübergang definieren
Als Nächstes müssen die vorhandenen Buttons mit Leben gefüllt werden. Im Fall des Cancel-Button soll der Prozess terminieren, während beim Ok-Button die Anmeldung vollführt werden soll. In beiden Fällen werden Zustandsübergänge, also Implementationen von Transition
benötigt, die zu den jeweiligen Folgezuständen führen. Beim Beenden des Prozesses ist das sehr einfach, da lediglich von einem Zustand, dem initialen Gate
zu einem anderen, dem Stop-Gate
gewechselt werden muss. Für den einfachen Übergang von einem Zustand zum anderen gibt es die Klasse GateChangeTransition
, deren Konstruktor der Zielzustand übergeben werden muss, zu dem der Übergang führen soll. Darüberhinaus existieren in dieser Klasse Transition
-Konstanten. Diese vorimplementierten Zustandsübergänge führen zu den in der Klasse SaleProcess
bereits definierten Zuständen, wie z.B. Stop-Gate
.
Die Anpassung der Buttons erfolgt in der getInitialGate()
-Methode und muss innerhalb einer FormSheetContentCreator
-Instanz vorgenommen werden. Das Einleiten eines Zustandsübergangs wird bei einem UIGate
-Objekt über die Methode setNextTransition(Transition t)
getätigt.
protected Gate getInitialGate() { lof_initial = LogOn.getLogOnForm(true, UserManager.getGlobalUM(), null, null); lof_initial.addContentCreator(new FormSheetContentCreator() { protected void createFormSheetContent(FormSheet sheet) { sheet.getButton(FormSheet.BTNID_CANCEL).setAction(new sale.Action() { public void doAction(SaleProcess process, SalesPoint point) { uig_log_on.setNextTransition( GateChangeTransition.CHANGE_TO_STOP_GATE); } }); } }); uig_log_on.setFormSheet(lof_initial); return uig_log_on; }
Die vordefinierten Buttons der Klasse FormSheet
erhält man über die Methode getButton(int i)
mit Hilfe der Integer-Konstanten FormSheet.BTNID_OK
und FormSheet.BTNID_CANCEL
. Wie zu ersehen ist, wurde für den Wechsel zum Stop-Gate
auf die dafür vorgesehene Transition
-Konstante in GateChangeTransition
zurückgegriffen.
Das Wechseln zum Stop-Gate
und damit verbunden das Abbrechen des Anmeldeprozesses ist nun möglich. Die Umsetzung des Übergangs zum Haupt-Gate
gestaltet sich etwas komplizierter, da hier nicht einfach von einem Zustand zum anderen gewechselt werden kann. Innerhalb des Zustandsübergangs muss zunächst die Korrektheit der Anmeldedaten geprüft werden. Abhängig davon wird der Prozess zum Haupt- oder Fehler-Gate
geleitet. Demnach ist eine eigene Implementation des Interface Transition
notwendig, die in einer neuen Methode in der Klasse SaleProcessLogOn
vorgenommen wird.
public class SaleProcessLogOn extends SaleProcess { . . . private User u_current = null; . . . private Transition logOn() { return new Transition() { public Gate perform(SaleProcess process, User user) { lof_initial.ok(); u_current = lof_initial.getResult(); if (u_current != null) { ((SalesPoint) process.getContext()).attach(u_current); return getMainGate(); } return getFaultGate(); } }; } }
Eine Implementation von Transition
muss in der Methode perform(SaleProcess process, User user)
definieren, was während des Zustandsübergangs passieren soll. Das Gate
, welches die Methode zurückgibt, definiert den Folgezustand.
In diesem spezifischen Fall wird durch den Aufruf der ok()
-Methode der LogOnForm
-Instanz geprüft, ob ein Name aus der Liste gewählt wurde und wenn ja, ob das Passwort stimmt. Ist beides der Fall, liefert getResult()
die dem Namen entsprechende User
-Instanz, andernfalls null
. Bei korrekter Anmeldung wird hier die User
-Instanz dem Videoautomaten durch den Aufruf attach(User u)
zugeordnet, von wo sie in weiterführenden Prozessen jederzeit abrufbar ist, und es wird das Haupt-Gate
zurückgegeben. Wurde kein Name oder ein inkorrektes Passwort gewählt, wechselt der Prozess zum Fehler-Gate
.
Die neu erstellte Transition
muss noch mit dem Ok-Button verbunden werden.
protected Gate getInitialGate() { lof_initial = LogOn.getLogOnForm(true, UserManager.getGlobalUM(), null, null); lof_initial.addContentCreator(new FormSheetContentCreator() { protected void createFormSheetContent(FormSheet sheet) { sheet.getButton(FormSheet.BTNID_OK).setAction(new sale.Action() { public void doAction(SaleProcess process, SalesPoint point) { uig_log_on.setNextTransition(logOn()); } }); sheet.getButton(FormSheet.BTNID_CANCEL).setAction(new sale.Action() { public void doAction(SaleProcess process, SalesPoint point) { uig_log_on.setNextTransition( GateChangeTransition.CHANGE_TO_STOP_GATE); } }); } }); uig_log_on.setFormSheet(lof_initial); return uig_log_on; }
Informieren über ein MsgForm
Am Fehler-Gate
soll über die fehlgeschlagene Anmeldung informiert werden und nach erfolgter Bestätigung der Prozess zum Startzustand zurückkehren. Eine für diesen Zweck geeignete Spezialisierung der Klasse FormSheet
ist MsgForm
. Dabei handelt es sich um ein Formular, das eine Information darstellen kann und einen OK-Button enthält.
Das für das Fehler-Gate
benötigte Formular wird über eine neue Methode in der Klasse LogOn
geliefert.
public class LogOn { . . . public static FormSheet getFaultFormSheet() { return new MsgForm( "Log on failed!", "You didnt choose a user name or the passphrase didn`t match!"); } }
Das Hinzufügen des Formulars zum Fehler-Gate
wird in getFaultGate()
erledigt. Außerdem muss eine neue Aktion mit dem Bestätigungs-Button verbunden werden, in der der einfache Zustandswechsel zum Startzustand mit Hilfe einer GateChangeTransition
realisiert wird.
private Gate getFaultGate() { FormSheet fs_fault = LogOn.getFaultFormSheet(); fs_fault.addContentCreator(new FormSheetContentCreator() { protected void createFormSheetContent(FormSheet sheet) { sheet.getButton(FormSheet.BTNID_OK).setAction(new sale.Action() { public void doAction(SaleProcess process, SalesPoint point) { uig_fault.setNextTransition( new GateChangeTransition(getInitialGate())); } }); } }); uig_fault.setFormSheet(fs_fault); return uig_fault; }
Das Haupt-Gate
Um die Implementation des Anmeldeprozesses abzuschließen, muss noch das Haupt-Gate
vervollständigt werden, wo die weiteren Aktivitäten gewählt werden können. Diese Aktivitäten sind im Einzelnen: die Ausleihe, die Rückgabe, die Administration und die Abmeldung. Mit Ausnahme des Abmeldens soll für jede der einzelnen Aktionen ein eigener Prozess gestartet werden. Im Grunde muss das hierfür benötigte Formular lediglich vier Knöpfe für die unterschiedlichen Aktivitäten bieten. Um den Anreiz des Ausleihens zu steigern und die Anzeige nicht völlig leer aussehen zu lassen, wird zusätzlich die Video-Kollektion präsentiert. Das Formular wird wie gehabt in der GUI-Klasse des Anmeldeprozesses über eine statische Methode zur Verfügung gestellt. Der Videobestand wird wie im Abschnitt Eine Angebotstabelle erstellen durch ein Tabellenformular angezeigt. Die Standard-Buttons werden entfernt, die neu benötigten hinzugefügt.
public class LogOn { public static int FB_RENT = 1; public static int FB_HANDBACK = 2; public static int FB_ADMIN = 3; public static int FB_LOGOUT = 4; . . . public static FormSheet getMainFormSheet( CountingStock cs, UIGate uigGate, boolean show_zeros, TableEntryDescriptor ted) { SingleTableFormSheet stfs_main = SingleTableFormSheet.create( "Select an action!", cs, uigGate, show_zeros, ted); stfs_main.addContentCreator(new FormSheetContentCreator() { public void createFormSheetContent(FormSheet fs) { fs.removeAllButtons(); fs.addButton("Rent", FB_RENT, null); fs.addButton("Hand back", FB_HANDBACK, null); fs.addButton("Administrate", FB_ADMIN, null); fs.addButton("Logout", FB_LOGOUT, null); } }); return stfs_main; } }
Außerdem muss das Formular dem Zustand in der Methode getMainGate()
zugeordnet werden. Dabei wird dem Abmelde-Button eine Aktion zugeordnet, die einen Zustandsübergang zum Stop-Gate
durchführt. Die Zuordnung erfolgt gekapselt innerhalb eines FormSheetContentCreator
. Darüberhinaus wird der Nutzer über die Methode detachUser()
vom VideoAutomaten
abgekoppelt.
private Gate getMainGate() { FormSheet fs_main = LogOn.getMainFormSheet( VideoShop.getVideoStock(), uig_main, false, new TEDVideoStock()); fs_main.addContentCreator(new FormSheetContentCreator() { protected void createFormSheetContent(FormSheet fs) { fs.getButton(LogOn.FB_LOGOUT).setAction(new sale.Action() { public void doAction(SaleProcess p, SalesPoint sp) { sp.detachUser(); uig_main.setNextTransition( GateChangeTransition.CHANGE_TO_STOP_GATE); } }); } }); uig_main.setFormSheet(fs_main); return uig_main; }
Die restlichen Buttons können erst dann mit Aktivitäten verbunden werden, wenn die Folgeprozesse implementiert sind.
![]() | Das Geld ![]() |