wiki:skripte-als-webservice

Version 11 (modified by Wolfgang Schmidle, 13 years ago) (diff)

--

XML-Workflow-Skripte auf Webservice vorbereiten

Ziel: Die Skripte sollen als funktionieren als

  • Textfilter
  • auf der Commandline: mindestens einzeln, möglichst auch in einer Unix-pipe
  • mit einem in Java geschriebenen Wrapper als Webservice

Die Skript-Parameter sollen direkt auf den Webservice abbildbar sein. Parameter-Typen:

  • ja/nein, entspricht einer Checkbox (dieser Parameter-Typ ist selten)
  • Wert aus einer festen Liste, die sich selten ändert: Beispiel --DESpecs=1.1.2
  • Wert aus einer Liste von Dateinamen: Beispiel --wordlist=datei1,datei2

BBEdit 10.0

In BBEdit 10.0 gibt es eine Änderung für Textfilter: der input ist nicht mehr eine temporäre Datei, sondern kommt via STDIN. Konkret hat das wenig Auswirkungen für die Programmierung der Skripte.

Und es gab ja in BBEdit 9.6.3 das Problem, dass UTF-8-output nach STDERR von BBEdit als MacRoman interpretiert wurde und deshalb unleserlich wurde. Das Problem ist in 10.0 gelöst. Der Workaround, das Ergebnis in eine Datei zu schicken, ist daher nicht mehr nötig, deshalb werde ich ihn nicht mehr verwenden. Übergangsweise gibt es das Problem, dass man BBEdit braucht, um den XML-Workflow mit Textfiltern zu verwenden, denn Textwrangler wird den Fehler noch haben. (Alternative ist die Commandline.)

Bei BBEdit 10.0 sind Textfilter jetzt in ~/Library/Application Support/BBEdit/Text Filters. Das alte Textfilter-Verzeichnis von BBEdit 9 wird automatisch an die neue Stelle kopiert.

BBedit 10.0 ist immer noch kein reines Cocoa, aber sie haben sich viel Mühe gegeben, dass man das praktisch nicht mehr merkt.

Getopt

Ich verwende jetzt das Perl-Modul Getopt::Long (und Pod::Usage). Dadurch ergibt sich recht natürlich folgendes Format, das auch für Textfilter funktioniert:

perl script.pl [options] [file.xml]

bzw. [file.txt] für die ersten Skripte im XML-Workflow. Wahrscheinlich ist dieses Format auch pipe-tauglich, aber das habe ich bisher nur sehr oberflächlich getestet.

Bei den Optionen werden dadurch automatisch mehrere Formate akzeptiert, zum Beispiel

  • --wordlist=datei1 --wordlist=datei2
  • --wordlist=datei1,datei2
  • -wordlist=datei1,datei2
  • --wordl datei1,datei2
  • --w datei1,datei2

Beispiel (mit Zeilenumbrüchen, um es übersichtlicher zu machen):

perl "/Users/ich/Library/Application Support/BBEdit/Text Filters/Filter_5_01_insert_reg.pl"
--dir=/Pfad/zum/Repository/trunk/trunk/texts/aux
--wordlist=reg-wordlist-lat.txt --wordlist=reg-wordlist-lat-hentisberi.txt --wordlist=reg-wordlist-lat-alvarus.txt
--simple=reg-simple-lat.txt,reg-simple-lat-alvarus.txt
Alvarus-Zeile.txt

Die Reihenfolge der Parameter ist egal. Man kann also auch nicht --dir angeben, dann ein paar Dateien, dann ein neues --dir und weitere Dateien. Innerhalb eines Parameters wie wordlist ist die Reihenfolge der Dateien wichtig.

Beim Webservice wird es eine Liste von Standard-Hilfsdateien geben. Zusätzlich muss der Benutzer die Möglichkeit haben, weitere Hilfsdateien in das System hochzuladen oder mit beliebigen URLs auch Dateien einzubinden, die nicht im System sind.

Parameter --dir besser mit / am Ende? Bisher: wenn es $dir gibt, hänge $dir/ vorne an den Dateinamen dran. Mit / könnte ich $dir einfach auf Verdacht ohne Änderungen dranhängen. Oder: Wenn $dir ungleich "" ist, aber nicht mit / endet, hänge / dran? Was ist üblich?

Input und output sind immer UTF-8. Der input wird im Skript wie bisher mit "while(<>)" ausgelesen. Das funktioniert mit STDIN und Dateinamen. Ich verwende jetzt doch nicht "input=", weil alles, was nicht eine Option ist, als Dateiname interpretiert wird. (Mehr als ein Dateiname ist eigentlich bei keinem Skript sinnvoll, aber es ist nicht verboten). Ich verwende auch kein "output=", weil die defaults STDOUT für Text und STDERR für Fehlermeldungen eigentlich gut genug sind. Umleitungen kann man dann auf der Commandline mit Unix-Mitteln machen.

Error conditions: Wenn ein Skript nach STDERR schreibt, wird die Verarbeitung des Skripts oder der pipe abgebrochen. Falls nötig, kann ich in die Fehlermeldungen auch noch Fehler-Codes hineinschreiben.

Wo nach Optionen gesucht wird

Das Skript versucht, Optionen an folgenden Stellen zu finden:

  1. Commandline

(nach Schritt 1 wird der Text eingelesen)

  1. <metadata> <parameters>: wenn nur ein Textausschnitt übergeben wird, muss das aber nicht beim Textfilter ankommen
  1. falls das Skript als Textfilter läuft, eine zusätzliche Datei in Filter_parameters/$name.txt. Schritt 3 ist nötig, weil man einem Textfilter keine Parameter übergeben kann. Beispiel: Text Filters/Filter_5_01_insert_reg.pl sucht in Text Filters/Filter_parameters/Filter_5_01_insert_reg.txt.

Ich verwende jetzt, im Gegensatz zu vorher, einen relativen Pfad, weil es sonst nicht ohne Änderungen funktioniert, nachdem man das Verzeichnis trunk/schema/scripts/workflow aus dem Repository in das Textfilter-Verzeichnis kopiert hat. (Man sollte mal eine Liste machen, wohin die Teile im Repository kopiert werden müssen.)

Die Dateien "reg-wordlist-lat-alvarus.txt" etc. würde man dann wohl nicht mehr in trunk/texts/aux, sondern in Filter_parameters haben.

Unterschiedliche Parameter-Formate

Der Parameter "wordlist" heißt "reg.wordlist" in <parameters>, etc. Wenn man das nicht will, muss man auf namespaces verzichten und hoffen, dass die Skript-Parameter-Namen sich nicht beißen. Und es gibt (wenige) Parameter, die zu mehr als einem Skript gehören. Unterelemente von <parameters> finde ich übertrieben. Oder <parameters skript="Filter_insert-reg">, etc.? Dann kann man die namespaces weglassen.

Und alle Parameter in einem <parameters> ohne @skript würden sich auf alle Skripte beziehen. Außerdem könnte man den Inhalt von <parameters> ebenfalls mit Getopt::Long einlesen, um ein einheitliches Parameter-Format zu haben. Ein einzelnes Skript würde dann den Inhalt von <parameters> und <parameters script="name"> übergeben. Oder vielleicht doch kein <parameters> ohne @script, denn in "Filter_parameters/" gibt es auch keine Datei, die für alle Skripte gilt. Dann muss man allgemeine Parameter eben für alle relevanten Skripte wiederholen. Kein großer Aufwand.

Vorschlag also: <parameters> kann mehrfach verwendet werden, mit obligatorischem @script. Darin das gleiche Optionen-Format wie auf der Commandline, einlesen mit GetOptionsFromString.

Commandline versus Textfilter

Ob das Skript als Textfilter als Textfilter aufgerufen wird, erkennt es daran, dass die runtime environment variable $ENV{'BB_DOC_NAME'} existiert. Das ist BBEdit-spezifisch. Für Textwrangler oder andere Editoren wäre es anders.

Wenn man bereits Parameter in <parameters> angegeben hat, werden deshalb trotzdem auch noch die Parameter aus der zusätzlichen Datei eingelesen. Der Aufruf auf der Commandline ohne Parameter

perl script.pl file.xml

wäre dann nicht identisch mit dem Aufruf als Textfilter.

Alternative wäre, zum Beispiel zu prüfen, ob in Schritt 1 und 2 irgendwelche Optionen gesetzt wurden. Dann würden beim Aufruf auf der Commandline ohne jeden Parameter wie bei der Verwendung als Textfilter die Standard-Parameter aus der zusätzlichen Datei verwendet werden, aber sobald man irgendeinen Parameter setzt, wird die zusätzlich Datei nicht mehr eingelesen. Welche Variante führt zu möglichst wenig Verwirrung?

Nachbilden der Möglichkeit, den Textfilter mit einem Ausschnitt einer Datei aufzurufen: Optionen wie "startzeile" und "endzeile" oder gar die genauen Spalten haben wir ja erstmal verschoben. Vielleicht könnte man das auch sowieso besser im Webservice-Wrapper machen. Dann muss es nicht in jedem Skript einzeln stehen. (BBEDIT übergibt übrigens runtime environment variables für Startzeile und -spalte etc. an den Textfilter. Diese Information wird jedoch zurzeit nicht verwendet.)