Chris Anderson hat sich dem Lock-in Problem von AppEngine-Anwendungen angenommen und Appdrop veröffentlicht und auf seinem Blog angekündigt.. Über Appdrop kann man Anwendungen, die für AE progammiert wurden auf Amazons EC2 deployen. Das ganze ist ein cleverer Hack und beruht auf einer modifizierten Version des AppEngine SDKs. Es ist also zu erwarten, dass damit gehostete Anwendungen in ihrer Skalierbarkeit nicht mit nativ gehosteten mithalten werden können.

Das modifizierte SDK ist auf github veröffentlicht und wird bereits geforkt.

Will jemand vielleicht ein CouchDB und Yaws Backend bauen?

Tags: , ,



Tooltips sind eine feine Sache. Sie lassen sich einsetzen, um zusätzliche Informationen zu präsentieren oder um Kontexthilfen anzuzeigen.
Es gibt verschiedene Methoden mittels JavaScript solche Tooltips anzuzeigen. Viele davon nutzen dazu die Title-Attribute im HTML-Tag. Das Title-Attribut ist ja schon von sich aus dazu gedacht, Tooltips anzuzeigen, unterliegt aber verschiedenen Beschränkungen:

  • Die Standardtooltips lassen sich nicht mittels CSS in Form bringen
  • Die Tooltips werden nicht angezeigt, wenn jemand das Tag mit dem Mauszeiger nicht erreichen kann, oder überhaupt keine Maus benutzt
  • Die Tooltips werden nicht wirklich lange angezeigt (Die Ausnahme bildet hier der Opera)
  • Längere Texte in Tooltips werden häufig ungünstig umgebrochen oder abgeschnitten

Die genannten Beschränkungen lassen sich umgehen, in dem man den Tooltip mittels JavaScript selbst erzeugt. Hier wird exemplarisch eine Version gezeigt, die mit jquery arbeitet.

HTML (Ausschnitt)

 <a class="useredit" title="Dieser Link öffnet die Bearbeitunsgmaske für Benutzer.
 Dort können die persönlichen Einstellungen des jeweiligen Benutzers geändert werden." href="/edituser/23">
 Benutzer 23 editieren</a>

JavaScript(Ausschnitt)

$(document).ready(function(){

$(".useredit").hover(function(){

    var content = $(this).attr("title");

    $(this).attr("title",""); // Damit der Browser keinen eigenen Tooltip zeigt,
                                      //  das Title-Attribut leeren
   // Um das Title-Attribut später wieder setzen zu können,
  // ihn in ein temporäres Attribut schreiben.
    $(this).attr("tempTitle",content); 

   var hilfeBox = $('
<div>').attr('class','tooltip');

   var ePosition = getPosition(this); // Bestimme x und y Wert des Hilfe-Links

   var eWidth = getElementWidth(this); // Bestimmte die Breite des Hilfe-Links

   $(hilfeBox).css({
        position: "absolute",
        top: ePosition[1],
        left: ePosition[0]+ eWidth +"px",
        width: 250 + "px",
        border: "2px solid red",
        padding: "6px",
        background: "#FFF"
        });

   $("div.tooltip").text(content);
   $("body").append(hilfeBox);

},function(){

 var content = $(this).attr("tempTitle"); // Den Text wieder einlesen
 $(this).attr("title",content); // Wieder in das Title-Attribut schreiben
 $(this).attr("tempTitle",""); // Temporäres Attribut leeren 

 $('div.tooltip').remove() // Tooltip entfernen
});

});

Will man die Tooltips als Hilfen zur Bedienung benutzen und gleichzeitig eine zentrale Hilfeseite bereitstellen, ergeben sich verschiedene Probleme. Insbesondere die doppelte Datenhaltung (Hilfetexte in den Title-Attributen und im HTML der Hilfeseite) verkompliziert die Wartung der Seiten unnötig. Längere Texte in den Title-Attributen sind unschön, insbesondere wenn die Texte mehrfach verwendet werden.

Um diese Hürden zu überwinden, bietet sich der Einsatz von Ajax an. Man legt eine zentrale Hilfeseite an, die sich über die Navigation erreichen läßt. Hier können Bilder, Screencasts und Texte angezeigt werden, die dem Benutzer helfen sollen. Nun können Texte aus dieser Seite mittels Ajax abgefragt und als Tooltip angezeigt werden. Außerdem lassen sich so auch andere Elemente wie Bilder anzeigen, was bei der Title-Attribut-Methode nur schwerlich möglich ist.

Seit der Version 1.2 bietet jquery in seiner load() Methode eine einfache Möglichkeit über CSS-Selektoren HTML aus einem Dokument zu extrahieren und in das DOM einzufügen. Im folgenden Beispiel wird genau diese Methode verwendet.

Zunächst wird neben ein Eingabefeld zur Suche, ein Link gesetzt, der auf den entsprechenden Anker der Hilfeseite verweist.

HTML (Ausschnitt)

 <fieldset>
  <legend>Suche</legend>
<form method="post">

<label>Suchbegriff:</label>
<input class="txt" name="textfield" type="text" />
  <a id="sH" class="hilfe" name="suche" href="hilfeseite.html#suche">?</a>
  <!-- verweist auf den entsprechenden Anker der Hilfeseite -->

<input name="Submit" type="submit" value="Suchen" />
</form>

</fieldset>

Im JavaScript wird ein Eventlistener aktiviert, der auf MouseOver den Hilfetext in einer Box anzeigt und bei MouseOut, die Box wieder aus dem DOM löscht.

JavaScript (Ausschnitt)

 $("a.hilfe").hover(function(){zeigeHilfe(this)},function(){$('#hilfeBox').remove()})

Dieser Eventlistener wird auf alle Links mit der Klasse “hilfe” gelegt, so dass man beliebig viele Hilfelinks setzen kann.

JavaScript(Ausschnitt)

// Kofigurationsparameter, kann am Anfang des JS gesetzt werden
var helpBox =
  {
      width: 250,
       id: "hilfebox"
  }
;
// Diese Funktion erzeugt die Hilfebox

function zeigeHilfe(element){

    var finalLeft = 0; // X-Position der Hilfebox wird auf Null gesetzt

/* Nun wird das href-Attribut des Links zerlegt. Die beiden Teile werden später im Ajax-Aufruf
    verwendet, um die richtige Seite abzufragen und den richtigen CSS-Selector zu verwenden.
     (s.u. HTML der Hilfeseite)
*/

    var hilfeDaten = element.href.split("#");

    var ePosition = getPosition(element); // Bestimme x und y Wert des Hilfe-Links

    var eWidth = getElementWidth(element); // Bestimmte die Breite des Hilfe-Links

    var viewSize= getViewSize(); // Darstellungsbreite ermitteln

    // wenn das Fenster über den rechten Bildrand ragen sollte, nach links verschieben
    if (ePosition[0] + eWidth + 10 + 250 > viewSize[0])
    {
        finalLeft = ePosition[0] - eWidth - 10 - helpBox.width;
    }
    // Sonst nach Links ausrichten
    else{
        finalLeft = ePosition[0] + eWidth + 10 ;
    }
      // Ein DIV bauen und ihm die ID aus den Konfigurationsparametern (s.o.) geben
    var hilfeBox = $('
<div>').attr('id', helpBox.id) 

    // Styles setzen, Rahmen und Farben sollten besser über eine
    // CSS-Datei gesetzt werden Der Einfachheit halber jetzt aber hier.

    $(hilfeBox).css({
        position: "absolute",
        top: ePosition[1],
        left: finalLeft+"px",
        width: helpBox.width + "px",
        border: "2px solid red",
        padding: "6px",
        background: "#FFF"
    });

    // Hilfebox ins DOM einhängen

    $("body").append(hilfeBox);

/* Inhalte aus der Hilfeseite laden
 hilfeDaten[0] ist hier die Hilfeseite aus dem Href-Attribut des Links,
 hilfeDaten[1] ist hier der Name des Ankers. Dieser entspricht gleichzeitig
 der ID des li-Elements der Hilfethemen. (s.u. HTML)

*/
	$("#"+helpbox.id).load(hilfeDaten[0]+"  #"+hilfeDaten[1]+" p.desc");

}

Die Hilfeseite wird per Ajax geladen. Über den CSS-Selektor wird der Inhalt des Absatzes (.desc) im entsprechenden List-Element (#suche) ausgelesen und in die Hilfebox geschrieben.
Der Link verweist dabei auf die richtige Stelle der Hilfeseite, die dann per Klick erreicht werden kann.

HTML der Hilfeseite (Auschnitt):

<ul id="hilfethemen">
	<li id="useredit"> <!--ID des Abschnitts. Muss dem Name-Attribut
   des Ankers entsprechen. -->
  <a name="useredit"></a> <!--Anker zum Verlinken des Hilfethemas -->
<h3>User Editing</h3>
<img src="http://beispiel.de/einScreenshot.jpg" alt="screenshot" />

Lorem ipsum ...</li>
	<li id="suche">
   <a name="suche"></a>
<h3>Suchfunktion</h3>
<img src="http://beispiel.de/einaAndererScreenshot.jpg" alt="screenshot" />

Per Ajax aus der Hilfeseite geladen. Lorem ipsum dolor sit amet, consetetur
     sanct...</li>
</ul>

Natürlich kann man auf diesem Wege auch ganz einfach mehrere Hilfeseiten anlegen, die nach Themen sortiert sind. Eine größere Webanwendung wird dies zweifelsfrei notwendig machen.

Eine Live-Demo des Codes
Die vollständigen Dateien zum Download

Quellen:

http://jquery.com

Tags: , , ,



App Engine LogoGoogle hat in der letzten Woche ein neues Mitglied seiner großen Webfamilie vorgestellt. Mit AppEngine gibt Google Entwicklern die Möglichkeit seine Infrastruktur zu nutzen. Sie machen es damit sehr einfach Anwendungen zu Entwickeln und auf ihren Serverfarmen laufen zu lassen, Daten auf Google-Datenbanken zu speichern und sogar das User-Authentifizierungs-System mitzubenutzen.

In diesem Artikel versuche ich, einen groben Überblick zu geben.

AppEngine != EC2+S3

Auf den ersten Blick mag es so erscheinen, als hätten Google ein System wie Amazons EC2 und S3 implementiert. Bei näherer Betrachtung wird aber schnell klar das AppEngine eine andere Ausrichtung hat. Während EC2 dem Kunden nichts von den Möglichkeiten und Schwierigkeiten virtualiserter Server vorenthält, konzentriert sich AE darauf, die Hürden bei der Entwicklung von Webanwendungen zu verringern. Der Benutzer von AE muss sich nicht darum kümmern eine Datenbank oder einen Server aufzusetzen, das Deployment einer Anwendung ist ein einziger Befehl und Statistiken werden automatisch erstellt.
Der große Vorteil von AWS ist die Flexibilität des Systems. Cronjobs, verschiedene Sprachen und alles was auf einer oder x virtualisierten Maschinen laufen kann sind möglich. AE hingegen bietet eine sehr eingeschränkten Funktionsumfang. Eine Sprache, eine Datenbank, HTTP-Request und Response. AE ist simpel und skaliert.

Charakter

AppEngine hat einige Charakteristika, die erwähnt sein wollen.
Die Sprache, in der Anwendungen entwickelt werden ist Python. Google plant weitere Sprachen zur Verfügung zu stellen, vorerst ist aber Python die einzige. Meiner Meinung nach eine sehr gute Wahl, zumal die Idee hinter AppEngine -wie gesagt- ist, ein niederschwelliges Angebot zu sein.
Hierbei ist alledings anzumerken, das einige Veränderungen an der Standard-Library vorgenommen wurden. Vereinfacht dargestellt wurde alle Module entfernt, die auf das Dateisystem oder Sockets zugreifen. Keine urllib! Keine urllib2! Zugriff auf Webresourcen geschieht über Googles eigenes URL Fetch Modul. Diese Einschränkungen sind recht gravierend und machen das portieren von bestehenden Python-Webanwendungen aufwendig. Beim Entwickeln neuer Anwendungen auf AppEngine sollten sie allerdings nicht zu sehr ins Gewicht fallen.

Weiterhin gibt keine relationale Datenbank. Daten werden in Googles selbstentwickelter Datenbank Bigtable abgelegt. Bigtable ist eine schemalose Datenbank für strukturierte Daten. Jeder Datensatz in Bigtable wird durch einen eindeutigen Schlüssel identifiziert und kann verschiedene Felder enthalten. Es gibt eine ganze Reihe Datentypen, von einfachen wie String und Float über Listen bis hin zu komplizierteren wie Geokoordinaten oder Adressen; auch für Google-Nutzer gibt es hier einen eigenen Datentyp. Die Kommunikation mit Bigtable ist in eine API abstrahiert, die das Erstellen von Datenstrukturen sowie das Schreiben und Lesen in die DB vereinfacht.

Ein Codebeispiel sagt in diesem Fall wohl mehr als viele Worte

from google.appengine.ext import db
from google.appengine.api import users

class Pet(db.Model):
    name = db.StringProperty(required=True)
    type = db.StringProperty(required=True, choices=set(["hund", "katze", "maus"]))
    birthdate = db.DateProperty()

pet = Pet(name="Fluffy", type="cat", owner=users.get_current_user())
pet.weight_in_pounds = 2

pet.put()

Hier wird eine Datenstruktur für Haustiere angelegt. Es gibt Felder für Name und Tierart (beschränkt auf eine vorgegebene Auswahl) sowie ein Integer und ein Datumsfeld. Im Feld owner wird ein Google-Account referenziert. Es wird ein Tier namens “Fluffy” angelegt und per .put() in die Datenbank geschoben.
Des weiteren gibt es mit GQL (gesprochen: gequel) eine SQL ähnliche Sprache, mit der Daten aus der Datenbank abgefragt werden. Die Abfrage

pets = db.GqlQuery(“SELECT * FROM Pet WHERE pet.owner = :1″, users.get_current_user())

gibt beispielsweise ein Query-objekt mit den Haustieren des gerade angemeldeten Google-Benutzers zurück. Diese Query kann weiter gefiltert werden oder man kann per pets.fetch(3) die ersten 3 Tiere in einer Liste erhalten.

Da Bigtable darauf ausgelegt ist im Google-Maßstab zu Skalieren, ist sie nicht darauf optimiert einzelne Abfragen möglichst schnell auszuführen, sondern darauf auch unter größter Last und bei großen Datenmengen gleichbleibende Performance zu bringen. Es ist also angebracht auf Entwicklerseite etwas umzudenken. Zum Beispiel ist es geschickt Daten stark zu denormalisieren oder Funktionen, wie z.B. Summenbildung, die RDMSe normalerweise on-the-fly anbieten beim Abspeichern der Daten zu erledigen und im Datensatz zu hinterlegen.

Auch gibt es in AE keine Entsprechung zu einer Session. Wenn Daten über mehrere HTTP-Requests persisitert werden sollen, muss dies über die Datenbank geschehen. Alledings bietet die Datastore Engine die Möglichkeit dynamisch Felder zu einem Datensatz hinzuzufügen.

SDK

Das Entwickeln von AE-Anwendungen läuft über ein SDK, das die AE-Infrastruktur lokal Simuliert. Das SKD gibt es für Linux, Mac und Windows und man kann es sich auch herunterladen ohne einen AppEngine-Account zu haben. Beinhaltet sind unter anderem, ein Entwicklungs-Server, lokaler Datastorage und simulierte User-Anmeldungen.
Eine Anwendung besteht im einfachsten Fall aus einer yaml -Konfigurationsdatei und einer Python-Datei. In der Konfiguration (app.yaml) werden die AppEngine-ID und die Version der Anwendung eingetragen und über Regular-Expressions festgelegt welche Skripte für welche URLs verantworlich sind.

Deployment

Wenn man eine Anwendung mit dem SDK entwickelt und getestet hat lässt sie sich mit dem Befehl:

appcfg.py update myapp/

im Web auf den Google-Servern veröffentlichen. Wobei myapp/ der Pfad, ist unterhalb dem die lokalen Skripte liegen. Die Anwendung ist nun unter http://my-app-id.appspot.com/ zu erreichen.
Unkomplizierter kann ich es mir kaum vorstellen.

Es gibt ein Admin-Interface für AE-Apps (Dashboard) über das Statistiken und Anwendungs-Logs angeschaut werden können. Hier gibt es ein Interface zum Betrachten und editieren der Datenbank-Inhalte sowie die Möglichkeit Mitarbeiter für kollaboratives Entwickeln einer Anwendung festzulegen. Ein weiteres schönes Feature ist die Versionsverwaltung. Wenn man seine Anwendung in der app.yaml versioniert hat kann man nun mit einem Klick aussuchen welche Version Live gehen soll.

Das System für das Deployment von Anwendungen, sehe ich als ein großes Plus für AppEngine.

Django

Eine der überraschenderen Enthüllungen im Rahmen des Releases von AppEngine ist, das die Möglichkeit besteht das Framework Django zu benutzen. Im Prinzip sollte jedes Python-Webframework, das sich an den WSGI-Standard hält unter AppEngine laufen, aber das AE-Team scheint sich auf Django zu konzentrieren. Beim SDK ist die Version v0.96.1 mit inbegriffen.
Die Community um das Django-Project ist natürlich in hellem Aufrur. Zum einen auf Grund der “Adelung” durch google und der neuen Möglichkeiten. Zum anderen gibt es aber auch viel Enttäuschung, da beim näheren hinsehen schnell klar wird das Django unter AppEngine stark eingeschränkt läuft.
Als wichtigster Punkt (neben weiteren) ist aufzuführen, das der ORM nicht mehr verwendet werden kann, da Bigtable keine relationale Datenbank ist. Dies führt natürlich dazu das Djangos eingebautes Administrations-Interface und die meisten der third-party-apps, die Django nicht zuletzt so attraktiv machen, nicht funktionieren.

Django kann also auf AppEngine bei weitem nicht so glänzen wie auf LAMP.

Wenn man sich AppEngine anschaut und bereits mit Django vertraut ist wird einem allerdings auffallen, das das Web-Framework an vielen Stellen Pate gestanden haben mag.
Das Templating-System wurde unverändert von Django übernommen. Das Mapping zwischen URLs und Skripten in der app.yaml läuft wie bei Django über Regular-Expressions. Der Django URL-Mapper ist deutlich leistungsfähiger, kann aber Problemlos verwendet werden.

Bei Abfragen der Datenbank wird bei AE ein GqlQuery -Objekt zurueckgegeben, welches dem Django equivalent QuerySet nahekommt.

Die Datenbank API ist Djangos ORM erstaunlich ähnlich. Zum Vergleich die Modelle aus Djangos offiziellem Tutorial für beide Systeme (von mir leicht angepasst):


Hier die Version für AppEngine (von Shabda Raaj):

from google.appengine.ext import db

class Poll(db.Model):
    question = db.StringProperty()
    created_on = db.DateTimeProperty(auto_now_add = 1)
    created_by = db.UserProperty()

class Choice(db.Model):
    poll = db.ReferenceProperty(Poll)
    choice = db.StringProperty()
    votes = db.IntegerProperty(default = 0)

und im Django ORM Original:

from django.db import models
from django.contrib.auth.models import User

class Poll(models.Model):
    question = models.CharField(max_length=200)
    created_on = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey(User)

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice = models.CharField(max_length=200)
    votes = models.IntegerField()

Community

Die Reaktion in der Developer-Community ist recht heftig. Die 10.000 ersten Invites gingen weg wie warme Semmeln. Es sind bereits über 700 Blog-Postings zum Thema veröffentlicht worden (davon sind allerdings recht wenige auf deutsch und bis jetzt noch keiner von mir ;-) ).
Auf appgallery, der freiwilligen, zentralen Registrierung für Apps sind über 100 Anwendungen registriert. Es gibt bereits einige Open-Source-Projekte, die sich mit AppEngine befassen. In der Google Group gibt es über 2000 Beiträge.
Im Issue Tracker sind uber 200 Tickets angelegt worden, von denen sind einige (wenige) vom Team bereits bearbeited worden, viele noch offen und in einigen ist Chaos und Flamewar ausgebrochen.

Es gibt Anzeichen dafür, das das Team das Feedback ernstnimmt und in der weiteren Entwicklung darauf eingehen möchte. Ein häufig bemängeltes Thema war z.B. schnell nach dem Release das Problem, das man sich mit der Entscheidung, eine Anwendung auf der neuen Plattform zu Programmieren, stark vom grossen G abhängig macht. (Als Beispiel ein Post von Tim O’Reilly). Worauf das AppEngine Team schnell mit der Ankündigung eines Daten Im- und Export-features reagiert hat und die Community nach ihren Vorstellungen fragt.
(Das löst natuerlich das Problem nicht, das AppEngine-Code nicht (ohne weiteres) auf einer anderen Platform läuft.)

Fazit

AppEngine ist eine äusserst vielversprechende Erweiterung der Web-(2.0 wenn man so will)-Landschaft. Google versucht hier anscheinend eine Art Blogger für Web-Anwendungen zu schaffen, bei dem der User sich nur noch um den tatsächlichen Content kümmern muss. Content wäre hier natürlich Quellcode. Die Schwelle mal schnell eine Webanwendung zu entwickeln und zu sehen ob es funktioniert oder Geld bringt ist so sehr niedrig, da wenig bis nichts ausser Arbeitszeit investiert werden muss und der Aufwand, die Anwendung zu betreiben und zu administrieren, minimal ist.

Ob AE ein Erfolg wird muss sich noch zeigen. Ich denke es wird stark davon abhängen wie Google auf die Bedürfnisse der Entwickler von Anwendungen eingehen kann und in welche Richtung die Entwicklung gehen wird.


Zum Beispiel könnte man sich vorstellen, das sich Google AppEngine als den zukünftigen Kleister zwischen seinen zahllosen anderern Webprodukten vorstellt, von denen viele bereits per Python angesrochen werden können.

Tags: ,