FLUC-Byteschnittstelle

Die Byteschnittstelle (Zugriffsmethode) basiert auf der Elementschnittstelle des FLUC und ermöglicht den sequentiellen, byteweisen Zugriff auf originale Datenbestände, wie dies beim File-I/O in der PC-Welt (fread, fwrite (C) bzw. über Stream-Klassen (Java, C#, C++)) üblich ist. Hierbei stehen alle Konvertierungsmöglichkeiten von FLAM beim Lesen und Schreiben von Originaldaten zur Verfügung. Dies bedeutet, dass man zum Beispiel transparent UTF-16LE mit 0x0A00 als Delimiter byteweise lesen kann, obwohl hierbei die Daten von einem Member einer konkatenierten GZIP-Datei stammen, welche sich verschlüsselt und BASE64-enkodiert auf einer lokalen Platte oder einem fremden System (Cloud/HOST/DMZ) befindet und deren klarer Text eigentlich in Latin1 enkodiert und mit 0x0D0A terminiert war.
 

Die Byteschnittstelle wird für JavaEE und ähnliche Umgebungen als Stream-Klasse und unter anderem auch über das Netzwerk mittels unserem FLIES-Server bereitgestellt (Remote Procedure Call). Hierdurch kann man zum Beispiel FLAM4FILEs innerhalb eines Webservers als normalen Stream über das Netz lesen, obwohl diese auf einem Großrechner record-orientiert geschrieben und dort durchaus auch belassen werden können. Um dies sicher realisieren zu können, wird hier in der Regel ein zweiter FLIES-Server benötigt, welcher als Gateway den Webserver über die DMZ mit dem zentralen System (FLIES-Server als Endnode) verbindet.

Allgemein kann man über die Byteschnittstelle jede Art von Dataset (PS, PDS, PDSE, VSAM, FB, VB mit und ohne ASA bzw. Maschinensteuerzeichen) auf der Host lesen und bekommt die Records beispielsweise im gewünschten Zeichensatz mit den dazugehörigen Delimitern als Bytestrom präsentiert. Hierdurch ist es unter anderem möglich, klassische record-orientierte Host-Datasets wie normale, sequentielle, byte-orientierte Dateien zu lesen.

Die Byteschnittstelle überführt hierbei Texte (format.text()) immer in den systemspezifischen Zeichensatz, es sei denn ein anderer Zeichensatz (z.B. format.text(ccsid='IBM1141')) wird angegeben, damit man die Daten zum Beispiel mit Stringkonstanten direkt vergleichen kann. Die Byteschnittstelle bietet somit ein plattformneutrales Interface für den Zugriff auf alle Arten und Formate von Dateien.

Werden die Zeichen in den systemspezifischen Zeichensatz in Abhängigkeit der Umgebungsvariablen LANG überführt, dann erhält man beim Lesen unter Windows so beispielsweise Latin-1 mit 0A als Delimiter. Unter UNIX wäre es meist UTF-8 mit 0x0A und auf der Host zum Beispiel IBM1047 mit 0x15. Sprich im Speicher (format.text()) überführt die Byteschnitstelle den Windowsdelimiter 0x0D0A in 0x0A, damit man hier platformunabhängig Strings verarbeiten kann. Nur beim Schreiben von Textdaten auf die Platte (write.text()) wird unter Windows 0x0D0A verwendet.

Neben dem byte-orientierten Lesen und Schreiben von Text (format.text()), binären Daten (format.binary() und Characterstreams (format.char()) unterstützt die Schnittstelle auch das sequentielle Lesen und Schreiben von Records (format.record()). Hier ist das Verhalten analog zur z/OS Laufzeitumgebung (type=record) implementiert. Es gibt jedoch ein paar feine Unterschiede: Zum einen kann man entscheiden, ob man mit ASA oder Maschinensteuerzeichen Lesen und Schreiben möchte (Retain) oder ob diese ignoriert (DETACH (default)) oder ob diese wie beim Drucken umgesetzt werden sollen (RPLFFD=26), wenn es sich um eine FBA bzw. VBA Datei handelt. Bei relativen Dateien kann man die Lücken als leere Sätze oder halt einfach nicht bekommen. Des Weiteren steht auch hier die Zeichensatzkonvertierung zur Verfügung. Man kann entscheiden, ob Trailing Whitespace entfernt, ob Steuerzeichen umgesetzt werden sollen und vieles mehr. Des weiteren wird über den 'size' Parameter gesteuert, ob Records einfach abgeschnitten werden (size>0), wenn sie nicht in den übergeben Buffer passen oder ob ein entsprechender Fehler (size=0) kommt, der es einem gestattet mehr Speicher zu beantragen, um das Record nochmal lesen zu können. Man kann Records zurückstellen und nochmal lesen, was vorallem dann Sinn macht, wenn man sich XML-Element als Records aufbereiten lässt und einen Parser schreibt, wo man jedes Element individuell konvertieren lassen kann. Hierdurch ist es unter anderem möglich Whitspaces in Strings pro Element entsprechend zu behandeln (collapse, remove, ...) oder Zahlen unabhängig von ihrer Angabe in der XML-Datei (   .45   ) in ein festes Format (0.45) wandeln zu lassen, was es ermöglicht, die Daten zu validieren und gleich weiter zu verarbeiten.

Wenn man unter Java eine FB Datei ließt, kann es bei der UTF-8 Wandlung dazu kommen, dass die einst fixen Records auf einmal variabel lang werden, weil Multibytezeichen (ü, ä, ö, ß, ...) vorkommen können. Hierbei ist es dann ggf. wichtig zu wissen, wie lang der Satz ursprünglich war. Dies lässt sich über einen Delimiter (Stream-Orientierung) kenntlich machen oder über die Satzlänge (Record-Orientierung). Man kann aber solche Daten auch weiterhin in einem Format mit fixer Länge verarbeiten, wenn man einen Single- oder Multibyte-Byte-Zeichensatz mit fixer Breite (z.B. UCS-16 oder UTF-32) wählt.

Im Rahmen des Table-Supports kann man sich zum Beispiel eine FB-Datei als CSV- oder XML-Stream zur Verarbeitung aufbereiten lassen. Wenn sich mehrer Tabellen in einer Datei befinden, kann man den End-Of-Table-Support beim Lesen aktivieren, welcher es gestattet nach dem Open den Tabellennamen abzufragen, damit man die Daten richtig verarbeiten kann. Der Switch führt auch dazu, dass man am Ende der Tabelle einen EOT-Error bekommt, wonach wieder der neue Tabellennamen abgefragt werden kann, bis man am Ende EOF bekommt. Beim Schreiben von verschiedenen Tabellen in eine Datei kann man über den Tabellennamen, welcher in der Row-Spezifikation vergeben wird, den Aufbau der nachfolgenden Daten vorgeben.  Wird dies nicht genutzt wird, die Autodetektierung der Tabellenformate genutzt, um die Daten richtig zu interpretieren.

Über die Byteschittstelle lässt sich frei entscheiden, wie die Daten gelesen werden und in welcher Form sie präsentiert werden sollen. Sie bietet auch die Möglichkeit alle Konvertierungen des FLUC im Speicher aus zu üben. Was FLAM damit zu einen Enterprise-Service-Bus für Transaktionelle/Online-Daten macht. Diese Lösung wird zum Beispiel für die Realisierung des Instant-Payments in der Krditwirtschaft genutzt.

Beispiele

C

Das folgende C-Beispiel liest einfach das Member 'test.txt' in Records aus einem FLAMFILE (dat.adc), welche auf einem IBM Großrechner in EBCDIC erzeugt wurde und schreibt es als Text in UTF-16LE mit 0x0D000A00 als Delimiter binär in eine normale Datei.

static int uiReadFlamFile(void)
{
   unsigned int       uiLen;
   void*                  pvRed=NULL;
   FILE*                 pfWrt=NULL;
   unsigned char    acBuf;

   pvRed=fcbopen("read.flam(file='dat.adc/?test.bin'"
                 " encoding='IBM1141')",
                 "format.txt(method=CRLF" 
                 " encoding='UTF-16LE')");
   if (pvRed==NULL || fcberrno) {
      printf("ERRMSG: %d - %s\n",fcberrno,fcberrms());
      printf("ERRTRC:\n%s\n",fcberrtr());
      return(-1);
   }
   pfWrt=fopen("test.txt","wb");
   if (pfWrt==NULL || errno) {
      printf("ERRMSG: %d - %s\n",errno,errormsg(errno))
      return(-1);
   }
   uiLen=fcbread(acBuf,1,sizeof(acBuf),pvRed);
   while(uiLen && fbcerrno==0) {
      fwrite(acBuf,1,uiLen,pfWrt);
      uiLen=fcbread(acBuf,1,sizeof(acBuf),pvRed);
   }
   if (pvRed!=NULL) fcbclose(pvRed);
   if (pfWrt!=NULL) fclose(pfWrt);
   return(0);
}

C++

Für C++ steht die Klasse flcbyt_buffer (FLCBYTBF) zur Verfügung, mit deren Hilfe sich das Byte Interface über die C++ Stream API benutzen lässt:

void readFlamFile() {
   flcbyt_buffer rbuf(
      "read.flam(file='dat.adc/?test.bin' encoding='IBM1141')",
      "format.txt(method=CRLF encoding='UTF-16LE')");
   flcbyt_buffer wbuf("write.bin(file='test.txt')",
                 "format.bin()");
   std::istream in(&rbuf);
   std::ostream out(&wbuf);

   char buf[1024];

   while(in) {
      in.read(buf, 1024);
      if (!in.eof() && in.fail()) {
         std::cout << "read failed" << std::endl;
         break;
      }
      out.write(buf, in.gcount());
      if (out.fail()) {
         std::cout << "write failed" << std::endl;
         break;
      }
   }
   std::cout << wbuf.close() << std::endl;
}

Java

Im folgenden Java-Beispiel wird dieselbe Funktionalität wie oben implementiert. Dazu wird allerdings der FLIES-Server genutzt, welcher in diesem Fall auf dem lokalen System ausgeführt wird:

private void readFlamFile() throws IOException {
   InputStream flamIn = new BufferedInputStream(
      new FlamInputStream("127.0.0.1", 17996,
      "read.flam(file='dat.adc/?test.bin'"
      +" encoding='IBM1141')",
      "format.txt(method=CRLF encoding='UTF-16LE')"));
   OutputStream fileOut = new BufferedOutputStream(
      new FileOutputStream("test.txt"));
   byte[] buffer = new byte[4096];
   int len;

   try {
      while ((len = flamIn.read(buffer)) != -1)
         fileOut.write(buffer, 0, len);
   } finally {
      if (flamIn != null)
         flamIn.close();
      if (fileOut != null)
         fileOut.close();
   }
}

Wenn man im "format.text()" einfach nix angibt, dann werden die Daten per Default als Textstream im jeweiligen platformspezifischen Zeichensatz mit dem entsprechenden Delimiter bereitgestellt. Hierdurch ist es möglich, dass man den gleichen Code ohne Veränderung auch auf anderen Platformen nutzen kann und auf welcher Platform am Ende das FLAMFILE aus dem Beispiel rumsteht, ist letzendlich auch egal, das Lesen würde einfach immer richtig funktionieren.

Mehr Information können Sie der Schnittstellenspezifikation im Downloadbereich entnehmen.