Autor Beitrag
hydemarie
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 407
Erhaltene Danke: 50



BeitragVerfasst: Fr 29.07.16 11:17 
Heute machen wir mal etwas lustiges, heute programmieren wir eine Website in COBOL.

So genannte Single Serving Sites habt ihr bestimmt schon mal gesehen, das sind im Wesentlichen Ein-Seiten-Websites, die nur einen einzigen bestimmten Zweck erfüllen, zum Beispiel purple.com, das euch zeigt, was Lila ist, und hasthelargehadroncol...royedtheworldyet.com. Ihr seht: Das ist unter Umständen durchaus sinnvoll. Es muss nicht immer ein CMS sein.

COBOL (Common Business Oriented Language) ist eine 1960 erstmals standardisierte Programmiersprache, die seit ihrer ersten Version zusammen mit Fortran (früher: FORTRAN, Formula Translator) die Weltwirtschaft beherrscht und bis heute aktiv weiterentwickelt wird. Wer schon mal eine Bank von innen gesehen hat, der ahnt, dass eine Menge Logik nötig ist, um ein Konto zu verwalten. Es wäre Irrsinn, diese Logik, wenn sie einmal funktioniert, in einer anderen Sprache neu zu programmieren, nur weil die alte Sprache nicht mehr im Trend liegt. Das bedeutet, dass Unternehmen mit viel Geld dringend Leute suchen, die Software pflegen sollen, die mitunter älter ist als diejenigen, die sie derzeit entwickeln. Ihr ahnt, warum es interessant sein kann, mal was mit COBOL gemacht zu haben. Und was liegt näher als eine Webanwendung in einer solch rustikalen Sprache zu schreiben?

Eben.

Um ein praxistaugliches Beispiel zu haben: Nehmen wir an, ihr wollt eine Website namens "HalloWelt.coffee" (ist tatsächlich noch frei) erstellen, die deutschsprachige Benutzer mit "Hallo Welt!", anderssprachige Benutzer aber mit "Hello World!" begrüßt. Ein alltägliches Problem, nicht wahr?

Damit ihr anfangen könnt, braucht ihr zunächst einmal ein wenig Software:

  • Einen FastCGI-tauglichen Webspace oder Webserver. (Wenn ihr das nicht so genau wisst, fragt euren Hoster. Wenn euer Hoster kein FastCGI anbietet, habt ihr einen schlechten Hoster. Wenn ihr einen schlechten Hoster habt, besorgt euch bitte einen besseren und lest dann hier weiter.) Ich empfehle Uberspace.
  • GnuCOBOL oder einen anderen COBOL-Compiler. Es gibt höchstwahrscheinlich auch einen für euer Betriebssystem (wenn ihr nicht gerade RISC OS oder TempleOS einsetzt). Das Kompilat muss später auf eurem Webspace oder Webserver lauffähig sein, wenn ihr also zum Beispiel einen Linux-Webserver habt, müsst ihr auch ein GnuCOBOL unter Linux benutzen. Unter den meisten GNU/Linux-Distributionen und den "großen" BSDs ist GnuCOBOL auch über euren Paketmanager verfügbar.
  • Einen Texteditor, am besten einen, der COBOL-Highlighting beherrscht. Notepad funktioniert aber auch. Ihr werdet sehen, dass Highlighting für COBOL nicht so wichtig ist. Unter welchem System ihr den Code schreibt, ist egal, so lange ihr ihn später unter dem "richtigen" System kompiliert.


Erstellt nun mit besagtem Texteditor eine neue Datei namens hallowelt.cob (oder hallowelt.cbl, da gibt es keine einheitliche Norm). Wir fangen klein an, wir geben erst mal nur den HTML-Code für die Website aus. Wie ihr den dann in eurem Browser anzeigen könnt, erkläre ich euch weiter unten.

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
       IDENTIFICATION DIVISION.
       PROGRAM-ID. Hallo-Entwickler-Ecke.

       PROCEDURE DIVISION.
      *    HTML-Text ausgeben:
           DISPLAY "<!doctype html>"
           DISPLAY "<html>"
           DISPLAY "  <head>"
           DISPLAY "    <title>HalloWelt.coffee</title>"
           DISPLAY "  </head>"
           DISPLAY "  <body>"
           DISPLAY "    <h1>Hallo Welt!</h1>"
           DISPLAY "  </body>"
           DISPLAY "</html>"
      *    HTML-Text ist zu Ende.

           STOP RUN.


Ihr seht: COBOL ist eine hübsche Sprache, man versteht sie eigentlich sofort. COBOL-Befehle könnt ihr inzwischen auch in normaler Schreibweise benutzen, also zum Beispiel "Display" statt "DISPLAY", ihr müsst den Code also nicht so "brüllen" wie im Beispiel. Ich finde es so aber übersichtlicher, damit man auch in Editoren, die kein COBOL-Highlighting können, Variablennamen und Strings optisch besser von Anweisungen unterscheiden kann.

Allerdings fängt das Programm absichtlich "eingerückt" an. Die Aufteilung in Spalten hat historische Gründe: COBOL stammt aus einer Zeit, in der man noch "zweidimensional" auf Lochkarten programmiert hat, die ersten sieben Spalten waren dort etwa für die Zeilennummer reserviert. Zwar ist auch die "Freiform" - also das Abweichen von der Lochkartenformatierung, in der das Programm in Spalte 8 anfangen muss, Kommentare mit einem "*" in Spalte 7 eingeleitet werden müssen und so weiter - inzwischen Teil des Standards, aber viele Texteditoren, die COBOL-Highlighting beherrschen, legen Wert darauf, dass ihr ebenfalls wenigstens ein bisschen konservativ programmiert, sonst funktioniert das Highlighting nicht richtig. Wenn ihr euch das von Anfang an angewöhnt, werdet ihr es später auch leichter mit fremdem Code haben, der sich in der Regel an die Konventionen hält. Übrigens: Ein Zeilenumbruch am Ende (den dieses Forum anscheinend verschluckt?) ist zwar nicht zwingend notwendig, wird aber gern gesehen. GnuCOBOL beschwert sich auch, wenn ihr ihn weglasst.

Wenn ihr diesen Code jetzt kompiliert ...

ausblenden Quelltext
1:
cobc -x -static hallowelt.cob					


..., bekommt ihr nach einer kurzen Wartezeit ein Programm namens "hallowelt.exe" (oder, wenn ihr auf einem unixoiden Computer arbeitet, "hallowelt") heraus, das nach dem Ausführen den gewünschten HTML-Code ausgibt:

Erstes_Kompilat

(Wenn sich cobc beim Kompilierversuch beschwert, dass es cl.exe nicht finden kann, benutzt ihr offenbar die Windowsversion. Die Windowsversion sucht Microsofts C-Compiler, zum Beispiel aus der Visual-Studio-Community-Edition, in eurem %PATH%. GnuCOBOL wandelt COBOL-Code als Zwischenschritt in C-Code um, wenn ihr also bereits C könnt, kann euch GnuCOBOL auf Wunsch auch nur den erzeugten C-Code ausgeben, den ihr dann weiterbearbeiten und selbst kompilieren könnt. Wenn ihr stattdessen einen anderen C-Compiler nutzen möchtet, müsst ihr GnuCOBOLs defaults.h.tmpl anpassen und GnuCOBOL neu kompilieren. Nehmt also lieber cl.exe.)

Nun ist das natürlich noch recht langweilig, denn dafür hättet ihr kein COBOL gebraucht. Wie kriegt ihr denn überhaupt das Programm ins Web und wie kriegt ihr das mit der Mehrsprachigkeit hinein?

Die Lösung heißt FastCGI.

FastCGI ist heute so was wie "ein Standard", es ist nicht unwahrscheinlich, dass auch eure PHP-basierte Website (ihr habt doch sicher eine PHP-basierte Website? Jeder hat eine PHP-basierte Website! ;)) unter FastCGI läuft. Stark vereinfacht heißt das, dass euer Webserver bei der Abfrage nach "*.php" das zugeordnete Programm, eben den PHP-Interpreter, aufruft und das Ergebnis, also eure "generierte" Website, zurückschickt. Der Trick: Statt eines PHP-Interpreters könnt ihr auch ein beliebiges anderes Programm starten, etwa ein Perlscript, ein C-Programm oder eben euer "hallowelt".

Ab hier müsst ihr also auf eurem Server/Webspace weitermachen.

  • Wenn ihr bei Uberspace seid, erklärt euch deren Wiki, wie ihr FastCGI einrichtet und nutzt. Das FastCGI-Programm ist in diesem Fall euer kompiliertes "hallowelt".
  • Auf einem OpenBSD-Server sind folgende zwei Einträge unter server "hallowelt.coffee" nötig:

    Zitat:
    root "/irgendein/pfad/hallowelt"
    fastcgi

  • Unter Apache braucht ihr mod_fcgid, dann könnt ihr in eurer httpd.conf das "hallowelt"-Programm einfach als Handler festlegen. (Wenn ihr Hilfe braucht: Entschuldigt - Apache habe ich schon zu lange nicht mehr so wirklich benutzt.)
  • Unter nginx müsst ihr nur die location ändern, indem ihr mindestens die "root"-Direktive sowie die beiden FastCGI-Parameter "SCRIPT_FILENAME" und "SCRIPT_NAME" (beide müssen auf euer "hallowelt" zeigen) einfügt.


Keine Sorge - das sieht nur so kompliziert aus. Wenn alles so weit funktioniert hat, solltet ihr beim Aufruf eurer Website ein großes "Hallo Welt!" sehen. Wenn nicht, habt ihr etwas falsch eingestellt. ;)

FastCGI gibt also die Ausgabe des zugeteilten Scripts/Programms zurück. Es macht aber noch mehr: Beim Aufruf übergibt es ein paar Umgebungsvariablen (PHP-Programmierern vielleicht als $_SERVER[] bekannt) an das aufgerufene Script, darunter HTTP_ACCEPT_LANGUAGE, in das euer Browser ungefähr so was reinschreibt:

Zitat:
de,de-DE,de-AT;q=0.8,fr;q=0.6,en-US;q=0.4


Das ist die Liste eurer "bevorzugten" Sprachen mit ihren Prioritäten, in den meisten Browsern könnt ihr die einfach einstellen. Fest steht: Die ersten 2 Zeichen dieses Wertes sind höchstwahrscheinlich die Muttersprache desjenigen, der gerade den Browser bedient. Und damit genau das, was wir brauchen!

So, wie kriegen wir das jetzt in unser COBOL-Programm hinein? Um das zu verstehen, müsst ihr Variablen verstehen. Variablen werden in COBOL traditionell (ähnlich wie z.B. unter Delphi) oberhalb des eigentlichen Programmcodes (PROCEDURE DIVISION) definiert, nämlich in der DATA DIVISION. Unterschieden werden hier der "local storage" (Variablen, die für jede Programminstanz definiert werden) und der "working storage" (jede Instanz greift auf dieselben Variablen zurück). Da so eine Website ja durchaus für mehrere Benutzer gleichzeitig funktionieren sollte, greifen wir hier auf den "local storage" zurück.

Der COBOL-Datentyp für Zeichenketten - denn damit wollen wir hier arbeiten - ist das PICTURE, kurz "PIC". Unterschieden wird im Wesentlichen zwischen alphabetischen Zeichenketten (A), alphanummerischen Zeichenketten (X), "nationalen" Zeichenketten mit erweitertem Zeichensatz (N), nummerischen Zeichenketten (9) und booleschen Werten (1). Da COBOL nicht nur die Datenlogik, sondern auch die Datenbank selbst enthalten kann, besteht auch die Möglichkeit, Zeichenketten zu gruppieren, etwa "Vorname und Nachname" als "Vollständiger Name" zusammenzufassen; aber dies würde hier den Rahmen sprengen.

Wir brauchen höchstens drei Variablen auf der ersten Ebene:

  1. Liste aller Sprachen: Eine Zeichenkette von, sagen wir, höchstens 75 Zeichen Länge, in der wir das Ergebnis von HTTP_ACCEPT_LANGUAGE zwischenspeichern können.
  2. Hauptsprache: Eine Zeichenkette von zwei Zeichen Länge, in der dann entweder "de" oder eben etwas völlig anderes steht.
  3. Ausgabetext: Der Ausgabetext (also "Hallo Welt!", "Hello World!" oder etwas völlig anderes). Warum wir diese Variable berücksichtigen sollten, werdet ihr noch sehen.


Über der "PROCEDURE DIVISION" ist nun also folgender Code einzufügen:

ausblenden Quelltext
1:
2:
3:
4:
5:
       DATA DIVISION.
       LOCAL-STORAGE SECTION.
       01  alle-sprachen  PIC X(75).
       01  hauptsprache   PIC A(2).
       01  ausgabetext    PIC X(20).


Der erste Teil des Programmcodes ist bis zum <h1> identisch, aber wir wollen ja nach dem <h1> einen Text einfügen, der von einer anderen Variable abhängt. Ändert also folgende Zeile

ausblenden Quelltext
1:
            DISPLAY "    <h1>Hallo Welt!</h1>"					


wie folgt:

ausblenden Quelltext
1:
           DISPLAY "    <h1>" WITH NO ADVANCING.					


"WITH NO ADVANCING" sorgt dafür, dass am Ende kein Zeilenumbruch eingefügt wird. Das ist technisch gesehen zwar egal, sieht hinterher im generierten Code aber schöner aus.

Jetzt können wir die Umgebungsvariable HTTP_ACCEPT_LANGUAGE, die wir von FastCGI "bekommen" haben, auslesen und in unsere Variable "alle-sprachen" schreiben:

ausblenden Quelltext
1:
2:
           ACCEPT alle-sprachen FROM ENVIRONMENT "HTTP_ACCEPT_LANGUAGE"
           MOVE alle-sprachen (1:2) TO hauptsprache.


Was passiert hier? Es wird per ACCEPT (unter C++ wäre das ungefähr cin) in die Variable "alle-sprachen" das Ergebnis der Umgebungsvariable "HTTP_ACCEPT_LANGUAGE" geschrieben. Deren erste zwei Zeichen (2 Zeichen ab Zeichen 1) werden anschließend in "hauptsprache" gespeichert.

Jetzt hat die Variable "hauptsprache" drei mögliche Werte: "Leer" beziehungsweise "nicht definiert" (FastCGI hat keine HTTP_ACCEPT_LANGUAGE gesendet), "de" oder etwas völlig anderes. Das ist genau das, was wir brauchen. Mit einem einfachen Vergleich dieser Variablen können wir jetzt den richtigen Text herausfinden:

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
           IF hauptsprache = SPACE OR hauptsprache = LOW-VALUE THEN
               MOVE "HACK0RZ!" TO ausgabetext
           ELSE
               IF hauptsprache IS EQUAL TO "de" THEN
                   MOVE "Hallo Welt!" TO ausgabetext
               ELSE
                   MOVE "Hello World!" TO ausgabetext
               END-IF
           END-IF.


Natürlich könnt ihr den Text auch einfach ändern. :)

Nun, warum "MOVE" und nicht gleich "DISPLAY"? Das hat kosmetische Gründe: COBOL füllt ein PICTURE immer bis zur maximalen Länge auf, im HTML-Code stünde also ungefähr so etwas:

ausblenden Quelltext
1:
<h1>Hallo Welt!         </h1>					


Das ist nun nicht schlimm, aber wir sind ja bekanntlich allesamt Perfektionisten hier. Zum Glück beherrscht GnuCOBOL wie auch andere COBOL-Compiler die intrinsische Funktion trim(), die wir einfach nutzen können, um Leerzeichen am Ende "abzuschneiden". Das sieht zwar im COBOL-Umfeld etwas merkwürdig aus (es werden Klammern benutzt!), aber es spart uns eine Menge Bastelei. (Natürlich würde das auch mit etwas Bastelei hinzukriegen sein, aber warum das Rad neu erfinden?)

ausblenden Quelltext
1:
2:
3:
           DISPLAY
               FUNCTION TRIM (ausgabetext TRAILING) WITH NO ADVANCING
           END-DISPLAY.


Hinter dem <h1> steht nun also eine zurechtgekürzte Version eures Ausgabetextes - wiederum ohne weiteren Zeilenumbruch. Alles Weitere bleibt wie bisher: </h1> schließen, Rest auch schließen. Hier einmal die vollständige Version mit ein paar Kommentaren:

ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
       IDENTIFICATION DIVISION.
       PROGRAM-ID. Hallo-Entwickler-Ecke.

       DATA DIVISION.
       LOCAL-STORAGE SECTION.
       01  alle-sprachen  PIC X(75).
       01  hauptsprache   PIC A(2).
       01  ausgabetext    PIC X(20).

       PROCEDURE DIVISION.
      *    HTML-Text oben:
           DISPLAY "<!doctype html>"
           DISPLAY "<html>"
           DISPLAY "  <head>"
           DISPLAY "    <title>HalloWelt.coffee</title>"
           DISPLAY "  </head>"
           DISPLAY "  <body>"
           DISPLAY "    <h1>" WITH NO ADVANCING.

      *    Hauptsprache des Browsers auslesen.
      *    Meist die ersten 2 Zeichen aus HTTP_ACCEPT_LANGUAGE.
           ACCEPT alle-sprachen FROM ENVIRONMENT "HTTP_ACCEPT_LANGUAGE"
           MOVE alle-sprachen (1:2) TO hauptsprache.

           IF hauptsprache = SPACE OR hauptsprache = LOW-VALUE THEN
      *        HTTP_ACCEPT_LANGUAGE ist nicht gesetzt.
               MOVE "HACK0RZ!" TO ausgabetext
           ELSE
               IF hauptsprache IS EQUAL TO "de" THEN
      *            Hauptsprache ist deutsch.
                   MOVE "Hallo Welt!" TO ausgabetext
               ELSE
      *            Hauptsprache ist irgendwas anderes.
                   MOVE "Hello World!" TO ausgabetext
               END-IF
           END-IF.

      *    "DISPLAY ausgabetext" würde hier Folgendes tun:
      *      - An "ausgabetext" so viele Leerzeichen hängen, bis die
      *        Maximallänge (20 Zeichen) voll ist.
      *      - Einen Zeilenumbruch anhängen.
      *    Da wir das nicht wollen, müssen wir hier stattdessen die
      *    intrinsische Funktion trim() verwenden, die die meisten
      *    aktuellen COBOL-Implementierungen unterstützen, und
      *    wiederum "WITH NO ADVANCING" anhängen, damit am Ende der
      *    gesamte <h1>-Tag in einer Zeile steht.

           DISPLAY
               FUNCTION TRIM (ausgabetext TRAILING) WITH NO ADVANCING
           END-DISPLAY.

      *    HTML-Text unten:
      *    (Wir sind immer noch in der <h1>-Zeile!)
           DISPLAY "</h1>"
           DISPLAY "  </body>"
           DISPLAY "</html>"

           STOP RUN.


Wenn ihr das Programm jetzt noch einmal kompiliert und lokal ausführt, ist das Ergebnis natürlich merkwürdig:

H4CK0RZ

Klar: Ohne Webserver kein HTTP_ACCEPT_LANGUAGE. Versuchen wir es doch mal hinter unserem FastCGI:

HalloWelt

Tatah!

Alles Weitere überlasse ich euch. Ihr wollt eine "richtige" Website mit Routing und allem? Das ist gar kein Problem.

Diese Anleitung ist übrigens das Ergebnis von einem Tag mit COBOL. Wenn hier jemand mit mehr Erfahrung reinguckt: Entschuldigung! Ergänzungen sind gern gesehen. Sicherlich werde ich auch noch besser.
Einloggen, um Attachments anzusehen!


Zuletzt bearbeitet von hydemarie am Mo 15.08.16 15:00, insgesamt 1-mal bearbeitet
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 407
Erhaltene Danke: 50



BeitragVerfasst: Fr 29.07.16 21:47 
FastCGI-Fallstrick:

Ist mir hinterher erst aufgefallen, vielleicht aber auch in anderem Kontext (etwa für FastCGI mit Delphi) interessant: Unter Umständen sucht FastCGI am Anfang der Ausgabe eines Programms nach den HTTP-Headern. Es funktioniert auch ohne, dann wird aber möglicherweise die erste Ausgabezeile nicht geparst.

Richtig wäre also (gemäß RFC 2616):

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
Status: 200 OK\r\n
Content-Type: text/html\r\n
(eventuell weitere HTTP-Header, etwa für Caching)\r\n
\r\n
<!doctype html>
<html>
...


Solltet ihr dieses Problem auch haben: Hier ist eine (vielleicht nicht die kürzeste) der möglichen Lösungen.

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
       PROCEDURE DIVISION.
      *    HTTP-Header:
           DISPLAY "Status: 200 OK" WITH NO ADVANCING.
           DISPLAY X'0D' WITH NO ADVANCING.
           DISPLAY X'0A' WITH NO ADVANCING.
           DISPLAY "Content-Type: text/html" WITH NO ADVANCING.
           DISPLAY X'0D' WITH NO ADVANCING.
           DISPLAY X'0A' WITH NO ADVANCING.
           DISPLAY X'0D' WITH NO ADVANCING.
           DISPLAY X'0A' WITH NO ADVANCING.

      *    HTML-Text oben:
      *    ...


Das Literal '0D' steht hierbei für den "Carriage Return", '0A' für "Line Feed".
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 407
Erhaltene Danke: 50



BeitragVerfasst: Mo 31.10.16 18:45