All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kapott.hbci.callback.package.html Maven / Gradle / Ivy

Go to download

HBCI4j - Home Banking Computer Interface for Java - Clone from https://github.com/hbci4j/hbci4java

There is a newer version: 3.5.46
Show newest version


HBCI4Java kommuniziert mit der Anwendung bzw. dem Anwender über Callbacks.

Eine Beschreibung der Callback-Schnittstelle befindet sich in der Interface-Beschreibung zu HBCICallback. Hier soll kurz erläutert werden, wie ein generischer Callback-Handler implementiert werden kann. Unter einem generischen Callback-Handler stelle man sich ein HBCICallback-Objekt vor, welches auf alle möglichen auftretenden Callbacks sinnvoll reagiert, ohne jeden einzelnen Callback-Grund separat zu behandeln. Das hat zum einen den Vorteil, dass bei dieser Methode auch Callbacks behandelt werden, die zum Zeitpunkt der Entwicklung des Callback-Handlers noch gar nicht bekannt waren. Außerdem spart diese Methode am Anfang der Entwicklung einer HBCI-Anwendung Zeit. Zu einem späteren Zeitpunkt kann die Behandlung bestimmter Callback-Typen immer noch verfeinert werden.

Für den Anfang reicht es natürlich auch aus, eine der schon mitgelieferten HBCICallback-Klassen zu benutzen (HBCICallbackConsole oder HBCICallbackSwing).

Die Betrachtung der Callback-Methode log() soll an dieser Stelle ausgeklammert werden.

Die Methode status() des Callback-Interfaces dient dazu, der Anwendung mitzuteilen, in welchem Abarbeitungsschritt eines HBCI-Dialoges sich HBCI4Java gerade befindet. Dazu wird der Methode ein entsprechendes Tag (ein Integer-Wert) übergeben, der den aktuellen Abarbeitungsschritt identifiziert. Das Interface HBCICallback definiert STATUS_*-Konstanten für diese Integer-Werte. Normalerweise würde Code für die Auswertung dieser Tags in etwa so aussehen:

  public void status(HBCIPassport passport,int statusTag,Object[] o)
  {
      switch (statusTag) {
          case STATUS_DIALOG_INIT:
              printStatusInfo("fuehre dialog-initialisierung aus");
              break;
          case STATUS_DIALOG_INIT_DONE:
              printStatusInfo("dialog-init durchgeführt, dialogid ist "+o[0].toString());
              break;
          // usw. usf.
      }
  }

Das ist natürlich für eine Vielzahl von STATUS_*-Konstanten sehr aufwändig. Leider gibt es dafür im Moment auch keinen sinnvollen Workaround. Die pragmatischste Lösung wäre folgender Code:

    public void status(HBCIPassport passport,int statusTag,Object[] o) 
    {
        try {
            Class cl=this.getClass();
            Field[] fields=cl.getFields();
    
            for (int i=0;i<fields.length;i++) {
                Field field=fields[i];
                String name=field.getName();
    
                if (name.startsWith("STATUS_")) {
                    int value=field.getInt(this);
                    if (value==statusTag) {
                        printStatusInfo("status: "+name);
                        if (o!=null) {
                            for (int j=0;j<o.length;j++) {
                                if (o[j] instanceof String)
                                    printStatusInfo("statusdata: "+o[j]);
                                else
                                    printStatusInfo("class of status data: "+o[j].getClass().getName());
                            }
                        }
                        break;
                    }
                }
            }
        } catch (Exception e) {
            throw new HBCI_Exception("error while getting name of constant for statusTag "+statusTag,e); 
        }
    }

Damit würden zumindest der Name der entsprechenden Konstante sowie einige Informationen zu den zusätzlichen Daten extrahiert werden, und zwar ohne dass es nötig wäre, tatsächlich alle möglichen STATUS_*-Konstanten zu kennen.

Sinnvoller ist die Entwicklung eines generischen Handlers allerdings für die callback()-Methode. Diese Methode wird immer dann von HBCI4Java aufgerufen, wenn irgendeine Interaktion mit dem Anwender nötig ist. Dabei gibt es folgende "Unterfälle":

  • es wird eine Aktion vom Anwender erwartet, wobei das Ende der Aktion von HBCI4Java selbst erkannt wird, also keine zusätzliche Eingabe oder Bestätigung vom Anwender benötigt (z.B. das Einlegen der Chipkarte in das Lesegerät)
  • es wird eine Text-Eingabe vom Anwender erwartet
  • es wird eine Entscheidung vom Anwender verlangt (ja/nein, weiter/abbrechen usw.)
  • es muss eine Information angezeigt werden

Alle diese Fälle werden durch die Methode callback() abgedeckt und lassen sich anhand der jeweils übergebenen Parameter unterscheiden. Der Parameter reason gibt dabei die Ursache des Callbacks an. Das ist genau der Parameter, für den es sehr viele mögliche Belegungen gibt. In der Regel wird in einem Callback-Handler dieser Parameter als erstes ausgewertet -- je nach Wert ist eine entsprechende Reaktion vorgesehen. Es wird also i.d.R. ein ähnlicher switch-case-Block wie in einer status()-Methode implementiert werden. In einem generischen Callback-Handler soll dieser Parameter möglichst nicht ausgewertet werden müssen, weil hier ständig neue Werte hinzukommen können. Deshalb muss die richtige Reaktion auf einen Callback von den Werten der anderen Parameter abhängig gemacht werden.

Der Parameter msg enthält einen String, der die Ursache des Callbacks und die erwartete Reaktion/Aktion im Klartext beschreibt. Dieser Text kann also in jedem Fall anzeigt werden. Der Parameter datatype enthält den erwarteten Datentyp der Nutzereingabe. Ist der Wert dieses Parameters ungleich TYPE_NONE, so wird tatsächlich eine Nutzereingabe erwartet. In diesem Fall ist also ein entsprechendes Eingabefeld vorzusehen. HBCI4Java gibt dazu im Object retData bereits eine default-Eingabe vor. Diese ist in den meisten Fällen leer. Es ist auf jeden Fall zu empfehlen, diesen vorgeschlagenen default-Wert trotzdem anzuzeigen, da er in einigen Fällen tatsächlich benutzt wird (Das ist konkret beim Callback HAVE_CRC_ERROR der Fall, wo die evtl. zu korrigierenden Kontoverbindungsdaten im retData-Objekt übergeben werden). Die Nutzereingabe ist dann wieder in den StringBuffer retData einzustellen (im gleichen Datenformat wie der evtl. vorher darin enthaltene default-Wert). Nähere Informationen dazu ist in der Beschreibung der einzelnen TYPE_*-Konstanten zu finden.

Ist datatype allerdings gleich TYPE_NONE, so bedeutet das, dass keine Eingabe vom Anwender erwartet wird. In diesem Fall soll also entweder eine Information angezeigt werden (die Info selbst steht im Parameter msg), oder es wird eine Aktion vom Anwender erwartet, die er nicht explizit bestätigen muss. Die Unterscheidung zwischen beiden Fällen kann anhand des Parameters retData erfolgen: ist dieser Parameter gleich null, so handelt es sich um die Aufforderung zu einer Aktion, ist der Parameter ungleich null, so soll eine Information angezeigt werden, die der Anwender erst bestätigen muss.

Eine generische Callback-Methode kann also wiefolgt aussehen:

    public void callback(HBCIPassport passport,int reason,String msg,int datatype,StringBuffer retData)
    {
        try {
            output(msg);
            if (datatype!=TYPE_NONE) {
                switch (datatype) {
                    case TYPE_TEXT:
                    case TYPE_SECRET: // dafuer muss nur die Eingabe durch Sternchen ("*") versteckt werden
                        // Eingabe in Rückgabeobjekt schreiben
                        output("current value: "+retData.toString());
                        retData.replace(0,retData.length(),readUserInput());
                        break;
                    
                    case TYPE_BOOLEAN:
                        String input=readUserInput();
                        // Eingabe von "yes" für den einen Fall (TRUE),
                        // jeder andere Text führt zu dem anderen Fall (FALSE)
                        // siehe dazu Beschreibung von TYPE_BOOLEAN
                        if (input.equals("yes"))
                            retData.replace(0,retData.length(),"");
                        else
                            retData.replace(0,retData.length(),"error");
                        break;
                }
            } else {
                if (retData!=null) {
                    // auf bestätigung warten
                    waitForConfirmation();
                }
            }
        } catch (Exception e) {
            throw new HBCI_Exception("error while handling callback",e);
        }
    }

Auf diese Art und Weise können alle existierenden und zukünftigen Callbacks mit einer einzigen generischen Methode behandelt werden. Der Nachteil ist natürlich, dass es an entsprechendem Komfort fehlt, den man bei bestimmten konkreten Callbacks anbieten könnte.





© 2015 - 2024 Weber Informatics LLC | Privacy Policy