Videoautomat - Die Anmeldung

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.



Der Anmeldeprozess als Zustandsdiagramm
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 Gates 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 LogOnForms 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 Transitions-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.


previous Die NutzerregistrierungDas Geld next



by Thomas Ryssel