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.


  • Prozedurales COBOL
    Details
  • Objektorientiertes COBOL
    Details
  • Kommunikation mit C# Klassen (Es hätten auch Java Klassen sein können.)
    Details
  • Web Services mit REST JSON
    Details

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:


  1. 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.
  2. 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

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".

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

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.

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.

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.

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.

Alle Sourcen herunterladen

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.

Alle Sourcen herunterladen

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.

Code der Klasse HandleTranactions anzeigen

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.

Code der Klasse EntTransaction anzeigen

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.

Code der Klasse HandleAccount anzeigen

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.


Code der Klasse EntAccount anzeigen

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.

Code der Klasse EntAccountBooking anzeigen

Code der Klasse HandleAccountBooking anzeigen

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.

Code der Klasse EntTransactionReason anzeigen

Code der Klasse HandleTransactionReason anzeigen

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.

Code der Klasse EntTransactionView anzeigen

Code der Klasse HandleTransactionView anzeigen


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.

Alle Sourcen herunterladen

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)

Code des Backendprogrammes BusinessLogicTransaction (Client Lösung)


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:

  1. 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.
  2. 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.

Alle Sourcen herunterladen

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:

  1. 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.
  2. 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.

Code des Backendprogrammes BusinessLogicTransaction (API Lösung)

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:

Die Datei BussinesLogicTransactions.cdt enthält eine XML Schema Beschreibung der Linkage Section von BussinesLogicTransactions.cbl.

Die Datei IfTransaction.svi enthält als weiteres XML Schema die Zuordnung von Request- und Response-Bodys zur BusinessLogicTransactions.cdt.

Die Datei IfTransaction.json enthält die JSON Beschreibung aller Ressourcen und Operationen des API-Interfaces IfTransaction