Main Page   Modules   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

(German) Design-Überblick

Note:
Sorry, this is only German
In dieser Datei finden sich einige Texte, die Neulingen helfen sollen einen besseren Einstieg in OpenHBCI zu erhalten. Bei Unstimmigkeiten ist immer die Kommentierung im source bzw. die durch doxygen generierte HTML-Dokumentation der API massgeblich. Die Benutzung von OpenHBCI wird auch in der Klassen-Dokumentation von HBCI::API erklaert und ist dort evtl. aktueller.

Inhalt

  1. Struktur von OpenHBCI
  2. Hinzufuegen neuer Geschaeftsvorfaelle
  3. Verwendung vom HBCI::Pointer
  4. Verwendung von libchipcard fuer ChipKarten-Support

Struktur von OpenHBCI

Im folgenden mal eine kleine "Einleitung" zur Struktur von OpenHBCI.

Die Bibliothek ist in zwei verschiedenen Ebenen organisiert: Zum einen gibt es die "core"-Klassen im Verzeichnis src/openhbci/core/, zum anderen die Applikations-Klassen für den Programmablauf der Applikation im Verzeichnis src/openhbci/.

Die "core"-Klassen bestehen zum einen aus der Definition von abstrakten Basisklassen (Interfaces) für die grundlegenden Datenstrukturen in HBCI (z.B. HBCI::Bank, HBCI::Account etc.), zum anderen enthalten die core-Klassen die Implementierungen dieser Basisklassen und der notwendigen Segmente/Datenformate für das konkrete HBCI-Protokoll. Für den Anwendungsprogrammierer sind *nur* die Kenntnis der Basisklassen wichtig! Der Anwendungsprogrammierer sollte sich *nicht* um die Implementierungen jener Basisklassen oder gar um die Segmentstrukturen des HBCI-Protokolls kümmern müssen.

Die Applikations-Klassen bestehen aus der Definition der möglichen Geschäftsvorfälle in jeweils einer OutboxJob-Klasse sowie der zentralen Verwaltungsklasse HBCI::API, die alle anderen Objekte in passenden Listen gespeichert hält und Warteschlangen für alle auszuführenden Geschäftsvorfälle verwaltet. Außerdem lädt und speichert die Klasse HBCI::API die notwendigen Konfigurationsdaten.

Grundlegende "core"-Klassen

Klasse HBCI::Bank

Eine Bank in HBCI. Die Bank wird identifiziert durch Bankleitzahl und Ländercode. Die Bank hat eine Liste ihrer Benutzer (User) und eine Liste der bei ihr existierenden Konten (Account).

Klasse HBCI::User

Ein Benutzer, d.h. eine reale Person, die bei genau einer Bank als HBCI Benutzer registriert ist. Ein Benutzer besitzt genau ein Sicherheitsmedium (HBCI::Medium), wo seine geheimen Schlüssel gespeichert sind. Ein Benutzer besitzt mindestens eine Kundennummer (Customer) bzw. eine Liste von Kundennummern, mit der er/sie auf Konten (Account) zugreifen darf.

Klasse HBCI::Customer

Eine Kundennummer (Customer) beschreibt die Rolle eines Benutzers (User), die festlegt, mit welchen Rechten der Benutzer auf Konten zugreifen kann. Jedes Konto (Account) hat eine Liste von Kundennummern (Customer), denen der Zugriff auf jenes Konto erlaubt ist. Über die Kundennummer (Customer) ergibt sich dann der Benutzer (User), der diesen Kontozugriff ausführen darf.

Klasse HBCI::Account

Ein Konto gehört zu genau einer Bank und wird dort eindeutig durch die Kontonummer identifiziert. Ein Konto besitzt eine Liste von Kundennummern (Customer), denen der Zugriff auf dieses Konto erlaubt ist.

Klasse HBCI::Medium

Das Sicherheitsmedium speichert die geheimen Schlüssel eines Benutzers (User) und implementiert die notwendigen Methoden zum Ver-/Entschlüsseln und Signieren von Nachrichten. Manche Sicherheitsmedien können mehrere Schlüssel speichern (jeden Schlüssel in einem sogenannten Context) und damit für mehrere Benutzer als Sicherheitsmedium dienen. Ein Sicherheitsmedium kann mit symmetrischen Schlüsseln arbeiten (DES-DES-Verfahren, DDV) oder mit asymmetrischen Schlüsseln (RSA-DES-Hybridverfahren, RDH).

Applikations-Klassen

Klasse HBCI::API

Die zentrale Verwaltungsklasse, die alle anderen Objekte in passenden Listen gespeichert hält und Warteschlangen für alle auszuführenden Geschäftsvorfälle verwaltet. Außerdem lädt und speichert die Klasse HBCI::API die notwendigen Konfigurationsdaten.

Abgeleitete Klassen von HBCI::OutboxJob

Alle implementierten Geschäftsvorfälle sind als eine abgeleitete Klasse von OutboxJob zu finden. Für eine Applikation ist damit nur noch maßgeblich, welche der von OutboxJob abgeleiteten Klassen für den Geschäftsvorfall benötigt wird, und welche zusätzlichen Objekte diese Klassen erfordern könnte. Alle von OutboxJob abgeleiteten Klassen können in die Warteschlange der HBCI::API eingereiht werden und dann als HBCI-Geschäftsvorfall durch HBCI::API::executeQueue ausgeführt werden.

Beispiele

Als Beispiele zu Verwendung von OpenHBCI in C++ dient der Programmcode von aqmoney, zu finden im separaten Projekt aqmoney.sourceforge.net. Ein minimalistisches Beispiel in C ist das kurze Programm cmoney im OpenHBCI-Verzeichnis src/cmoney.

Ansonsten

Das openHBCI-Team ist ansonsten uebereingekommen, dass die Verwendung von konventionellen Pointern eingeschraenkt bleiben soll. Wenn ein Objekt einen Pointer uebergeben bekommt, darf es seinen Inhalt NICHT loeschen, allerdings ist inzwischen sowieso an den allermeisten Stellen HBCI::Pointer in Benutzung.

Hinzufuegen neuer Geschaeftsvorfaelle

Ich habe zwar noch mal versucht den Job und die Segmente betreffend Saldoabfrage (GetBalance) ausfuehrlicher zu dokumentieren, aber das allein ist noch nicht aussagekraeftig genug. Deshalb hier mal eine Einfuehrung in die Arbeit mit Segmenten und Jobs.

Interne Implementierungs-Klassen

Jobs, Segmente

Zur Kommunikation mit dem Server dienen direkt die Segmente (in adminsegs/accountsegs). Die werden von Programmen aber nie direkt verwendet, stattdessen erzeugt das Programm einen Job, der seinerseits fuer die Erzeugung aller noetigen Segmente sorgt. Jobs die Administrationszwecken dienen, sind in den Dateien "adminjobs.h"/"adminjobs.cpp" definiert. Jobs zum Umgang mit Konten sind hingegen in den Dateien "accountjobs.h"/"accountjobs.cpp" zu finden.

Klasse HBCI::MessageQueue

Wenn die vom User gewuenschten Jobs erzeugt sind, will man sie ja auch an den Mann bringen (respektive den Server). Dazu dient die MessageQueue. Man erzeugt sich ein Objekt dieser Klasse und ruft dann fuer jeden Job "addJob" auf. Als naechstes braucht man dann "HBCI::Connection".

Klasse HBCI::Connection

Diese Klasse managed die Verbindung zum Server, ausserdem kann man mit ihr den Inhalt einer HBCI::MessageBox an den Server senden sowie dessen Antwort abholen.

Unterscheidung JOBS/Segmente

Das Interface zum HBCI-Server sind die Segmente, sie stellen die unterste Ebene von openHBCI dar. Kein Programm erstellt diese Segmente selber !

Ein Programm erzeugt stattdessen einen HBCI::OutboxJob als Applikations-Klasse von OpenHBCI. Die Applikations-Klasse erzeugt ihrerseits einen HBCI::JOB, der zwar seinerseits wieder ein bis mehrere Segmente erzeugen kann, aber das geht das Programm nix an. Der Gedanke, der dahinter steckt, ist folgender: Selbst die JOBS wissen nicht, wie die einzelnen Segmente aussehen, die an den Server geschickt werden (und der OutboxJob schon gar nicht). Wenn sich also spaeter an dem Format der Segmente etwas aendert, muss man nur die Segment-Klassen aendern, die JOBS und OutboxJobs hingegen kaum, und das Hauptprogramm im besten Fall sogar gar nicht. Damit ist dann das Hauptprogramm weit genug von HBCI abstrahiert.

Einbinden eigener Geschaeftsvorfaelle

Wenn man einen Geschaeftsvorfall einbinden moechte, muss man als erstes in den HBCI-Specs nachsehen, welche Segmente dabei im Spiel sind. Dabei gibt es meistens drei Segmente:

Im Beispiel von GetBalance:

Zum Server: Segment "HKSAL" ->SEGGetBalance

Dieses Segment muessen wir nur erzeugen koennen, wir brauchen es aber nicht Parsen zu koennen, da nur ein Server jemals so ein Segment geschickt bekommt. Daher brauchen wir hier auch nur die Methode "toString()" implementieren.

Vom Server: Segment "HISAL" ->SEGBalance

Dieses Segment werden wir niemals erzeugen muessen, aber da es die Antwort des Servers ist und wir an der Antwort interessiert sind, muessen wir es parsen koennen. Also ist hier die Methode "parse()" implementiert, dafuer aber nicht "toString()".

Beschreibung: Segment "HISALS"

Dieses Segment enthaelt zusaetzliche Parameter fuer den Geschaeftsvorfall "Saldenabfrage".

Jobs

Ok, jetzt, wo wir grundsaetzlich in der Lage sind, uns mit dem Server ueber Salden zu unterhalten, muessen wir das ganze noch in eine einfache Form fuer die Applikations-Klassen bringen. Denn der Entwickler einer Applikation will sich ja nicht mit der untersten Ebene - den Segmenten - herumschlagen. Bei JOBS gibt es auch wieder 2 wichtige Methoden:

Erzeugen der Segmente fuer den Server

Die Methode "toString()" erzeugt die noetigen Segmente aus den Daten, die im Job gespeichert sind. In diesem Fall ist das beispielsweise die Kontonummer und Bankleitzahl des Kontos, dessen Saldo wir abfragen wollen. Diese Methode ruft man uebrigends nicht selber auf, das tut spaeter die HBCI::MessageQueue automatisch.

Verarbeiten der Antwort des Server

Sobald die Antwort des Servers eintrifft, die von der Klasse HBCI::MessageQueue entgegengenommen wird und aus einzelnen Segmenten besteht, wird sofort nachgesehen, welches Segment die Antwort auf welchen Job darstellt. Jeder Job weiss, wass fuer Antwort-Segmente er zu erwarten hat, und wie er sie zu parsen hat (das macht dann die Methode "parseResponse()". Tatsaechlcih parsed der Job natuerlich die Segmente nicht selbst, sondern ruft die entsprechende Methode der Segmentklassen auf -> "parse()"). Die HBCI::MessageQueue ruft also zu jedem Segment die Methode "parseResponse()" des passenden Jobs auf. Der Job sieht sich nun jeweils die Segmente an und entnimmt ihnen die Informationen, auf die er gewartet hat (hier ist es zum Beispiel der Kontostand).

Wichtig ist, dass die Account-Segmente und Jobs die Daten nur in sich selbst speichern duerfen !! Sie duerfen also nicht einfach ihre Daten in den in der HBCI-Klasse gespeicherten Listen ablegen, sonderm muessen sie lokal als Member ihrer Klasse speichern. Wenn die Applikations-Klasse interesse an den Daten hat, soll es sie gefaelligst selbst aus den Jobs holen und ihrerseits Member-Methoden für die Applikation anbieten. Im Falle des JOBGetBalance kann man den frischen Kontostand beispielsweise mit "getBalance()" abholen, und dann, wenn man will, in der HBCI-Klasse oder wo auch immer speichern.

Verwendung vom HBCI::Pointer

Dieses Thema sieht in den Sourcen manchmal etwas verwirrend aus, deshalb hier eine kurze Einleitung zu dem HBCI::Pointer.

Ein HBCI::Pointer ist ein referenzzählender Zeiger auf ein Objekt. Das Problem mit Zeigern ist, dass man sich immer merken muss, WER das Objekt, auf das er zeigt, loeschen darf oder MUSS. Ausserdem will man auch vermeiden, dass eine Klasse dieses Objekt loescht, waehrende eine andere Klasse noch damit arbeitet. Eine andere Geschichte ist, wenn man in einer Prozedur/Methode viele Pointer benutzt, und dann wegen eines Fehlers abbrechen muss. Welche Pointer hatte man schon gesetzt? Welche muessen freigegeben werden und welche DUERFEN nicht? Mit dem HBCI::Pointer reicht ein einfaches "return", und die Freigabe der Ressourcen geht automatisch.

Diese Arbeit uebernimmt nun der HBCI::Pointer. Er speichert intern einen Zeiger auf das zu verwaltende Objekt und zusätzlich einen Benutzerzaehler/Referenzzähler. Solange noch irgendein HBCI::Pointer auf das angegebene Objekt zeigt, wird es nicht geloescht. Sobald aber KEIN HBCI::Pointer mehr darauf zeigt, wird es AUTOMATISCH freigegeben, d.h. delete() wird aufgerufen.

Das bedeutet fuer den Programmierer aber: Solange man mit dem Objekt arbeiten moechte, muss man dafuer sorgen, dass irgendein HBCI::Pointer noch auf dieses Objekt verweist. Folgendes waere beispielsweise ein Fehler:

HBCI::Pointer<HBCI::Account> hptr_acc = new HBCI::Account(...);
HBCI::Account *ptr_acc;

// hole einen konventionellen Zeiger auf das Objekt
ptr_acc=hptr_acc.ptr();

/* HBCI::Pointer zeigt jetzt nicht mehr auf das Konto (zeigt nun
 auf garnix mehr. Dadurch wird aber das Objekt, auf das er
 zeigte geloescht, wenn er der letzte HBCI::Pointer war) */
hptr_acc=0;

/* der konventionelle Pointer zeigt nun auf ein Objekt, das gerade 
 geloescht wurde. Deshalb gibt es gleich einen Segfault ! */
printf("AccountId==%s\n",ptr_acc->accountId());

Manche Klassen sind aber nicht vorbereitet auf die Verwendung von HBCI::Pointer, sie erwarten konventionelle Pointer. Fuer diesen Fall gibt es die eben gezeigte Methode HBCI::Pointer::ptr(), die genau DAS liefert. (Der Operator "*" tut das uebrigends auch, macht die Sourcen aber schwerer lesbar.) Man muss dann aber sicherstellen, dass diese Klassen den konventionellen Pointer nicht loeschen, denn sonst würden zum einen noch existierende HBCI::Pointer plötzlich ungültig und zum anderen würde das Objekt durch den letzten HBCI::Pointer ein zweites Mal gelöscht.

Will man eine Referenz auf das Objekt haben, verwendet man HBCI::Pointer::ref().

Wenn das automatische Loeschen nicht durchgeführt werden soll, gibt es die Methode HBCI::Pointer::setAutoDelete(). Mit setAutoDelete(false) wird der letzte HBCI::Pointer *nicht* das Objekt löschen, sondern der Programmierer muß sich (wahrscheinlich) selber darum kümmern. Manchmal braucht man das halt. Es wäre wohl besser, diese Methode nicht so oft zu benutzen.

Werden HBCI::Pointer kopiert, so erhoeht sich automatisch der Benutzerzaehler/Referenzzähler des Objektes, auf das der Pointer zeigt.

Verwendung von libchipcard fuer ChipKarten-Support

Seit Version 0.3.1 ist die Unterstuetzung von Chipkarten deutlich verbessert. Der Grund dafuer sind tiefgreifende Aenderungen an libchipcard (zu finden bei http://libchipcard.sf.net). Seit dessen Version 0.3 benoetigt es beim Linken und kompilieren keine CTAPI Bibliothek mehr (das ist die Treiber-Bibliothek der Chipkarten-Terminals). Diese wird nunmehr dynamisch und erst bei Bedarf nachgeladen.

Sobald die Chipkarten-Medien nur noch über plugins zur Laufzeit in OpenHBCI geladen werden, ist libchipcard überhaupt nicht mehr in OpenHBCI notwendig. Dies wird wohl ab Version 0.9.10 so sein.

Autoren

Martin Preuss<openhbci@aquamaniac.de>, Hamburg, 11. Maerz 2002

Christian Stimming <stimming@tuhh.de, 23. April 2003


Generated on Mon Jun 23 13:41:08 2003 for openhbci by doxygen1.2.17