<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Zustandsforschung &#187; programmierung</title>
	<atom:link href="http://www.zustandsforschung.de/index.php/tag/programmierung/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.zustandsforschung.de</link>
	<description>Mit offenen Augen durch die Welt</description>
	<lastBuildDate>Wed, 04 Jan 2012 13:22:09 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Java Profiling (fast) ohne Tools</title>
		<link>http://www.zustandsforschung.de/index.php/java-profiling-fast-ohne-tools/</link>
		<comments>http://www.zustandsforschung.de/index.php/java-profiling-fast-ohne-tools/#comments</comments>
		<pubDate>Wed, 14 Dec 2011 13:06:50 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[profiling]]></category>
		<category><![CDATA[programmierung]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=2205</guid>
		<description><![CDATA[In manchen Situationen kommt der Einsatz eines professionellen Profiling-Tools nicht in Frage. Das kann beispielsweise dann vorkommen, wenn es gilt, eine durch diverseste Firewalls geschützte Java-Applikation zu profilen und in der Firma, in der man arbeitet kein entsprechendes Tool eingesetzt werden kann oder darf (oder wenn der für das Tool zuständige &#8220;Experte&#8221; nicht greifbar oder [...]]]></description>
			<content:encoded><![CDATA[<p>In manchen Situationen kommt der Einsatz eines professionellen Profiling-Tools nicht in Frage. Das kann beispielsweise dann vorkommen, wenn es gilt, eine durch diverseste Firewalls geschützte Java-Applikation zu profilen und in der Firma, in der man arbeitet kein entsprechendes Tool eingesetzt werden kann oder darf (oder wenn der für das Tool zuständige &#8220;Experte&#8221; nicht greifbar oder nicht kooperativ ist).</p>
<p>Die JVM bringt aber auch ein eingebautes Profiling mit, das zwar nicht mit dem was eben die &#8220;richtigen&#8221; Profiler liefern vergleichbar ist, aber eben doch ganz brauchbar, wenn es darum geht herauszufinden, warum eine Applikation lahm ist. Die Funktion nennt sich <em>hprof</em> und viele kennen das vielleicht als Methode, um Heap-Dumps zu erzeugen. Aber das Ding kann noch mehr: um zu zeigen, wie man damit Performance-Probleme erkennen kann, verwende ich hier ein einfaches Beispiel, das Fibonacci-Zahlen rekursiv berechnet (der Source-Code für das Beispiel ist <a href="https://github.com/zustandsforschung/hprof2html/blob/master/example/Fib.java">hier</a> verlinkt).</p>
<p>Das hier ist die Java-Klasse, mit der wir die entsprechenden Daten erzeugen:</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">package</span> <span style="color: #006699;">example</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> Fib <span style="color: #009900;">&#123;</span>
&nbsp;
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">long</span> calculate<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">long</span> n<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>n <span style="color: #339933;">==</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000000; font-weight: bold;">if</span><span style="color: #009900;">&#40;</span>n <span style="color: #339933;">==</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000000; font-weight: bold;">return</span> calculate<span style="color: #009900;">&#40;</span>n<span style="color: #339933;">-</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> calculate<span style="color: #009900;">&#40;</span>n<span style="color: #339933;">-</span><span style="color: #cc66cc;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">void</span> main<span style="color: #009900;">&#40;</span><span style="color: #003399;">String</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> args<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		Fib fib <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Fib<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">println</span><span style="color: #009900;">&#40;</span>fib.<span style="color: #006633;">calculate</span><span style="color: #009900;">&#40;</span><span style="color: #003399;">Long</span>.<span style="color: #006633;">valueOf</span><span style="color: #009900;">&#40;</span>args<span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Ruft man nun diese Klasse mit folgenden Parametern auf, erhält man eine Datei java.hprof.txt, die jede Menge Stack-Traces und am Ende eine Tabelle mit Profiling-Informationen enthält:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">java -agentlib:hprof=cpu=times,depth=100 example.Fib 20</pre></div></div>

<p>Den vollständigen Satz an möglichen Optionen erhält man mit <em>java -agentlib:hprof=help</em> oder im Artikel <a href="http://java.sun.com/developer/technicalArticles/Programming/HPROF.html">HPROF: A Heap/CPU Profiling Tool in J2SE 5.0</a>.</p>
<p>Die Tabelle am Ende der Datei sieht ungefähr so aus:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">CPU TIME (ms) BEGIN (total = 96) Wed Dec 07 16:53:53 2011
rank   self  accum   count trace method
   1 15.63% 15.63%    5020 301386 example.Fib.calculate
   2  9.38% 25.00%    4760 301385 example.Fib.calculate
   3  8.33% 33.33%    3632 301387 example.Fib.calculate
   4  7.29% 40.62%    2942 301384 example.Fib.calculate
   5  6.25% 46.87%    2026 301388 example.Fib.calculate
   6  5.21% 52.08%    1024 301389 example.Fib.calculate
   7  5.21% 57.29%     512 301390 example.Fib.calculate
   8  2.08% 59.38%     174 300332 java.lang.StringBuffer.append
   9  2.08% 61.46%    1152 301383 example.Fib.calculate
  10  1.04% 62.50%     256 301375 java.lang.Long.&amp;lt;init&amp;gt;
  11  1.04% 63.54%     256 301374 java.lang.Number.&amp;lt;/init&amp;gt;&amp;lt;init&amp;gt;
  12  1.04% 64.58%       1 301231 java.util.Hashtable.get
  13  1.04% 65.63%       5 301162 java.lang.StringBuilder.toString
  14  1.04% 66.67%       1 301111 java.io.FilePermissionCollection.&amp;lt;clinit&amp;gt;
  15  1.04% 67.71%       1 301031 java.io.Win32FileSystem.normalize
  16  1.04% 68.75%       1 300988 sun.net.www.protocol.file.FileURLConnection.getPermission
  17  1.04% 69.79%       1 300986 java.io.FilePermission.init
  18  1.04% 70.83%       1 300982 java.io.FilePermission$1.run
  19  1.04% 71.88%      44 300950 java.lang.StringBuffer.append
  20  1.04% 72.92%       1 300886 sun.net.www.protocol.file.Handler.createFileURLConnection
...</pre></div></div>

<p>Hier kann man schon sehen, dass unsere Methode calculate() wohl nicht die schnellste ist. Außerdem sieht man noch, dass die langen Laufzeiten in dieser Methode in unterschiedlichen Threads immer mal wieder vorkommen.</p>
<p>Jetzt kann man in der gleichen Datei nach der Nummer, die in der Spalte <em>trace</em> steht suchen und findet dazu für die erste Zeile folgenden Thread</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">TRACE 301386:
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.calculate(Fib.java:Unknown line)
example.Fib.main(Fib.java:Unknown line)</pre></div></div>

<p>Hier kann man dann ablesen, dass es sich wohl um den rekursiven Aufruf handelt, der hier das meiste der Performance auffrisst. Wenn man Glück hat, dann stehen da auch noch die Zeilennummern dabei &#8211; warum das hier nicht der Fall ist, weiß ich ehrlich gesagt nicht.</p>
<p>Weil diese java.hprof.txt-Files gerne mal ziemlich riesig werden können und die Sucherei in den Files nicht wirklich Spass macht, habe ich ein kleines Python-Skript geschrieben, das die Files in eine HTML-Seite umwandelt, in der man per Klick von der Liste zu den Threads springen kann und wo man auch bestimmte Klassen ausfiltern kann, von denen man schon weiß, dass die nix mit dem Problem zu tun haben. Das Skript habe ich hprof2html getauft und es kann hier heruntergeladen werden: <a href="https://github.com/zustandsforschung/hprof2html">https://github.com/zustandsforschung/hprof2html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/java-profiling-fast-ohne-tools/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Versionsnummerierung</title>
		<link>http://www.zustandsforschung.de/index.php/versionsnummerierung/</link>
		<comments>http://www.zustandsforschung.de/index.php/versionsnummerierung/#comments</comments>
		<pubDate>Thu, 17 Feb 2011 11:40:38 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[programmierung]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[versionierung]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=1857</guid>
		<description><![CDATA[Zu irgendeinem Zeitpunkt kommt in jedem Software-Entwicklungs-Projekt die Frage nach der Nummerierung der Versionen auf. Spätestens bei der ersten Lieferung an den Kunden bzw. die QA-Abteilung möchte man wissen, auf welcher Version getestet und Fehler gefunden werden. Auch wenn man eine Bibliothek mit einer öffentlichen API entwickelt, muss man seinen Benutzern ab der ersten Veröffentlichung [...]]]></description>
			<content:encoded><![CDATA[<p>Zu irgendeinem Zeitpunkt kommt in jedem Software-Entwicklungs-Projekt die Frage nach der Nummerierung der Versionen auf. Spätestens bei der ersten Lieferung an den Kunden bzw. die QA-Abteilung möchte man wissen, auf welcher Version getestet und Fehler gefunden werden. Auch wenn man eine Bibliothek mit einer öffentlichen API entwickelt, muss man seinen Benutzern ab der ersten Veröffentlichung eine Versionsnummer mitteilen.</p>
<p>Da gibt es dann die unterschiedlichsten Paradigmen, nach denen man eine solche Versionsnummer vergeben kann. Am bemerkenswertesten finde ich die Variante bei <a href="http://www.tug.org/">TeX</a>, wo sich die Versionsnummer mit jedem Release der Zahl <em>pi</em> annähert &#8211; aktuell ist man bei 3.1415926.</p>
<p>Aber was ist wirklich sinnvoll und gibt es eine Art Standard dafür? Tatsächlich gibt es wirklich so etwas wie einen Standard. Er heißt <a href="http://semver.org/">SemVer</a> (Semantic Versioning) und wurde von <a href="http://tom.preston-werner.com/">Tom Preston-Werner</a> (Gravatar/GitHub) verfasst. Obwohl sich dieser Standard hauptsächlich auf alles bezieht, was eine öffentliche API zur Verfügung stellt, kann das dort beschriebene Schema doch auch für andere Software-Produkte angewandt werden.</p>
<p>Darin folgt eine Versionsnummer folgendem Format:</p>
<pre>&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;&lt;tag&gt;</pre>
<p>Daraus ergeben sich dann Versionsnummern wie z.B. 1.4.2 oder 0.4.1beta2 &#8211; Tags in der Versionskontrolle lauten dann v1.4.2 oder v0.4.1beta2. Dabei gilt es folgende Regeln zu beachten:</p>
<p><strong>Major</strong></p>
<p>Sollte immer dann inkrementiert werden, wenn sich essentielle Dinge geändert haben. Im Fall einer API könnte das eine Inkompatibilität zur vorhergehende Version sein. Bei einem Produkt ein komplettes Neuschreiben oder ein deutlicher Sprung in der Funktionalität. Ich würde empfehlen vor dem ersten Release eine 0.x.x-Version zu verwenden und danach auf 1.x.x zu erhöhen &#8211; außer im Fall von Open Source Software natürlich <img src='http://www.zustandsforschung.de/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p><strong>Minor</strong></p>
<p>Kleinere Funktionserweiterungen erhöhen die Minor-Version. Bei APIs gilt, dass diese Änderungen in jedem Fall abwärtskompatibel zur nächstkleineren Minor-Version sein müssen.</p>
<p><strong>Patch</strong></p>
<p>Bugfixes, Konfigurationsänderungen und eigentlich alles, was nicht die bestehende Funktionalität erweitert erhöht die Patch-Version.</p>
<p><strong>Tag</strong></p>
<p>Um besondere Versionen besonders zu kennzeichnen, kann an die Versionsnummer noch ein Tag angehängt werden (z.B. &#8220;beta2&#8243; oder &#8220;criticalfix&#8221;).</p>
<p>Die SemVer ist natürlich nicht das einzige Versionierungs-Schema. Zum Beispiel gibt es noch die <a href="http://apr.apache.org/versioning.html">Versionierungsregeln des Apache Portable Runtime-Projekts</a> oder die <a href="http://mojo.codehaus.org/versions-maven-plugin/version-rules.html">Maven Version Number Rules</a>. Egal welche Versionierungsregeln man am Ende wählt &#8211; wichtig ist vor allem, dass diese Regeln klar dokumentiert sind und sich über die Projektlaufzeit nicht ändern, damit man tatsächlich auch aus der Versionsnummer Informationen ableiten kann.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/versionsnummerierung/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Experimente mit HTML5: Das Canvas-Element</title>
		<link>http://www.zustandsforschung.de/index.php/experimente-mit-html5-das-canvas-element/</link>
		<comments>http://www.zustandsforschung.de/index.php/experimente-mit-html5-das-canvas-element/#comments</comments>
		<pubDate>Wed, 17 Nov 2010 18:54:56 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[programmierung]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=1718</guid>
		<description><![CDATA[Nachdem HTML5 ja mittlerweile von immer mehr Browsern unterstützt wird, ist es an der Zeit, sich doch mal damit zu beschäftigen. Ich habe mir für mein erstes Experiment das &#8220;Canvas&#8221;-Element ausgesucht. Canvas heißt auf Deutsch &#8220;Leinwand&#8221; und ungefähr so wird das Element auch benutzt. In der HTML5-Spezifikation wird das Element folgendermaßen definiert: 
&#8220;The canvas element [...]]]></description>
			<content:encoded><![CDATA[<p>Nachdem HTML5 ja mittlerweile von immer mehr Browsern unterstützt wird, ist es an der Zeit, sich doch mal damit zu beschäftigen. Ich habe mir für mein erstes Experiment das &#8220;Canvas&#8221;-Element ausgesucht. Canvas heißt auf Deutsch &#8220;Leinwand&#8221; und ungefähr so wird das Element auch benutzt. In der <a href="http://www.w3.org/TR/html5">HTML5-Spezifikation</a> wird das Element folgendermaßen definiert: </p>
<blockquote><p>&#8220;The canvas element provides scripts with a resolution-dependent bitmap canvas, which can be used for rendering graphs, game graphics, or other visual images on the fly.&#8221;</p></blockquote>
<p>Das Canvas-Element wird irgendwo auf der Seite platziert und tut selbst erst mal gar nichts:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;canvas id=&quot;canvas&quot; &gt;&lt;/canvas&gt;</pre></div></div>

<p>Das was dieses Element so speziell macht, ist die damit verknüpfte JavaScript-API: Man kann Linien und Kästchen zeichnen, Bilder und Text dort hineinmalen, mit Schatteneffekten versehen und transformieren. </p>
<p><span id="more-1718"></span><br />
In diesem Beispiel werden wir zunächst einen Text auf unsere Leinwand schreiben. Dazu benötigt man zunächst den Context des Canvas (den Parameter &#8220;2d&#8221; muss man immer mitgeben):</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">var</span> canvas <span style="color: #339933;">=</span> document.<span style="color: #660066;">getElementById</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;canvas&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #003366; font-weight: bold;">var</span> context <span style="color: #339933;">=</span> canvas.<span style="color: #660066;">getContext</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;2d&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Dann können wir in den Context etwas schreiben:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">context.<span style="color: #660066;">shadowColor</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;#440a0a&quot;</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">shadowOffsetX</span> <span style="color: #339933;">=</span> <span style="color: #CC0000;">100</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">shadowOffsetY</span> <span style="color: #339933;">=</span> <span style="color: #CC0000;">100</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">shadowBlur</span> <span style="color: #339933;">=</span> <span style="color: #CC0000;">10</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">fillStyle</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;#0a0a0a&quot;</span><span style="color: #339933;">;</span>  
context.<span style="color: #660066;">font</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;bold 100px sans-serif&quot;</span><span style="color: #339933;">;</span> 
context.<span style="color: #660066;">textAlign</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;center&quot;</span><span style="color: #339933;">;</span> 
context.<span style="color: #660066;">textBaseline</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;middle&quot;</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">fillText</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;Amsterdam&quot;</span><span style="color: #339933;">,</span> width <span style="color: #009966; font-style: italic;">/ 2 - 100, height /</span> <span style="color: #CC0000;">2</span> <span style="color: #339933;">-</span> <span style="color: #CC0000;">100</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Wir setzen eine Farbe, Position und Verschwommenheitswert (<em>shadowBlur</em>) für den Schatten, bestimmen dann wie der Text geschrieben werden soll und schreiben den Text am Ende in den Context. In diesem Fall hat der Text die gleiche Farbe wie der Hintergrund, so dass wir tatsächlich nur den verschwommenen Schatten sehen können &#8211; was für einen netten Effekt sorgt.</p>
<p>Der nächste Schritt besteht darin, Bilder in das Canvas zu malen, die darüberhinaus auch gleich noch animiert werden sollen. Animation im Canvas-Element bedeutet ganz einfach alle paar Millisekunden den kompletten Inhalt in leicht veränderter Form neu zu zeichnen. Man erreicht dies über <em>setTimeout</em> oder wie im Beispiel über <em>setInterval</em>:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">setInterval<span style="color: #009900;">&#40;</span>draw<span style="color: #339933;">,</span> <span style="color: #CC0000;">10</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Zuvor wurden zunächst 20 Objekte erzeugt, die ein Bild enthalten und zusätzliche Parameter erhalten können. Diese Parameter werden zunächst in der initialize-Methode initialisiert, um den Bildern eine Richtung, Orientierung, Initialposition und -rotation zu geben:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> initialize<span style="color: #009900;">&#40;</span>image<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    image.<span style="color: #660066;">rot</span> <span style="color: #339933;">=</span> Math.<span style="color: #660066;">random</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> <span style="color: #CC0000;">360</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> xOrientation <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>Math.<span style="color: #660066;">ceil</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0.5</span> <span style="color: #339933;">-</span> Math.<span style="color: #660066;">random</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">-</span> <span style="color: #CC0000;">0.5</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> <span style="color: #CC0000;">2</span><span style="color: #339933;">;</span>
    <span style="color: #003366; font-weight: bold;">var</span> yOrientation <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>Math.<span style="color: #660066;">ceil</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0.5</span> <span style="color: #339933;">-</span> Math.<span style="color: #660066;">random</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">-</span> <span style="color: #CC0000;">0.5</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> <span style="color: #CC0000;">2</span><span style="color: #339933;">;</span>
    image.<span style="color: #660066;">x</span> <span style="color: #339933;">=</span> xOrientation <span style="color: #339933;">&gt;</span> <span style="color: #CC0000;">0</span> <span style="color: #339933;">?</span> width <span style="color: #339933;">:</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">240</span><span style="color: #339933;">;</span>
    image.<span style="color: #660066;">y</span> <span style="color: #339933;">=</span> yOrientation <span style="color: #339933;">&gt;</span> <span style="color: #CC0000;">0</span> <span style="color: #339933;">?</span> height <span style="color: #339933;">:</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">240</span><span style="color: #339933;">;</span>
    image.<span style="color: #660066;">dirX</span> <span style="color: #339933;">=</span> xOrientation <span style="color: #339933;">*</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span> <span style="color: #339933;">*</span> Math.<span style="color: #660066;">random</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    image.<span style="color: #660066;">dirY</span> <span style="color: #339933;">=</span> yOrientation <span style="color: #339933;">*</span> <span style="color: #339933;">-</span><span style="color: #CC0000;">1</span> <span style="color: #339933;">*</span> Math.<span style="color: #660066;">random</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Dann wird wie oben erwähnt die draw-Funktion alle 10 Millisekunden aufgerufen. Dort wird immer erst der gesamte Zeichenbereich gelöscht und anschließend neu gezeichnet. Dabei zeichnen wir zunächst den bereits beschriebenen Schriftzug. Dann wir innerhalb einer Schleife die Position und Drehung für jedes Bild neu berechnet und dann das Bild in das Canvas gezeichnet. </p>
<p>Wichtig ist hier das Prinzip zu verstehen, wie beim Canvas-Element transformiert wird: Es existiert eine Transformationsmatrix, die mit den entsprechenden Befehlen geändert wird. Danach wird beim Zeichnen eines Objekte in das Canvas diese Matrix auf das zu zeichnende Element angewendet. Zusätzlich gibt es einen Stack für Transformationsmatrizen (und Canvas-Attribute), den man verwenden kann, wenn man so wie in diesem Beispiel mehrere Elemente unterschiedlich transformieren möchte.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">x</span> <span style="color: #339933;">+=</span> amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">dirX</span><span style="color: #339933;">;</span>
amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">y</span> <span style="color: #339933;">+=</span> amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">dirY</span><span style="color: #339933;">;</span>
amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">rot</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">rot</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">%</span> <span style="color: #CC0000;">360</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">save</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">translate</span><span style="color: #009900;">&#40;</span>amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">x</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">120</span><span style="color: #339933;">,</span> amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">y</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">120</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">rotate</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">2</span> <span style="color: #339933;">*</span> Math.<span style="color: #660066;">PI</span> <span style="color: #339933;">/</span> <span style="color: #CC0000;">360</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">rot</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">translate</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">-</span><span style="color: #009900;">&#40;</span>amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">x</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">120</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color: #009900;">&#40;</span>amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">y</span> <span style="color: #339933;">+</span> <span style="color: #CC0000;">120</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">drawImage</span><span style="color: #009900;">&#40;</span>amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">image</span><span style="color: #339933;">,</span> amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">x</span><span style="color: #339933;">,</span> amsterdam<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #660066;">y</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
context.<span style="color: #660066;">restore</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Um jedes Bild einzeln drehen zu können ist folgendes Vorgehen notwendig:</p>
<ol>
<li>Der aktuelle Zustand des Canvas-Contextes wird abgespeichert. Der Zustand enthält nicht das was im Moment im Canvas zu sehen ist, sondern die aktuelle Transformationsmatrix und gesetzte Attribute.</li>
<li>Die Transformationsmatrix wird geändert und der Ursprungspunkt des Koordinatensystems zum Bildmittelpunkt verschoben.</li>
<li>Die Transformationsmatrix wird für eine Rotation geändert.</li>
<li>Das Koordinatensystem wird wieder zurückverschoben.</li>
<li>Das Bild wird tatsächlich auf das Canvas gezeichnet.</li>
<li>Der vorherige Zustand des Canvas wird wiederhergestellt, damit die nächste Drehung wieder korrekt abgebildet werden kann.</li>
</ol>
<p>Das ganze machen wir alle 10 ms für jedes Bild und dadurch entsteht dann die Animation, die alle Bilder durch das Fenster rotieren lässt. Hinter folgendem Link befindet sich das hier beschriebene Beispiel (funktioniert natürlich nur mit einem HTML5-fähigen Browser):</p>
<p><a href="/wp-content/uploads/2010/12/canvas/">Fliegende Bilder in HTML5</a></p>
<p>HTML5 besteht natürlich aus noch viel mehr als nur dem Canvas-Element. <a href="http://diveintohtml5.org/">Dive Into HTML5</a> ist ein ausgezeichnetes englisches Tutorial, das die wichtigsten Neuerungen von HTML5 verständlich vorstellt.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/experimente-mit-html5-das-canvas-element/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Versionsverwaltung mit Mercurial</title>
		<link>http://www.zustandsforschung.de/index.php/versionsverwaltung-mit-mercurial/</link>
		<comments>http://www.zustandsforschung.de/index.php/versionsverwaltung-mit-mercurial/#comments</comments>
		<pubDate>Wed, 14 Jul 2010 10:49:50 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[mercurial]]></category>
		<category><![CDATA[programmierung]]></category>
		<category><![CDATA[scm]]></category>
		<category><![CDATA[versionierung]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=1549</guid>
		<description><![CDATA[Vor ein paar Monaten bin ich über das verteilte Source-Control-Management-Tool Mercurial gestoßen und habe angefangen mich damit zu beschäftigen. Mittlerweile setze ich es im privaten wie im professionellen Bereich ein und weil ich es so toll und praktisch finde, möchte es euch hier ein bisschen näher bringen.

Unterschied zum zentralisierten Modell
Der wichtigste Unterschied zwischen Mercurial und [...]]]></description>
			<content:encoded><![CDATA[<p>Vor ein paar Monaten bin ich über das verteilte Source-Control-Management-Tool <a href="http://mercurial.selenic.com/">Mercurial</a> gestoßen und habe angefangen mich damit zu beschäftigen. Mittlerweile setze ich es im privaten wie im professionellen Bereich ein und weil ich es so toll und praktisch finde, möchte es euch hier ein bisschen näher bringen.</p>
<p><span id="more-1549"></span></p>
<h2>Unterschied zum zentralisierten Modell</h2>
<p>Der wichtigste Unterschied zwischen Mercurial und traditionellen Versionierungssystemen wie CVS oder SVN ist das verteilte Modell. Das bedeutet, es gibt nicht mehr nur ein zentrales Repository, sondern jeder Entwickler hat quasi eine Kopie des zentralen Repositories bei sich lokal liegen.</p>
<p>Der Entwickler checkt nun zunächst in sein lokales Repository ein und kann damit arbeiten wie z.B. mit einem SVN, über das er die alleinige Kontrolle hat. Zu einem Zeitpunkt, an dem der Entwickler mit seiner Arbeit so weit zufrieden ist, dass er sie den anderen an dem Projekt beteiligten Entwicklern zur Verfügung stellen möchte, schiebt er alles in das übergeordnete Repository.</p>
<p>Bei diesem Vorgang wird nicht nur der aktuelle Stand hochgeschoben, sondern auch alle Zwischenstände, die der Entwickler an seinem lokalen Repository eingecheckt hat. Außerdem kann sich das übergeordnete Repository selbst von einem weiteren Repository bedienen, so dass nicht nur zwei Ebenen sondern eine ganze Repository-Hierarchie möglich ist.</p>
<p>Für mich selbst war zunächst vor allem die Tatsache ungewohnt, dass man bei sich lokal eincheckt &#8211; das klingt ein bisschen so als wäre der Code in größerer Gefahr, weil er ja nur auf dem eigenen Rechner liegt. In der Praxis erweist sich das aber als großer Vorteil, denn man kann auf die Art viel freier ausprobieren und auch mal Zwischenstände einchecken, die nicht so hundertprozentig stabil sind und die man normalerweise seinen Kollegen nicht als Checkin zumuten würde.</p>
<h2>Mercurial</h2>
<p>Mercurial selbst ist Open-Source-Software und benötigt Python ab Version 2.4 zum Funktionieren. Mitte 2005 gab Mat Mackall bekann, daran zu arbeiten. Mittlerweile hat Mercurial die Versionsnumer 1.6 erreicht und ist recht verbreitet &#8211; so werden unter anderem die Sourcen von FireFox, OpenOffice und Python damit verwaltet. Außerdem kann man bei den bei <a href="http://code.google.com">Google Code</a> gehosteten Projekten außer SVN auch Mercurial einsetzen.</p>
<p>Es gibt ein gutes Eclipse-Plugin, das sich <a href="http://www.javaforge.com/project/HGE">MercurialEclipse</a> nennt und eine Shell-Extension für Windows ähnlich dem bekannten TortoiseSVN, die sich <a href="http://tortoisehg.bitbucket.org/">TortoiseHG</a> nennt. </p>
<p>Das folgende Mini-Tutorial zeigt allerdings den Standardfall: Mercurial über die Kommandozeile.</p>
<h2>Mini-Tutorial</h2>
<p>Der Befehl, um Mercurial aufzurufen ist hg (das chemische Zeichen für Quecksilber, englisch eben &#8220;mercurial&#8221; &#8211; soll wohl so &#8216;ne Art Wortspiel sein). Mercurial bring eine gute Dokumentation gleich mit. Eine Kurzversion wird angezeigt, wenn man nur <code>hg</code> aufruft &#8211; eine ausführlichere Variante wird bei <code>hg help</code> angezeigt.</p>
<p>Um ein neues Repository anzulegen, muss man nur in das Verzeichnis seiner Wahl wechseln (wo evtl. bereits nicht-versionierte Dateien liegen) und den Befehl <code>hg init</code> ausführen. Damit erstellt Mercurial ein neues Verzeichnis <code>.hg</code>, in das das komplette Repository abgelegt wird.</p>
<p>Nun kann man mit <code>hg add</code> und einem anschließenden <code>hg commit -m "Adding files."</code> seine Dateien in das Repository einchecken. Nun kann man den Source-Code weiterbearbeiten, irgendwann dann mit einem <code>hg status</code> nachschauen, was man so alles geändert hat und dann wieder mit <code>hg commit</code> einchecken.</p>
<p>Interessant wird es dann, wenn mehrere Repositories ins Spiel kommen. Möchte man jetzt eine Kopie des Repositories erstellen, wechselt man beispielsweise ins übergeordnete Verzeichnis und führt <code>hg clone <em>Original-Verzeichnis</em> <em>Clone-Verzeichnis</em></code> aus. Dann kann man im Clone-Verzeichnis weiterarbeiten und auch hier einchecken. </p>
<p>Das Original-Verzeichnis ist so lange davon nicht betroffen, wie man ein <code>hg push</code> ausführt, das dann die Änderungen ins Original-Verzeichnis hochschieben würde. Idealerweise macht man vorher erst ein <code>hg pull</code>, um das Clone-Repository auf den neuesten Stand zu bringen und dann ein <code>hg update</code>, um die Dateien im Clone-Verzeichnis zu aktualisieren. Falls es im übergeordneten Repository schon Änderungen gegeben hat, muss vor dem Push mit <code>hg merge</code> gemergt werden.</p>
<p>Weitere Detail zur Nutzung von Mercurial kann man dem sehr guten <a href="http://mercurial.selenic.com/guide/">Tutorial auf der offiziellen Mercurial-Seite</a> entnehmen oder dem Tutorial von Joel Spolsky auf <a href="http://hginit.com" >hginit.com</a>.</p>
<h2>Mercurial zusammen mit SVN</h2>
<p>Man kann mit Mercurial auch dann nutzbringend einsetzen, wenn für das aktuelle Projekt ein anderes SCM wie zum Beispiel Subversion im Einsatz ist. Das kann dann folgendermaßen funktionieren:</p>
<p>Zunächst checkt man den Code wie gewohnt aus dem Subversion-Repository aus. Dann führt man im obersten Verzeichnis <code>hg init</code> aus. Dann legt man dort ein zusätzliches File <code>.hgignore</code> mit folgendem Inhalt an:<br />
<code><br />
syntax: glob<br />
.svn<br />
</code><br />
Das verhindert, dass man die SVN-spezifischen Files mit eincheckt. Umgekehrt sollte man das Einchecken des <code>.hg</code>-Ordners ins SVN verhindern, indem man das <code>svn:ignore</code>-Property entsprechend setzt. Danach kann man mit <code>hg add</code> und <code>hg commit</code> das lokale Mercurial-Repository mit Inhalt füllen.</p>
<p>Nun arbeitet man immer zunächst gegen das Mercurial-Repository und checkt dort ein. Wenn man seinen Code der Allgemeinheit zur Verfügung stellen möchte, macht man schließlich ein <code>svn commit</code>. Man muss allerdings darauf achten, wenn man ein Update aus dem SVN macht, dieses Update auch in das lokale Mercurial-Repository einzuchecken.</p>
<h2>Ein Wort zu git</h2>
<p>Wenn es um verteilte Versionskontrollsysteme geht, fällt einem meistens vermutlich <a href="http://git-scm.com/">git</a> ein. Wahrscheinlich hat man schon davon gehört, wie toll git ist und was git alles kann. Es gibt sogar eine Internetseite, die erklärt <a href="http://whygitisbetterthanx.com/">warum git besser ist als alles andere</a>. Warum also Mercurial ausprobieren?</p>
<p>Für meine Begriffe liegt die Popularität von git &#8211; abgesehen davon, dass es sicher ein geniales SCM ist &#8211; hauptsächlich daran, dass die git-Benutzer lauter schreien als die Mercurial-Benutzer <img src='http://www.zustandsforschung.de/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />  Ich bin immer davon abgeschreckt, wenn irgendetwas zu sehr gehypt und zu vollmundig angepriesen wird. Deshalb habe ich mich dazu entschieden, Mercurial auszuprobieren. Mercurial-Benutzer müssen nicht rumlaufen und Leute missionieren &#8211; sie benutzen einfach ihr SCM und sind glücklich damit dass es so toll funktioniert.</p>
<p>Nein. Scherz. Ich denke die Entscheidung sollte im ersten Schritt nicht zwischen Mercurial oder git getroffen werden, sondern <em>für</em> ein verteiltes Versionskontrollsystem. Denn das ist das zentrale Merkmal, das beide Systeme auszeichnet. Der Rest ist dann Geschmackssache. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/versionsverwaltung-mit-mercurial/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Die Vor- und Nachteile von ClearCase</title>
		<link>http://www.zustandsforschung.de/index.php/die-vor-und-nachteile-von-clearcase/</link>
		<comments>http://www.zustandsforschung.de/index.php/die-vor-und-nachteile-von-clearcase/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 15:03:15 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[clearcase]]></category>
		<category><![CDATA[programmierung]]></category>
		<category><![CDATA[scm]]></category>
		<category><![CDATA[versionierung]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=1503</guid>
		<description><![CDATA[Was ich selbst von ClearCase halte, habe ich ja bereits deutlich gemacht &#8211; aber ich habe mal sehen wollen, was denn andere Entwickler so davon halten. Daher hatte ich vor ungefähr einem Jahr auf Stackoverflow die Frage nach den Vor- und Nachteilen von ClearCase gestellt.
Mittlerweile wurde der Artikel mehr als 3000 mal geklickt, hat 27 [...]]]></description>
			<content:encoded><![CDATA[<p>Was ich selbst von ClearCase halte, habe ich ja <a href="http://www.zustandsforschung.de/index.php/arbeitsverhinderungstool-clearcase/">bereits deutlich gemacht</a> &#8211; aber ich habe mal sehen wollen, was denn andere Entwickler so davon halten. Daher hatte ich vor ungefähr einem Jahr auf Stackoverflow die <a href="http://stackoverflow.com/questions/1074580/clearcase-advantages-disadvantages">Frage nach den Vor- und Nachteilen</a> von ClearCase gestellt.</p>
<p>Mittlerweile wurde der Artikel mehr als 3000 mal geklickt, hat 27 Antworten und eine intensive Diskussion produziert. Obwohl sich tatsächlich auch einige Leute gefunden haben, die ein paar Vorteile von ClearCase aufzeigen, ist doch die Mehrheit der Antworten damit beschäftigt Nachteile aufzuzeigen. Für mich kommt das natürlich nicht wirklich überraschend, aber es ist doch interessant zu sehen, dass es nicht nur daran liegt, dass ich <a href="http://www.zustandsforschung.de/index.php/arbeitsverhinderungstool-clearcase/#comment-7441">keine Ahnung von ClearCase</a> habe.</p>
<p>Was mich seit ich ClearCase kenne immer gewundert hat, ist dass es mindestens eine Person braucht, die sich um nichts anderes kümmert und andere Leute sehen das ebenfalls so:</p>
<blockquote><p>The very fact that it&#8217;s complicated enough to require a full-time nanny is also worrying.</p></blockquote>
<p>Auch immer noch ein Rätsel ist mir der Zweck der Dynamischen Views in ClearCase &#8211; seit wir wieder darauf umgestellt hatten, haben wir gemerkt, dass das Resultat davon, dass man sofort alle Änderungen sieht, ist dass man sofort alle Fehler hat, die jemand anders gemacht hat. Auch das war Thema der Diskussion:</p>
<blockquote><p>Dynamic views are terrible unless you are in a very small team. And if that&#8217;s the case, why do you even have ClearCase? I have seen countless people&#8217;s views getting hosed because someone checked in files that broke the views of everyone else. You should always update and merge any conflicts on your own view. That way, the changes only affect you. With a dynamic view, you cannot merge down before pushing back up; you just commit and hope.</p></blockquote>
<p>Dann habe ich noch einen nicht ganz ernst gemeinten Ratschlag bekommen, der zwar für uns leider nichts gebracht hat, weil ja ClearCase schon gesetzt war, den ich aber wegen seinem Unterhaltungswert hier wiedergeben möchte:</p>
<blockquote><p>If you need an argument to convince people not to use &#8216;clearcase&#8217;, you can say that all the good developers hate it. I know, I wouldn&#8217;t work at any place who used clearcase as their source control. Heck, I would gladly suffer the pain of VSS to avoid clearcase.</p></blockquote>
<p>Wer möchte kann sich einfach mal <a href="http://stackoverflow.com/questions/1074580/clearcase-advantages-disadvantages">die ganze Diskussion auf Stackoverflow</a> durchsehen &#8211; es sind echt ein paar interessante Kommentare dabei. </p>
<p>Als Schlusswort möchte ich noch diesen Kommentar zitieren, den ich so voll und ganz unterschreiben könnte:</p>
<blockquote><p>If you&#8217;re considering integrating ClearCase into your development environment, I can offer only one piece of advice: don&#8217;t.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/die-vor-und-nachteile-von-clearcase/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Wir sind zu 90% fertig</title>
		<link>http://www.zustandsforschung.de/index.php/wir-sind-zu-90-prozent-fertig/</link>
		<comments>http://www.zustandsforschung.de/index.php/wir-sind-zu-90-prozent-fertig/#comments</comments>
		<pubDate>Tue, 04 May 2010 07:30:45 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[programmierung]]></category>
		<category><![CDATA[ps3]]></category>
		<category><![CDATA[videospiele]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=1367</guid>
		<description><![CDATA[Der für die Entwicklung des lange erwarteten PS3-Titels &#8220;Gran Turismo 5&#8243; verantwortliche Kazunori Yamauchi gab in einem Interview bekannt, man sei aktuell bei einem Fertigstellungsgrad von 90%. Das Spiel ist seit 5 Jahren in der Entwicklung und der Release-Termin wurde mittlerweile mehrfach verschoben.
Für einen Softwareentwickler klingt der Spruch &#8220;wir sind zu 90% fertig&#8221; allerdings gar [...]]]></description>
			<content:encoded><![CDATA[<p>Der für die Entwicklung des lange erwarteten PS3-Titels &#8220;Gran Turismo 5&#8243; verantwortliche Kazunori Yamauchi gab in einem Interview bekannt, man sei <a href="http://blogs.insideline.com/straightline/2010/04/kazunori-yamauchi-gran-turismo-5-about-90-finished-gt-academy-might-come-to-us.html">aktuell bei einem Fertigstellungsgrad von 90%</a>. Das Spiel ist seit 5 Jahren in der Entwicklung und der Release-Termin wurde mittlerweile mehrfach verschoben.</p>
<p>Für einen Softwareentwickler klingt der Spruch &#8220;<em>wir sind zu 90% fertig</em>&#8221; allerdings gar nicht gut. Denn in der Softwareentwicklung kennt man die sogenannte 90-90-Regel, die besagt, dass die ersten 90 Prozent der Entwicklung 90 Prozent der Zeit brauchen und die restlichen 10 Prozent die anderen 90 Prozent der Zeit. Die letzten 10 Prozent bis zur Fertigstellung einer Entwicklung dauern immer am längsten und sind auch immer die schwierigsten.</p>
<p>Wenn der Typ nun also sagt, &#8220;<em>es sind doch nur noch 10 Prozent</em>&#8220;, dann soll das eigentlich heißen &#8220;<em>wir sind bald fertig</em>&#8221; aber tatsächlich heißt es eher &#8220;<em>es gibt noch eine ganze Menge zu tun</em>&#8220;. Yamauchi liefert quasi selbst den Beweis dafür ab, denn in einem Interview Anfang dieses Jahres <a href="http://www.edge-online.com/news/gran-turismo-5-is-90-per-cent-complete">waren&#8217;s auch schon 90 Prozent</a> <img src='http://www.zustandsforschung.de/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>Mehr Infos zum Thema <a href="http://en.wikipedia.org/wiki/Ninety-ninety_rule">ninety-ninety rule gibt&#8217;s bei der Wikipedia</a> oder im Artikel <a href="http://www.processimpact.com/articles/essays.shtml#ninety">Ninety Percent Done</a>, der auch ein paar Ansätze beschreibt, was man dagegen tun kann.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/wir-sind-zu-90-prozent-fertig/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Der Weg in den Java-Dschungel</title>
		<link>http://www.zustandsforschung.de/index.php/der-weg-in-den-java-dschungel/</link>
		<comments>http://www.zustandsforschung.de/index.php/der-weg-in-den-java-dschungel/#comments</comments>
		<pubDate>Wed, 17 Mar 2010 15:13:54 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[programmierung]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=1220</guid>
		<description><![CDATA[Der Artikel &#8220;New to Java programming&#8221; aus der IBM-developerWorks-Reihe bietet außer einer kurzen Einführung was Java eigentlich ist, auch für den nicht mehr ganz so neuen Java-Programmierer eine schöne Übersicht über die wichtigsten Aspekte, Themen und Technologien der Java-Welt, wie z.B.

How does Java technology relate to Web application development?
How does Java technology relate to SOA/Web [...]]]></description>
			<content:encoded><![CDATA[<p>Der Artikel &#8220;<a href="http://www.ibm.com/developerworks/java/newto/index.html?ca=drs-">New to Java programming</a>&#8221; aus der IBM-developerWorks-Reihe bietet außer einer kurzen Einführung was Java eigentlich ist, auch für den nicht mehr ganz so neuen Java-Programmierer eine schöne Übersicht über die wichtigsten Aspekte, Themen und Technologien der Java-Welt, wie z.B.</p>
<ul>
<li><a href="http://www.ibm.com/developerworks/java/newto/index.html?ca=drs-#4">How does Java technology relate to Web application development?</a></li>
<li><a href="http://www.ibm.com/developerworks/java/newto/index.html?ca=drs-#5">How does Java technology relate to SOA/Web services?</a></li>
<li><a href="http://www.ibm.com/developerworks/java/newto/index.html?ca=drs-#6">How does Java technology relate to dynamic languages and functional programming?</a></li>
<li><a href="http://www.ibm.com/developerworks/java/newto/index.html?ca=drs-#7">How does Java technology relate to open source software development?</a></li>
</ul>
<p>Jeder Abschnitt enthält weiterführende Links zu developerWorks- oder auch anderen Tutorials, wo man seine Kenntnisse dann auffrischen oder vertiefen kann.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/der-weg-in-den-java-dschungel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$</title>
		<link>http://www.zustandsforschung.de/index.php/a-z0-9-_-a-z0-9-a-z2/</link>
		<comments>http://www.zustandsforschung.de/index.php/a-z0-9-_-a-z0-9-a-z2/#comments</comments>
		<pubDate>Thu, 10 Dec 2009 10:35:28 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[programmierung]]></category>
		<category><![CDATA[regex]]></category>
		<category><![CDATA[validierung]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=1009</guid>
		<description><![CDATA[Das immer wieder gerne angeführte Standardbeispiel für die Anwendung von Regular Expressions ist die E-Mail-Validierung. Mittels eines mehr oder weniger komplizierten Ausdrucks soll dabei geprüft werden, ob die von einem Benutzer in ein Webformular eingegebene E-Mail-Adresse den Regeln für eine gültige Adresse genügt. Dabei reichen die Varianten von eher simplen wie z.B. 
^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$
über komplizierte wie [...]]]></description>
			<content:encoded><![CDATA[<p>Das immer wieder gerne angeführte Standardbeispiel für die Anwendung von Regular Expressions ist die E-Mail-Validierung. Mittels eines mehr oder weniger komplizierten Ausdrucks soll dabei geprüft werden, ob die von einem Benutzer in ein Webformular eingegebene E-Mail-Adresse den Regeln für eine gültige Adresse genügt. Dabei reichen die Varianten von eher simplen wie z.B. </p>
<p><code>^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$</code></p>
<p>über komplizierte wie </p>
<p><code>[a-z0-9!#$%&#038;'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&#038;'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?</code></p>
<p>bis hin zu solchen, die man schon eher als <em>völlig irre</em> bezeichnen könnte:</p>
<p><code>(?:[a-z0-9!#$%&#038;'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&#038;'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])</code></p>
<p>die allesamt darauf abzielen eine mehr oder weniger genaue Abdeckung der in <a href="http://tools.ietf.org/html/rfc2822#section-3.4.1">RFC 2822 &#8211; Internet Message Format</a> definierten Vorgabe zu erfüllen.</p>
<p>Die Frage ist doch aber &#8211; warum tut man so etwas? Klar, man möchte dass der Benutzer eine gültige E-Mail-Adresse (im besten Fall seine eigene) in ein Formular einträgt. Aber dient eine Validierung der Gültigkeit mittels Regular Expressions wirklich diesem Zweck?</p>
<p>Mit fallen zwei Szenarien ein, wo ein Benutzer eine ungültige E-Mail-Adresse eingibt: Entweder er macht es unabsichtlich, wenn er sich vertippt oder so. Oder er macht es absichtlich, um irgendeine Zwangsregistrierung zu umgehen. In beiden Fällen greift die Validierung über Regular Expression nicht wirklich.</p>
<p>Erster Fall &#8211; Versehen: Wenn ich mich beim Eingeben der Mailadresse vertippe, dann dreht sich das doch meist um einzelne Buchstaben &#8211; dass ich das &#8220;@&#8221; vergesse kommt eher selten vor. D.h. ich kann mich leicht so vertippen, dass ich zwar eine formal gültige Adresse eingebe, die aber leider trotzdem falsch ist (z.B. Buchstabendreher im Vornamen).</p>
<p>Zweiter Fall &#8211; Absicht: Ich kann mir leicht eine lustige Mailadresse ausdenken, die bei der Validierung zwar durchgeht, aber völlig frei erfunden ist. Die Mailadresse hab.ich@erfunden.de erfüllt zwar die Validierung &#8211; ist aber deshalb noch lange keine gültige E-Mail-Adresse.</p>
<p>Eine mögliche Lösung könnte daher sein, die Mailadresse über Double Opt-in zu validieren, also dass man dem Benutzer eine Mail an diese Adresse schickt, die einen Link enthält, den der Benutzer klicken muss, um seine E-Mail-Adresse zu bestätigen. Aber auch diese Variante schützt nicht gegen mutwillige Falscheingabe &#8211; es gibt immer noch <a href="http://www.spamgourmet.com/">Spamgourmet</a>, <a href="http://www.mailinator.com/">Mailinator</a> und ähnliche Sites, die Wegwerf-E-Mail-Adressen bereitstellen.</p>
<p>Fazit: Mit Regular Expressions kann man zwar den Benutzer bei der Eingabe etwas unterstützen, aber der Weisheit letzter Schluss sind sie für die E-Mail-Validierung deshalb noch lange nicht. Und gerade deshalb braucht man sich auch nicht verkünsteln, um zu beweisen, wie toll man Regular Expressions (kopieren) kann.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/a-z0-9-_-a-z0-9-a-z2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Programmieren mit der Google App Engine</title>
		<link>http://www.zustandsforschung.de/index.php/programmieren-mit-der-google-app-engine/</link>
		<comments>http://www.zustandsforschung.de/index.php/programmieren-mit-der-google-app-engine/#comments</comments>
		<pubDate>Wed, 19 Aug 2009 17:15:19 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[appengine]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[programmierung]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=810</guid>
		<description><![CDATA[Was ist die Google App Engine?
Die Google App Engine ist die Cloud-Lösung von Google. Das Prinzip ist, dass man sich in der Implementierung seiner Applikation an die Vorgaben von Google hält und im Anschluss daran seine Applikation auf die Server von Google hochlädt und dort deren Ressourcen bis zu einem gewissen Grad kostenlos nutzen kann. [...]]]></description>
			<content:encoded><![CDATA[<h2>Was ist die Google App Engine?</h2>
<p>Die Google App Engine ist die Cloud-Lösung von Google. Das Prinzip ist, dass man sich in der Implementierung seiner Applikation an die Vorgaben von Google hält und im Anschluss daran seine Applikation auf die Server von Google hochlädt und dort deren Ressourcen bis zu einem gewissen Grad kostenlos nutzen kann. Darüberhinaus hat man die Möglichkeit gegen Bezahlung weitere Ressourcen zu benutzen. Die Vorgaben sind entweder Python oder Java als Programmiersprache zu verwenden und gewisse Einschränkungen bei der Implementierung zu beachten, wie zum Beispiel keine direkten Filesystemzugriffe zu machen.</p>
<p>Im Gegenzug bekommt man eine API, mit der man auf die von Google zu Verfügung gestellte Infrastruktur zugreifen kann. Daten werden beispielsweise nicht in einer Datenbank gespeichert, sondern im Bigtable-Datastore. Das fühlt sich im ersten Moment an wie eine Datenbank, hat allerdings z.B. die Einschränkung keine Joins zu unterstützen, wodurch man ab und zu dazu gezwungen ist, seine Daten zwecks besserer Performance zu denormalisieren (siehe Artikel <a href="http://highscalability.com/how-i-learned-stop-worrying-and-love-using-lot-disk-space-scale">How I Learned to Stop Worrying and Love Using a Lot of Disk Space to Scale</a>).</p>
<h2>Das Projekt</h2>
<p>Eins gleich vorweg: Ich zeige euch hier nicht, wie ihr mit der Google App Engine ein Blog implementiert. Ich glaube es gibt tausende Tutorials, die das tun. In diesem Artikel werde ich mit der Python-Version der Google App Engine eine kleine Applikation implementieren, die von Twitter die neuesten Tweets zieht, die mit dem Hashtag &#8220;#confession&#8221; getaggt sind. Wie die fertige Applikation einmal aussehen soll, könnt ihr euch hier ansehen: <a href="http://confessiontweets.appspot.com">http://confessiontweets.appspot.com</a>.</p>
<p>Ich werde die wichtigsten Features der Google App Engine hier jeweils kurz anreißen &#8211; weitere Details findet ihr in der sehr guten, aber leider nicht auf Deutsch verfügbaren Anleitung unter <a href="http://code.google.com/appengine/">http://code.google.com/appengine/</a>. Ich bitte meine manchmal vielleicht ein bisschen unbeholfene Art, Python zu implementieren zu entschuldigen &#8211; eigentlich komme ich aus der Java-Welt und habe die Google App Engine auch dazu benutzt, ein bisschen Python zu lernen.<br />
<span id="more-810"></span><br />
Der vollständige Source-Code kann hier heruntergeladen werden: <a href="http://www.zustandsforschung.de/wp-content/uploads/2009/07/ConfessionTweets.zip">ConfessionTweets.zip</a></p>
<p><strong>Update:</strong> Der Source-Code ist jetzt auch auf GitHub zu finden: <a href="https://github.com/zustandsforschung/ConfessionTweets">https://github.com/zustandsforschung/ConfessionTweets</a>.</p>
<h2>Die Applikation</h2>
<p>Die Applikation selbst basiert auf dem mit der App Engine mitgelieferten <a href="http://code.google.com/intl/de-DE/appengine/docs/python/tools/webapp/">webapp Framework</a>. Der Einstiegspunkt in die Applikation ist der folgende Code in der Datei &#8220;<em>controller.py</em>&#8220;:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">application = webapp.<span style="color: black;">WSGIApplication</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span>, ConfessionListPage<span style="color: black;">&#41;</span>,
                                      <span style="color: black;">&#40;</span><span style="color: #483d8b;">'/c/.*'</span>, SingleConfessionPage<span style="color: black;">&#41;</span>,
                                      <span style="color: black;">&#40;</span><span style="color: #483d8b;">'/ajax'</span>, AjaxConfessionService<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
  run_wsgi_app<span style="color: black;">&#40;</span>application<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">&quot;__main__&quot;</span>:
  main<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Hier wird im Wesentlichen definiert, dass Zugriffe auf &#8220;<em>/</em>&#8221; auf die Klasse &#8220;<em>ConfessionListPage</em>&#8221; gemappt werden sollen, Zugriffe auf &#8220;<em>/c/.*</em>&#8221; &#8211; also auf alles was mit &#8220;<em>/c/</em>&#8221; anfängt auf die Klasse &#8220;<em>SingleConfessionPage</em>&#8221; und Zugriffe auf &#8220;<em>/ajax</em>&#8221; auf die Klasse &#8220;<em>AjaxConfessionService</em>&#8220;.<br />
Für die beiden Klassen, die einzelne Seiten darstellen, habe ich eine gemeinsame Superklasse &#8220;<em>Page</em>&#8221; geschrieben, die bereits einige Variablen bereitstellt, die von beiden Klassen benötigt werden. Diese Klasse erbt selbst von &#8220;<em>webapp.RequestHandler</em>&#8220;, und hat damit deren Funktion als Einsprungpunkt in die Applikation.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Page<span style="color: black;">&#40;</span>webapp.<span style="color: black;">RequestHandler</span><span style="color: black;">&#41;</span>:
  collection = <span style="color: #008000;">None</span>
  template_values = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
  template_url = <span style="color: #483d8b;">''</span>
&nbsp;
  <span style="color: #ff7700;font-weight:bold;">def</span> get<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
&nbsp;
    header_path = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span>__file__<span style="color: black;">&#41;</span>, <span style="color: #483d8b;">'../templates/header.html'</span><span style="color: black;">&#41;</span>
    footer_path = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span>__file__<span style="color: black;">&#41;</span>, <span style="color: #483d8b;">'../templates/footer.html'</span><span style="color: black;">&#41;</span>
&nbsp;
    live = <span style="color: #483d8b;">'localhost'</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">host</span>    
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">template_values</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span> <span style="color: #483d8b;">'header_path'</span> : header_path,
                                 <span style="color: #483d8b;">'footer_path'</span> : footer_path,
                                 <span style="color: #483d8b;">'live'</span> : live <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">page_title</span> = <span style="color: #483d8b;">'ConfessionTweets'</span>
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">service</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #008000;">self</span>.<span style="color: black;">template_values</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span> <span style="color: #483d8b;">'page_title'</span> : <span style="color: #008000;">self</span>.<span style="color: black;">page_title</span> <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span>
&nbsp;
    path = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">&#40;</span>__file__<span style="color: black;">&#41;</span>, <span style="color: #008000;">self</span>.<span style="color: black;">template_url</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span>template.<span style="color: black;">render</span><span style="color: black;">&#40;</span>path, <span style="color: #008000;">self</span>.<span style="color: black;">template_values</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> TemplateDoesNotExist:
        <span style="color: #dc143c;">logging</span>.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Template %s not found!'</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">self</span>.<span style="color: black;">template_url</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Die Methode &#8220;<em>get</em>&#8221; wird automatisch vom webapp Framework aufgerufen, wenn eine Seite (per GET-Request) angefragt wurde. Ebenso gibt es Framework-Methoden für POST- oder HEAD-Requests. Dann werden hier einige Werte initialisiert wie beispielsweise der Pfad zu Header und Footer oder der Seitentitel. Dann folgt ein Aufruf der Methode &#8220;<em>service</em>&#8220;, die wie wir gleich sehen werden von den erbenden Klassen implementiert wird. Abschließend wird die Methode &#8220;<em>template.render</em>&#8221; aufgerufen, die als Parameter den Pfad zum Template, das die Seite darstellt und ein Dictionary mit den auf dem Template verwendeten Werten bekommt. Innerhalb dieser Methode werden die Werte in das Template geschrieben und das Resultat in die Response ausgegeben.</p>
<h2>Die ConfessionListPage</h2>
<p>Von dieser Klasse wird nun die Klasse &#8220;<em>ConfessionListPage</em>&#8221; abgeleitet. Diese Klasse ist für die Listendarstellung der einzelnen Tweets verantwortlich.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> ConfessionListPage<span style="color: black;">&#40;</span>Page<span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> service<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">template_url</span> = <span style="color: #483d8b;">'../templates/confession_list.html'</span>
&nbsp;
        twitter_request = TwitterRequest<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        tweets = twitter_request.<span style="color: black;">get_latest_tweets</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">template_values</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span> <span style="color: #483d8b;">'tweets'</span> : tweets <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Da alles andere schon in der &#8220;<em>Page</em>&#8220;-Klasse passiert, muss hier nur noch die &#8220;<em>service</em>&#8220;-Methode implementiert werden, die von der Page-Klasse aufgerufen wird. Als erstes wird der Pfad zum Template, also der View für diese Seite definiert. Dann wird mit Hilfe der Klasse &#8220;<em>TwitterRequest</em>&#8220;, die wir gleich implementieren werden, eine Liste von &#8220;<em>Tweets</em>&#8221; erzeugt, die dann mit der Anweisung &#8220;<em>self.template_values.update</em>&#8221; an das Template übergeben werden.</p>
<p>Im Template selbst findet sich der folgende Code:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">{% include header_path %}
{% for tweet in tweets %}
	&lt;script type=&quot;text/javascript&quot; &gt;
		confessions[&quot;{{ tweet.key.name }}&quot;] = &quot;{{ tweet.key.name }}&quot;;
	&lt;/script&gt;
	{% if forloop.first %}
		&lt;div id=&quot;{{ tweet.key.name }}&quot; class=&quot;first_confession&quot;&gt;
			&lt;h3&gt;&lt;a href=&quot;/c/{{ tweet.key.name }}&quot;&gt;{{ tweet.title }}&lt;/a&gt;&lt;/h3&gt;
			&lt;p&gt;Confessed {{ tweet.published|timesince }} ago by &lt;a href=&quot;{{ tweet.author_url }}&quot;&gt;{{ tweet.author }}&lt;/a&gt;&lt;/p&gt;
		&lt;/div&gt;
	{% else %}
		&lt;div id=&quot;{{ tweet.key.name }}&quot; class=&quot;confession&quot;&gt;
			&lt;h3&gt;&lt;a href=&quot;/c/{{ tweet.key.name }}&quot;&gt;{{ tweet.title }}&lt;/a&gt;&lt;/h3&gt;
			&lt;p&gt;Confessed {{ tweet.published|timesince }} ago by &lt;a href=&quot;{{ tweet.author_url }}&quot;&gt;{{ tweet.author }}&lt;/a&gt;&lt;/p&gt;
		&lt;/div&gt;
	{% endif %}
{% endfor %}
	&lt;div id=&quot;new_confessions&quot; &gt;&lt;/div&gt;
	&amp;darr; &lt;a id=&quot;more_button&quot; href=&quot;javascript:void(0)&quot; onclick=&quot;getConfessions(confessions)&quot; &gt;more&lt;/a&gt; &amp;darr;
{% include footer_path %}</pre></div></div>

<p>Die Templates für die Goolge App Engine werden mit Hilfe des Django-Frameworks erzeugt. Eine Liste von verwendbaren Template-Tags kann man daher der Django-Dokumentation selbst entnehmen: <a href="http://docs.djangoproject.com/en/dev/ref/templates/builtins/">Built-in template tags and filters</a>. Die erste und letzte Zeile binden den Header und Footer entsprechend den in der Klasse &#8220;<em>Page</em>&#8221; definierten Pfaden ein. Dann wird über die Liste der Tweets mittels &#8220;<em>for tweet in tweets</em>&#8221; iteriert. Innerhalb dieser Schleife stehen die einzelnen Listenelemente dann in der Variable &#8220;<em>tweet</em>&#8221; zur Verfügung. Da wir das erste Element dieser Liste speziell formatieren wollen, machen wir von der innerhalb von &#8220;<em>for</em>&#8220;-Schleifen vorhandenen Variable &#8220;<em>forloop.first</em>&#8221; gebrauch: Wenn wir das erste Element haben, dann schreiben wir einen anderen Div um die Ausgabe drumherum als für alle anderen Schleifenelemente. Zur Optimierung könnte man sicherlich nur das &#8220;<em>class</em>&#8220;-Attribut in den if-else-Block setzen, ich habe das hier aber nicht gemacht, um zu verdeutlichen, was mit diesem Template-Tag so alles möglich ist.</p>
<h2>Die SingleConfessionPage</h2>
<p>Da ich auch in der Lage sein möchte, jeden Tweet unter seiner eigenen URL darstellen zu können, habe ich eine Tweet-Klasse implementiert, die alle Daten aus dem Feed im Datastore speichert:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> Tweet<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:
    published = db.<span style="color: black;">DateTimeProperty</span><span style="color: black;">&#40;</span>required=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    status_url = db.<span style="color: black;">StringProperty</span><span style="color: black;">&#40;</span>required=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    title = db.<span style="color: black;">StringProperty</span><span style="color: black;">&#40;</span>required=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    content = db.<span style="color: black;">StringProperty</span><span style="color: black;">&#40;</span>required=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    profile_image = db.<span style="color: black;">StringProperty</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    author = db.<span style="color: black;">StringProperty</span><span style="color: black;">&#40;</span>required=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
    author_url = db.<span style="color: black;">StringProperty</span><span style="color: black;">&#40;</span>required=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Diese Klasse erbt von der vom App-Engine-Framework zur Verfügung gestellten Basisklasse db.Model und hat damit schon eine Menge Funktionen, wie z.B. die get_or_insert-Methode, die innerhalb der __get_tweet_from_xml__-Methode der TwitterRequest-Klasse aufgerufen wird, und die alle übergebenen Daten auch gleich im Datastore speichert, falls es den gleichen Tweet nicht schon gibt:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">        tweet = Tweet.<span style="color: black;">get_or_insert</span><span style="color: black;">&#40;</span>key_name=key_name, published=published, status_url=status_url, title=title, content=content, profile_image=profile_image, author=author, author_url=author_url<span style="color: black;">&#41;</span></pre></div></div>

<p>Man muss diese statische Methode nicht benutzen, um ein Objekt im Datastore zu erzeugen, man kann auch einfach den Konstruktor der Klasse verwenden und dann mit der &#8220;<em>put</em>&#8220;-Methode die Daten persistieren:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">	tweet = Tweet<span style="color: black;">&#40;</span>key_name=key_name, published=published, status_url=status_url, title=title, content=content, profile_image=profile_image, author=author, author_url=author_url<span style="color: black;">&#41;</span>
	tweet.<span style="color: black;">put</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Ich identifiziere dabei die einzelnen Tweets anhand der base-64-codierten Status-URL, die mir von Twitter geliefert wird und die pro Tweet eindeutig ist. Diesen Wert verwende ich nicht nur hier als key_name, sondern auch in der URL für die einzelnen Tweets, die dann etwa  &#8220;<em>/c/eG94T3BhbEFtYW5kYS9zdGF0dXNlcy8yMjgzMTI0MDg5</em>&#8221; lautet. Eine solche URL wird dann vom Controller auf die get-Methode der Klasse SingleConfessionPage gemappt:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> SingleConfessionPage<span style="color: black;">&#40;</span>Page<span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> service<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">template_url</span> = <span style="color: #483d8b;">'../templates/confession.html'</span>
&nbsp;
        match = <span style="color: #dc143c;">re</span>.<span style="color: black;">match</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;/c/(.*)&quot;</span>, <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">path</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> match <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">None</span>:
            key_name = <span style="color: #dc143c;">urllib</span>.<span style="color: black;">unquote</span><span style="color: black;">&#40;</span>match.<span style="color: black;">group</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
        tweet = Tweet.<span style="color: black;">get_by_key_name</span><span style="color: black;">&#40;</span>key_name<span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">if</span> tweet <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
            <span style="color: #008000;">self</span>.<span style="color: black;">redirect</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span>
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">page_title</span> += <span style="color: #483d8b;">' - A confession by '</span> + tweet.<span style="color: black;">author</span> + <span style="color: #483d8b;">' made '</span> + timesince.<span style="color: black;">timesince</span><span style="color: black;">&#40;</span>tweet.<span style="color: black;">published</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">' ago '</span> 
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">template_values</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span> <span style="color: #483d8b;">'tweet'</span> : tweet <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Dort lese ich den Key wieder aus der URL aus, und hole dann den entsprechenden Tweet per &#8220;<em>Tweet.get_by_key_name(key_name)</em>&#8221; aus dem Datastore und übergebe ihn ans Template, das dann eine vereinfachte Variante von dem für die ConfessionListPage ist:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">	{% include header_path %}
			&lt;div id=&quot;{{ tweet.key.name }}&quot; class=&quot;first_confession&quot;&gt;
				&lt;h3&gt;&lt;a href=&quot;{{ tweet.status_url }}&quot;&gt;{{ tweet.title }}&lt;/a&gt;&lt;/h3&gt;
				&lt;p&gt;Confessed {{ tweet.published|timesince }} ago by &lt;a href=&quot;{{ tweet.author_url }}&quot;&gt;{{ tweet.author }}&lt;/a&gt;&lt;/p&gt;
			&lt;/div&gt;
	{% include footer_path %}</pre></div></div>

<h2>Der TwitterRequest-Service</h2>
<p>Diese Klasse führt in der Methode &#8220;<em>get_latest_tweets</em>&#8221; den eigentlichen Request an Twitter aus, mit dem die Applikation ihre Daten erhält. Dabei wird in der Methode &#8220;<em>__get_xml__</em>&#8221; mittels der urlfetch-Methode des Frameworks von der Twitter-Suche ein XML geholt und in eine DOM-Datenstruktur überführt:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> TwitterRequest:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">request_url</span> = <span style="color: #483d8b;">'http://search.twitter.com/search.atom?q=%23confession&amp;amp;rpp=5'</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> get_latest_tweets<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, page=<span style="color: #483d8b;">'1'</span><span style="color: black;">&#41;</span>:
        tweets = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
&nbsp;
        <span style="color: #008000;">self</span>.<span style="color: black;">request_url</span> += <span style="color: #483d8b;">'&amp;amp;page='</span> + page<span style="color: #66cc66;">;</span>
        <span style="color: #dc143c;">logging</span>.<span style="color: black;">debug</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Requesting %s'</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">self</span>.<span style="color: black;">request_url</span><span style="color: black;">&#41;</span>
&nbsp;
        twitter_response = <span style="color: #008000;">self</span>.__get_xml__<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">request_url</span><span style="color: black;">&#41;</span>                
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">if</span> twitter_response <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
            <span style="color: #dc143c;">logging</span>.<span style="color: black;">warning</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Trying again...'</span><span style="color: black;">&#41;</span>
            twitter_response = <span style="color: #008000;">self</span>.__get_xml__<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">request_url</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">if</span> twitter_response <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">None</span> 
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">for</span> entry <span style="color: #ff7700;font-weight:bold;">in</span> twitter_response.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'entry'</span><span style="color: black;">&#41;</span>:
            tweets.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.__get_tweet_from_xml__<span style="color: black;">&#40;</span>entry<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">return</span> tweets
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> __get_xml__<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request_url<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">try</span>:
            result = urlfetch.<span style="color: black;">fetch</span><span style="color: black;">&#40;</span>request_url<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">except</span> DownloadError:
            <span style="color: #dc143c;">logging</span>.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Could not contact webservice.'</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">None</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> result.<span style="color: black;">status_code</span> == <span style="color: #ff4500;">200</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> parseString<span style="color: black;">&#40;</span>result.<span style="color: black;">content</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> __get_tweet_from_xml__<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, entry<span style="color: black;">&#41;</span>:
        published_string = entry.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'published'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">firstChild</span>.<span style="color: black;">data</span>
        published = <span style="color: #dc143c;">datetime</span>.<span style="color: #dc143c;">datetime</span>.<span style="color: black;">strptime</span><span style="color: black;">&#40;</span>published_string, <span style="color: #483d8b;">&quot;%Y-%m-%dT%H:%M:%SZ&quot;</span><span style="color: black;">&#41;</span>        
&nbsp;
        status_url = entry.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'link'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">getAttribute</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'href'</span><span style="color: black;">&#41;</span>
        title = entry.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'title'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">firstChild</span>.<span style="color: black;">data</span>
        content = entry.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'content'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">firstChild</span>.<span style="color: black;">data</span>
        author = entry.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'author'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">firstChild</span>.<span style="color: black;">data</span>
        author_url = entry.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'author'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'uri'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">firstChild</span>.<span style="color: black;">data</span>
        profile_image = entry.<span style="color: black;">getElementsByTagName</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'link'</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>.<span style="color: black;">getAttribute</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'href'</span><span style="color: black;">&#41;</span>
&nbsp;
        key_name = <span style="color: #dc143c;">base64</span>.<span style="color: black;">standard_b64encode</span><span style="color: black;">&#40;</span>status_url<span style="color: black;">&#91;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'http://twitter.com/'</span><span style="color: black;">&#41;</span>:<span style="color: #008000;">len</span><span style="color: black;">&#40;</span>status_url<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
        tweet = Tweet.<span style="color: black;">get_or_insert</span><span style="color: black;">&#40;</span>key_name=key_name, published=published, status_url=status_url, title=title, content=content, profile_image=profile_image, author=author, author_url=author_url<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> tweet</pre></div></div>

<p>Dann rufe ich für jeden &#8220;<em>entry</em>&#8220;-Knoten in diesem XML die &#8220;<em>__get_tweet_from_xml__</em>&#8220;-Methode auf, die die relevanten Daten aus dem Knoten ausliest und letztendlich mit der &#8220;<em>get_or_insert</em>&#8220;-Methode von db.Model die Daten im Datastore abspeichert. Die &#8220;<em>get_latest_tweets</em>&#8220;-Methode gibt schließlich eine Liste mit den einzelnen Tweet-Objekten, die aus dem XML des Webservice abgerufen wurden zurück.</p>
<h2>Der AjaxConfessionService</h2>
<p>Ich möchte auf der Startseite auch die Möglichkeit bieten, durch die Liste der Tweets blättern zu können. Zu diesem Zweck benutze ich eine AJAX-Schnittstelle, um beim Klick auf den more-Link weitere Tweets von Twitter abzuholen und einzublenden. Das JavaScript schickt den Request an den Pfad &#8220;<em>/ajax</em>&#8220;, der auf die Klasse &#8220;<em>AjaxConfessionService</em>&#8221; gemappt wird. Auch diese Klasse erbt wieder von &#8220;<em>webapp.RequestHandler</em>&#8221; und implementiert die &#8220;<em>get</em>&#8220;-Methode:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> AjaxConfessionService<span style="color: black;">&#40;</span>webapp.<span style="color: black;">RequestHandler</span><span style="color: black;">&#41;</span>:
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> get<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        page = <span style="color: #008000;">self</span>.<span style="color: black;">request</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'page'</span>, <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">;</span>
&nbsp;
        twitter_request = TwitterRequest<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        tweets = twitter_request.<span style="color: black;">get_latest_tweets</span><span style="color: black;">&#40;</span>page<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'{ &quot;confessions&quot;: {'</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">for</span> tweet <span style="color: #ff7700;font-weight:bold;">in</span> tweets:
            <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'&quot;'</span> + tweet.<span style="color: black;">key</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">name</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">'&quot; : { '</span><span style="color: black;">&#41;</span>
            <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'&quot;title&quot; : &quot;'</span> + tweet.<span style="color: black;">title</span>.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'&quot;'</span>, <span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\\</span>&quot;'</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">'&quot;, '</span><span style="color: black;">&#41;</span>
            <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'&quot;author&quot; : &quot;'</span> + tweet.<span style="color: black;">author</span>.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'&quot;'</span>, <span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\\</span>&quot;'</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">'&quot;, '</span><span style="color: black;">&#41;</span>
            <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'&quot;author_url&quot; : &quot;'</span> + tweet.<span style="color: black;">author_url</span>.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'&quot;'</span>, <span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\\</span>&quot;'</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">'&quot;, '</span><span style="color: black;">&#41;</span>
            <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'&quot;published&quot; : &quot;'</span> + timesince.<span style="color: black;">timesince</span><span style="color: black;">&#40;</span>tweet.<span style="color: black;">published</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">'&quot;'</span><span style="color: black;">&#41;</span>
            <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'}'</span><span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">if</span> tweet <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #ff7700;font-weight:bold;">not</span> tweets<span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>:
                <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">', '</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">response</span>.<span style="color: black;">out</span>.<span style="color: black;">write</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'}}'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Hier werden auch wieder die aktuellsten Tweets der entsprechenden Ergebnisseite bei Twitter abgeholt. Hier habe ich kein Template verwendet, sondern wandle die resultierenden Tweet-Objekte direkt in einen JSON-String um, den ich in die Response schreibe. Auf der Clientseite liest jetzt mein JavaScript wiederum das JSON aus und konstruiert HTML, das in die Seite eingefügt wird.</p>
<h2>Konfiguration</h2>
<p>Damit die Applikation überhaupt in der Google-App-Engine lauffähig ist, braucht sie noch eine Konfiguration. Diese wird in der Markup-Language YAML geschrieben und landet in einer Datei &#8220;<em>app.yaml</em>&#8221; im Wurzelverzeichnis der Applikation:</p>

<div class="wp_syntax"><div class="code"><pre class="yaml" style="font-family:monospace;">application: confessiontweets
version: 1
runtime: python
api_version: 1
&nbsp;
handlers:
- url: /robots.txt
  static_files: robots.txt
  upload: robots.txt
&nbsp;
- url: /css
  static_dir: css
  expiration: 1h
&nbsp;
- url: /images
  static_dir: images
  expiration: 1d
&nbsp;
- url: /js
  static_dir: js
  expiration: 60s
&nbsp;
- url: /.*
  script: confession/controller.py</pre></div></div>

<p>Zuoberst muss man das Label seiner Applikation deklarieren, so wie man es später auch in der Verwaltung der App-Engine angibt. Dann kommt die Version der Applikation und die Runtime &#8211; in unserem Fall Python. Die API-Version ist aktuell noch immer 1.</p>
<p>Unterhalb dieser Sektion definiert man nun einzelne Handler für verschiedene Files. Man kann keine der hochgeladenen Files direkt aufrufen, daher definiere ich hier zunächst die Position der robots.txt und der Verzeichnisse für CSS, Bilder und JavaScript, jeweils mit einer unterschiedlichen Cache-Expiry-Zeit.</p>
<p>Als letztes definiere ich noch, dass alles, was bisher nicht gemappt werden konnte an das Skript &#8220;<em>/confession/controller.py</em>&#8221; delegiert werden soll.</p>
<h2>Und jetzt ins Internet damit&#8230;</h2>
<p>Um das ganze wirklich ins Netz zu bringen, muss man sich mit einem Google-Account auf der Seite anmelden und bekommt dann einen Freischaltcode per SMS zugeschickt. Damit kann man dann bis zu 10 Google-App-Engine-Applikationen hosten lassen. Im Web-Interface muss ich jetzt zunächst eine Applikation mit dem gleichen Label wie in der Konfiguration unter &#8220;application&#8221; angegeben anlegen:</p>
<p><img class="alignnone size-full wp-image-959" title="Applikation in der Google App Engine anlegen" src="http://www.zustandsforschung.de/wp-content/uploads/2009/08/create_application_app_engine.jpg" alt="Applikation in der Google App Engine anlegen" width="500" height="287" /></p>
<p>Da diese Applikations-Ids über die gesamte Google-App-Engine eindeutig sind, muss man unter Umständen ein bisschen rumprobieren, bis man einen Namen gefunden hat, der noch verfügbar ist.</p>
<p>Der Upload der Applikation erfolgt dann über ein mitgeliefertes Python-Skript:</p>
<pre>	python appcfg.py update
<pfad -zur-applikation></pfad></pre>
<p>Man wird noch nach Mailadresse und Passwort gefragt und kann anschließend sein Werk im Internet bewundern.</p>
<p>Wenn man jetzt noch ein bisschen Styling und eine nette Hintergrundgrafik hinzufügt, erhält man bereits ein nettes kleines Mashup. Ich habe der ganzen Sache noch ein paar nette Animationseffekte spendiert und im Endergebnis sieht das ganze dann folgendermaßen aus:</p>
<p><a href="http://confessiontweets.appspot.com/"><img class="alignnone size-full wp-image-820" title="ConfessionTweets" src="http://www.zustandsforschung.de/wp-content/uploads/2009/06/confession-tweets.jpg" alt="ConfessionTweets" width="500" height="607" /></a></p>
<h2>Fazit</h2>
<p>Natürlich hat man bei Verwendung der Google-App-Engine einen gewissen Vendor-Lock-In-Effekt, vor allem durch die Verwendung des Datastore statt einer Datenbank. Auf der anderen Seite bietet die GAE eine interessante Möglichkeit, mit einer neuen Idee schnell und günstig &#8211; weil kostenlos &#8211; Live zu gehen und erst wenn die Applikation ein gewisses Maß an Usern erreicht hat muss man sich über Rentabilität überhaupt Gedanken machen.</p>
<p>Außer dem hier beschriebenen Mini-Mashup habe ich noch meine Video-Verwaltungsapplikation &#8220;<a href="http://www.veedemus.com">veedemus &#8211; movie collection</a>&#8221; mit der Google App Engine implementiert. Dort kann man schon eher sehen, dass nicht nur Spielereien, sondern auch komplexe Applikationen mit der GAE umsetzbar sind.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/programmieren-mit-der-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Arbeitsverhinderungstool ClearCase</title>
		<link>http://www.zustandsforschung.de/index.php/arbeitsverhinderungstool-clearcase/</link>
		<comments>http://www.zustandsforschung.de/index.php/arbeitsverhinderungstool-clearcase/#comments</comments>
		<pubDate>Thu, 09 Jul 2009 09:48:22 +0000</pubDate>
		<dc:creator>Benedikt</dc:creator>
				<category><![CDATA[Allgemeines]]></category>
		<category><![CDATA[clearcase]]></category>
		<category><![CDATA[kurioses]]></category>
		<category><![CDATA[programmierung]]></category>
		<category><![CDATA[scm]]></category>
		<category><![CDATA[versionierung]]></category>

		<guid isPermaLink="false">http://www.zustandsforschung.de/?p=849</guid>
		<description><![CDATA[In meinem neuen Projekt ist die Maßgabe mit IBM Rational ClearCase zu arbeiten. Eigentlich ist ClearCase ein Tool zur Versionsverwaltung ähnlich wie CVS oder Subversion &#8211; allerdings kein gutes. ClearCase hält einen verdienten Platz 4 bei dreckstool.de für Entwicklungswerkzeuge. 
Abgesehen davon dass das Team ein Produkt verwenden soll, von dem es keine Ahnung hat, ist [...]]]></description>
			<content:encoded><![CDATA[<p>In meinem neuen Projekt ist die Maßgabe mit <a href="www.ibm.com/software/awdtools/clearcase">IBM Rational ClearCase</a> zu arbeiten. Eigentlich ist ClearCase ein Tool zur <a href="http://de.wikipedia.org/wiki/Versionsverwaltung">Versionsverwaltung</a> ähnlich wie CVS oder Subversion &#8211; allerdings kein gutes. ClearCase hält einen verdienten Platz 4 bei <a href="http://dreckstool.de">dreckstool.de</a> für Entwicklungswerkzeuge. </p>
<p>Abgesehen davon dass das Team ein Produkt verwenden soll, von dem es keine Ahnung hat, ist ClearCase selbst ziemlicher Schrott. </p>
<p>Ein kleines Beispiel: Um eine Datei zu löschen, reicht es nicht das File zu löschen und einzuchecken. Man muss zunächst zusätzlich zur Datei den übergeordneten Ordner auschecken, dann kann man die Datei erst löschen und dann am Ende alles zusammen wieder einchecken.</p>
<p>Ein weiteres interessantes Thema: Die dynamischen Views, für die ClearCase so berühmt ist. Schade nur, dass wir feststellen mussten, dass ein Build auf der dynamischen View bei uns so zwischen 15 und 30 Minuten dauert &#8211; hat man die Files lokal, dann ist das eine Sache von ungefähr einer Minute. Daher pfeifen wir jetzt auf dynamische Views und arbeiten mit Snapshot-Views.</p>
<p>Auch lustig: Es gibt keine Möglichkeit, Dateien von der Versionskontrolle auszuschließen. Also sowas wie die ignore-Funktionalität bei CVS oder SVN. Eine absolute Notwendigkeit, um vernünftig arbeiten zu können. Geht bei ClearCase einfach nicht. Resultat: Wir haben bereits nach 2 Wochen ein lustiges Durcheinander an Dateien im Repository, die dort nie hätten rein sollen.</p>
<p>Noch mehr über die interessanten Features von ClearCase kann man in den beiden Artikeln <a href="http://www.digitaltorque.ca/2006/12/18/clearcase-making-easy-things-hard/">ClearCase, making easy things hard</a> und <a href="http://www.aldana-online.de/2009/03/19/reasons-why-you-should-stay-away-from-clearcase/">Reasons NOT to use ClearCase</a> lesen.</p>
<p>Insgesamt bekommt man bei der Bedienung so ein bisschen den Eindruck, dass sich jemand wirklich viele Gedanken gemacht hat, wie er den Entwicklern die Arbeit mit dem Tool besonders schwer machen kann. Ich glaube ich habe in den letzten zwei Wochen die Hälfte der Zeit damit verbracht, mit ClearCase zu kämpfen und mit den Support zu telefonieren.</p>
<p>Ich bin schon sehr gespannt wieviel Zeit wir noch mit diesem Tool verlieren, das uns eigentlich die Arbeit hätte erleichtern sollen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zustandsforschung.de/index.php/arbeitsverhinderungstool-clearcase/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

