IOException.de

Icon

Ausgewählter Nerdkram von Informatikstudenten der Uni Ulm

Building node-webkit programs with Grunt

The days before Christmas were busy as usual: It’s not just that everyone is hunting gifts for family and friends. There’s also the annual German Youth Team Championship of Chess beginning on 25th December. Together with some friends I’m trying to broadcast this big event with more than 500 young participants to their families left at home, waiting for results.

In the last years, we used a simple mechanism to provide near to live results: The arbiters got a special email adress where they could send their tournament files to. On the server side there was a small node.js mail server (as mentioned in “Einfacher SMTP-Server zur Aufgabenverarbeitung” [german]) that took the proprietary file format, converted it and imported the results of already finished games. Although being a huge progress to the past where the results were imported once all games has been finished, this approach needed an arbiter constantly sending mails around.

Therefore I wanted to try another way: A program that keeps an eye on the tournament file and uploads it once it was changed and automatically triggers the import of new game results. Having just some days for its development it was necessary to stay with the same technology stack we used before for the mail server and tournament file converter: node.js.

As the tournament arbiters aren’t all familiar with command line tools, a graphical user interface was necessary. Strongloop published a blog post about node-webkit, which allows writing native apps in HTML and Javascript, some days before. This blog post is a good entry to the topic. Nettuts+ wrote a nice introduction recently too. Different from their approach I used the plugin for Grunt grunt-node-webkit-builder, which takes on the whole building process. Here’s my project’s setup:

/
├── dist
├── Gruntfile.js
├── package.json
└── src
    ├── index.html
    ├── package.json
    ├── js
    │   └── index.js
    └── css
        └── style.css

By using the grunt-node-webkit-builder it is necessary to keep the source of the building tool (all in the root directory) separate from the source code of the node-webkit program. Otherwise it may happen that the building tools (Grunt, you know?) get bundled in the node-webkit program as well which leads to high file sizes and slow execution times.

So it’s clear we specify in /package.json only the dependencies that are necessary for the building process:

{
  "name": "do-my-build",
  "version": "0.0.1",
  "description": "Using Grunt to build my little program",
  "author": "Falco Nogatz <fnogatz@gmail.com>",
  "private": true,
  "dependencies": {
    "grunt": "~0.4.2",
    "grunt-node-webkit-builder": "~0.1.14"
  }
}

We also have to create the Gruntfile.js:

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('src/package.json'),
    nodewebkit: {
      options: {
        build_dir: './dist',
        // specifiy what to build
        mac: false,
        win: true,
        linux32: false,
        linux64: true
      },
      src: './src/**/*'
    },
  });

  grunt.loadNpmTasks('grunt-node-webkit-builder');

  grunt.registerTask('default', ['nodewebkit']);
};

The real node-webkit program can be written now in the /src directory. As also mentioned in the tutorials linked above, the /src/package.json should be filled with some node-webkit related fields:

{
  "name": "my-program",
  ...
  "main": "index.html",
  "window": {
    "toolbar": false,
    "width": 800,
    "height": 600
  }
}

To build the node-webkit program for the architectures specified in /package.json you simply have to call the command:

grunt

This downloads the up-to-date binaries necessary for the specified architectures and builds an executable program. The result for Windows is simply a .exe file, for Linux an executable file. It contains all needed to run the program, so the user neither has to install node.js nor Chrome. The builds are located in /dist/releases.

By using this setup it was possible to automate the building process and develop the application within some days. The node-webkit runtime extends some native browser properties, for example it is possible to get the full path of a file selected by an <input type="file">. With that it was possible to create a graphical user interface to select tournament files and watch for their changes, which would trigger the update process.

Fünf Tage unterwegs: Rückblick zu MongoDBMunich und NodeDublin

It’s conference time! – Oder “was” vielmehr. Denn beide oben genannten Konferenzen liegen bereits mehrere Wochen zurück, mein defektes Notebook machte aber leider einen schnelleren Bericht unmöglich. Die Erinnerungen an die beiden Konferenzen sind aber nicht erblasst, sodass ich sie noch gerne teilen möchte.

Mitte Oktober. Für viele meiner Kommilitonen bedeutete dies vor allem eines: Beginn des neuen Wintersemesters. Und während die meisten dem wohl mit Blick auf die nun zu Ende gegangenen Semesterferien vorlesungsfreie Zeit etwas wehleidig entgegensahen, bedeutete es für mich nur eines: Eine ziemlich ereignisreiche Woche liegt vor mir. Bereits mehrere Monate zuvor hatte ich meine Teilnahme an der MongoDB Konferenz in München festgemacht. Recht kurzentschlossen entwickelte sich dann noch der Plan, von München direkt weiter nach Dublin zur Node.js-Konferenz NodeDublin zu fliegen, was dank des netten Angebots von Organisator Cian Ó Maidín, den Ticketpreis etwas studentenfreundlicher zu gestalten, und günstiger Flugpreise sogar realistisch schien. So begann für mich also das Semester ein wenig anders: Am Dienstag, den 16. Oktober ging es mit dem Zug nach München zur MongoDB Konferenz, von dort am nächsten Tag mit Aer Lingus in die irische Hauptstadt, wo Donnerstag und Freitag NodeDublin anstand. Samstag in aller Früh ging dann auch schon der Rückflug nach Frankfurt, da ich tagsüber noch eine (nein, diesmal nicht Technik-) Tagung der Deutschen Schachjugend in Heidelberg hatte. Klingt doch nach einem tollen Plan, oder?

16. Oktober: MongoDB Munich

Leider nicht mehr online verfügbar: Der Sessionplan von MongoDB Munich

Leider ist der Sessionplan von MongoDB Munich nicht mehr online, ich habe ihn aber mal eingescannt. Die Konferenz fand im Hilton Hotel in der Nähe vom Tucherpark statt. Von außen eine sehr gute Location, von innen war ich aber etwas enttäuscht. Keiner der sogenannten Ballsäle hatte auch nur ein einziges Fenster, sodass einem im Laufe des Tages irgendwann die Decke auf den Kopf fiel – auch wenn die Sessions von Coffee Breaks und Mittagsbuffets im lichtdurchfluteten Hotelfoyer unterbrochen waren.
Die Teilnehmer kamen aus allen Bereichen: Entwickler und Systemadministratoren, MongoDB-Anfänger und -Kenner. Als etwas erfahrenerer MongoDB-Nutzer war ich anfangs etwas von den Low-Level-Vorträgen enttäuscht. Zugegeben: Die Session “Building Your First App with MongoDB” hielt genau was sie versprach und war eben nur eine oberflächliche Einführung. Auch von Marc Schwerings “Schema Design” hatte ich mir mehr neue Erkenntnisse erhofft. Die gab es dafür am Abend im persönlichen Gespräch mit ihm, wo er ein bisschen aus dem Leben eines jungen Solution Architect bei 10gen, dem Unternehmen hinter MongoDB, plauderte.

Mit zunehmender Dauer wurde es dann aber doch inhaltlich spannender. Bislang benötigte ich die Replication-Features von MongoDB noch nie, dafür sind die Daten, mit denen ich arbeite (noch!) zu gering. Und selbst wenn ich mir unsicher bin, ob Replication damit für mich in absehbarer Zeit überhaupt ein Thema wird, waren schon allein die theoretischen Probleme, die es im Falle eines Mehr-Rechner-Systems zu lösen gibt, sehr interessant. Und ein Punkt, wo ich wieder merkte, dass das Studium doch etwas bringt, hörte ich in dieser Session doch viele Begriffe aus den uulm Vorlesungen Algorithmen und Datenstrukturen, Rechnernetze, uvm.
Während ich bis dahin im BallSaal B war, der meist theoretische Sessions beheimatete, wechselte ich vor dem Mittag zu “Pizza Quattro Shardoni with comSysto” in den BallSall A, wo die praktische Anwendung des NoSQL-Vertreters im Vordergrund stand. Für die Motivation, warum man das ganze eigentlich macht, war das sicher nicht verkehrt :)

Vor Abflug in München zum letzten Mal Sonne tanken

Nach dem Mittagessen und dem zufälligen Treffen der Ulmer Kollegen von Transporeon ging es mit “Indexing und Query Optimization” weiter, gefolgt von “Map/Confused”. Ich hatte im Sommersemester meine Seminararbeit unter dem Titel “Google’s MapReduce Programming Paradigm as Basis for NoSQL Database Views” verfasst und war gespannt, wie MongoDB diesen Ansatz umsetzt, wo Abfragemöglichkeiten wie das Aggregation Framework in der Community ja viel verbreiteter sind. Der Vortrag war allerdings recht enttäuschend: Kaum Fachwissen und als Beispiel, warum MapReduce schlecht sei, wurde eine Abfrage gemessen, die man schlichtweg nie mit M/R (und auch manch anderem alternativen Vorschlag) lösen würde.
Den Abschluss bildeten für mich das wirklich spannende und unterhaltsame “Scaling Tips & Tricks” und anschließende “Build your own Foursquare with MongoDB’s Spatial Features & the Cloud”. Nach über acht Stunden im Hilton war die Konferenz dann gegen 17.15 Uhr in den gemütlicheren Teil über und ich konnte mich bei kostenfreiem Bier mit einigen netten 10gen Mitarbeitern unterhalten.

18.-19. Oktober: NodeDublin

Der nächste Tag begann hektisch: Ich hatte bei einer Freundin in München übernachtet und reichlich Zeit eingeplant, um vom Arabellapark mit U- und S-Bahn zum Flughafen der bayerischen Landeshauptstadt zu kommen. Doch an diesem Mittwoch kam irgendwie alles zusammen: Die erste S-Bahn zum Airport fiel aus und die nächste hatte 20 Minuten Verspätung – und fuhr “aus betrieblichen Gründen” nicht mal bis zum Flughafen, was man den Fahrgästen dann in irgendeinem Nest vorher (man konnte schon die Flieger ganz tief landen sehen!) sagte und sie rauswarf. So kam ich 75 Minuten später als geplant am Flughafen an, musste mich an der Schlange an Checkin und Sicherheitskontrolle vorbeimogeln und kam gerade rechtzeitig zum Boarding. Dafür sollte es dann aber auch für die nächsten Tage erstmal mit dem Stress vorbei sein.

In Dublin angekommen die erste positive Überraschung. Der Linienbus kostete nur ein bisschen über zwei Euro, hatte aber freies WLAN on board. Davon kann sich München mal eine Scheibe abschneiden, das mir für die Tortur wenige Stunden zuvor mal eben einen Zehner abgeknöpft hatte. Dank des Tipps von Timo war ich sehr günstig und zentral in Dublin untergebracht: Bei 18€ pro Nacht bei einem Fünfbettzimmer für einen alleine in einem ordentlichen Hostel kann man nicht meckern! Dank der Stunde Zeitverschiebung hatte ich noch den ganzen Tag Zeit, Dublin zu Fuß zu erkunden und bei für Irland gute Wetterverhältnisse einen ausgedehnten Spaziergang zum Konferenzort von NodeDublin zu nutzen: Stattfinden sollte das Event, zu dem sich zahlreiche node.js-Größen aus der ganzen Welt angekündigt hatten, die nächsten zwei Tage in einer riesigen Guinness-Brauerei im Herzen der Stadt.

O’Connell Street in Dublins Zentrum – in der Parallelstraße war mein Hostel

Für irische Verhältnisse gutes Wetter

Auch architektonisch macht Dublin so einiges her

Ort des Geschehens: das Guinness Store House in Dublin

Leckeres Frühstücksbuffet (NK)

Guinness Store House (NK)

Die Konferenz begann am Donnerstagmorgen dann – natürlich – mit der Registrierung und anschließendem, ausgesprochen leckerem Frühstücksbuffet. Das Guinness Store House, in dem die Konferenz eine Etage für sich einnahm und das sonst durchgehend von Besuchern durchströmt war, bot ein exzellentes Ambiente: Moderne Konstruktion, lichtdurchflutet, im Konferenzsaal große runde Tische für jeweils zehn Personen. An jedem einzelnen waren, als es pünktlich um 9.00 Uhr mit den Keynotes von Mikeal Rogers und Isaac Schlueter losging, jeweils zwei Plätze für Speaker vorgesehen, was das Motto der nächsten Tage unterstreichen sollte: Eine Konferenz von Node-Usern für Node-User, keinen extra Bereich für die Speaker. Und so waren keine zwei Stunden vergangen, als plötzlich Isaacs (der Mann hinter npm und heute quasi “Chefentwickler” von Node) sich neben mich setzte und wir uns locker unterhalten konnten. Und auch die nächsten Tage boten sich immer wieder zahlreiche Gelegenheiten, mit bekannten node.js-Größen ebenso zu reden wie mit enthusiastischen Entwicklern aus aller Welt.

Wo ich gerade bei Enthusiasmus bin: Der war enorm, sowohl wie er bei den einzelnen Sessions vorgetragen wurden, aber auch in den Gesprächen der Teilnehmer vorgelebt wurde. Hätte man eine Umfrage gemacht, so wären wahrscheinlich alle Kernbereiche der Informatik abgedeckt gewesen (“Machine Learning using Node.js”, wie geil ist das denn bitte?!). Auf der anderen Seite neigt so eine Konferenz wahrscheinlich per Definition zur Selbstbeweihräucherung. Kein Talk, in dem nicht – manchmal ja doch etwas unreflektiert – herausgestellt werden musste, wie awesome Node doch sei ist. Bei all den “Wir können die Zukunft gestalten!”-Talks fehlte mir dann irgendwann schon die Ernsthaftigkeit und wirklich neue Erkenntnisse, ich hätte mir in der Summe mehr technische Beiträge gewünscht. Felix Geisendörfers “Writing fast streaming binary parsers in node.js” war fantastisch, vom anderen deutschen Speaker Jan Lehnardt (“Community and Open Source, and the Node Community”) hätte ich lieber etwas zu CouchDB gehört als einen weiteren Talk im Keynote-Stile.

Ein Blick ins Plenum (NK)

Keynote von Isaac Schlueter (NK)

Alles in allem war für mich bei der Vielzahl an Talks und guten, bekannten Speakern aus der Szene am Ende zu wenig dabei, was ich an praktischen Tipps für meine Arbeit mit Node nun mitnehmen konnte. Das machten aber die vielen, vielen Gespräche mit anderen Entwicklern wieder wett. Die Zahl deutscher Teilnehmer war überschaubar, sehr sympathisch fand ich die vielen Iren, die die Konferenz organisierten oder einfach teilnahmen. Die Studentenquote hätte vermutlich auch höher sein können, die uulm-Kollegen und Mitblogger Benjamin und Michi konnten leider auch nicht mitkommen.

Am Freitag Abend stand dann die After Party in einem typischen Irish Pub mit leider wenig typisch irischer, sondern vielmehr gewöhnungsbedürftiger Technomusik an. Nochmal eine gute Gelegenheit, neue Bekanntschaften zu vertiefen, mal Jan Lehnardt nach Studentenpreisen für die JSConf EU zu fragen (wird es auch in Zukunft nicht geben) und ein paar Bilder mit Daniel Shaw mit Pferdekopf zu schießen. Da mein Flug bereits am Samstagmorgen um 6.30 Uhr gehen sollte, hatte ich geplant, die letzte Nacht in Dublin durch zu machen und gar kein Zimmer mehr. So bin ich also nach der Party um 3 Uhr in den AirCoach gestiegen, zum Flughafen gefahren, hab mich über McDonald’s aufgeregt und noch ein kleines Nickerchen machen können, ehe ich ins sonnige Frankfurt zurückflog.

Alles in allem war es eine äußerst interessante Woche. Nachdem wenige Tage später mein Notebook kaputtging, begann ich nochmal darüber nachzudenken, ob es das Geld, was ich für die beiden Konferenztickets, Flug und Hostel in dieser Woche ausgegeben hatte, tatsächlich wert war… – und ja, absolut! Der ganze Tripp war an sich schon unglaublich aufregend, es hat Spaß gemacht, diese vielen enthusiastischen Leute kennen zu lernen; manche, die man bislang nur von Twitter kannte. Und dass eine solche Unternehmung als Student durchaus erschwinglich ist, werde ich auch mal in einem kommenden Beitrag hier exemplarisch vorrechnen. Liebe Kommilitonen: Nutzt die Chance, jetzt habt Ihr wahrscheinlich noch eher die Zeit dafür, als später im Berufsleben.

Die mit (NK) gezeichneten Bilder sind von Nico Kaiser (CC BY 2.0), den ich in Dublin kennengelernt habe. Weitere Impressionen von NodeDublin findet Ihr in seinem Flickr-Album.

Wie wird man guter Programmierer? – Metaphorisch formuliert.

Eben noch von @maxogden rumgesponnen, wenige Minuten später online: realtimecats.com

Einfacher SMTP-Server zur Aufgabenverarbeitung

Häufig ärgere ich mich über Webanwendungen, die ihren Nutzern Mails schicken, wenn irgendein Ereignis eingetreten ist oder eine Aktion des Users benötigt wird, eine einfache Antwort auf die Mail aber nicht möglich ist. Ein typisches Szenario ist etwa das Bestätigen der Mailadresse, wenn man sich bei einem neuen Dienst registriert: Man gibt seine Mailadresse in ein Online-Formular ein, bekommt eine Aktivierungsmail zugeschickt und muss darin einen Link anklicken, der wiederum zum Webdienst führt und meine Mailadresse bestätigt. Wäre es nicht einfacher, die Mail so wie sie ist an den Absender zurückzuschicken um sie zu bestätigen?

Dies sei nur als ein Beispiel genannt. Beim nicht mehr gepflegten node.js-Modul smtpevent sind auch noch weitere Szenarien aufgeführt: Viele Mailinglists bieten etwa die Möglichkeit, sich über eine Mail an unsubscribe@mailinglist.de davon abzumelden. Bei Github kann man auf einen Issue direkt antworten, indem man die Benachrichtigungsmail einfach beantwortet. Weitere Szenarien sind leicht vorstellbar: Warum soll ich von unterwegs mit dem Handy eine Weboberfläche bemühen, um remote meine Heizung zu steuern, wenn ich schneller eine Mail mit dem Betreff “On” an heating@meinserver.de schicken kann?

Um eine solche Anwendung zu realisieren wird ein SMTP-Server benötigt. Node.js versteht sich sehr gut auf solche Protokolle und der eventbasierte Ansatz ist bei eingehenden Mails sicher auch nicht verkehrt. Und so gibt es bereits eine kleine Zahl an guter SMTP-Server-Implementierungen. Mir ist vor allem der estländische Programmierer Andris Reiman aufgefallen, der für node.js viele Module geschrieben hat, die sich mit dem Versand und Empfang von Mails beschäftigen. So reichen seine beiden Projekte simplesmtp und mailparser, um einen einfachen SMTP-Server aufzusetzen und die Mails und Anhänge zu parsen.

Zuerst bauen wir den SMTP-Server auf:

var simplesmtp = require('simplesmtp');
var MailParser = require('mailparser').MailParser;

var server = simplesmtp.createServer({
  validateSenders: false,
  validateRecipients: false
});

server.on('startData', function(envelope) {
  envelope.mailparser = new MailParser();
  envelope.mailparser.on('end', takeMail);
});

server.on('data', function(envelope, chunk){
  envelope.mailparser.write(chunk);
});

server.on('dataReady', function(envelope, callback){
  envelope.mailparser.end();
  callback();
});

server.listen(25);

Der Code ist relativ selbsterklärend: Bei Eingang einer Mail wird das startData-Event gefeuert, einzelne Datenchunks werden durch den data-Listener bearbeitet, und wenn die Mail vollständig übertragen wurde, feuert dataReady. Der Rahmen envelope wird dabei allen Funktionen übergeben, sodass envelope.mailparser auch in allen Funktionsaufrufen für eine Mail der gleiche ist. So werden bei zwei gleichzeitig eingehenden Mails diese nicht versehentlich vermischt.

Sobald die Mail vollständig übertragen wurde (dataReady), lösen wir mailparser.end() aus, was das Parsen der Mail beginnt. Fehlt noch die Definition, was mit unserer geparsten Mail denn passieren soll:

function takeMail(mail_object) {
    console.log('From:', mail_object.from); //[{address:'sender@example.com',name:'Sender Name'}]
    console.log('Subject:', mail_object.subject); // Hello world!
    console.log('Text body:', mail_object.text); // How are you today?

    // Irgendein Datenbankaufruf o.ä.
}

Hier kann die Mail nun also ausgewertet und so die gewünschte Aktion ausgelöst werden. Das tolle am Mailparser ist die Tatsache, dass Dateianhänge ebenfalls soweit wie möglich geparst werden. So enthält das mail_object auch einen Eintrag attachments, der etwa so aussehen könnte:

attachments = [{
    contentType: 'image/png',
    fileName: 'image.png',
    contentDisposition: 'attachment',
    contentId: '5.1321281380971@localhost',
    transferEncoding: 'base64',
    length: 126,
    generatedFileName: 'image.png',
    checksum: 'e4cef4c6e26037bcf8166905207ea09b',
    content: <Buffer ...>
}];

Attachments-Beispiel von mailparser

So könnte man leicht einen Service wie den von Twitpic einrichten, dass man durch das Senden an eine userspezifische Mailadresse ein Bild hochladen kann.

Wem das ganze zu unsicher scheint, weil ja sonst jeder, der die Mailadresse kennt, Dummheiten anstellen könnte, der sei auf die Serverkonfiguration ganz oben hingewiesen: Werden validateSenders und validateRecipients auf true gesetzt, können Sender und Empfänger in einer eigenen Funktion überprüft und ggf. zurückgewiesen werden. Die simplesmtp-Bibliothek bietet daneben auch noch andere Authorisierungsmechanismen.

Benachrichtigungen für die Piratenpads skripten

Die Piratenpads sind eine bequeme, kostenfreie Möglichkeit um kollaborativ an Texten zu arbeiten oder etwa Ideen zu sammeln. In letzter Zeit nutze ich den Service mit einigen anderen Leuten sehr exzessiv. Über die verschiedenen Pads in verschiedenen Teams habe ich aber inzwischen einfach den Überblick verloren. Um herauszufinden, ob es irgendwo Neuerungen gibt muss ich alles manuell abklappern und mich bei jedem einzelnen Team einloggen. Ich bin der Ansicht, dass man genau solche Aufgaben nach Möglichkeit immer automatisieren sollte.

Deswegen habe ich einen Notificationservice für die Piratenpads geschrieben. Das Skript loggt sich ein, fragt die aktuelle Version des Dokuments ab und speichert diese lokal zwischen. Beim nächsten Überprüfen (idealerweise als Cronjob) wird die lokale Kopie mit dem online-Original verglichen. Gibt es eine Differenz wird eine E-Mailbenachrichtigung mit dem diff versandt.

Um das Ganze zu Skripten habe ich node.js mit der Bibliothek request verwendet:

Request is designed to be the simplest way possible to make HTTP calls. It support HTTPS and follows redirects by default.

Tolle Sache! Ohne HTTPS-Support kommen wir eh nicht weit, bei einigen Pads ist der Zugriff auf HTTPS eingeschränkt. Außerdem muss man sich dank der Bibliothek nicht um das mühsame Parsen von “Set-Cookie” Feldern kümmern. request übernimmt die Cookies standardmäßig einfach global für zukünftige Requests.

Um die Session zu initialisieren, also sich vor dem Login einen Cookie zu holen, sieht der Code etwa so aus:

var request = require('request'); 
var base = 'https://foo.piratenpad.de';

(function initiateSession() {
	request.get(base, function (error, response, body) {
		if (!error && response.statusCode == 200) {
			login();
		}
	});
})();

Die Loginfunktion habe ich zusammengebaut, nachdem ich den kompletten Skriptablauf im Firefox durchgespielt habe und alle Requests mittels des (unheimlich praktischen) Live HTTP Headers Add-ons aufgezeichnet habe.

function login() {
	var options = {
			url: base + '/ep/account/sign-in', 
			form: {'email': 'john@doe.de', 'password' : 'mypw'}
	};
	
	request.post(options, function (err, res, body) {	
			request.get(base + '/' + acc.padId, function (err, resp, body) {
				// get the latest version of the pad document
				var linkToLatestVersion = body.match(/[\w\d\/\-\.]*(latest')/gi);
				linkToLatestVersion = linkToLatestVersion[0].replace("'", '');

				getLatestDocument(linkToLatestVersion);
			});			
		}
	);	
}

Die aktuelle Version des Dokuments lässt sich dann mit einem einfachen GET-Request abfragen:

function getLatestDocument(linkToLatestVersion) {
	request.get(base + linkToLatestVersion, function (err, resp, body) {
		var start = body.search('id="padcontent">') + 'id="padcontent">'.length;
		var end = body.search("<!-- /padeditor -->");
		var padContent = body.substring(start, end);
		
		// strip all tags and entities
		padContent = padContent.replace(/(<[^>]+>)|(&[#\w]+;)/gi, '');
		
		console.log(padContent.trim());
	});
}

Das Ganze zusammengefasst als ordentliches, sauber konfigurierbares, Projekt gibt es hier auf GitHub. Das Skript kann sehr einfach für ähnliche Aufgaben wiederverwendet werden. Als Anregung: Beispielsweise wäre es möglich das Uni Hochschulportal anzuzapfen um E-Mailbenachrichtigungen zu versenden, wenn neue Prüfungsergebnisse eingetragen sind.

Update: Ich habe noch die Möglichkeit hinzugefügt, Benachrichtigungen für Änderungen an dem Inhalt hinter einer URL zu konfigurieren (im Ordner simple-webpage/). Ich benutze das als simple Lösung für Seiten, die keinen RSS-Feed bereitstellen.

Einfache Visualisierung von Geodaten – Teil 2: Leaflet & jquery.couch.js

Im vorherigen Teil haben wir gesehen, wie man Geodaten mithilfe von CouchDB abspeichern kann. Da diese Datenbank zugleich ein Webserver ist und die Daten im JSON-Format gespeichert werden, eignet sie CouchDB auch gut für AJAX-Abfragen. Hierfür gibt es eine auf jQuery aufbauende Library namens jquery.couch.js, die von den AJAX-Requests abstrahiert und direkt browser-seitige Interaktionen mit der Datenbank ermöglicht.

Im diesem Beitrag soll gezeigt werden, wie man mit der offenen Karten-Library Leaflet und jquery.couch.js geographische Daten aus CouchDB heraus auf einer Karten anzeigen kann.

Beispiel-Visualisierung von ulmapi.de, ebenfalls basierend auf CouchDB und Leaflet.

Wir verwenden die CouchApp aus dem ersten Teil weiter, und fügen zu den bisherigen Map/Reduce Views noch statische HTML- und Javascript-Dateien hinzu (im _attachments Ordner), die dann im Browser abgerufen werden können. Beim Aufruf dieser Webseite wird ein HTML-Grundgerüst übertragen, sowie eine JavaScript-Datei, die beim Aufruf die eigentlichen Datensätze via jquery.couch.js aus der CouchDB nachlädt.

Als Mapping-Library verwenden wir Leaflet, eine Open-Source Bibliothek für Kartendarstellungen im Browser. Leaflet abstrahiert von verschiedenen Kartenprovidern und erlaubt es somit, unterschiedliche Datenquellen zu verwenden, wie zum Beispiel auch Bing Maps oder Cloudmade. Letzteres ist ein Service, der auf Basis der Open Street Map Daten Kartenkacheln mit individuellen Stilen rendert und hostet – für Visualisierungen oft sehr hilfreich, da reguläre Karten meist zu viele Karteninformationen enthalten oder farblich überladen sind. In unserem Fall haben wir einen einfach Graustufenkarte gewählt. Leaflet selbst lässt sich relativ leicht verwenden, es muss eine CSS-Datei sowie eine JavaScript-Datei importiert werden, und ein div-Block im HTML enthalten sein, worin später die Karte gerendert werden soll. Somit sieht unser HTML-Gerüst zu Beginn so aus:

<!doctype html>
<html>
<head>
	<link rel="stylesheet" href="style/leaflet.css" />
	<script type="text/javascript" src="js/jquery.min.js"></script>
	<script type="text/javascript" src="js/jquery.couch.js"></script>
	<script type="text/javascript" src="js/leaflet.js"></script>
	<script type="text/javascript" src="js/maploader.js"></script>
</head>
<body>
	<div id="map"></div>
</body>
</html>

Es werden jQuery, jquery.couch.js und die Leaflet-Libs geladen, und die letzte importierte JavaScript-Datei soll nun unseren Code zum initialisieren der Karte und dem Laden der Daten aus der CouchDB enthalten. Zunächst erstellen wir eine Karte und rendern sie, sobald die Seite vollständig geladen wurde (jQuery Callback für document.ready):

$(document).ready(function(){

		var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/[YOUR_API_KEY]/33481/256/{z}/{x}/{y}.png';
		var cloudmadeAttribution = 'UlmApi.de / Shape Files: Stadt Ulm (cc-by-sa), Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade';
		var cloudmade = new L.TileLayer(
			cloudmadeUrl, {
				maxZoom : 18,
				attribution : cloudmadeAttribution
		});

		var map = new L.Map('map', {
			center : new L.LatLng(48.399976,9.995399),
			zoom : 12,
			layers : [ cloudmade ],
			zoomControl : false
		});
});

In der cloudmadeUrl muss für Cloudmade Karte ein korrekter API-Key angegeben werden, der nächste Parameter im Pfad identifiziert den Kartentyp. Beim Initialisieren der Karte wird dann die ID des divs angeben, bei uns ‘map’. Nun sollte unsere Karte bereits dargestellt werden, nachdem wir die CouchApp neu deployen (außerhalb des Fokus dieses Artikels, mehr dazu auf couchapp.org).

Was nun noch fehlt, ist das Nachladen der Geodaten aus der CouchDB und die Anzeige auf der Karte. Hierfür verwenden wir jquery.couch.js als Wrapper für die AJAX-Requests gegen CouchDB und die GeoJSON-Funktionalität von Leaflet:

$.couch.db('database_name').view('design_doc_name/view_name', {

	success: function(data){
		if(data && data.rows && data.rows.length){

			var geoJsonLayer = new L.GeoJSON();

			for(var i = 0;i<data.rows.length;i++){
				geoJsonLayer.addGeoJSON(data.rows[i].value.geometry);
			}

			map.addLayer(geoJsonLayer);
		}
	}
});

Das obige Snippet sollte im vorherigen Code hinter der Erzeugung der Karte eingefügt werden. Es ruft von der Datenbank ‘database_name’ den View ‘view_name’ des Design-Dokuments ‘design_doc_name’ auf, und iteriert bei erfolgreicher Abfrage über alle Zeilen. Von jeder Zeile wird dabei die geometry-Property zu einem GeoJSON-Layer hinzugefügt, der am Ende an die Karte übergeben wird. Da unser View aus Teil 1 bereits GeoJSON generiert, und Leaflet nativ GeoJSON lesen und darstellen kann, ist das Hinzufügen von Geodaten auf die Karte sehr einfach.

Hier noch ein paar weiterführende Links mit vertiefenden Inhalten zu den einzelnen Themen:

Funktionale Ansätze in einer imperativen Programmiersprache

Im Zuge der Vorlesung “Paradigmen der Programmierung” kam ich erstmals mit dem alternativen Programmierkonzept der funktionalen Programmierung in Berührung, das sich von dem der weiter verbreiteteren imperativen Programmiersprachen wie etwa Java und C grundsätzlich unterscheidet. Als Übung auf die Klausur versuchte ich die Ideen der funktionalen Programmiersprache Haskell in einer imperativen Sprache umzusetzen. In Javascript ist die Nutzung von anonymen Funktionen und ihre Übergabe selbst als Parameter einer anderen Funktion bereits verbreitet, womit sie per se bereits einige Konzepte der funktionalen Programmierung umsetzt. Um weitere Dinge wie partielle Funktionsaufrufe ebenfalls zu ermöglichen, habe ich ein Javascript-Modul “functionalJS” geschrieben, das mit dem Konstruktor Functional() ein Äquivalent zum Javascript-eigenen Function() bietet. Der Quellcode sowie einige Beispiele finden sich auf github. Im Folgenden möchte ich die Ideen der funktionalen Programmierung kurz vorstellen und erklären, wie sie in dieser Klasse umgesetzt wurden.

Alles ist eine Funktion

Beim funktionalen Programmierstil besteht das Programm einzig aus Funktionen. Auf den ersten Blick mag das bei imperativen Programmiersprachen kaum anders sein, auch hier bilden Funktionen bzw. Methoden einen wesentlichen Bestandteil. Es gibt jedoch Unterschiede bei der Art der Aufrufe: Imperative Programme führen die Funktionen als eine Folge von Anweisungen aus (“Tu das! Dann tu das solange dies gilt!” – imperativ eben), bei der funktionalen Programmierung treten die Funktionen nur ineinander geschachtelt auf – das Ergebnis einer Funktion ist also zugleich Parameter der nächsten. Dadurch sind Variablen unnötigt, was einen Vorteil der funktionalen Programmiersprachen begründet: Es gibt keine Seiteneffekte! Während in der imperativen Programmierung das Resultat vom Programmzustand abhängt, hängen hier die Rückgabewerte einzig von den Parametern ab und etwa nicht vom Zeitpunkt des Aufrufes.

Für die Umsetzung in Javascript bedeutet dies, dass alle Funktionen ohne lokale Variablen auskommen, sondern stattdessen einzig ein return-Statement beinhalten. In diesem können nach dem eben genannten Konzept Funktionen aufgerufen werden. Dadurch ist Rekursion möglich, die nötig wird, um Schleifen zu vermeiden. Jede Funktion muss zudem mindestens einen Parameter und genau einen Rückgabewert besitzen, um dem funktionalen Anspruch gerecht zu werden.

Beispiel: Durchschnittsfunktion für eine übergebene Liste in Javascript (Ausschnitt der functional.js)

function(l) { return ratio(sum(l), length(l)); }

Mustervergleiche (Stichwort: Pattern matching)

Haskell erlaubt die mehrfache Definition einer Funktion. Dies unterscheidet sich wesentlich vom “Überladen” bei Java, wo die Unterschiede bei der mehrfachen Definition in der Methodensignatur liegen. In funktionalen Programmiersprachen werden dagegen semantische statt syntaktische Muster für die Eingabeparameter angelegt, d.h. es wird quasi eine Musterbasis aufgebaut, die dann beim Aufruf der Funktion von oben nach unten getestet wird. Sobald das erste Muster mit den übergebenen Parametern zusammenpasst, wird dessen Ergebnis oder Funktionsrumpf genutzt. Das Ergebnis hängt also wesentlich von der Reihenfolge der Musterdefinitionen ab. Basisfälle für Rekursionen werden so stets als erstes definiert, der allgemeinste Fall zum Schluss.

Beispiel: (Primitive) Definition der Fibonacci-Funktion in Haskell:

fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n-1) + fibonacci (n-2)

Die beiden Basisfälle der Fibonacci-Funktion werden also vor der allgemeinen Rekursionsvorschrift definiert. In Javascript ist ein Überladen von Funktionen nicht möglich, auch bei der functionalJS-Klasse müssen so die Muster gleich dem Konstuktor übergeben werden. Eine äquivalente Definition der Fibonacci-Funktion mittels der functionalJS-Klasse sieht so aus:

Beispiel: Definition der Fibonacci-Funktion in Javascript (modifizierter Ausschnitt der test.js)

fibonacci = new Functional("fibonacci",
	[0, 0],
	[1, 1],
	["n", function(n) { return fibonacci(n-1)+fibonacci(n-2); }]
);

Die Muster werden als Arrays dem Functional übergeben. Dabei sind für reine Funktionen wie der letzte Fall auch Verkürzungen möglich. Näheres dazu ist in der Dokumentation aufgeführt. Ansonsten gilt: Das letzte Array-Element ist das Resultat oder die aufzurufende Funktion bei Übergabe dieses Musters. Optional kann als einfacher String der Name der Funktion übergeben werden, was für die interne Realisierung der Rekursion nötig ist.

Wird nun die Fibonacci-Funktion aufgerufen, so werden die Muster mittels Pattern matching nacheinander mit den tatsächlichen Parametern verglichen. Dabei sind durchaus auch kompliziertere Muster möglich, die die Haskell-typischen Listen erkennen. So wird die einfache length-Funktion, die die Anzahl der Elemente einer Liste zurückgibt, in beiden Sprachen wie folgt definiert:

Beispiel: Definition der length-Funktion in Haskell

length []	=  0
length (x:xs)	=  1 + length xs

Analog sieht eine solche Definition mittels functionalJS aus, mit dem Unterschied, dass hier für Listen (egal ob leer oder nicht) stets die eckigen Klammern genutzt werden:

Beispiel: Definition der length-Funktion in Javascript (Ausschnitt der functional.js)

length = new Functional("length",
	["[]", 		0],
	["[x:xs]", 	function(x, xs) { return 1+length(xs); }]
);

Über vordefinierte special operators ist es auch nach der Definition eines Functionals möglich, die Musterbasis zu erweitern. Dies könnte in diesem Beispiel etwa wie folgt geschehen, wodurch die Funktion bei Übergabe einer leeren Liste 42 zurückgibt:

Beispiel: Nachträgliche Erweiterung der Musterbasis in Javascript

length('__addOnTop',
	["[]", 42]
);

In Haskell ist ein solches Verhalten nachträglich etwa im GHCi, dem Haskell-Interpreter, nicht möglich. Dort könnten weitere Muster stets nur am Ende das Basis hinzugefügt werden. Ein solches Verhalten lässt sich bei functionalJS mittels des Keywords ‘__add’ realisieren.

Partielle Aufrufe (Stichwort: Currying)

Haskell erlaubt das partielle Aufrufen von definierten Funktionen, d.h. das Übergeben zu weniger Parameter um daraus eine neue Funktion zu erhalten. Dieses Verhalten ist sinnvoll, um eigene und vordefinierte Funktionen mehrfach nutzen zu können und insbesondere in der Verwendung als Funktional (siehe nächsten Punkt). Anders als bei imperativen Programmiersprachen stellen Übergabeparameter einer Funktion also kein Tupel dar (param1, param2, param3), sondern lassen sich eher als Liste param1 -> param2 -> param3 -> result auffassen. Werden alle Parameter übergeben, so wird ganz normal das Funktionsresultat zurückgegeben. Wird die Funktion dagegen nur mit zwei Parametern aufgerufen, so wird ein neue Funktion zurückgegeben, die von einem Parameter abhängt. Man sagt, dass eine Funktion mit einer Stelligkeit von 3 bei Übergabe von zwei Parametern eine Funktion der Stelligkeit 1 zurückliefert.

Das Verhalten, dass die Anzahl der Parameter einer Funktion in Javascript variabel sein kann, kann durch die Nutzung des arguments-Objekts realisiert werden. Schwieriger gestaltet sich die Realisierung der partiellen Auswertung (Ausschnitt). Während bei der normalen Auswertung, also bei Übergabe aller Parameter, einfach das erste Muster gesucht wird, welches zutrifft, müssen bei der partiellen Auswertung alle zutreffenden Muster gesucht werden, da diese in dieser Reihenfolge Teil des zurückzugebenen Functionals werden. Anhand dieser Muster wird ein neues Functional erzeugt, in deren Funktionsrümpfe die gebundenen Variablen durch die gematchten Parameter ersetzt werden. So wird also aus der zweistelligen add-Funktion (Definition), die die Summe zweier Zahlen berechnet, bei Übergabe des Parameters 1 ein einstelliges Functional, das eine Zahl erwartet und als Resultat den Nachfolger der Zahl zurückliefert.

Beispiel: Definition der Nachfolger-Funktion durch partiellen Aufruf in Javascript

succ = add(1); // add :: a -> a -> a

Anders als in Haskell können Funktionsnamen in functionalJS nur aus Buchstaben und Zahlen bestehen, eine Funktionsdefinition als “<” ist so etwa nicht möglich, stattdessen gibt es eine lessthan-Funktion. Dies erschwert partielle Aufrufe von nicht-kommutativen Funktionen.

Beispiel: Falsche Definition der negative-Funktion durch partiellen Aufruf in Javascript

negative = lessthan(0);

Die oben genannte Definition der negative-Funktion liefert eben nicht true zurück für Zahlen kleiner null, sondern genau das Gegenteil. Dies liegt an der Definition von lessthan:

Beispiel: Definition der standardmäßigen lessthan-Funktion (Ausschnitt der functional.js)

lessthan = new Functional("lessthan",
	function(a, b) { return a < b; }
);

Der partielle Aufruf lessthan(0) liefert also ein neues einstelliges Functional zurück, dass stets 0<x prüft. Dies entspricht also nicht dem gewünschten Verhalten. In Haskell sind dagegen auch partielle Aufrufe wie oben gewünscht möglich:

Beispiel: Definition der negative-Funktion in Haskell

negative = (<0)

Daher sind in den standardmäßigen Funktionen der functionalJS-Klasse zu jeder zweistelligen nicht-kommutativen Funktion auch eine entsprechende mit verdrehten Parametern definiert, die den gleichen Namen endend auf “Re” trägt. Unser obiges Beispiel müsste also korrekt lauten:

Beispiel: Korrekte Definition der negative-Funktion durch partiellen Aufruf in Javascript

negative = lessthanRe(0);

Funktionen als Parameter (Stichwort: Funktionale)

Die Möglichkeit des partiellen Aufrufs einer Funktion ist bereits ein mächtiges Instrument. Seine volle Wirkung können solche Funktionen aber bei Übergabe an andere entfalten. Anders als in vielen anderen imperativen Programmiersprachen ist die Übergabe von Funktionen als Parameter in Javascript möglich und ein häufiger Einsatzzweck, insbesondere als Callbacks für Events. Javascript-Anhängern ist ein solches Verhalten also bereits bekannt. In der funktionalen Programmierung bezeichnen wir Funktionen, die als Eingabe oder Ausgabe selbst Funktionen aufnehmen, als Funktionen höherer Ordnung oder Funktionale. So ist es etwa bei der Map-Funktion:

Beispiel: Definition der map-Funktion in Javascript (Ausschnitt der functional.js)

map = new Functional("map",
	["f", 	"[]", 		function(f) { return []; }],
	["f", 	"[x:xs]", 	function(f, x, xs) { return unshift(f(x), map(f, xs)); }]
);

Wie wir sehen, erwartet die map-Funktion also zwei Parameter: Zum einen eine Funktion f, zum anderen eine Liste. Das erste Muster beschreibt den Basisfall der leeren Liste, während der zweite den Rekursionsfall nennt. Kurz gesagt: Im Falle der leeren Liste wird eine leere Liste zurückgegeben, ansonsten wird das Kopfelement genommen, die Funktion f darauf angewendet, und das ganze mit dem Rest der Liste wiederholt und zusammengefügt. Die map-Funktion führt also eine Funktion f auf alle Elemente einer Liste aus. Bekannte Haskell-Beispiele der Funktionale sind auch die fold-Funktionen, die ebenfalls in dem Modul definiert wurden.

Unter Anwendung der Funktionen höherer Ordnung sowie partieller Aufrufe lassen sich so sehr schnell nette Funktionen zusammenstellen:

Beispiel: Anwendungen (Ausschnitt der test.js)

foldr(mult, 1, [1,2,3,4]); // 24, berechnet das Produkt aller Listenelemente

all(lessthanRe(5), [1,2,3,4]); // true, da alle Elemente kleiner 5

until(greaterthanRe(80), mult(2), 1); /* 128, da der Initialwert 1 solange mit 2 multiplitziert wird, bis der Schwellenwert 80 erreicht ist */

Fazit

Den funktionalen und imperativen Programmierstil zu vereinen ist schwierig und mag in der Frage münden: Warum? Zum einen beschneidet man die mächtigen Instrumente von Javascript, indem etwa Variablen und Schleifen komplett verboten werden, zum anderen kann man nicht aus den vollen Vorteilen von Haskell schöpfen, etwa bei der Nutzung unendlicher Listen. Und dennoch bieten die Functionals einige Vorteile, allen voran die partiellen Aufrufe. Ob durch die strikte Nutzung des funktionalen Stils tatsächlich auch in Javascript Seiteneffekte ausbleiben, wäre noch zu testen. Insbesondere, da sich einige Funktionen derzeit nicht atomar realisieren lassen (es gibt etwa keinen “(x:xs)”-Operator zur Listenerzeugung), darf in der aktuellen Fassung nicht davon ausgegangen werden. Prinzipien wie anonyme Funktionen und Funktionale sind in Javascript bereits enthalten, wodurch es sich zum Teil bereits ähnlich anfühlt. Das oben geführte until-Beispiel würde man mit normalen Javascript-Mitteln vermutlich wie folgt lösen:

Beispiel: Aufruf einer herkömmlichen until-Funktion in Javascript

until(function condition(x) { return x > 80; }, function do(x) { return x*2; }, 1);

Während ein solches Verhalten nicht in allen imperativen Programmiersprachen möglich ist, bietet Javascript offensichtlich bereits etwas ähnliches an. Auf partielle Aufrufe und die einfache Funktionsdefinition mittels Pattern matching muss man dennoch verzichten.

In der vorgestellten Klasse fehlen noch Haskell-typische Elemente, allen voran die Implementierung von Tupeln. So ist derzeit die bekannte zip-Funktion noch nicht eingebaut. Daneben ist die Implementierung der Mengendefinition, also das einfache Erzeugen einer Liste anhand einer Bildungsvorschrift, umsetzbar. Komplizierter wird es bei der Realisierung von unendlichen Listen, wo Haskell etwa in der Berechnung großer Fibonacci-Zahlen imperativen Programmiersprachen überlegen ist. Schlussendlich lassen sich dann auch ganz grundsätzliche Dinge, wie etwa die Nachbildung der lazy-evaluation als Auswertungsstrategie nicht nachträglich in Javascript umsetzen.

TCP Echo-Server in einer Zeile node.js

Der folgende Einzeiler erzeugt einen TCP-Server, der alle eingehenden Daten direkt zurücksendet:

require('net').createServer(function(s){require('util').pump(s,s);}).listen(8000);

Und nein, das ist kein Perl ;-)

Barrier points in Node.js

Node.js ist ein serverseitiges, hochskalierbares Framework für die Entwicklung von asynchronen Netzwerkanwendungen in JavaScript. Aufgrund der Asynchronität ist das Framework in der Lage, tausende Verbindung gleichzeitig offen zu halten und zu verarbeiten. Ein wesentliches Merkmal hier von ist, dass ausschließlich mit Callbacks gearbeitet wird, um auf Beendigungen von Operationen zu reagieren.

Im Normalfall führt das zu einem Chaining von Callback. Im folgenden Beispiel sollen zwei Dateien geöffnet werden und ihr kompletter Inhalt zurückgegeben werden:

var size = 0;

fs.readFile('/home/benjamin/file1', function(err, data)
{
	if (!err)
	{
		// first file read
		size += data.length;
		fs.readFile('/home/benjamin/file2', function(err, data)
		{
			if (!err)
			{
				// second file read
				size += data.length;
				fs.writeFile('/home/benjamin/size', size, function(err)
				{
					if (!err)
					{
							// both file read and content written to file
							sys.log('done');
						}
					});
			}
		});
	}
});

Das Problem hierbei ist, dass zwei Aktionen, die eigentlich parallel ablaufen könnten, nämlich das lesen beider Dateien, nacheinander ablaufen müssen. Für einen parallelen Ablauf ist eine zusätzliche Koordination notwendig, da die Resultate beider Aufrufe in Verbindung stehen. Hierfür habe ich mich an den CyclicBarrier Klassen von Java orientiert und eine einfache Klasse für die Koordination von asynchronen Callbacks geschrieben. Eine Barriere wird erzeugt unter Angabe der Anzahl von teilnehmenden Parties. Desweiteren können optional ein Callback für die Beendigung sowie ein optionaler Callback im Abbruchsfall registriert werden. Anschließend können die einzelnen Aufgaben der Parties angestoßen werden. Im Erfolgsfall müssen diese an der Barriere mit submit() anzeigen, dass sie beendet sind. Durch abort() kann auch ebenfalls ein Abbruch signalisiert werden. Eine Propagation der Ergebnisdaten lässt am besten durch eine externe Variable realisieren, auf den die Funktionen ebenfalls Zugriff haben.

Der Code des Beispielszenarios ändert sich dann wie folgt:

var size = 0;

var b = new Barrier(2, function()
{
	//Success callback
	fs.writeFile('/home/benjamin/size', size, function(err)
	{
		sys.log('done');
	});
}, function()
{
	//Aborted callback
	sys.log("aborted");
});

fs.readFile('/home/benjamin/file1', function(err, data)
{
	if (err)
	{
		b.abort();
	}
	else
	{
		size += data.length;
		b.submit();
	}
});

fs.readFile('/home/benjamin/file2', function(err, data)
{
	if (err)
	{
		b.abort();
	}
	else
	{
		size += data.length;
		b.submit();
	}
});

Wie im Vergleich zu erkennen ist, lässt sich die Ausführung deutlich beschleunigen. Ein paar Rahmenbedingungen gibt es jedoch. Zunächst dürfen die Teilaufgaben keine Abhängigkeiten untereinander besitzen. Dann darf ein Abbruch einer Teilaufgabe keine Auswirkung auf noch laufende Teilaufgaben besitzen, dessen Ergebnis später verworfen wird. Schließlich muss bei der Programmierung darauf geachtet werden, dass in jedem Fall eine Teilaufgabe mit submit() oder abort() terminiert, da ansonsten ein Lock entsteht.

Die Klasse ist relativ einfach:

/**
 * @class
 * 
 * Creates a new barrier for the given amount of parties. 
 * @param parties
 * @param barrierCallback
 * @param abortCallback
 * @return
 */
var Barrier =  function(parties, barrierCallback, abortCallback)
{
	this.parties = parties;
	this.barrierCallback = barrierCallback;
	this.abortCallback = abortCallback;

	this.running = true;
	this.count = 0;
};

/**
 * Signals a completion of one of the parties.
 * @return
 */
Barrier.prototype.submit = function()
{
	if (++this.count === this.parties && this.running)
	{
		this.barrierCallback();
	}
};

/**
 * Signals an abort by one of the parties. If not callback is passed, the default abort callback will be executed.
 * @param customAbortCallback Optional callback that should be executed due to the abort.
 * @return
 */
Barrier.prototype.abort = function(customAbortCallback)
{
	if (this.running && customAbortCallback)
	{
		customAbortCallback();
	}
	else if (this.running && this.abortCallback)
	{
		this.abortCallback();
	}
	this.running = false;
};

Klasse auf github: http://gist.github.com/464179

ioexception.de

Benjamin Erb [] studiert seit 2006 Medieninformatik und interessiert sich insbesondere für Java, Web-Technologien, Ubiquitous Computing, Cloud Computing, verteilte Systeme und Informationsdesign.


Raimar Wagner studiert seit 2005 Informatik mit Anwendungsfach Medizin und interessiert sich für C++ stl, boost & Qt Programmierung, Scientific Visualization, Computer Vision und parallele Rechenkonzepte.


David Langer studiert seit 2006 Medieninformatik und interessiert sich für Web-Entwicklung, jQuery, Business Process Management und Java.


Sebastian Schimmel studiert seit 2006 Informatik mit Anwendungsfach Medizin und interessiert sich für hardwarenahe Aspekte, Robotik, webOs, C/C++ und UNIX/Linux.


Timo Müller studiert seit 2006 Medieninformatik. Er interessiert sich allen voran für Mobile and Ubiquitous Computing, systemnahe Enwticklung und verteilte Systeme, sowie Computer Vision.


Achim Strauß studiert seit 2006 Medieninformatik. Seine Interessen liegen in Themen der Mensch-Computer Interaktion sowie Webentwicklung und UNIX/Linux.


Tobias Schlecht studiert seit 2006 Medieninformatik und interessiert sich vor allem für Software Engineering, Model Driven Architecture, Requirements Engineering, Usability Engineering, Web-Technologien, UML2 und Java.


Fabian Groh studiert seit 2006 Medieninformatik. Seine Interessengebiete sind Computer Graphics, Computer Vision, Computational Photography sowie Ubiquitos Computing.


Matthias Matousek studiert seit 2007 Medieninformatik und interessiert sich besonders für Skriptsprachen, Echtzeitsysteme und Kommunikation.


Michael Müller [] studiert seit 2009 Medieninformatik. Er interessiert sich vor allem für Web-Technologien, Ubiquitous Computing, User-Interfaces, UNIX und Creative Coding.


Falco Nogatz [] studiert seit 2010 Informatik mit Anwendungsfach Mathematik. Er interessiert sich für Web-Technologien, Programmierparadigmen und theoretische Grundlagen.

Archiv

Februar 2015
M D M D F S S
« Mrz    
 1
2345678
9101112131415
16171819202122
232425262728