Skip to content

Externes JavaScript in Pentaho Kettle laden

Kettle bietet die Möglichkeit in einer Transformation auch JavaScript aus externen Files zu laden.
Dazu braucht man lediglich einen Step "Modified Java Script Value" in dem man die Funktion "LoadScriptFile()" aufruft.
Damit die Datei nicht bei jeder Zeile erneut geladen wird, sollte optimalerweise ein neuer Tab erzeugt werden und via "Set Start Script" so markiert werden, dass er nur beim Laden der Transformation aufgerufen wird.

Das ist soweit nichts Neues, und durch ein bisschen googlen relativ schnell heraus zu finden.
Was hingegen nicht so einfach zu ergründen ist: wie kann man das benutzen, ohne den kompletten Pfad hardcoded angeben zu müssen?

Denn die ganzen Felder kann man nicht benutzen - die stehen ja nur bei der zeilenweisen Verarbeitung zur Verfügung. Aber da soll die Lib ja nicht geladen werden.
Das Beispiel das Kettle bereit stellt ist da auch alles andere als hilfreich:

// Load's a JavaScript File into your actual running Context.
// This function should called from your defined StartScript
// otherwise your JavaScript-File is loaded on each processing
// Row.
//
// Usage:
// LoadScriptFile(var);
//
// 2006-11-15
//
var xPfad = "Your Filename, with Path";
LoadScriptFile(xPfad);

Wie üblich bleibt mal wieder nichts anderes übrig als Probieren.
Das Rumspielen mit Parametern und Argumenten der Transformation war nicht von Erfolg gekrönt, aber da das JavaScript-File in meinem Fall im selben Verzeichnis wie die Transformation liegt, hat sich folgendes bewährt:
var workingDirectory = getVariable("Internal.Transformation.Filename.Directory", "/var/APP/ETL/");
var libraryPath = workingDirectory + "/etl-lib.js";
LoadScriptFile(libraryPath);
Ich habe irgendwo gesehen das jemand "getEnvironmentVar()" statt "getVariable()" benutzt hat - das hat jedoch nicht funktioniert.
Zumindest hat "getVariable()" den Vorteil, dass als zweiter Parameter ein Fallback spezifiziert werden kann, falls die Variable nicht gefunden wird.

Mass (D)Effect 3...

ACHTUNG SPOILER!

Vorweg: Mass Effect 3 ist ein geiles Spiel. Es verbindet IMO das Beste aus den ersten beiden Teilen, welche ich mehrfach durch gespielt habe (was bei mir wahrlich nicht häufig vorkommt).

Und ich hoffe, dass die Bedeutung dieser Worte erkennbar ist, wenn ich sage: BioWare hat es geschafft, in den letzten 5 - 10 Minuten das alles zu ruinieren und mit anlauf in die Tonne zu treten! Ich hatte mich eigentlich schon darauf gefreut, Teil 3 mit all meinen Chars durch zu spielen, die ich in den ersten beiden Teilen gespielt hatte - vor allem weil ja der Einfluss der eigenen Entscheidungen so hoch angepriesen wurde. Aber nach DEM Ende, tendiert der Wiederspielwert aller drei Teile für mich gegen null.

Ich komme mir einfach verarscht vor! Oder wie der Nerd sagen würde "What were they thinking!?"

Im Prinzip hätten sie einfach Shepard neben Anderson sterben lassen sollen, und den Tiegel die Reaper vernichten lassen. Das wäre zwar ein relativ einfallsloses Ende gewesen, aber damit hätte man ja leben können.
Nein, stattdessen baut man irgendein Deus ex machina ein, der die ganzen bisherigen Handlungen, Entscheidungen und Motivationen mal eben ad absurdum führt. Und als wäre das nicht schon schlimm genug, wird mein Shepard, der bislang voll der "eisenharte, durchgeknallte Cracknigger" war, aufeinmal als totale Weichbirne und Sitzpisser hingestellt, der alles einfach so hinnimmt.

Shepard, der Held des Galaxie - der ganze Spezies ausgerottet hat, der über Leichen gegangen ist um die Reaper zu bekämpfen und sich an ihnen zu rächen - muss sich von einem Kind sagen lassen, dass die Reaper nur dessen Spielzeug gewesen sind. Das dieser, aufgrund einer unbewiesenen Theorie, regelmäßig alle entwickelten Bioorganismen massakriert - aber das wäre ja schon in Ordnung. Sein "Spielzeug" bräuchte er jetzt nicht mehr, also kann Shepard es ruhig kaputt machen (aber nur mit weitreichenden Kollateralschäden) und er würde jetzt nach einen neuen "Endlösung" suchen. W.T.F.????

Bin ich denn der einzige, der diesem vorlauten Bängel dafür aufs Maul hauen möchte dass es "shepard"?
Wo ist die vielgelobte Entscheidungsfreiheit, wenn jede Dialog-option nur ein Alias für "Resignation" ist? Da würde ich doch lieber das ganze Universum vor die Hunde gehen lassen, als diese Pseudogottheit freiwillig damit durchkommen zu lassen!

Das ich mich dann auch noch zwischen drei "Enden" "entscheiden" muss, die mir quasi nur die "Wahl" lassen, in welcher Farbe ich die Galaxie gerne "explodieren" sehen würde, setzt dem ganzen dann noch die Krone auf!
Da hätte man auch einfach so einen Dialog hinpacken können: http://cdn2.gamefront.com/wp-content/uploads/2012/03/mass-effect-your-choices-matter1.jpg

Dazu fehlen mir echt die Worte. Aber zum Glück gibt es Bilder, die ausdrücken, was ich denke:



Im offiziellen Forum gibt es einen schönen Beitrag: was wäre, wenn Herr der Ringe so geendet hätte, wie Mass Effect 3

Auf die Logikfehler und Plotlöcher, die dieses aufgesetzt wirkende Ende hat, will ich gar nicht weiter eingehen. Das haben andere schon getan: http://social.bioware.com/forum/1/topic/355/index/9815872/1
Auch das nichts über das Schicksal der restlichen Crewmitglieder und Rassen erzählt wird ist schlicht Dreist ...oder Faulheit ...oder beides.

Vielleicht hätten die mal bei Hideo Kojima fragen sollen, wie ein gescheites Ende aussehen muss!
Ich empfinde dieses Ende (ab dem Punkt, wo der leuchtende Aufzug kommt) jedenfalls als einen Tritt ins Gesicht jedes Fans!


...Zeit für ein wenig passende Musik: http://www.youtube.com/watch?v=W1VTeXe3FnA

TriGalaxy (open Beta) wartet auf euch

Mir ist aufgefallen, dass es im letzten Jahr wenig Posts von mir gab. Das lag daran, dass ich äußerst beschäftigt war mit der Entwicklung von:


Das neue, kostenlose Open-World Multiplayer-Science-Fiction-Rollenspiel für Android!



Unnötig zu erwähnen, dass auch dieses neueste Spiel nicht allein von mir ist. Maßgeblich für den Client verantwortlich ist Uwe Post.

Im Moment ist das Spiel noch in der "open beta"-Phase.
Die epische Storyline wird, zusammen mit einigen anderen Funktionen (zB multiplayer-quests), erst in Version 1.0 starten.
Alles andere funktioniert aber bereits!

Es gilt eine komplette Galaxie zu erkunden!


Fremde Planeten und Raumstationen zu besuchen...


...Handel zu betreiben ...


...und Quests für, mehr oder weniger gut aussehende, Aliens zu erledigen.

JSON vs. xstream

Das diverse Tools oder Bibliotheken sich gerne nicht so gut verstehen ist nichts neues. Aber manchmal fällt schwer, zu sagen, welches davon jetzt Mist baut.
Aber von vorne: Für ein neues Projekt werden Metadaten in XML angeliefert, die in einer Datenbank gespeichert werden sollen. Nun ist das auseinander Pflücken von XML-Files ziemlich mühselig. Ganz zu schweigen davon, dass die Daten in einem anderen Schritt wieder zusammen gesetzt werden müssen.
Es bot sich also eine elegante Lösung an: die XML-Files mit xstream in ein POJO de-serialisieren, validieren und ebenfalls mit xstream in eine JSON-Repräsentation umwandeln - und ab damit in die CouchDB!
Das funktioniert auch ganz wunderbar. Auch der umgekehrte Weg - den JSON-String wieder in ein POJO de-serialisieren - klappt ohne Probleme.

Die Probleme fangen erst an, wenn man mit einer entsprechenden Library an die CouchDB geht, und ein org.json.JSONObject zurück bekommt. Denn in dem daraus de-serialisiertem POJO, war auf einmal in einer darin enthaltenen Map systematisch key und value vertauscht. Die CouchDB war als Problemquelle sehr schnell ausgeschlossen, denn mit dem von ihr zurück gelieferten JSON-String trat das Problem nicht auf. Der String jedoch, der von org.json.JSONObject.toString() geliefert wurde, unterschied sich merklich von dem, was CouchDB lieferte.

Nun ist es so, dass xstream eine Map, wie zB "Map" folgendermaßen als JSON serialisiert:
"mapObjectName": {
   "entry": [
      { 
         "string": "theKey",
         "CustomObject": { "someField": "fieldValue" }
      }
   ]
}


Von org.json.JSONObject.toString() wird aber folgendes ausgegeben:
"mapObjectName": {
   "entry": [
      { 
         "CustomObject": { "someField": "fieldValue" }
         "string": "theKey",
      }
   ]
}

Dafür gibt es zwei Gründe: zum einen schreibt JSON nicht vor, das die Daten in der richtigen Reihenfolge sein müssen. Zum anderen, versucht es das JSONObject auch gar nicht erst - das Problem tritt nämlich nicht erst beim ausgeben der Daten auf, sondern liegt bereits darin begründet, wie diese intern gespeichert werden.

Und das ist genau die Stelle, an der es schwer fällt einen schuldigen zu finden. Ist xstream schuld (bzw. der verwendete Driver für JSON), weil über die JSON-spec hinaus darauf vertraut, dass die Daten immer in der richtigen Reihenfolge sind? Könnte xstream ein Map überhaupt anders serialisieren, um das zu umgehen? Oder macht das "Feature" von JSON, Daten eben explizit unsortiert zu behandeln, mehr Probleme als es löst?

Da ich weder die Zeit noch die Lust hatte, diesen Fragen auf den Grund zu gehen, und es das Problem ohnehin nicht gelöst hätte, hab ich einfach bei der Implementierung angesetzt. Da die Java-library freie Software ist, war das die einfachste Lösung.
Es musste nur in zwei Konstuktoren von org.json.JSONOBject folgende Zeile geändert werden:
Von
this.map = new HashMap();

Zu
this.map = new LinkedHashMap();


...das "import java.util.LinkedHashMap;" fügt eine gute IDE automatisch ein ;-)

Urlaub in der Unterwelt


Gestresst von all den Katastrophen in der Welt der Lebenden?
Eröffne dein eigenes Hotel in Santa Daemonica, dem Lieblings-Urlaubsort der Unterwelt, dem Mallorca der Monster, Usedom der Untoten, Domrep der Dämonen!

Ein Android-handy ist dafür allerdings schon nötig. Und auch eine Internet-flat für selbiges sollte vorhanden sein, denn es handelt sich um einen "Multiplayer Hotel Manager From Hell".
Und um etwaigen Fragen zuvor zu kommen: nein, das Spiel nicht nicht allein von mir - aber ich hab einen nicht unerheblichen Teil dazu beigetragen.
Es ist vielleicht nicht mega umfangreich, aber ein netter Zeitvertreib für Zwischendurch.

Also auf nach Santa Daemonica!