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 7.1 verdeutlicht den Ablauf des Prozesses anhand eines Zustandsdiagramms.
Abbildung 7.1: Der Anmeldeprozess als Zustandsdiagramm
Deklaration eines SaleProcess
Im Folgenden wird die Klasse SaleProcessLogOn
als Spezialisierung von SaleProcess
implementiert. Es
werden zunächst die in
Abbildung
7.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, selbige werden innerhalb des jeweiligen
Gate
s definiert und ihrem 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 { public SaleProcessLogOn() { super("SaleProcessLogOn"); } protected Gate getInitialGate() { UIGate uig_log_on = new UIGate(null, null); return uig_log_on; } public Gate getFaultGate() { UIGate uig_fault = new UIGate(null, null); return uig_fault; } public Gate getMainGate() { UIGate uig_main = new UIGate(null, null); return uig_main; } }
Der Prozess wird durch die Aktion, wie im Abschnitt Der FormSheetContentCreator beschrieben, 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.
Innerhalb der Methode getInitialGate()
, dem Anfangszustand des Prozesses, wird eine neue Instanz von
LogOnForm
erstellt, die die Oberfläche des Anmeldeprozesses darstellt. Die Standard-Buttons
Ok und Cancel werden dabei beibehalten.
protected Gate getInitialGate() { UIGate uig_log_on = new UIGate(null, null); LogOnForm lof_initial = new LogOnForm( "Are you a registered user?", "Select your user name", "Enter your passphrase", true, UserManager.getGlobalUM(), null, null); return uig_log_on; }
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. In diesem Falle
ist eine Sortierung nicht nötig, weswegen dafür der Wert null
übergeben wird.
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. Das geschieht, indem das Formular durch den Aufruf
setFormSheet(FormSheet fs)
an den Startzustand gebunden wird.
protected Gate getInitialGate() { UIGate uig_log_on = new UIGate(null, null); LogOnForm lof_initial = new LogOnForm( "Are you a registered user?", "Select your user name", "Enter your passphrase", 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.
Zunächst wird eine eigene FormSheetContentCreator
-Klasse für das LogOnForm
definiert. Warum das
sinnvoll ist, wurde bereits im Kapitel Der FormSheetContentCreator erläutert.
Diese Klasse wird dann dem LogOnForm
hinzugefügt.
package videoautomat.contentcreator; public class LogOnLOFContentCreator extends FormSheetContentCreator { protected void createFormSheetContent(FormSheet fs) { } }
public class SaleProcessLogOn extends SaleProcess { . . . protected Gate getInitialGate() { UIGate uig_log_on = new UIGate(null, null); LogOnForm lof_initial = new LogOnForm( "Are you a registered user?", "Select your user name", "Enter your passphrase", true, UserManager.getGlobalUM(), null, null); lof_initial.addContentCreator(new LogOnLOFContentCreator()); . . . } . . . }
Im folgendenen werden nun die Buttons des LogOnForm
s mit Aktionen verknüpft.
Der Cancel-Button soll lediglich vom initialen Zustand zum Stop-Gate
wechseln.
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
.
Zur Realisierung dieses Überganges wird eine eigene Action
-Klasse definiert, in deren doAction
-
Methode das aktuelle Gate
des übergebenen Prozesses geholt wird und selbigem die nächste Transition
zugewiesen wird. Diese Action
-Klasse kann nun auch von anderen FormSheetContentCreator
-Klassen
genutzt werden.
package videoautomat.contentcreator.stdactions; public class StopAction implements Action { private static final long serialVersionUID = -8719336755565989807L; public void doAction(SaleProcess saleProcess, SalesPoint salePoint) throws Throwable { UIGate currentGate = (UIGate)saleProcess.getCurrentGate(); currentGate.setNextTransition(GateChangeTransition.CHANGE_TO_STOP_GATE); } }
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
.
Damit der Zustandsübergang beim Klick auf den Cancel-Button
auch vollzogen wird,
muss ihm die Action
noch über die Methode setAction(Action)
zugewiesen werden.
package videoautomat.contentcreator; public class LogOnLOFContentCreator extends FormSheetContentCreator { protected void createFormSheetContent(FormSheet fs) { fs.getButton(FormSheet.BTNID_CANCEL).setAction(new StopAction()); } }
Schlussendlich muss nun noch der Übergang zum Haupt-Gate
implementiert werden. Hierbei kann nicht einfach
von Gate
zu Gate
gewechselt werden, zwischen den Gates
müssen noch die
Anmeldedaten auf Korrektheit geprüft werden. Abhängig davon wird der Prozess zum Haupt- oder Fehler Gate
umgeleitet. Um nicht einfach zwischen den Gates
zu wechseln sondern zwischen beiden noch Daten auszuwerten etc.,
bedarf es der Klasse Transition
, die zwischen zwei Gates
ausführbar ist.
Im Package videoautomat.transition
wird dazu eine eigene Transition
s-Klasse definiert. Selbige muss
die Methode perform(SaleProcess sp, User user)
enthalten, welche ein Gate
zurückgibt, das den
nächsten Zustandsübergang definiert. Es wird außerdem ein Konstruktor definiert, dem eine Instanz von
LogOnForm
übergeben wird, damit die Anmeldedaten überhaupt auswertbar sind.
Durch den Aufruf der ok()
-Methode der LogOnForm
-Instanz wird 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 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 zum Haupt-Gate
weitergeleitet. Wurde kein Name oder ein inkorrektes Passwort gewählt, wechselt der Prozess zum Fehler-Gate
.
package videoautomat.transition; public class LogOnTransition implements Transition { private static final long serialVersionUID = 6520554025625844846L; private LogOnForm lof; public LogOnTransition(LogOnForm lof) { this.lof = lof; } public Gate perform(SaleProcess sp, User user) { SaleProcessLogOn processLogOn = (SaleProcessLogOn) sp; lof.ok(); User user_current = lof.getResult(); if(user_current != null) { ((SalesPoint) processLogOn.getContext()).attach(user_current); return processLogOn.getMainGate(); } return processLogOn.getFaultGate(); } }
Die neu erstellte Transition
muss noch mit dem Ok-Button verbunden werden, wobei die
Hilfsklasse TransitWithAction
, welche dem aktuellen Gate
die nächste Transition
zuweist, verwendet wird.
package videoautomat.contentcreator; public class LogOnLOFContentCreator extends FormSheetContentCreator { protected void createFormSheetContent(FormSheet fs) { fs.getButton(FormSheet.BTNID_CANCEL).setAction(new StopAction()); fs.getButton(FormSheet.BTNID_OK).setAction( new TransitWithAction(new LogOnTransition((LogOnForm) fs))); } }
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 benötigte Formular wird entsprechend im Fehler-Gate
getFaultGate
definiert und selbigem
zugewiesen:
public Gate getFaultGate() { UIGate uig_fault = new UIGate(null, null); MsgForm mf_fault = new MsgForm( "Log on failed!", "You didn`t choose a user name or the passphrase didn`t match!"); uig_fault.setFormSheet(mf_fault); return uig_fault; }
Anschließend muss dem einzigen Button des MsgForms
noch eine Aktion zugewiesen werden, die zum Start-
Gate
des Anmeldeprozesses wechselt. Dies geschieht durch Definition einer Transitions
-Klasse,
welche den Gate
-Wechsel durchführt. Da das Start-Gate
getInitialGate()
im Prozess
geschützt ist, folglich von anderen Klassen nicht darauf zugegriffen werden kann, wird ein Hilfs-Gate
restart()
definiert, was einfach das Start-Gate
zurückgibt.
package videoautomat.transition; public class LogOnFailTransition implements Transition { private static final long serialVersionUID = -8898487693729860732L; public Gate perform(SaleProcess sp, User user) { SaleProcessLogOn processLogOn = (SaleProcessLogOn) sp; return processLogOn.restart(); } }
package videoautomat.contentcreator; public class LogOnMFContentCreator extends FormSheetContentCreator { private static final long serialVersionUID = 760936296585763228L; protected void createFormSheetContent(FormSheet fs) { fs.getButton(FormSheet.BTNID_OK).setAction(new TransitWithAction(new LogOnFailTransition())); } }
public Gate getFaultGate() { UIGate uig_fault = new UIGate(null, null); MsgForm mf_fault = new MsgForm( "Log on failed!", "You didn`t choose a user name or the passphrase didn`t match!"); mf_fault.addContentCreator(new LogOnMFContentCreator()); uig_fault.setFormSheet(mf_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.
Der Videobestand wird wie im Abschnitt Eine Angebotstabelle erstellen durch ein Tabellenformular angezeigt. Die
Standard-Buttons werden entfernt, die neu benötigten hinzugefügt und das Formular dem Gate
zugeordnet.
Die Buttons sind bereits mit Actions
belegt, welche aber erst in den Folgeprozessen erklärt werden.
public Gate getMainGate() { UIGate uig_main = new UIGate(null, null); SingleTableFormSheet stfs_main = SingleTableFormSheet.create( "Select an action!", VideoShop.getVideoStock(), uig_main, false, new TEDVideoStock()); stfs_main.addContentCreator(new LogOnSTFSContentCreator(this)); return uig_main; }
public class LogOnSTFSContentCreator extends FormSheetContentCreator { private User user; private static final long serialVersionUID = 6150685570383575134L; public LogOnSTFSContentCreator(SaleProcessLogOn process) { user = (User) process.getContext().getCurrentUser(process); } protected void createFormSheetContent(FormSheet fs) { fs.removeAllButtons(); fs.addButton("Rent", 1, new RunProcessAction(new SaleProcessRent(), new DataBasketImpl())); fs.addButton("Hand back", 2, new RunProcessAction(new SaleProcessHandBack(), new DataBasketImpl())); fs.addButton("Administrate", 3, (ActionCapability) user.getCapability(AutomatUser.CAPABILITY_ADMIN)); fs.addButton("Logout", 4, new LogOutAction()); } }
Für den Abmeldebutton wird eine neue Action
definiert, wobei der Nutzer über die Methode
detachUser()
vom VideoAutomaten
abgekoppelt und ein Zustandsübergang zum Stop-Gate
durchgeführt wird. Diese Action
wird dem Button dann noch angehangen.
package videoautomat.contentcreator.stdactions; public class LogOutAction implements Action { private static final long serialVersionUID = 8638643725086381180L; public void doAction(SaleProcess saleProcess, SalesPoint sp) throws Throwable { sp.detachUser(); UIGate currentGate = (UIGate)saleProcess.getCurrentGate(); currentGate.setNextTransition(GateChangeTransition.CHANGE_TO_STOP_GATE); } }
Hinweis: Beim Testen nicht vergessen, im StartFSContentCreator
beim "Login" - Button, das null
durch new RunProcessAction(new SaleProcessLogOn())
zu ersetzen.
Die Nutzerregistrierung | Das Geld |