Archive by Author

Debuggen von Server-side JavaScript

27 Jul

Ich habe in meinem privaten Blog einen Post verfasst, wie man (NodeJS) basierten Server-side JavaScript Code debuggen kann. Damit möglichst viele was davon haben, habe ich das ganze auf Englisch verfasst und stelle deshalb hier einfach den Link zum Original-Post ein.

http://somethingaboutcode.wordpress.com/2011/07/26/debug-etherpad-lite-server-side-javascript/

Etherpad speckt ab – Etherpad-lite

22 Jul

Etherpad krankt bis dato an dem Problem, dass es vor allem auf der Serverseite ein ziemlich aufgeblasenes, resourcenverschwendendes Biest ist. Dieser Umstand ist geschichtlich gewachsen und rührt daher, dass Etherpad ursprünglich dafür konzipiert wurde, die Vorzüge des AppJet Frameworks unter Beweis zu stellen. Diese Wurzeln machen Etherpad auf der Serverseite jedoch behäbig, unhandlich und verschwenden Unmengen an Ressourcen, von der schwierigen Wartbarkeit einmal ganz abgesehen.

Auf der etherpad-dev Mailingliste kam nun heute eine Mail herein, dass ein Fork namens etherpad-lite (https://github.com/Pita/etherpad-lite) existiert, welcher eine Portierung von Etherpad von AppJet auf NodeJS zum Ziel hat. Laut README ergeben sich daraus eklatante Codeeinsparungen u.a. mit folgenden Effekten (siehe https://github.com/Pita/etherpad-lite#readme)

Etherpad Etherpad Lite
Size of the folder 30 MB 1.5 MB
Languages used server side Javascript, Java, Scala Javascript (node.js)
Lines of server side Javascript code 101410 5330
RAM Usage immediately after start 257 MB 16 MB

Man beachte vor den Unterschied zwischen knapp 100000 LOC für AppJet auf der Serverseite vs. knapp 5000 LOC for NodeJS.

Leider kommt dieser Fork für meine Arbeit wohl etwas zu spät, da erst Mitte August mit der offiziellen Veröffentlichung gerechnet wird. So werden im Augenblick essentielle Features wie bsp. Plugins noch nicht unterstützt.

Sobald das Plugin-System funktioniert, scheint es jedoch der Etherpad-Fork der Wahl für zukünftige Weiterentwicklungen zu sein.

Forken eines bestehenden GitHub Projekts

6 Jul

Etherpad, der kooperative Texteditor auf dem meine Arbeit basiert, ist ein Open-Source Projekt, welches das verteilte Versionskontrollsystem GIT einsetzt. Die Seite github.com stellt für solche Projekte eine kostenlose Plattform dar. Im folgenden möchte ich kurz beschreiben, wie man einen Fork eines bestehenden, auf GitHub gehosteten Projekts anlegt.

Weiterlesen

Die Etherpad Plugin-Infrastruktur

2 Jul

Die folgenden Ausführungen basieren auf dem Dokument README.plugins und der Analyse des Etherpad Plugins heading1.

Plugin-Infrastruktur und Hooks

Bei der Plugin-Infrastruktur von Etherpad handelt es sich um ein sog. Hook-Infrastruktur, d.h. dass an geeignetter Stelle im Etherpad Quellcode sog. Hook-Calls ausgelöst werden, für welche sich ein oder mehrere Plugins registrieren können.

Ein Beispiel hierfür sind die Hook-Calls serverStartup und serverShutdown in der Datei etherpad/src/main.js in den Funktionen serverhandlers.startupHandler und serverhandlers.shutdownHandler.

/*
 * etherpad/src/main.js
 */
...
serverhandlers.startupHandler = function() {
  // Order matters.
  checkSystemRequirements();
  ...
  log.onStartup();
  statistics.onStartup();
  migration_runner.onStartup();
  pad_migrations.onStartup();
  model.onStartup();
  collab_server.onStartup();
  pad_control.onStartup();
  dbwriter.onStartup();
  blogcontrol.onStartup();
  importexport.onStartup();
  pro_pad_editors.onStartup();
  noprowatcher.onStartup();
  team_billing.onStartup();
  collabroom_server.onStartup();
  readLatestSessionsFromDisk();

  plugins.callHook('serverStartup');
};
…
serverhandlers.shutdownHandler = function() {
  plugins.callHook('serverShutdown');

  appjet.cache.shutdownHandlerIsRunning = true;
  log.callCatchingExceptions(writeSessionsToDisk);
  log.callCatchingExceptions(dbwriter.onShutdown);
  log.callCatchingExceptions(sqlcommon.onShutdown);
  log.callCatchingExceptions(pro_pad_editors.onShutdown);
};
...

Dabei kann grundsätzlich jeder in JavaScript zulässige Funktionsname als Hook-Name verwendet werden. Die Datei README.plugins führt eine (unvollständige) Liste von zur Verfügung stehenden Hooks auf. Ein Beispiel für einen wichtigen Hook, welcher u.a. nicht aufgeführt wird, ist aceCreateDomLine, doch dazu später mehr.

Serverseitige vs. clientseitige Plugin-Teile

Etherpad führt Code auf dem Server, als auch auf dem Client aus. In beiden Fällen kommt JavaScript zum Einsatz. Auf der Serverseite bildet dabei ein Gespann aus dem von den ehemaligen Etherpadentwicklern konzipierten AppJet und einem eigenen JavaScript Micro-Framework die Basis für serverseitiges JavaScript.

Ein Plugin besteht immer wenigstens aus der Datei main.js, im Falle des Plugins heading1 also bsp. etherpad/src/plugins/heading1/main.js. Diese wird auf der Serverseite geladen und ausgeführt. Diese Datei muss einen Objektkonstruktur enthalten, welcher der folgenden Benennungsvorschrift entsprechen muss

PluginNameInit

Im Falle des Plugins heading1 heißt der Objektkonstruktur als heading1Init und ist im folgenden als Bestandteil der Datei heading1/main.js dargestellt.

import("etherpad.log");
import("plugins.heading1.hooks");
import("plugins.heading1.static.js.main");

function heading1Init() {
  this.hooks = ['editBarItemsLeftPad', 'aceAttribsToClasses',
  'aceCreateDomLine'];
  this.description = 'heading1';
  this.client = new main.heading1Init();
  this.editBarItemsLeftPad = hooks.editBarItemsLeftPad;
  this.aceAttribsToClasses = main.aceAttribsToClasses;
  this.aceCreateDomLine = main.aceCreateDomLine;
  this.install = install;
  this.uninstall = uninstall;
}

function install() {
  log.info("Installing heading1");
}

function uninstall() {
  log.info("Uninstalling heading1");
}

Die Aufgaben des Objektkonstrukturs sind u.a.

  • Registrierung für bestehende Hook-Calls (this.hooks = …)
  • Definitionen der Hook-Funktionen, welche bei einem entsprechen Hook-Call aufgerufen werden (z.B. this.editBarItemsLeftPad = … oder this.aceAttribsToClasses = …)

An dieser Stelle kann man bereits die ersten Unterschiede zwischen serverseitigem und clientseitigem JavaScript Code erkennen. Vergleicht man einmal die Definitionen der beiden Hooks editBarItemsLeftPad und aceAttribsToClasses

  • this.editBarItemsLeftPad = hooks.editBarItemsLeftPad;
  • this.aceAttribsToClasses = main.aceAttribsToClasses;

so fällt einem auf, dass die eine Funktion unterhalb von hooks und die andere unterhalb von main definiert wird. Die entsprechenden Funktionsdefinitionen finden sich in den Dateien heading1/hooks.js und heading1/static/js/main.js.

Bei editBarItemsLeftPad handelt es sich um serverseitigen JavaScript Code, bei aceAttribsToClasses handelt es sich um Code, welcher sowohl client-, als auch serverseitig ausgeführt wird.

An dieser Stelle wird es zugegebener Maßen etwas unübersichtlich, dennoch ist es die gängige Praxis bei den bestehenden Etherpad-Plugins. Die Hauptplugindatei main.js inkludiert sowohl die Datei hooks.js mit dem serverseitigen Code, als auch die Datei static/js/main.js, welche den clientseitgen JavaScript Code beinhaltet. Dazu der entsprechende Abschnitt aus der Datei README.plugins, welcher diesen Sachverhalt relativ präzise auf den Punkt bringt.

The hook system is replicated on the client side - there is a
plugins.callHook() and plugins can register client side functions to
be called when that hook is called for.

This registration is done from a client side java script file called
static/js/main.js. There is one major catch with this file: It runs
both on the server and client! Beware!

static/js/main.js is modelled after the main main.js, and imported by
that one on the server. The descriptor object created by
pluginNameInit() in static/js/main.js is also included as a property
on the main plugin descriptor object.

Beispiel: Aufruf von heading1 auf der Serverseite

  1. src/ethpad/plugins/headin1/main.js wird aufgerufen
  2. src/ethpad/plugins/headin1/hooks.js wird inkludiert
  3. src/ethpad/plugins/headin1/static/js/main.js wird inkludiert
  4. Mit Hilfe des Objektkonstruktors heading1Init wird ein neues Objekt mit den folgenden Eigenschaften erstellt
    1. hooks = [‚editBarItemsLeftPad‘, ‚aceAttribsToClasses‘, ‚aceCreateDomLine‘];
    2. description = ‚heading1‘;
    3. client = new main.heading1Init();
    4. editBarItemsLeftPad = hooks.editBarItemsLeftPad;
    5. aceAttribsToClasses = main.aceAttribsToClasses;
    6. aceCreateDomLine = main.aceCreateDomLine;
    7. install = install;
    8. uninstall = uninstall;

Beispiel: Aufruf von heading1 auf der Clientseite

  1. src/etherpad/plugins/heading1/static/js/main.js wird aufgerufen
  2. Mit Hilfe des Objektkonstrukturs in DIESER Datei wird ein neues Objekt mit den folgenden Eigenschaften erstellt
    1. hooks = [‚aceAttribsToClasses‘, ‚aceCreateDomLine‘, ‚collectContentPre‘, ‚collectContentPost‘];
    2. aceAttribsToClasses = aceAttribsToClasses;
    3. aceCreateDomLine = aceCreateDomLine;
    4. collectContentPre = collectContentPre;
    5. collectContentPost = collectContentPost;

Vergleicht man nun einmal die Eigenschaft hooks der beiden Objekte, so fällt auf, dass es genau drei verschiedene Arten von Plugincode gibt

  1. Code welcher nur auf der Serverseite ausgeführt wird
    • z.B. editBarItemsLeftPad
  2. Code, welcher nur auf der Clientseite ausgeführt wird
    • z.B. collectContentPre und collectContentPost
  3. Code, welcher sowohl auf der Server- also auch auf der Clientseite ausgeführt wird
    • z.B. aceAttribsToClasses

Man erkennt ebenfalls gut, wie der clientseitge Code serverseitig über die Eigenschaft client eingebunden wird, nämlich via client = new main.heading1Init().

Es gilt nun, anhand der bestehenden Plugins die Verwendung der bestehenden Hook-Calls, wie etwa editBarItemsLeftPad, aceAttribsToClasses oder aceCreateDomLine zu analysieren.

Masterarbeit: SciflowWriter – Ein echtzeitfähiger Editor für kooperatives Schreiben

25 Mai

Die Aufgabe, einen kooperativen Editor für wissenschaftliches Schreiben zu entwickeln verfolgt zwei unterschiedliche Aufgaben.  Zum einen soll es einer Gruppe von Autoren möglich sein, kooperativ an einem Text zu arbeiten. Darüber hinaus sollen alle Bearbeitungs- und Auszeichnungsmöglichkeiten gegeben sein, die wissenschaftliches Schreiben fordert. Bei diesen Möglichkeiten handelt es sich vor allem um das Management von Literaturverweisen, Abbildungen und Textreferenzen. Der Zusatz der Echtzeitfähigkeit verschärft die Anforderung an kooperatives Schreiben dahingehend, dass Änderungen eines Autors augenblicklich für jeden anderen Autor sichtbar werden. Weiterlesen