Showcase
Die Themen
Ich habe lange Jahre professionell COBOL und CICS programmiert.
Die Grundzüge der Sprache waren immer gegenwärtig. Strukturiertes Programmieren habe ich verinnerlicht und die wichtigsten Techniken sind mir geläufig. Die vielen Artefakte des prozeduralen COBOL habe ich aufgefrischt und mich darüber hinaus intensiv mit objektorientiertem COBOL und COBOL Web Services beschäftigt.
Das habe ich in einem Showcase festgehalten, wobei mir diese Themen besonders wichtig sind.
Anmerkung:
Der Showcase ist mit Mircofocus Visual COBOL programmiert. Microfocus stellt kostenlos eine vollumfängliche Visual COBOL Personell Edition zur Verfügung. Dafür bin ich Microfocus überaus dankbar.

Prozedurales COBOL

Es galt, die alten Kenntnisse aufzufrischen und ein paar neue Erkenntnisse dazu zu gewinnen.
- Nutzung der wichtigsten COBOL Artefakte
- Gute Strukturierung von COBOL Programmen
- Zugriff auf eine relationale Daten mittels statischem Embedded SQL
Für dynamisches Embedded SQL habe ich in meinem Showcase nicht eingesetzt. Das ist aber nur noch ein kleiner Schritt. - Aufruf von COBOL Klassen aus einem prozeduralen COBOL Programm.
- Aufruf von C# Klassen aus einem prozeduralen COBOL Programm.
- Datenspeicherung und Datenzugriff über das COBOL Dateisystem und die zugehörigen COBOL Konstrukte Input-Output Section und File Section.
- COBOL typische Gruppenwechsel.
(Die beiden letzten Punkte sind nicht Bestandteil des Showcase)
Objekt Orientiertes COBOL

Objektorientiertes COBOL ist ein Schlüssel zur Interoperabilität von Legacy COBOL mit aktuellen Programmierumgebungen. OO COBOL zu beherrschen halte ich daher für eine zentrale Kompetenz zur Wartung und Weiterentwicklung von COBOL-Programmen. OO COBOL gehört nicht zum COBOL Standard (2014). Einige Compiler-Hersteller haben eine OO COBOL Version. Der marktrelevanteste ist sicher Microfocus.
Mein Showcase umfasst also:
- Objektorientiertes Denkens lernen und da reicht eine oberflächliche Management Sicht nicht. Also erst mal gründlich C# lernen, es hätte auch Java sein können.
- Die Syntax von OO COBOL lernen.
- Die Implementierung von OO COBOL verinnerlichen.
- Den Einsatz von OO COBOL in einer virtuellen Maschine verstehen und üben.
Das kann - wie in meinem Fall - die Microsoft CLR sein, natürlich aber auch Java RE. - Erstellen von COBOL Klassen mit Properties und Methoden.
- Aufruf eines COBOL Legacy Programmes aus einer OO COBOL Methode mit einem CALL.
- Übergabe von "COBOL-typischen" Datenstrukturen an OO-COBOL Objekte und umgekehrt.
Kommunikation mit c# Klassen

Microfocus Visual COBOL lässt sich vollständig in Microsoft Visual Studio oder in Eclipse und natürlich in die zugehörigen virtuellen Maschinen integrieren. Damit stehen COBOL die kompletten Klassenbibliotheken der jeweiligen Umgebung zur Verfügung. Weitere Frameworks können die Umgebungen zu gigantischen Klassensammlungen erweitern. Das können natürlich auch in COBOL geschriebene Klassenbibliotheken sein.
Die folgenden Techniken waren mir wichtig:
- Aus C# heraus OO COBOL Klassen instanziieren und OO COBOL Methoden aufrufen.
- Aus OO COBOL heraus C# Klassen instanziieren und C# Methoden aufrufen.
- COBOL Objekte in C# Kollektion zu sammeln und damit zu arbeiten.
- Sich in COBOL Klassen bei Delegaten(Events) aus C# Klassen zu registrieren und dafür COBOL Handler zu schreiben.
- Eine OO COBOL Klasse von einer C# Klasse zu erben und Methoden zu erweitern oder zu überschreiben.
- C# Objekte in COBOL Kollektion zu sammeln und damit zu arbeiten.
- Ein Event in einer COBOL Klasse zu definieren zu veröffentlichen.
(Die beiden letzten Punkte sind nicht Bestandteil des Übungsprojektes)
API mit REST JSON

Moderne Cobol Entwicklungsumgebungen erlauben aus COBOL Legacy Programmen heraus Web API zu publizieren. Natürlich können COBOL Programme auch Web Clients sein, die Web API aufrufen. Ich habe mich mit der Implementierung und Publikation von REST JSON API mit Hilfe des Microfocus IMTK (Interfaces Mapping Toolkit) beschäftigt, um mir folgendes zu erwerben.
- Ein gutes Grundverständnis des Microfocus Enterprise Server (Anwendungsserver), der die Web Kommunikation regelt.
- Vorbereiten von Legacy Programmen für Web Services
Das ist der Hauptbestandteil der Arbeit. Der Aufwand ist allerdings bei gut strukturierten Cobol Programmen recht gering. - Erkennen und Definieren der zu veröffentlichen Web Ressourcen
- Erstellen der Ressourcen Interfaces mit dem IMTK
- Erstellen der Services der jeweiligen Ressource mit dem IMTK
- Erstellen einer (anwendungsbezogenen) Serverinstanz auf dem Enterprise Server
- Erstellen geeigneter Listener auf der Serverinstanz
- Web Services in der Serverinstanz installieren
- Service testen (Ich habe Postman benutzt)
Das Projekt
Für meinen COBOL Showcase habe ich ein kleines Projekt aufgesetzt, ein Haushaltsbuch
Die Inhalte des Projektes sind in einer Kurzfassung folgende:
- Ich kann Ausgaben buchen, wobei ich eine Beschreibung der Ausgabe, die Kosten und das Datum der Ausgabe festhalte. Die Kosten müssen von einem aus einer Liste zu wählenden Konto abgebucht werden. Zudem wird eine Kategorie für die Ausgabe festgelegt.
zur User Story - Ich kann Einnahmen analog zu Ausgaben buchen
zur User Story - Ich kann Umbuchungen innerhalb von Konten durchführen, beispielsweise bei einer Barabhebung. Hier wird vom Girokonto auf das Bargeldkonto gebucht. Dabei wird ein Buchungstext, der Buchungsbetrag und eine Buchungsdatum erfasst. Die Buchungskonten werden aus einer Liste der Konten ausgewählt.
zur User Story - Ich kann Konten mit Name und Anfangsbestand pflegen. Der Anfangsbestand wird zunächst auch Buchungsbestand.
zur User Story - Ich kann Kategorien für Ausgaben und Einnahmen pflegen. Hier wird lediglich eine Bezeichnung erfasst.
zur User Story - Ich kann meine monatlichen Ausgaben und Einnahmen auswerten, gegenüberstellen und einen Saldo bilden.
zur User Story
Der Showcase wurde zwei mal implementiert:
- Als
Client Applikation
Die Client Applikation wurde vollständig implementiert. Für die UI-Komponenten habe Microsoft Technologie mit XAML und C# Code-Behind gewählt. Die Backend-Komponenten sind mit prozeduralen Cobol realisiert. Die Kommunikation findet über OO Cobol Klassen statt. - Als
Web REST API Applikation
Ich habe mich entschlossen, für diese Implementierung auf den Microfocus Enterprise Server und das Microfocus Interface Mapping Toolkit zurückzugreifen. Das IMTK ist ein grafisches Werkzeug, das die Services und Kommunikation mit der Geschäftslogik vollständig generiert. Das muss nicht programmiert werden. Ich habe mich dazu entschlossen, weil das IMTK das "Profi-Werkzeug" von Microfocus für WEB API in COBOL ist. Hier habe ich lediglich das Backend mit den Services realisiert. Es wurden 5 Ressourcen mit insgesamt 21 Services erstellt. Die Services habe ich mit Postman getestet.
Anmerkung 1
Liebend gerne hätte ich die Web Anwendung vollständig mit einer Web Oberfläche mit Angular programmiert. Das müsste ich allerdings erst lernen und dazu noch als Vorbedingung jede Menge HTML, CSS und Java Script. Wäre sicherlich sehr spannend. Ich habe aber beschlossen, mich (zunächst) auf COBOL zu konzentrieren.
Anmerkung 2
Alternativ zum IMTK hätte ich die REST API Services in COBOL programmieren können. In meiner Windows Umgebung wären das dann WCF Services geworden. Die Services hätten aus neun in COBOL geschriebenen Interfaces und deren Implementierung als Services bestanden. Die entsprechenden Backend-Wrapper wären den Communication-Handlern der Clientlösung sehr ähnlich gewesen.
Das Video illustriert die Projektinhalte
Ausgaben buchen
User Story
Ich möchte alle meine Ausgaben buchen können, um jederzeit dadurch den Überblick über meine Finanzsituation zu haben.
Akzeptanzkriterien
- Ausgabenbuchungen können erfasst, geändert oder gelöscht werden.
- Es soll gleichzeitig eine Liste und eine Einzelansicht verfügbar sein.
- Einzelansicht und Listenansicht sollen synchronisiert sein.
- Erfasst und gespeichert werden folgende Daten:
- Text der Buchung
- Der Text darf nicht leer sein.
- Der Text darf maximal 50 Zeichen lang sein.
- Grund der Ausgabe
- Der Ausgabengrund muss aus der Liste der Ausgabengründe ausgewählt werden.
- Konto, von dem die Ausgabe abgebucht wird
- Das Konto muss aus der Liste der Konten ausgewählt werden.
- Bei speichern der Ausgabenbuchung soll auch der Kontenstand aktualisiert werden.
- Buchungsdatum
- Das Datum soll über einen Datepicker gewählt oder erfasst werden können.
- Das Datum muss gültig sein.
- Das Datum muss größer als der 1.1.2000 und kleiner als der 31.12.2199 sein.
- Betrag der Ausgabe
- Es können nur Euro erfasst werden.
- Der Betrag muss größer als 0,00 Euro sein.
- Der Betrag muss kleiner als 10 Milliarden Euro sein.
- Der Betrag muss den Saldo des Buchungskontos aktualisieren.
- Erfassen: Betrag vom Konto abziehen
- Löschen: Betrag zum Konto addieren
- Verändern: Differenz zur "alten" Buchung zum Konto addieren
zurück zu Das Projekt oder Prozedurales Cobol
Einnahmen buchen
User Story
Ich möchte alle meine Ausgaben buchen können, um jederzeit dadurch den Überblick über meine Finanzsituation zu haben.
Akzeptanzkriterien
- Es gelten alle Akzeptanzkriterien analog zu "Ausgaben buchen".
zurück zu Das Projekt oder Prozedurales Cobol
Konten umbuchen
User Story
Ich möchte den Saldo meiner Konten aktuell halten, um jederzeit dadurch den Überblick über meine Finanzsituation zu haben.
Dies gilt z.B. bei Barabhebungen oder dem monatlichen Einzug der Kreditkartenzahlungen vom Girokonto durch das Kreditkarteninstitut.
Akzeptanzkriterien
- Kontenbuchungen können erfasst, geändert oder gelöscht werden.
- Es soll gleichzeitig eine Liste und eine Einzelansicht verfügbar sein.
- Einzelansicht und Listenansicht sollen synchronisiert sein.
- Erfasst und gespeichert werden folgende Daten:
- Text der Buchung
- Der Text darf nicht leer sein.
- Der Text darf maximal 50 Zeichen lang sein.
- Konto, von dem abgebucht wird
- Das Konto muss aus der Liste der Konten ausgewählt werden.
- Bei speichern der Kontenbuchung soll auch der Kontenstand aktualisiert werden.
- Konto, auf das gebucht wird
- Das Konto muss aus der Liste der Konten ausgewählt werden.
- Bei speichern der Kontenbuchung soll auch der Kontenstand aktualisiert werden.
- Buchungsdatum
- Das Datum soll über einen Datepicker gewählt oder erfasst werden können.
- Das Datum muss gültig sein.
- Das Datum muss größer als der 1.1.2000 und kleiner als der 31.12.2199 sein
- Betrag der Ausgabe
- Es können nur Euro erfasst werden.
- Der Betrag muss größer als 0,00 Euro sein.
- Der Betrag muss kleiner als 10 Milliarden Euro sein.
- Der Betrag muss den Saldo des Kontos anpassen, von dem abgebucht wird.
- Erfassen: Betrag vom Konto abziehen
- Löschen: Betrag zum Konto addieren
- Verändern: Differenz zur "alten" Buchung zum Konto addieren
- Der Betrag muss den Saldo des Kontos anpassen, auf das gebucht wird.
- Erfassen: Betrag zum Konto addieren
- Löschen: Betrag vom Konto abziehen
- Verändern: Differenz zur "alten" Buchung vom Konto abziehen
zurück zu Das Projekt oder Prozedurales Cobol
Einnahme- und Ausgabgründe verwalten
User Story
Ich möchte meine Einnahmen und Ausgaben kategorisieren, um einen zusammengefassten Überblick über meine Finanzsituation zu erhalten.
Akzeptanzkriterien
- Die Kategorien sind einstufig.
- Die Kategorien können erfasst, geändert und gelöscht werden.
- Eine Löschung ist nur möglich, wenn eine Kategorie noch keiner Transaktion zugeordnet ist.
- Einnahmenkategorien und Ausgabekategorien sollen in getrennten Tabs dargestellt werden.
- Die Kategorien werden tabellarisch dargestellt.
- Alle Modifikationen werden in der entsprechenden Tabellenzeile durchgeführt.
- Es wird lediglich eine Bezeichnung der Kategorie erfasst.
- Die Bezeichnung muss mindestens ein Zeichen haben.
- Die Bezeichnung darf maximal 50 Zeichen umfassen.
zurück zu Das Projekt oder Prozedurales Cobol
Konten verwalten
User Story
Ich möchte für meine Einnahmen und Ausgaben Konten führen, die meinen realen Konten entsprechen und meine Finanzsituation vollständig im Haushaltsbuch abzubilden.
Akzeptanzkriterien
- Die Konten können erfasst, geändert und gelöscht werden.
- Die Konten werden tabellarisch dargestellt.
- Alle Modifikationen werden in der entsprechenden Tabellenzeile durchgeführt.
- Eine Löschung ist nur möglich, wenn es noch keine Buchung zu diesem Konto gibt.
- Es wird eine Bezeichnung des Kontos erfasst.
- Die Bezeichnung muss mindestens ein Zeichen haben.
- Die Bezeichnung darf maximal 50 Zeichen umfassen.
- Es wird ein Anfangsbestand für das Konto erfasst.
- Der Anfangsbestand wird bei der Erfassung und Änderung in den Kontensaldo übertragen.
- Der Anfangsbestand des Kontos darf nur geändert werden, wenn es noch keine Buchung zu diesem Konto gibt.
- Es wird angezeigt, ob es Buchungen zu diesem Konto gibt.
zurück zu Das Projekt oder Prozedurales Cobol
Monatliche Auswertung
User Story
Ich möchte in einer Monatsübersicht meine Einnahmen und Ausgaben Konten gegenüberstellen und einen Monatssaldo bilden, um einen Überblick über meine monatlichen Ausgaben und Einnahmen zu haben.
Akzeptanzkriterien
- Monat und Jahr für die Monatsauswertung können ausgewählt werden.
- Die monatlichen Einnahmen und Ausgabe werden in einer Kontendarstellung gegenüber gestellt.
- Es wird ein Saldo über Einnahmen und Ausgabe erstellt.
- Die Einnahmen und Ausgaben werden zu den Kategorien zusammengefasst dargestellt.
- Die Kategorie können zur Darstellung der Einzelbuchungen "aufgeklappt" werden.
zurück zu Das Projekt oder Prozedurales Cobol
Die Architektur
Die Client Lösung
Die Client Lösung läuft innerhalb der Microsoft Common Language Runtime. Das betrifft auch alle COBOL Komponenten.
Die Lösung besteht aus drei Schichten:
- Das Backend
umfasst alle Legacy Programme mit der Business Logik. Das sind alles klassische prozedurale COBOL Programme.
- Das User Interface
besteht aus vier Windows-Fenstern, deren Oberfläche mit der zur Zeit aktuellen XAML Technologie erstellt wurde. Die Steuerung der Oberfläche und die Kommunikation in Richtung Backend erfolgt über C# Klassen (Code-Behind).
- Das Objekt-Prozedurale Mapping
Die Kommunikation erfolgt über eine Schicht, die ich mit "Objekt-Prozedurales Mapping" bezeichnen will. Diese Schicht ist eine Sammlung aus "relativ leichten" COBOL Klassen, die Datenanfragen oder Datenaktualisierungen über Unterprogramm-Call an die prozeduralen Programme der Business Logik übergeben und von dort Daten und Verarbeitungsergebnisse zurück bekommen. Als "Übergabemedium" habe ich Entity Klassen (oder deren Collections) gewählt, die sowohl in den Kommunikationsklassen, als auch in den prozeduralen Programmen der Businesslogik verwendet werden können. Alternativ hätte ich auch in beiden Komponenten die Entitäten als COBOL Datenstruktur und die Kollektionen als COBOL Tabelle (Datenstruktur mit einer OCCURS-Klausel) repräsentieren können.
Anmerkung:
Warum ich die .net Architektur gewählt habe, kann ich nicht wirklich begründen. Es ist so. Aber die ganze Entwicklung hätte genauso in der JVM sein können.

Die Web API Lösung
Die Web Lösung habe ich mit REST JSON API realisiert. Dabei habe ich mich auf die Serverseite konzentriert, also auf das Angebot der Services. Die API sind lediglich inhaltlich ausgestaltet, ich habe auf alle Aspekte wie Sicherheit, Authentifizierung, Sessiontracking etc. verzichtet. Der Fokus war, die Geschäftslogik von COBOL Legacy Programmen für das Netz zu öffnen.
Microfocus stellt dazu einen Enterprise Server genannten eigenen Web Server und ein Tool (IMTK oder Interface Mapping Toolkit) zur Generierung der Web Interfaces zur Verfügung. Das IMTK ist das "Profi-Werkzeug" von Microfocus für WEB API in COBOL. Das macht die Implementierung extrem einfach.
Die Anwendung besteht aus zwei Komponenten:
- Der Applikation Container
enthält alle Legacy Programme mit der Geschäftslogik. Hauptsächliche Herausforderung der Implementierung ist es, der Geschäftslogik angemessene, gute Ressourcen (nicht gleich DB-Entitäten) bereitzustellen, auf die dann die nötigen Rest Operationen angewendet werden. Die Ressourcen sind in den COBOL Programmen als COBOL Datenstrukturen in der Linkage Section repräsentiert.
- Der Request Handler
enthält die definierten Ressourcen Interfaces und die Logik, Rest Anfragen in COBOL Datenstrukturen umzuwandeln und über einen Call Aufruf an die COBOL Programme mit der Geschäftslogik weiter zu reichen, bzw. umgekehrt, angefragte Daten oder Verarbeitungsergebnisse als COBOL Datenstruktur entgegen zu nehmen und in die Rest Strukturen umzusetzen. Die Ressourcen Interfaces (Web Services) werden vollständig durch das IMTK generiert. Das erleichtert die Arbeit ungemein.
Anmerkung:
Die Web API Lösung basiert auf einer Technologie der Firma Microfocus. In jedem Falle werde ich mir (ein bisschen später) anschauen, wie IBM mit dem Thema umgeht.

Das UI
Überblick
Das UI ist mit Windows XAML und C# Code-Behind realisiert.
Es besteht neben einer Klasse zur Implementierung von Routed Commands und einigen Hilfsklassen zur Verbesserung der Eingabe oder Darstellung einiger XAML Standard Controls aus vier Fenstern.
- Das MainWindow
enthält die Menüstruktur und die gesamte UI Funktionalität für die Bearbeitung von Einnahmen, Ausgaben und Umbuchungen.
- Das Fenster windowAccount
wird aus dem MainWindow gestartet und enthält die UI Funktionalität für die Bearbeitung von Konten. Die Liste der Konten wird in einem Grid dargestellt. Darin findet auch die Bearbeitung statt. Ich kann in dem Grid ohne "Einzelspeicherung" beliebige Änderungen, Löschungen oder Neuaufnahme durchführen und diese dann gesammelt speichern.
- Das Fenster windowTransactionReason
wird aus dem MainWindow gestartet und enthält die UI Funktionalität für die Bearbeitung von Transaktionsgründen, also Gründen für Einnahmen und Ausgaben. Die Liste der Transaktionsgründe wird in einem Grid dargestellt. Darin findet auch die Bearbeitung statt. Ich kann in dem Grid ohne "Einzelspeicherung" beliebige Änderungen, Löschungen oder Neuaufnahme durchführen und diese dann gesammelt speichern.
- Das Fenster windowMonthlyEvaluation
wird aus dem MainWindow gestartet und enthält die UI Funktionalität für den Monatsreport.

Wer in die Sourcen blicken möchte, kann das hier tun. Alle Sourcen des UI sind in einer ZIP-Datei zusammengestellt.
Ein paar ausgewählte Aspekt
Ich freue mich sehr über einen Blick in die Sourcen. Es macht aber keinen Sinn, hier näher auf den gesamten Sourcecode einzugehen. Ich will lediglich ein paar kleine Aspekte der Interoperabilität von C# und COBOL aufgreifen. Wichtig zu wissen ist, dass die komplette Anwendung mit allen C# und COBOL Komponenten in .net bzw. der Common Language Runtime läuft.
Alle Komponenten können daher miteinander kommunizieren, mit einer Ausnahme: Eine C# Klassen kann kein prozedurales Cobol (Unter)-Programm aufrufen.
Das Gesagte gilt natürlich in gleicher Weise für Java und die JVM.
Folgende zwei Beispiele illustrieren die Interaktion aus Sicht von C#:
1. Lesen aller Einnahmebuchungen und binden an die entsprechenden Data Controls
Das folgende Code Snippet ruft das Lesen aller Eingabebuchungen auf und bindet die Daten an ein Grid und ein Datagrid.
// Reading and binding income
Instanziieren einer Cobol Klasse
hdlTransactions = new HandleTransactions();
Aufruf einer Cobol Methode
hdlTransactions.ReadIncome();
Instanziieren einer Collection Klasse des .net-Framework
mit einer Property aus einer Cobol Klasse als Konstruktor.
Die Property ist ebenfalls eine Collection Klasse des .net-Framework.
lcvIncome = new ListCollectionView(hdlTransactions.obscIncome);
grIncome.DataContext = lcvIncome;
dgIncome.DataContext = lcvIncome;
Zunächst wird die Cobol Klasse HandleTransactions instanziiert, damit über die Methode ReadIncome() das Lesen aller Einnahmebuchungen angestoßen werden kann. Ein Property der Klasse HandleTransactions ist die Collection obscIncome vom Typ ObservableCollection in der alle Eingabebuchungen gesammelt werden. ObservableCollection ist eine der vielen Collection-Klassen aus dem .net Framework. Mit diese Collection als Konstruktor wird eine neue Instanz der Collection ListCollectionView (ebenfalls aus dem .net Framework) instanziiert, die dann an benötigte Daten Controls gebunden wird. Die ListCollectionView enthält unter anderem schicke Navigationsmethoden.
Dieses Beispiel zeigt die Kommunikation von (selbstgeschriebenen) C# Klassen mit (selbstgeschrieben) COBOL Klassen, die wiederum C# Klassen aus dem .net Framework nutzen.
2. Speichern einer erfassten Transaktionsbuchung
Dieser Code Snippet stößt die Speicherung einer neu erfassten Transaktion (Ausgabe oder Einnahme) an und führt anhand eines Return Wertes entsprechende Aktionen aus.
if (currentAddItem != null) ==> Neu erfasster Datensatz
{
// Store new bookingtransaction in database
Instanziieren einer Cobol Klasse
HandleTransactions handleTransactions = new HandleTransactions();
Ausruf einer Cobol Methode und weitere Aktionen abhängig vom Return Wert
if (handleTransactions.InsertTransaction(lcvTransaktionen.CurrentItem as EntTransaction))
{
ErrorMarking.MarkErrorTransaction(stack, lcvTransaktionen.CurrentItem as EntTransaction);
}
else
{
cmdCanExErsteTransaktion = true;
cmdCanExLetzteTransaktion = true;
cmdCanExNeueTransaktion = true;
cmdCanExTransaktionSpeichern = false;
cmdCanExRueckgaengig = false;
cmdCanExErfassungBenden = true;
HandleCmdNeueTransaktionExecuted(this, e);
}
}
...
"currentAddItem != null" kennzeichnet die zu speichernde Ausgabe oder Einnahme als neu erfasst. Um das Speichen der erfassten Transaktion einzuleiten, wird zunächst die Cobol Klasse HandleTransactions instanziiert. Dann wir über die COBOL Methode handleTransactions.InsertTransaction(lcvTransaktionen.CurrentItem as EntTransaction) das Speichern der Transaktion (Ausgabe oder Einnahme) angestoßen. Die zu speichernde Transaktion (Ausgabe oder Einnahme) ist der aktuelle Listeintrag der Collection ListCollectionView vom Typ EntTransaction (lcvTransaktionen.CurrentItem as EntTransaction). EntTransaction ist die COBOL Klasse, die alle Daten einer Transaktion (Ausgabe oder Einnahme) als Properties enthält.
Auch dieses Beispiel zeigt die Kommunikation von
(selbstgeschriebenen) C# Klassen mit
(selbstgeschrieben) COBOL Klassen
und C# Klassen aus dem .net Framework.
Anmerkung:
Ich habe diesen Kapitel bewusst kurz gehalten und auf die Interaktion von C# und COBOL fokussiert. Ich will ja nicht meine C# Kompetenz demonstrieren. C# Profis werden mich bei einem Blick auf den gesamten Code ohnehin sofort als Anfänger entlarven.
In den COBOL Kapiteln werde ich sicher etwas näher auf den Code eingehen. Ich werde mir aber Mühe geben, die Kapitel nicht zu überdehnen.
OO COBOL
Überblick
Die Kommunikation zwischen den UI aus XAML/C# und dem prozeduralen COBOL Backend regelt eine Sammlung aus "schmalen" COBOL Klassen.
Insgesamt 13 COBOL Klassen regeln die Kommunikation zwischen UI und Backend. Das sind fünf Entitiy Klassen, die die Daten für das UI oder in der Gegenrichtung zur Verarbeitung der Geschäftslogik enthalten. Korrespondierend dazu gibt es fünf Kommunikations-Handler. die die Kommunikation steuern. Aus diesen 10 Klassen besteht im Kern die Objekt-Prozedurale Kommunikation. Darüber hinaus gibt es drei weitere Klassen. Eine Klasse öffnet und schließt die Datenbank in der die Daten persistiert werden. Zwei Exception-Klassen werden alternativ geworfen, je nachdem ob in der Geschäftslogik ein inhaltlicher Eingabefehler oder ein SQL Fehler festgestellt wurde.
Die 10 "Kern"-Klassen will ich weiter unten etwas näher betrachten. Die 3 zusätzlichen Klassen sind:
HandleDB
Die Klasse besteht aus zwei statischen Methode. Die Methode OpenDB() verbindet zu einer SQL Server Express DB. Die Methode CloseDB() löst die Verbindung.
HandleInputExcepition und HandleSQLException
Die beiden Klasse Erben von von der Klasse Exception ohne deren Funktionalität zu erweitern. Es gibt in beiden jeweils eine Constructor Methode, die den Constructor von Exception ausruft. Die beiden Exception-Klassen werden in den Backend Programmen geworfen, je nachdem, ob ein inhaltlicher Verarbeitungsfehler oder ein SQL Fehler festgestellt wurde. Sie dienen damit der Differenzierung der Fehlerbehandlung.

Wer in die Sourcen blicken möchte, kann das hier tun. Alle Sourcen zur Objekt-Prozeduralen Kommunikation sind in einer ZIP-Datei zusammengestellt.
Das Wichtigste im Detail
In diesem Kapitel möchte ich etwas näher auf die 10 "Kern"-Klassen eingehen, also auf die Klassen, die die Kommunikation zwischen dem XAML/C# UI und den für die Geschäftslogik verantwortlichen COBOL Legacy Programmen regeln. Dabei soll mein besonderes Augenmerk den "Klassen-Paaren" HandleTransactions / EntTransaction und HandleAccount / EntAccount gelten.
HandleTransactions
Die Klasse verantwortet die Kommunikation zwischen UI und Backend bezüglich bezüglich Lesen und Bearbeiten von Ausgaben und Einnahmen. Die Klasse besteht aus zwei Properties und fünf Methoden:
1. obscSpendings und obscIncome
sind Properties vom Type ObservableCollection und sammeln und halten alle Ausgaben, bzw. Einnahmen. Alle Modifikationen finden auf diesen Kollektionen statt.
2. ReadSpendings und ReadIncome
sind Methoden, die das Lesen von Ausgaben, bzw Einnahmen im prozeduralen Legacy Programm BusinessLogicTransaktions aufrufen. Der entscheidende Code-Abschnitt in beiden Methoden ist:
call "ReadTransactions" using ls_Businesshandle obscIncome oder obscSpendings
Der Call ruft das prozedurale Legacy Programm BusinessLogicTransaktions auf, genau gesagt den Entry-Point ReadTransactions im Programm BusinessLogicTransaction. Die Aufrufparameter sind ein BusinessHandle zur Steuerung der Geschäftslogik und entweder die Collection obscIncome für alle Einnahmen oder obscSpendings für alle Ausgaben. Der Einstieg in das gerufene Programm über einen Entry-Point anstatt über die Procedure Devision geschieht, um die Übergabe-Parameter gegenüber den Modifikationsmethoden unterscheiden zu können.
Der Call-Aufruf aus einer OO COBOL Methode ist die Verbindung zu einem prozeduralen COBOL Programm.
3. UpdateTransaction
ist die Methode, die die Aktualisierung einer einzelnen Transaktion - also Einnahme oder Ausgabe - im prozeduralen Legacy Programm BusinessLogicTransaktions aufruft. Der entscheidende Code-Abschnitt ist:
try
move "TRU" to BusinessHandle
call "SaveTransactions" using BusinessHandle updTransaction
set transactionInputError to false
catch hAcE as type HandleInputException
set transactionInputError to true
catch hAcSqlE as type HandleSQLException
invoke type MessageBox::Show(hAcSqlE::Message)
set transactionInputError to false
end-try
Der Call ruft das prozedurale Legacy Programm BusinessLogicTransaktions auf, genau gesagt den Entry-Point SaveTransactions im Programm BusinessLogicTransaction. Die Aufrufparameter sind ein BusinessHandle zur Steuerung der Geschäftslogik und das Objekt updTransaction als Instanz der Klasse EntTransaction, also eine eine einzelne Ausgabe oder Einnahme. Die Prüfung der Eingabedaten in der Geschäftslogik kann zu einen inhaltlichen Fehler führen, der durch die Exception HandleInputExcetion abgefangen wird. Ein (theoretischer) SQL-Fehler wird durch die Exception HandleSQLException abgefangen.
4. InsertTransaction und DeleteTransaction
sind Methoden, die analog zu UpdateTransaction, das Einfügen oder Löschen einer Transaktion aufrufen.
Anmerkung 1
Der COBOL Compiler von Microfocus ist syntaktisch überaus tolerant. Er lässt zu, dass OO Cobol Klassen "moderner" oder "coboliger" aussehen.
Folgende Beispiele führen zum gleichen Compile-Ergebnis:
(noch ein bisschen "cobolig")
method-id UpdateTransaction (updTransaction as type EntTransaction) returning transactionInputError as condition-value.
local-storage section.
01 BusinessHandle pic x(3).
procedure division.
...
end method.
(etwas moderner)
method-id UpdateTransaction (updTransaction as type EntTransaction) returning transactionInputError as condition-value.
01 BusinessHandle pic x(3).
...
end method.
(sehr klassisch)
method-id UpdateTransaction.
local-storage section.
01 BusinessHandle pic x(3).
linkage section.
01 updTransaction type EntTransaction.
01 transactionInputError condition-value.
procedure division using updTransaction returning transactionInputError.
...
end method.
Anmerkung 2
Die fünf Methoden der Klasse HandleTransactions hätten auch bequem auf zwei Methoden reduziert werden können. Eine Methode für das Lesen mit einem Parameter, der angibt, ob Einnahmen oder Ausgaben gelesen werden sollen. Eine zweite Methode für die Modifikationen mit einem Parameter, der angibt, ob aktualisiert, eingefügt oder gelöscht werden soll.
Klasse EntTransaction
Die Klasse EntTransaction enthält aller Properties, die für die Geschäftslogik des prozeduralen Programmes BusinessLogicTransactions erforderlich sind. Die Properties enthalten die Felder der DB-Entität Transactions und zu jedem Feld jeweils ein Feld Errorflag und Errormessage um sich aus der Geschäftslogik ergebende inhaltliche Fehler an das UI zur Bearbeitung übergeben zu können. Die Klasse hat keine Methoden. Die Properties werden im Backend bereitgestellt und können im UI bearbeitet werden. Die bearbeiteten Daten werden, sofern sie inhaltlich fehlerfrei sind, im Backend verarbeitet und persistiert.
Klasse HandleAccount
Die Klasse verantwortet die Kommunikation zwischen UI und Backend bezüglich bezüglich Lesen und Bearbeiten von Konten. Die Klasse besteht aus zwei Properties und drei Methoden:
1. obsAccount
ist eine Property vom Type ObservableCollection und sammelt und hält alle Konten. Alle Modifikationen finden auf dieser Kollektion statt.
2. lstDeleteAccount
ist eine Property vom Typ List und sammelt die ID aller aus obsAccount gelöschten Konten. Wie unter 1. erwähnt, finden auf obsAccount alle Veränderungen statt, also auch das Löschen eines Kontos aus der Kollektion. Ich muss mir daher in einer zweiten Kollektion lstDeletedAccount merken, welche Konten gelöscht wurden, um dies in der Geschäftslogik nachzuhalten.
3. ReadAccount
ist eine Methode, die das Lesen aller Konten im prozeduralen Legacy Programm
BusinessLogicAccount aufruft. ReadAccount hat zwei wichtige Code-Abschnitte:
move "RAC" to BusinessHandle
call
"BusinessLogicAccount"
using
BusinessHandle obsAccount
Hier ist die Verbindung zum prozeduralen Legacy Programm BusinessLogicAccount mit Übergabeparametern. BusinessHandle enthält einen dreistelligen Code der die Geschäftslogik steuert, in diesem Fall das Lesen aller Konten anstößt. obsAccount ist die Kollektion in der die gelesenen Konten gesammelt werden.
detach method HandleObsAccountChange(self, e) from obsAccount::CollectionChanged
attach method
HandleObsAccountChange(self,
e) to
obsAccount::CollectionChanged
Attach Method fügt den Methoden-Pointer der Methode HandleObsAccountChange dem Event(Deligaten) CollectionChanged der Instanz obsAccount der Klasse ObservableCollection hinzu. Detach Method löscht den Methoden-Pointer. Das Event CollectionChanged wird ausgelöst, wenn der Kollektion ein Konto hinzu gefügt oder ein Konto aus der Kollektion gelöscht wird. Über dieses Ereignis kann ich die Löschungen sammeln und der Geschäftslogik von BusinessLocigAccount zuführen.
4. SaveAccount
ist eine Methode, die die Verarbeitung aller Änderungen (aktualisieren, hinzufügen, löschen) in den Konten im prozeduralen Legacy Programm
BusinessLogicAccount aufruft. Die Methode arbeitet ähnlich, wie die Modifikationsmethoden der Klasse HandleTransactions. Der Unterschied zeigt sich im Aufruf des Programmes
BusinessLogicAccount.
call
"BusinessLogicAccount"
using
BusinessHandle obsAccount lstDeletedAccount
Mit diesem Aufruf wird nicht ein einzelnes Konto zur Verarbeitung an die Geschäftslogik übergeben, sondern die komplette Kollektion obsAccount der Konten. Diese werden in der Geschäftslogik von BusinessLogicAccount in einer Schleife auf Veränderung oder Neuaufnahme geprüft und verarbeitet. Ebenso wird die Kollektion lstDeletedAccount der Id's der gelöschten Konten an die Geschäftslogik von BusinessLogicAccount übergeben, um in einer Schleife die Löschungen auf der Datenbank durchzuführen.
5. HandleObsAccountChange
ist der Handler für das Event obsAccount::CollectionChanged.
method-id HandleObsAccountChange (sender as object, e as type NotifyCollectionChangedEventArgs).
Das Ereignis wurde durch eine Kontenlöschung ausgelöst
if e::Action = type NotifyCollectionChangedAction::Remove
Ein Instanz der Klasse EntAccount wird erzeugt
declare removedAccount as type EntAccount
Das gelöschte Konto wird dieser Instanz zugewiesen
set removedAccount to e::OldItems[0] as type EntAccount
Das gelöschte Konto wurde nicht unmittelbar vorher erfasst
if removedAccount::CAC_Modification not = "N"
Die Id des gelöschten Kontos wird der Löschliste hinzugefügt
invoke lstDeletedAccount::Add(removedAccount::CAC_ID)
end-if
end-if
end method.
Wichtig ist hier die Event-Argument Klasse NotifyCollectionChangedEventArgs. Die Klasse enthält unter anderem die Property Action. Hat die Property einen Wert vom Typ NotifyCollectionChangedAction::Remove, wurde das Ereignis durch eine Kontenlöschung in der Kollektion ausgelöst. Für diesen Fall kann ich mir das gelöschte Konto aus der Property e::OldItems[0] nehmen und der Instanz removedAccount der Klasse EntAccount zuweisen. Letztlich nehme ich die Id des Kontos removedAccount::CAC_ID in die "Lösch"-Liste lstDeletedAccount auf. Das mache ich nur dann, wenn des gelöschte Konto nicht in der gleichen Session neu aufgenommen wurde, also noch nicht gespeichert war.
Anmerkung:
Das gelöschte Konto erhalte ich in der oberen Methode aus dem ersten Eintrag in der Property e::OldItems[0]. Das Property könnte auch mehrere gelöschte Konten enthalten, wenn ich im Grid das gleichzeitige Löschen mehrerer Einträge zugelassen hätte.
Klasse EntAccount
Die Klasse EntAccount enthält alle Properties, die für die Geschäftslogik des prozeduralen Programmes BusinessLogicAccount erforderlich sind. Die Properties enthalten die Felder der DB-Entität Account und zusätzlich für die gesamte Entität ein Feld Errorflag und ein Feld Errormessage um sich aus der Geschäftslogik ergebende inhaltliche Fehler an das UI zur Bearbeitung übergeben zu können.
Die Klasse hat Setter Methoden um bei Veränderung eines Properties eine Statusüberwachung zu implementieren. Die Methode SetModifikation implementiert die Statusüberwachung.
Klassenpaar EntAccountBooking / HandleAccountBooking
Die beiden Klassen regeln die Kommunikation zwischen UI und Backend für alle Kontenumbuchungen. Sie sind analog zum Klassenpaar EntTransaction / HandleTransactions implementiert.
Klassenpaar EntTransactionReason / HandleTransactionReason
Die beiden Klassen regeln die Kommunikation zwischen UI und Backend für alle Ausgaben- und Einnahmengründe. Sie sind analog zum Klassenpaar EntAccount / HandleAccount implementiert.
Klassenpaar EntTrasactionView / HandleTransactionView
Die beiden Klassen regeln die Kommunikation zwischen UI und Backend für den Monatsreport. EntTransactionView enthält alle Properties, die für den Monatsreport (prozedurales Programm BusinessLogicMonthlyEvaluation) erforderlich sind. HandleTransactionView besteht aus einer Methode ReadTransactionView, die das Lesen der Daten für den Monatsreport im Programm BusinessLogicMonthlyEvaluation anstößt.
Prozedurales COBOL
Überblick
Das Backend, also Geschäftslogik und Datenspeicherung, ist mit "alten" aber nach wie vor sehr präsenten Cobol Techniken programmiert. Das Backend besteht aus einer relationalen Datenbank (SQLServer Express) und fünf prozeduralen Cobol Programmen.
Ein Cobol Programm bearbeitet dabei jeweils einen Dialog. Das entspricht in etwa der Architektur von CICS Programmen. Es hätte natürlich auch ein einziger Monolith oder etwas dazwischen sein können.
BusinessLogicAccount
implementiert das Backend zur Userstory Konten verwalten.
BusinessLogicTransactionReason
implementiert das Backend zur Userstory Einnahme- und Ausgabegründe verwalten.
BusinessLogicTransactions
implementiert das Backend zu den Userstories Einnahmen buchen und Ausgaben buchen. Dieses Programm möchte ich weiter unten stellvertretend für die übrigen Programme genauer betrachten.
BusinessLogicAccountBooking
implementiert das Backend zur Userstory Konten umbuchen.
BusinessLogicMonthlyEvaluation
implementiert das Backend zur Userstory Monatliche Auswertung.
Die
Datenbank ist mit vier Entitäten und deren relationale Beziehungen sehr einfach. Gespeichert werden Transaktionen (Ausgaben und Einnahmen), Kontoumbuchen, Konten und Transaktionsgründe(Gründe für Ausgaben und Einnahmen).

Wer in die Sourcen blicken möchte, kann das hier tun. Alle Sourcen zum Backend sind in einer ZIP-Datei zusammengestellt.
BusinessLogicTransactions im Detail
Die deklarative Struktur
BusinessLogicTransactions ist ein konventionelles prozedurales COBOL Programm und hat damit folgende prinzipielle COBOL Struktur in ihrem deklarativen Teil:
Indentification Division.
...
Environment Division.
...
Data Division.
...
Prozedure Division.
...
In der Identification Devision habe ich lediglich den Program-Id Eintrag. Auf dokumentierende Einträge habe ich verzichtet. Einträge mit einer Relevanz für geschachtelte Programme brauche ich nicht.
Die Environment Division entfällt, da das Programm (wegen der relationalen Datenbank) keine COBOL Datei Verarbeitung braucht. Ebenso braucht das Programm keine Konfigurationseintragungen wie Source-Computer, Object-Computer oder Special-Names.
Aus der vollen Struktur der Data Division benötige ich lediglich:
Die Working-Storage Section
zur Deklaration der Kommunikationsfelder zur Datenbank und zur Deklaration der Cursor. Darüber hinaus enthält die Working-Storage Section eine Reihe von Datenbeschreibungen, die ausschließlich zur internen Programmsteuerung verwendet werden. Die hätten natürlich auch in die Local-Storage Section gekonnt.
Die Linkage Section
für die Kommunikation mit den Cobol-Klassen der Kommunikationsschicht.
Die Programmstruktur
Die Programmstruktur wird in der Procedure Division abgebildet und hat für BusinessLogicTransactions folgenden Aufbau:
Procedure Division.
Main Section.
Hier erfolgt die Steuerung des Programmes
RSP_ReadSpendings Section.
Hier wird eine Kollektion aller Ausgabenbuchungen erstellt
RIC_ReadIncome Section.
Hier wird eine Kollektion aller Einnahmenbuchungen erstellt
TRM_Update Section.
Hier werden einzelne Transaktionen (Einnahmen oder Ausgaben) aktualisiert
TRM_InsertTransaction Section.
Hier werden einzelne neue Transaktionen hinzugefügt
TRM_DeleteTransaction Section.
Hier werden einzelne Transaktionen gelöscht
TRM_Reusables Section.
Hier finden Verarbeitungen statt, die aus unterschiedlichen Verarbeitungen heraus gemeinsam genutzt werden. Das sind:
- Vorbereiten der Modifikationen
- Inhaltliche Prüfung der Modifikationen
- Fehlerbehandlung
End Program BusinesslogicTransactions.
Die Sections dienen in diesem Programm lediglich der logischen Gliederung. Die Programmlogik ist über Paragraphen innerhalb der Sections implementiert.
Einzelne Realisierungsaspekte
Es ist sicher nicht sinnvoll, hier den vollständigen Programmcode zu beschreiben. Ich möchte aber einzelne, aus meiner Sicht interessante Aspekte der Implementierung herausgreifen.
Der Datenzugriff
Die Daten sind in der Relationen Datenbank Microsoft SQLServer Express gespeichert. Der allgemein gängige Weg aus COBOL auf beliebigen relationale Datenbank zuzugreifen ist Embedded SQL. Diese Technologie habe ich hier verwendet. Microfocus Visual COBOL hat für DB2 und Oracle direkte Zugriffmethoden, die sich syntaktisch wenig von Embedded SQL unterscheiden. Sie haben im Wesentlichen Erweiterungen im Bereich von Stored Procedures und Functions. Sicherlich werde ich mir noch die DB2 Community Edition und die Oracle Database Express Edition ansehen.
Anmerkung:
Ich habe keine Variante realisiert, die die Daten in COBOL Dateien (z.B. VSAM) anstatt in einer relationalen Datenbank speichert. Ich hoffe, der geneigte Betrachter glaubt mir, das ich COBOL File Handling beherrsche. Vielleicht werde ich aber doch irgendwann eine "Datei-Version" erstellen.
Kollektion oder COBOL Tabelle
Ich möchte etwas näher auf die Listen aller Transaktionen - hier am Beispiel von Ausgaben - und unterschiedliche Implementierungsmöglichkeiten eingehend. Folgender Codeabschnitt zeigt meine Implementierung:
RSP_Fetch.
PERFORM UNTIL SQLCODE < 0 OR SQLCODE = +100
EXEC SQL
FETCH CSR_Spendings INTO
:Transactions-TA_ID
,:Transactions-TA_Text
,:Transactions-TA_Amount
,:Transactions-TA_Reason_ID
,:Transactions-TA_Account_ID
,:Transactions-TA_Booking_Date
,:Transactions-TA_Transaction_Type
END-EXEC
IF SQLCODE = 0
PERFORM RSP_RIC_Format_TR_Text
set entTransaction to new EntTransaction() ==> neues Objekt EntTransaction
set entTransaction::CTR_ID to Transactions-TA_ID
set entTransaction::CTR_Text to Transactions-TA_Text(1:TR_Text_Length)
set entTransaction::CTR_Amount to Transactions-TA_Amount
set entTransaction::CTR_Reason_ID to Transactions-TA_Reason_ID
set entTransaction::CTR_Account_ID to Transactions-TA_Account_ID
set entTransaction::CTR_Booking_Date to new DateTime(Transactions-TA_Booking_Year,
Transactions-TA_Booking_Month, Transactions-TA_Booking_Day)
set entTransaction::CTR_TransactionType to Transactions-TA_Transaction_Type
invoke obscTransactions::Add(entTransaction) ==> Objekt wird in die Kollektion der Transaktionen aufgenommen
END-IF
END-PERFORM.
Über das Embedded SQL Commando Fetch wird das Ergebnis einer vorherigen Cursor Deklaration (eine Anzahl Ausgaben-Datensätze) vereinzelt in einer Schleife in eine COBOL Datenstruktur gebracht. Jeder einzelne Ausgaben-Datensatz wird in der gleichen Schleife in die Properties eines neuen Objektes vom Type EntTransaction geschrieben. Schließlich wird in dieser Schleife jede neue EntTransaction dem Objekt obscTransaction vom Typ ObservableCollection hinzugefügt. Am Ende der Schleife existiert also eine ObservableCollection die alle gespeicherten Ausgaben enthält.
Wenn ein prozedurales Cobol Programm in einer virtuellen Maschine (JVM oder CLR) läuft, erlaubt der Microfocus Visual COBOL Compiler im prozeduralen Programm den Zugriff auf und die Nutzung von allen Klassen der jeweiligen virtuellen Maschine. Es spielt dabei keine Rolle, ob diese Klassen in COBOL, C# oder Java geschrieben sind und ob es System-Klassen oder eigene Klassen sind.
Die Alternative dazu ist die Nutzung einer COBOL Tabelle, in der alle Ausgaben-Datensätze gespeichert werden. Diese COBOL Tabelle kann dann der "Kommunikations-Klasse" HandleTransactions übergeben werden. Spätestens da muss dann die COBOL Tabelle in eine Kollektion überführt werden, da die C# UI-Klassen wenig mit einer COBOL Tabelle anfangen können.
Eine COBOL Tabelle hat zudem den Nachteil, dass sie nicht beliebig viele Ausgaben-Datensätze aufnehmen kann. Eine COBOL Tabelle kann zwar grundsätzlich mit unterschiedlich vielen Einträgen umgehen, verlangt aber in der Definition die Eingabe eines Maximus. Dadurch ist sie natürlich eingeschränkt.
Noch ein paar Anmerkungen zu COBOL Datentypen. Neben den "klassischen" COBOL Daten kennt Microfocus Visual COBOL alle Datentypen, die auch Java oder C# kennen, sofern das COBOL Programm in einer virtuellen Maschine läuft. Sie heißen nur anders. int(Java, C#) heißt binary-long(COBOL), boolean(Java) oder bool(C#) heißt condition-value(COBOL). Will oder muss man "klassische" Datentypen in "moderne" Datentypen konvertieren, gibt es aber kleine Herausforderungen, wie die folgenden beiden Beispiele zeigen.
1. String
Ein String in COBOL wird mit der
Pic X(nn) Klausel beschrieben, wobei nn die fixe Länge des String ist. Die fixe Länge wird auch immer ausgenutzt und im Zweifel mit blank aufgefüllt. Schreibt man einen String von von Type
Pic X(nn) in einen flexiblen String vom Typ
string, schreibt man auch immer die Blanks mit. Der String "Hallo" aus einem
Pic X(10) Datentype führt also zu einem String "Hallo " bei einem
string Datentyp. Man muss also die tatsächliche Länge eines String beachten.
move 0 to TR_Text_TrailingSpaces
inspect Transactions-TA_Text tallying TR_Text_TrailingSpaces for trailing spaces
compute TR_Text_Length = 50 - TR_Text_TrailingSpaces ==> bei pic X(50)
set
entTransaction::CTR_Text
to
Transactions-TA_Text(1:TR_Text_Length)
2. Date
Der SQL Datentype Date wird typischerweise durch die Datenstruktur
03 Transactions-TA_Booking_Date .
05 Transactions-TA_Booking_Year PIC 9(4).
05 Transactions-TA_Separator1 PIC X.
05 Transactions-TA_Booking_Month PIC 9(2).
05 Transactions-TA_Separator2 PIC X.
05
Transactions-TA_Booking_Day PIC 9(2).
dargestellt.
Mit den Jahr-, Monat- und Tageinträgen als Konstruktor wird dann ein Date-Objekt als Property in der aufnehmenden Klasse geschrieben.
set entTransaction::CTR_Booking_Date
to new
DateTime(Transactions-TA_Booking_Year, Transactions-TA_Booking_Month, Transactions-TA_Booking_Day)
Mit der ToString Methode der Date-Klasse wird das Datum für das Zurückschreiben wider "entpackt".
set Transactions-TA_Booking_Date
to entTransaction::CTR_Booking_Date::ToString("yyyy-MM-dd")
Anmerkung:
Das Ansinnen, UI und Backend durch die Kommunikationsschicht zu entkoppeln, konnte ich nur unvollständig umsetzen. Die Implementierung von zwei Kollektionen im Backend - eine für Einnahmen, eine für Ausgaben - bedient die UI Entscheidung, Einnahmelisten und Ausgabelisten jeweils in einem eigenen Tab darzustellen. Wer in die Programme schaut, wird noch mehr solcher Fälle finden. Ist das für eine Client (Client - Server) Anwendung, die ja eine gewisse Geschlossenheit hat, wirklich schlimm?
Völlig anders sind Web Services zu betrachten.
Update einer aktualisierten Transaktion
Mit dem Update einer aktualisierten Transaktion sind eine Reihe von Programmlogiken verknüpft, die alle erfolgreich abgeschlossen sein müssen, bevor ein Commit alle Datenveränderungen final festschreibt.
TRM_StartUpdateTransaction.
perform TRM_Prepare_Check_Input
Führt alle fachlichen Prüfungen von Veränderungen durch
perform TRM_Prepare_Modifying
Führt einige zur Aktualisierung notwendige Vorbereitungen durch
perform
TRM_ReadAccount
Liest das relevante Konto, um den Saldo zu aktualisieren
perform
TRM_ReadTransaction
Liest die noch gespeicherte alte Version der veränderten Transaktion, um alten und neuen Transaktionsbetrag zu saldieren, damit der neue Kontensaldo errechnet werden kann.
perform
TRU_ModifyAccount
Errechnet den neuen Kontensaldo
perform TRM_Update_Account
Aktualisiert das Konto mit dem neuen Saldo
perform TRM_Update_Transaction
Aktualisiert die Transaction
exec sql commit end-exec
Speicher alle Veränderungen
Noch als kurzes Beispiel die Aktualisierung einer veränderten Transaktion (perform TRM_Update_Transaction). Die Aktualisierung nutzt - wie alle Aktualisierungen - statisches Embedded SQL in einer Try Catch Klammer.
TRM_Update_Transaction.
try
EXEC SQL
UPDATE dbo.Transactions
SET
TA_Text = :Transactions-TA_Text
, TA_Amount = :Transactions-TA_Amount
, TA_Reason_ID = :Transactions-TA_Reason_ID
, TA_Account_ID = :Transactions-TA_Account_ID
, TA_Booking_Date = :Transactions-TA_Booking_Date
WHERE (TA_ID = :Transactions-TA_ID)
END-EXEC
catch
move SQLCODE to sql_Code
string "SQL Systemfehler " sql_Code ". Bitte benachrichtigen sie Ihren Dienstleister." into sqlErrorMsg
end-string
perform Raise-SQLError
end-try
Statisches Embedded SQL bedeutet, das ich alle Felder der Entität Transactions aktualisiere, unabhängig davon, ob sie tatsächlich verändert wurden. Es wäre unproblematisch, mit dynamischem Embedded SQL nur die Felder zu aktualisieren, die tatsächlich verändert wurden. Das bedeutet aber eine komplexere Statusverwaltung in der Klasse EntTransaction. Aktuell verwalte ich nur, ob sich in der Klasse überhaupt eine Property verändert hat. Um dynamisches Embedded SQL sinnvoll anzuwenden, müsste ich die Statusverwaltung in der Klasse EntTransaction auf jedes einzelne Property erweitern.
Bei einem SQL Fehler wird der Paragraph Raise-SQLError ausgeführt. Der Paragraph "erhebt" die Fehler-Klasse HandleSQLException(sqlErrorMsg). Analoges gilt für die Fehler-Klasse HandleInputException(algErrorMsg) bei fachlichen Eingabefehlern.
Raise-Error.
raise new HandleInputException(algErrorMsg)
Raise-SQLError.
raise new
HandleSQLException(sqlErrorMsg)
COBOL Web Services
Überblick
Die COBOL Web Services sind als REST JSON API realisiert. Ausgangsbasis meiner zu veröffentlichen REST API ist natürlich die Geschäftslogik des Haushaltbuches. Das sind die (etwas veränderten) fünf prozeduralen Backendprogramme, wie ich sie im Kapitel prozedurale Programmierung beschrieben habe. Im Gegensatz zur Clientlösung, laufen die Programme aber nativ, also nicht innerhalb einer virtuellen Maschine. Damit stehen mir auch die in der Clientlösung verwendeten .net Klassen nicht zur Verfügung. Ich bin auf die "klassischen" Cobol Strukturen angewiesen.
Der erste Schritt Richtung API ist die Definition der Ressourcen.
Ressourcen Definition
Jedes Backendprogramm implementiert die Logik für ein Business Objekt (Transaktionen, Konten, Kontenumbuchungen, ...). Das impliziert, aus dem Business Objekt die Ressource abzuleiten. Es gibt daher Ressourcen analog zu den fünf Backendprogrammen. Für jedes Backendprogramm gibt es zwei Ressourcen, jeweils eine Listenressource und die dazugehörende "Einzel"-Ressource (Transactionlist - Transaction, Accountlist - Account, ...) Für BussinesLogicMonthlyEvaluation gibt es nur die Listenressource TrViews. Das Programm stellt lediglich Daten zur Auswertung bereit, Veränderungen finden hier nicht statt.
Für die Listenressourcen ist das Verb Get implementiert. Für die "Einzel"-Ressourcen ist der "volle Satz" implementiert (Get, Put, Post, Delete).
Die wichtigste Modifikation im Backend
Die Implementierung der Backendprogramme ist grundsätzliche gleich. Einige Anpassungen gibt es aber doch. Die wichtigste Anpassung ist die Repräsentanz der Daten, die an die REST API übergeben oder aus den REST API übernommen werden.
Die Daten werden in COBOL Datenstrukturen innerhalb der Linkage Section abgebildet. Die Listenressourcen werden durch variable, aber mit einem Maximum versehenen COBOL Tabellen abgebildet.
Interface Mapping Toolkit
Das Interface Mapping Toolkit (IMTK) ist ein grafisches Werkzeug der Firma Microfocus zur vollständigen Implementierung von REST API. Es hat zwei wichtige Funktionen:
- Grafische Interface Entwicklung
Das IMTK bietet eine grafische Oberfläche zur Definition von Ressourcen und darauf anzuwendenden Verben. Das beschreibt die REST API und die JSON Schemata. Diese können grafisch mit den in der Linkage Section der COBOL Programme beschrieben Datenstrukturen verknüpft werden. Dadurch erfolgt ein Mapping der JSON Daten mit den COBOL Daten. - Interface Generierung
Aus der grafisch entwickelten REST API Definition und dem COBOL Mapping generiert das IMTK die vollständige Interface Software, die das definierte Mapping durchführt.

Wer in die Sourcen blicken möchte, kann das hier tun. Alle Sourcen API Lösung sind in einer ZIP-Datei zusammengestellt.
Backend REST API im Detail
Im folgenden möchte ich etwas näher am Beispiel des Backend-Programms BusinessLogivTransaction und der Ressource IfTransaction (Ausgaben und Einnahmen) auf die Microfocus Lösung für Rest API und das Interface Mapping Toolkit (IMTK) eingehen.
COBOL Datenstruktur der API
Wie im Überblick erwähnt, mappt das IMTK JSON Schemata auf eine COBOL Datenstruktur im Programm, dass die Geschäftslogik zu dieser Ressource implementiert. Wie diese COBOL Datenstruktur aussieht, möchte ich anhand des Programmes BusinessLogicTransaktion zeigen.
linkage section.
01 transactionInfo.
05 TMethod pic 9.
05 Max-OT pic 9(3).
05 ErrFlag pic 9(9).
05 ErrMessage pic x(256).
05 tr-QueryType pic 9.
01 transactionList occurs 1 to 100 depending on Max-OT.
05 trList_ID PIC S9(09) COMP-5.
05 trList_Text PIC X(50).
05 trList_Amount PIC S9(7)V9(2) COMP-3.
05 trList_Reason_ID PIC S9(09) COMP-5.
05 trList_Account_ID PIC S9(09) COMP-5.
05 trList_Booking_Date PIC x(10).
01 transaction.
05 tr_ID PIC S9(09) COMP-5.
05 tr_Text PIC X(50).
05 tr_Amount PIC S9(7)V9(2) COMP-3.
05 tr_Reason_ID PIC S9(09) COMP-5.
05 tr_Account_ID PIC S9(09) COMP-5.
05 tr_Booking_Date PIC x(10).
Die Datenstrukturen in der Linkage Section enthält alle Daten, die JSON Daten aus den REST API entgegenzunehmen oder die JSON Daten zur Lieferung an den Web-Client zu bestücken. Das ist keine 1:1 Abbildung des JSON Schemata. Das Mapping erfolgt im IMTK. Die Daten müssen nur vollständig, typgerecht und hoffentlich einigermaßen ordentlich gegliedert sein.
In meinem Fall enthält transactionInfo alle nötigen Verarbeitungsrückmeldungen und mit TMethod einen Indikator, welche Ressource gerade mit welcher Operation eine Anfrage stellt. tr-QueryType ist zum einen ein Query-Parameter für die Get-Operation auf die Ressource TransactionLists und indiziert, ob eine Liste von Ausgaben oder eine Liste von Einnahmen zu erstellen ist. Zum anderen ist tr-QueryType ein Query-Parameter in der Get- und Put-Operation der Ressource Transaction und indiziert, welche einzelne Transaktion gelesen oder gelöscht werden soll. Es kommen auch nicht alle Verarbeitungsrückmeldungen in gleicher Weise in allen Ressourcen und Operationen vor.
Die Datenstruktur transactionList enthält bis zu 100 einzelne Transaktionen und dient dem Erstellen der Listenressource TransactionLists. Auf diese Ressource gibt es lediglich eine Get-Operation.
Die Datenstruktur transaction repräsentiert die Inhaltsdaten der Ressource Transaction. Aus diese Ressource werden die Operationen Get, Post, Put und Delete angewendet. Alleine aus den unterschiedlichen Operationen ist die unterschiedliche "Bestückung" der JSON Files ersichtlich. Beim Get stehen die Inhaltsdaten in der Response, beim Put natürlich im Request-Body. Beim Delete gibt es natürlich keinen Request-Body, denn ich brauche lediglich den Schlüssel der zu löschenden Daten als Query-Parameter. Auch Post und Put unterscheiden sich. Beim Put brauche ich natürlich den Schlüssel der zu verändernden Daten, beim Post darf ich bei einer automatischen Schlüsselvergabe keinen Key-Wert nutzen.
Diese vielfältigen Unterschiede regelt das Mapping mittels des IMTK.
Implementierung der Geschäftsprozesslogik
Auch hier möchte ich einige Unterschiede am Beispiel von BusinessLogicTransactions adressieren. Grundsätzlich verändert sich die Implementierung gegenüber der Client(/Server) Lösung nicht. Es sind ja schließlich die gleichen Geschäftsprozesse. Ein bisschen was ändert sich allerdings an Programmsteuerung, an dem Umgang mit Datenstrukturen und am Exception-Handling. Darauf möchte ich im folgenden eingehen.
Programmsteuerung
Die Programmsteuerung wird davon bestimmt, welche Ressource mit welcher Operation gerade die Geschäftslogik aufruft. Wie das geschieht, wird durch das Ressourcen-Mapping im IMTK bestimmt. Der folgende Code-Abschnitt zeigt, wie das Cobol-Programm darauf reagiert.
evaluate TMethod
when 1
perform RT_StartReadTransactionList
when 2
perform ST_StartInsertTransaction
when 3
perform ST_StartUpdateTransaction
when 4
perform ST_StartDeleteTransaction
when 5
perform RT_StartReadTransaction
end-evaluate
Das IMTK bietet die Möglichkeit, je Ressource und Operation einem Feld aus dem "COBOL Entry Point" (das ist die COBOL Datenstruktur in der Linkage Section) einen statischen ganzzahligen Wert zuzuweisen. Dieser Wert repräsentiert die Ressource und die Operation und wird von der generierten Interface Software an das Backend Programm übergeben. Dadurch kann sehr einfach die Programmsteuerung erfolgen.
Im Fall von BusinessLogicTransactions ist das Feld TMethod mit folgenden Werten gesetzt:
- 1 für TransactionLists / Get
- 2 für Transaction / Post
- 3 für Transaction / Put
- 4 für Transaction / Delete
- 5 für Transaction / Get
Umgang mit Datenstrukturen
Die Backend-Programme sind native Programme und laufen nicht innerhalb einer VM. Damit steht diesen Programmen auch nicht der riesige Fundus an Klassen und Kollektionen solche Umgebungen zur Verfügung. Kollektionen werden in diesem Fall durch COBOL Tabellen dargestellt. Dies zeigen folgende Code Abschnitte:
01 transactionList occurs 1 to 100 depending on Max-OT.
05 trList_ID PIC S9(09) COMP-5.
05 trList_Text PIC X(50).
05 trList_Amount PIC S9(7)V9(2) COMP-3.
05 trList_Reason_ID PIC S9(09) COMP-5.
05 trList_Account_ID PIC S9(09) COMP-5.
05 trList_Booking_Date PIC x(10).
.
.
.
move 0 to ix-TR
PERFORM UNTIL SQLCODE < 0 OR SQLCODE = +100
EXEC SQL
FETCH CSR_Transaction INTO
:Transactions-TA_ID
,:Transactions-TA_Text
,:Transactions-TA_Amount
,:Transactions-TA_Reason_ID
,:Transactions-TA_Account_ID
,:Transactions-TA_Booking_Date
END-EXEC
IF SQLCODE = 0
add 1 to ix-TR
if ix-TR > 100
move 90100 to ErrFlag
move "Es sind mehr als 100 Transaaktionen vorhanden." to ErrMessage
perform Exit_Error
end-if
Move Transactions-TA_ID to trList_ID(ix-TR)
Move Transactions-TA_Text to trList_Text(ix-TR)
Move Transactions-TA_Amount to trList_Amount(ix-TR)
Move Transactions-TA_Reason_ID to trList_Reason_ID(ix-TR)
Move Transactions-TA_Account_ID to trList_Account_ID(ix-TR)
Move Transactions-TA_Booking_Date to trList_Booking_Date(ix-TR)
END-IF
END-PERFORM
move
ix-TR
to
Max-OT
Um die Transaktionen zu sammeln, verwende ich die COBOL Tabelle transactionList. Zu einem verlangt das IMTK für Listenressourcen eine COBOL Tabelle und zum anderen steht mir auch keine Kollektionsklasse zur Verfügung.
Anmerkung:
Ich gestehe es lieber gleich. Wer in die beigefügten Sourcecodes schaut, wird feststellen, dass die Implementierung der Listenressourcen nicht "viable" ist. Ich entschuldige mich im Moment damit, das ich durch die aktuelle Implementierung das Prinzip erfasst und richtig umgesetzt habe, gelobe aber Nachbesserung.
Was ist also schräg:
- In einem ordentlichen Szenario wäre es angebracht, die Listen von Ausgaben und Einnahmen zu beschränken, z.B. auf Monate. Den Punkt kann man aber noch als "viable" durchgehen lassen. Ist zwar ungeschickt, führt aber nicht zu Fehlern.
- Die COBOL Tabelle hat ein Maximum von 100 Eintragungen, die könnte ich zwar auf 100000 erweitern (ich weiß gar nicht, ob der Compiler das mitmacht), wäre aber immer noch eine potenzielle Fehlerquelle und einen JSON-Body mit möglichen 100000 Werten möchte ein IT-Architekt wahrscheinlich auch nicht haben. Es muss also ein ordentliches Paging her.
Exception Handling
Für das Exception Handling stehen mir in diesen Umfeld keine Exception Klassen zur Verfügung. Das ist aber auch nicht notwendig. Die folgenden Code Abschnitte zeigen die einfache Lösung:
...
* Das Buchungsdatum muss einen gültigen Wert haben
call "Check-SQLDate" using Sql-Date SQL-Year-From SQL-Year-To SqlDate-Return
if SQLDate-False
move 90006 to ErrFlag
move "Das Datum muss einen gültigen Wert haben und mit YYYY-MM-TT formatiert sein." to ErrMessage
perform Exit_Error
end-if.
...
Exit_Error.
perform TR_CloseDB
exit program.
Der Responsebody aller JSON-Schemata hat ein Errorflag und eine Errormessage in denen inhaltliche oder systemische Fehler protokoliert und dem Aufrufer der API zurückgemeldet werden.
Interface Mapping
In einer .net Umgebung nutzt man WCF Services zur Implementierung von API, sowohl für SOAP, wie für Rest. Die Java Entsprechung wäre JAX-RS (Java API for RESTful Web Services) oder ein entsprechendes Framework wie Apache CXF oder Spring Boot.
Eine WCF oder JAX-RS Implementierung wäre auch mit Microfocus Visual Cobol möglich. Microfocus hat dazu allerdings auch das elegante grafische Toolkit IMTK, aus dem die vollständige API-Interface Software generiert wird. Wie das funktioniert zeige ich am Beispiel des API-Interfaces IfTransaction.

Das Interface hat zwei Ressourcen, TransactionLists und Transaction.
Auf Transactionlists wird die Operation Get angewendet. Auf die Ressource Transaction werden die Operationen Get, Put, Delete und Post angewendet. Das obere Bild zeigt das Mapping für GetTransactionLists.
Der Bereich COBOL Entry POINT referenziert auf die Linkage Section des Programmes BusinessLogicTransaction. Mit diesen Datenstrukturen ruft die generierte Interface-Software das Programm BusinessLogicTransaction auf. Die Datenstrukturen sind eine Obermenge für alle Ressourcen und Operationen des API-Interfaces. Es werden nur die für die jeweilige Operation benötigten Strukturen genutzt.
Der Bereich Interface Fields - Input (links oben ein wenig durch die aufgeklappte Operationen-Liste verdeckt) enthält die Query-Parameter und - wenn vorhanden - den Request-Body. Hier gibt es nur einen Query-Parameter der mir angibt, ob eine Einnahmen- oder eine Ausgabenliste gelesen werden soll.
Der Bereich Interface Fields - Output gibt den Response-Body an. In diesem ist dies das Objekt transactionInfo mit drei Feldern zur Anzahl der gelesenen Transaktionen, einem Errorflag und einer Errormessage, falls beim befüllen der Liste Fehler aufgetreten sind.
Zudem gibt es das Array transactionList, das alle gelesenen Transaktionen bis zu einem Maximum von 100 enthält. Das ist an der Occurs-Klausel im Bereich COBOL Entry POINT erkennbar.
Der Bereich COBOL Assignments weist der jeweiligen Operation einen ganzzahligen Wert zu, der im Backend-Programm die Routinen für die entsprechende Operation steuert.
Der Bereich
Reusable Fields ist - wenn man ihn nutzen will - lediglich eine Vereinfachung der grafischen Zuordnung, hat aber keine inhaltliche Relevanz.
Das folgende Beispiel zeigt das Mapping des Post der Ressource Transaction.

Auch hier ist zentral der Bereich COBOL Entry Point mit der Referenz auf die Linkage Section von BussinesLogicTransaction.
Auf die Datenstruktur transaction wird der im Bereich Interface Fields Input dargestellte Request-Body des Post gemappt.
Der im Bereich Interface Fields Output zu sehende Response-Body enthält Infos über fachliche oder systemische Fehler.
Die 2 im Feld TMethod in dem Bereich COBOL Assignments ist der Indikator, um im Backend-Programm auf einen ankommenden Post auf Transaction zu reagieren.
Über Compilier- und Deploy-Vorgänge werden alle so generierten Services im Mircofocus Enterprise Server implementiert.

Steuerdateien
Aus dem grafischen Tool IMTK generieren sich drei "Steuerungsdateien" die Input für die Generierung der API-Interface Software sind: