org.kapott.hbci.callback.package.html Maven / Gradle / Ivy
Show all versions of hbci4j-adorsys Show documentation
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.