Wednesday, September 21, 2011

WPO - DOM und CSS

In den letzen Monaten wanderte der Fokus in der Web Performance Optimierung von der Zeit, die zum Laden einer Seite benötigt wurde, zunehmend auf die Geschwindigkeit, in der das Rendering einer Seite und die anschließende Manipulation der Seite durchgeführt werden kann.

Der letzte Blog-Beitrag konzentrierte sich auf JavaScript, wohingegen sich dieser Blog-Beitrag mit dem DOM und CSS beschäftigt.

DOM

Die Verschachtelung des DOMs sollte nicht zu tief werden, genauer: Eine Schachtelungstiefe von mehr als 15 sollte unbedingt vermieden werden.

Elemente sollten über CSS gestylt werden. Direkt in Elementen sollten Style-Attribute nur in Ausnahmefällen (z.B. zur individuellen Platzierung) verwendet werden.

Leere DOM-Nodes sollten vermieden werden. Dies gilt auch für Spacer- oder Clear-DIVs.

Auch auf sonstige überflüssige Elemente (z.B. Grafiken für gerundete Ecken) sollte nach Möglichkeit (d.h. wenn dies der Style-Guide erlaubt) verzichtet werden.

Die Komplexität des DOMs lässt sich mit dem Bookmarklet DOM-Monster von Thomas Fuchs auch auf fremden Seiten überprüfen.

Benutze Hardware-Beschleunigung

Moderne Browser (IE9, Mozilla und Webkit) bieten hardwarebeschleunigtes Rendering. Dieses macht sich allerdings erst bei der Berechnung von Animationen (Ein- und Ausblenden von Dialogen, Bewegen von Infoboxen über den Bildschirm) wirklich bemerkbar.

Hardwarebeschleunigung steht allerdings (wenn man von Canvas und WebGL absieht) nur CSS, nicht aber JavaScript zur Verfügung. Darum sollten Animationen stets per CSS mit Transitions erfolgen. JavaScript sollte nur als Fallback eingesetzt werden.

#transition_animated { -moz-transition: all 5s ease-out; -o-transition: all 5s ease-out; -webkit-transition: all 5s ease-out; transition: all 5s ease-out; }

Nutze CSS-Features durchgänging

Effekte wie Schatten oder gerundete Ecken können und sollten mit CSS realisiert werden. Für Browser, die dies nicht unterstützen, wird dann auf eine Darstellung ohne diese Effekte zurückgegriffen. Einige CSS-Features kann man in alten IE-Versionen durch CSS3-Pie nachrüsten. Allerdings wirkt sich CSS3-PIE negativ auf die Laufzeit aus.

CSS-Selektoren

Ein Browser wertet CSS-Selektoren von rechts nach links aus. Eine Auswertung von rechts nach links ist nämlich technisch einfacher und performanter zu implementieren, da mit diesem Ansatz lediglich Listen gefiltert (reduziert) werden müssen. D.h., zunächst werden im Dokument alle Elemente ermittelt, die im rechten Teil des CSS-Selektors stehen, dann wird durch die Liste der gefundenen Elemente iteriert. Auf jedes vorher ermittelte Element wird dann der nächste Selektor ausgewertet. Die Liste wird um diesen Selektor reduziert.

Hierzu ein Negativbeispiel aus einem Stylesheet, das so wirklich existierte:

div#page_navigation #navbar_01 ul.navbar_02 li.act_childs a

• a – Es werden alle Links einer Seite in einer Liste gesammelt. Diese kann sehr lang werden, denn die Seite kann sehr viele Links enthalten.
• li.act_childs – Es werden von allen Elementen in der Liste die Parent-Nodes ermittelt, um zu prüfen, ob diese ein Listen-Element mit der Klasse act_childs enthalten. Die Liste wird anhand dieses Kriterium reduziert.
• ul.navbar_02 – Es werden von allen Elementen in der Liste die Parent-Nodes ermittelt, um zu prüfen, ob diese eine ungeordnete Liste mit der Klasse navbar_02 enhalten. Die Liste wird anhand dieses Kriterium reduziert.
• #navbar_01 – Es werden von allen Elementen in der Liste die Parent-Nodes ermittelt, um zu prüfen, ob ein Element die ID navbar_01 hat. Eine ID muss auf einer HTML-Seite eindeutig sein. Die Liste wird anhand dieses Kriterium reduziert.
• div#page_navigation – Obwohl im vorherigen Selektor bereits eine eindeutige ID angegeben wurde, werden von allen Elementen in der Liste die Parent-Nodes ermittelt, um zu prüfen, ob diese ein DIV mit der ID page_navigation enhalten. Ein ID-Selektor ist bereits eindeutig. Eine zusätzliche Überprüfung, ob dies ein DIV ist, ist überflüssig. Die Liste wird durch dieses Kriterium nicht wirklich reduziert, denn der vorhergehende ID-Selektor war bereits eindeutig.

Daher gelten einige Regeln für effektive CSS-Selektoren:

Verwende effektive Selektoren

IDs sind die effektivsten Selektoren, gefolgt von Klassen-, Tags- und Universal-Selektoren.

#main_navigation /* ID */
.main_navigation /* Class */
ul li a /* Tag */
li a [title='Zurück zur Startseite'] /* Universal */

Man sollte stets den schnellsten geeigneten Selektor möglichst weit rechts verwenden und den HTML-Code entsprechend gestalten. Selektoren auf Tags und Universal-Selektoren sollten komplett vermieden werden.

Ebenso sollten Descendant-Selektoren vermieden werden, die am langsamsten sind (siehe oben!). Oft lassen sich Descendant-Selektoren durch Child-Selektoren beschleunigen. Child-Selektoren sind zwar auch nicht schnell, aber doch schneller als Descendant-Selektoren, da von einem Element nicht potentiell alle Vorfahren, sondern lediglich das Elternelement ermittelt werden muss.

ul.navbar_02 li.act_childs /* bad */
ul.navbar_02 > li.act_childs /* still bad but better */

Überqualifiziere Selektoren nicht

IDs sind bereits eindeutig. Auch Klassen sollten nur für einen Tag definiert sein. Daher sind überqualifizierte Selektoren wie div#main_navigation oder li .act_childs überflüssig. Es genügt, die Elemente über den sehr schnellen ID-Selektor oder den Class-Selektor zu filtern.

Halte die Selektor-Kette kurz

Je länger die Selektor-Kette, desto mehr Operationen muss der Browser durchführen, um ein Element zu finden. Daher sollte die Kette der Selektoren kurz gehalten werden; überflüssige sollten ganz vermieden werden. Wenn sich eine Selektor-Kette nicht vermeiden lässt, dann sollte sie möglichst früh fehlschlagen, damit Elemente nicht überflüssig oft evaluiert werden müssen, bevor sie herausgefiltert werden.

Benutze Kaskadierung

Oft lässt sich eine lange Selektor-Kette durch Kaskadierung vermeiden. CSS kaskadiert Style-Informationen hin zu den Sub-Elementen. Daher lässt sich ein Style oft schon an einem Vater-Element notieren, damit er im Kinder-Element angewendet wird.

Lange Selektoren trifft man oft an: #navbar_01 ul.navbar_02 li a { font: „Arial“}

Dieser lange Selektor könnte durch die Verwendung eines kurzen Selektors minimiert werden.

#navbar_01 { font: „Arial“}

Dieser Blog-Beitrag sollte gezeigt haben, dass nicht nur dynamische Inhalte und Download-Größen sondern auch Struktur und Styling mitverantwortlich für die Performance einer Website sein können.

Dies ist ein Cross-Post vom Holisticon-Blog.

Tuesday, September 13, 2011

WPO - JavaScript und das DOM

Web Performance Optimierung konzentrierte sich in den letzten Jahren vor allem auf die Optimierung der Ladezeit einer Seite. Dazu haben sich Best Practices durchgesetzt, die bereits in einem Artikel in diesem Blog beschrieben wurden.

In jüngster Zeit wandert der Fokus jedoch immer mehr auf die Optimierung einer geladenen Seite, also auf die Zeit, die ein Browser für das Rendering und die Manipulation des DOMs benötigt.

In diesem Blog-Beitrag werden ich mich auf JavaScript konzentrieren.

Document.write

Die Verwendung von document.write ist generell – auch für JavaScripte von Dritten – untersagt. Es sind stattdessen DOM-Operationen (auch innerHtml) zu verwenden. Dies dient nicht nur der der Performance, sondern auch der Robustheit.

Inline-JavaScript

Inline-JavaScript sollte vermieden werden. Wenn dies nicht geht, sollten sie sich am Ende der Seite befinden. Das gilt auch für Event-Listener, die direkt in HTML-Elemente geschrieben werden. Diese sollten eigentlich erst nach dem Laden der Seite gebunden werden. Es sollten zudem so wenige JavaScript-Blöcke wie möglich verwendet werden, da jeder Block zu einer kurzen Verzögerung im Rendering führt.

JavaScript-Blöcke

Jeder JavaScript-Block wird isoliert abgearbeitet. Wenn in einem solchen ein Fehler auftritt, beeinflusst dies andere JavaScript-Blöcke nicht. Daher sollten Scripte, die mit Scripten von Dritten (Tracking, Targeting etc.) interagieren, in einem eigenen Block laufen. Dies widerspricht zwar der Anforderung, möglichst wenige Inline-JavaScripte einzubinden, sorgt aber für Robustheit der Seite. Um Performance-Einbußen zu minimieren, sollten diese Blöcke ausschließlich am Seitenende eingesetzt werden.

Selektoren

Selektoren, die z.B. in jQuery verwendet werden, sollten möglichst performant sein.

Nach Möglichkeit sollten nur Selektoren verwendet werden, die moderne Browser nativ implementieren können. Ob ein Selektor in der nativen Implementierung eines Browser funktioniert, kann man über die Funktion document.querySelectorAll in der Firebug-, Safari-, Internet Explorer- oder Chrome-Konsole testen (z.B. document.querySelectorAll(“.container .p–heading-1″)).

Zum Beispiel implementieren nicht alle Browser die Funktion document.getElementsByClassName. Ein Class-Selektor müsste also von einer Library wie jQuery implementiert werden. Wenn man einen Class-Selektor verwendet, dann muss jQuery alle Elemente der Seite (oder des Bereichs) über einen *-Selektor in eine Liste sammeln und jedes Element prüfen, ob seine Klasse der Klasse des Selektors entspricht. Dies kann auf einer Seite mit sehr vielen Elementen lange dauern. Statt Class-Selektoren sollte man also möglichst ID-Selektoren verwenden. Wenn ein ID-Selektor nicht möglich ist, kann man Events auch per Event Delegation (siehe unten!) an ein Vorfahren-Element binden, dass sich per ID referenzieren lässt.

Minimiere DOM-Manipulationen

JavaScript-Engines werden immer schneller. Dies gilt aber nicht unbedingt für den Zugriff auf das DOM. Manipulationen hieran sind teuer, weswegen sie minimiert werden sollten. Auf DOM-Manipulationen in Schleifen sollte nach Möglichkeit völlig verzichtet werden.

Cache DOM-Nodes und Attribute

Das Ermitteln von DOM-Elementen und -Attributen kostet Zeit. Daher sollten Elemente und Attribute einmalig ermittelt und dann in Variablen gecacht werden. Wenn sich das DOM verändert, so verändert sich auch automatisch das DOM-Element, das bereits ermittelt wurde – es besteht keine Notwendigkeit, erneut das Element im DOM zu suchen und auszuwerten. Module, die Objekte kapseln, können auch zur Zwischenspeicherung der DOM-Elemente genutzt werden.

Minimiere Redraws und Reflows

Jede Änderung im DOM führt zu einer Neuberechnung und einem Rendering der Seite. Dieser Redraw findet immer nach Events bzw. nach dem Beenden von JavaScript-Callbacks, die durch diese Events ausgelöst wurden, statt.

Änderungen sollten kumulativ erfolgen. Statt aus dem DOM zu lesen, in das DOM zu schreiben, erneut aus dem DOM zu lesen und wieder in das DOM zu schreiben, sollten Lese- und Schreiboperationen gebündelt erfolgen, so dass diese Operationen in einem einzigen Redraw bzw. Reflow erfolgen.

Das Document-Ready-Event

Im Document-Ready-Event sollte so wenig wie möglich getan werden. Lediglich Event-Listener dürfen an Elemente gebunden werden. Diese Elemente sollten nach Möglichkeit per ID referenziert werden. Wenn dies nicht möglich ist, bietet sich Event Delegation (siehe unten!) an.

Gänzlich verzichtet werden sollte auf DOM-Manipulationen, die schon beim Laden der Seite stattfinden (wie das Erzeugen von DIVs auf Reserve oder das Setzen von Attributen aufgrund von CSS-Klassen). Das DOM sollte bereits auf dem Server statt erst im Browser manipuliert werden.

Lazy Initialisation

Berechnungen und Bindings beim Document-Ready-Event sollten minimiert werden (siehe oben!), sondern erst durchgeführt werden, wenn sie benötigt werden (z.B. nach einen Klick-Event). Natürlich ist im Einzelfall abzuwägen, ob Lazy Initialisation einen Vorteil bietet. Eine Lazy Initialisation bei einem MouseOver-Event könnte als störend (verzögert) empfunden werden, während sie nach einem Klick-Event vom User nicht bemerkt wird.

Event Delegation

Es kann nach dem Laden der Seite lange dauern, bis alle Event Handler an DOM-Elemente gebunden sind. Um diese Zeit zu minimieren, bietet sich Event Delegation an, die der der Lazy Initalisation ähnelt. Man registriert ein Event (z.B. ein Klick-Event) an einem umschließenden Bereich, der sich z.B. über eine ID referenzieren lässt. Die Auflösung auf das einzelne geklickte Element findet dann erst nach dem Klick-Event statt. Die Zeit, die beim Laden der Seite eingespart wird, tritt dann also bei jedem einzelnen Event auf. Daher ist im Einzelfall abzuwägen, ob Event Delegation einen Vorteil bietet. Eine Event Delegation bei einem MouseOver-Event könnte als störend empfunden werden, während eine Event Delegation nach einem Klick-Event vom User nicht bemerkt wird.

Dies ist ein Cross-Post vom Holisticon-Blog und von Ajaxer.

Saturday, April 16, 2011

JSON-P mit Spring und CXF

Viele Webapplikationen laden Daten per JavaScript im JSON-Format von einem Server in den Browser, um sie dort darzustellen oder zu verarbeiten. Ein solcher Beispieldatensatz im JSON-Format könnte wie folgt aussehen:


{
  “cities”: [”Hamburg”, “New-York”, “Tokio”]
}

Ein Webbrowser lässt allerdings nicht zu, dass ein Script Daten von einer anderen Domain lädt als der, von der die Seite, in die das Script eingebettet wurde, geladen wurde. JavaScript hat also keinen Zugriff auf Daten, die von einer anderen Domain kommen. Diese Einschränkung ist ein Security-Feature des Browsers und als Same Origin Policy bekannt.

Oft ist es aber nötig, Daten von einer fremden Domain oder einer eigenen Subdomain zu ermitteln. Die saubere technische Lösung für dieses Problem ist Cross Origin Resource Sharing. Beim Cross Origin Resource Sharing werden Berechtigungen über HTTP-Header gesteuert. Leider funktioniert Cross Origin Resource Sharing nicht mit Legacy-Browsern, die noch häufig anzutreffen sind.

Ein Workaround, der in jedem Browser funktioniert, ist JSON-P: JSON with Padding. Ein Script darf selbst zwar keine Daten von einer fremden Domain anfordern, aber es darf einen Script-Tag generieren, der ein weiteres JavaScript von dieser fremden Domain lädt und direkt ausführt. Diesem nachgeladenen JavaScript übergibt man als GET-Parameter den Namen einer Callback-Funktion, die von dem nachgeladenen JavaScript ausgeführt werden sollte. Per Konvention trägt dieser GET-Parameter den Namen „_jsonp“.



script src="”http://my.otherdomain.de/rest-ws/myservice/mydata?_jsonp=jsonpCallback“" type="”text/javascript”"



Das so eingebundene Script muss dynamisch auf dem Server in einer Weise erzeugt werden, dass es die übergebene Callback-Funktion aufruft, sobald es vom Browser geladen wurde. Der Callback-Funktion werden in der Regel die Daten übergeben, die man ansonsten direkt als JSON-Objekt angefragt hätte:


jsonpCallback (
 
  {
 
    “cities”: [”Hamburg”, “New-York”, “Tokio”]
 
  }
 
);

Die Callback-Funktion wird von dem Script, das eigentlich die Daten anfordern wollte, bereitgestellt. So werden Daten von einer fremden Domain per JSON-P geladen. Front-End-Libraries wie jQuery stellen eine automatisch generierte Callback-Funktion bereit. Daher muss sich der Entwickler nicht um das Schreiben dieser Callback-Funktion kümmern.


return $.ajax({
 
type: “GET”,
 
url: “http://my.otherdomain.de/rest-ws/myservice/” + data,
 
error: errorcallback,
 
success: callback,
 
dataType: “jsonp”,
 
jsonp: “_jsonp”,
 
jsonpCallback: “ jsonpCallback “
 
});

Auf Client-Seite stellt JSON-P also kein Problem dar und wird von gängigen JavaScript-Bibliotheken unterstützt.

Auf dem Java Enterprise-Server wird für RESTful Services oft eine JAX-RS-Implementierung benutzt. Eine gängige JAX-RS-Implementierung ist Apache CXF. Allerdings stellt CXF im Moment noch kein JSON-P zur Verfügung (JIRA 3005). Daher muss man sich einen eigenen JSON-P-Provider schreiben. Dies ist glücklicherweise sehr einfach, denn man kann alle Funktionalitäten vom Default-JSONProvider erben:


@Produces("application/json")
 
public class JSONPProvider extends JSONProvider {
 
  @Override
 
  public void writeTo(
 
    Object obj, Class cls,
 
    Type genericType, Annotation[] anns,
 
    MediaType m, MultivaluedMap headers,
 
    OutputStream os
 
  ) throws IOException {
 
    final String prefix = request.getParameter("_jsonp"); // fix for demo
 
    final boolean hasPrefix = !StringUtils.isEmpty(prefix);
 
    if(hasPrefix) {
 
      // … check for injection first
 
      os.write(prefix); // simplified
 
      os.write('(');
 
      super.writeTo(obj, cls, genericType, anns, m, headers, os);
 
      os.write(')');
 
    } else {
 
      super.writeTo(obj, cls, genericType, anns, m, headers, os);
 
    }
 
  }
 
}

Dieser eigene JSON-P-Provider macht nichts, außer die vom JSON-Provider erzeugten JSON-Daten mit dem Aufruf einer Callback-Funktion zu umschließen, falls ein Request-Paramter „_jsonp“ übergeben wurde. Das abgedrucke Listing ist simplifiziert. Selbstverständlich muss man den Eingabeparameter „prefix“ prüfen, um eine Code-Injection auszuschließen.

Dieser selbst geschriebene JSON-P-Provider kann im Spring-Framework nun als JSON-P-Provider-Bean definiert werden. Letztere kann in einem JAX-RS-Server genutzt werden.



 
  
 
  
 
  
 
    
 
      
 
    
 
    
 
     
 
   
 
  
 


So lassen sich RESTful Services, die bisher nur JSON sprechen konnten, durch Konfiguration des JSON-P-Providers so erweitern, dass sie auch optional JSON-P sprechen können.

Dies ist ein Cross-Post vom Holisticon-Blog und von Ajaxer.

Thursday, March 03, 2011

Türstehen für Bohnen – Bean Validation mit domänenspezifischen Typen in der Praxis

Simon Zambrovski und Oliver Ochs, JavaMagazin Ausgabe 4.2011


Nachdem vor Kurzem Simon Zambrovski und Oliver Ochs bereits einen IT-Talk zum Thema Bean Validation gehalten haben, ist nun auch ein JavaMagazin-Artikel zum gleichen Thema erschienen.

In einer Java-Enterprise-Anwendung werden Daten erfasst und verarbeitet. Diese Daten müssen validiert werden. Die Validierungslogik ist meist eng an die Daten, die validiert werden, gekoppelt. Darum werden durch JSR-303 Bean Validation die Validierungsregeln direkt an die Daten annotiert. Der JSR-303 ist ein Teil der Java EE 6. Das heißt, man kann Bean Validation in Java EE 6 sowohl mit JSF als auch mit JPA einsetzen. Doch auch außerhalb der Java EE 6-Welt lässt sich Bean Validation z.B. mit Spring 3 verwenden.

Der Artikel bietet einem praxisnahen Überblick über den Einsatz von JSR-303 mit all diesen Frameworks. Zudem wird die Verwendung von domänenspezifischen Typen tiefer gehend erläutert.

Man findet den Artikel in der Rubrik "Enterprise".

Mehr Informationen findet man im Holisticon-Blog.

Friday, December 31, 2010

UKW-Sender in Hamburg

Hier eine Senderliste der in Hamburg empfangbaren UKW-Radiosender.

Die angegebenen Sendetürme sind die, die mir am wahrscheinlichsten vorkommen. Die Sender, die von Bergedorf aus abgestrahlt werden, kann ich leider selbst nicht empfangen.

No.SenderSendeturmFrequenz
1NDR 2Moorfleet87,6
2DeutschlandfunkMitte88,7
3DeutschlandRadio KulturMitte89,1
4NDR 1 Welle NordMoorfleet89,5
5NDR 90,3Moorfleet90,3
6917 xfmMitte91,7
7NDR InfoMoorfleet92,3
8FSK - Freies SenderkombinatMitte93,0
9Delta RadioMitte93,4
10NJOY RadioMitte94,2
11Oldie 95Mitte95,0
12Offener Kanal HamburgMitte96,0
13Energy 97,1Mitte97,1
14Klassik RadioMitte98,1
15NDR KulturMoorfleet99,2
16R.SHMitte100,0
17Radio ffnRosengarten100,6
18Radio NoraKaltenkirchen101,1
19R.SHKaltenkirchen102,9
20NDR 1 NiedersachsenRosengarten103,2
21Radio HamburgMoorfleet103,6
22Radio HamburgMitte104,0
23Hit-Radio AntenneRosengarten105,1
24NDR 1 Welle NordNeumünster106,4
25106!8 Alster Radio rock'n'popHöltigbaum106,8
26Delta RadioKaltenkirchen107,4




27Oldie 95Bergedorf88,1
28Energy 97,1 Bergedorf100,9
29R.SH Bergedorf102,0
30Delta Radio Bergedorf107,7

Monday, December 13, 2010

Nachteile funktionaler Vererbung in JavaScript

In meinem letzten Blog-Post hatte ich die funktionale Vererbung in JavaScript vorgestellt. Diese funktionale Vererbung hat allerdings einige Nachteile.

Jedes Mal, wenn ein neues Sub-Objekt erzeugt wird, werden zwei neue Funktionen erzeugt: einmal für das Sub-Objekt und einmal für das Super-Objekt. Diese Objekte sind nicht leichtgewichtig, denn das innere Super-Objekt wird als Closure im äußeren Sub-Objekt referenziert. Das Verwenden von vielen Closures kann zu Memory-Leaks führen.

Typen können nicht durch den Instanceof-Operator getestet werden. Sowohl das Super-Objekt "Square" als auch das Sub-Objekt "Box" sind einfache Funktionen. Eine Box-Objekt ist keine Instanz des Square-Objekts. Dies liegt daran, dass keine Konstruktorfunktion verwendet wird.

print(myBox instanceof square); // false


Typen lassen sich zudem nicht erweitern. Das Super-Objekt wird in einer Closure gehalten. Es ist von außen nicht sichtbar.

Der Syntax zum Erzeugen einer neuen Objekt-Instanz entspricht nicht den Konventionen von JavaScript.

var myBox = box(5);


Neue Objekt-Instanzen werden in JavaScript mit dem new-Operator auf einer Konstruktorfunktion erzeugt. Da das Entwurfsmuster der funktionalen Vererbung keine Konstrutkorfunktionen kennt, kann der new-Operator nicht verwendet werden. Dies kann zu Fehlern in der Verwendung des Codes führen.

new Box(5); // ReferenceError: Box is not defined

Funktionale Vererbung sollte also nur in Ausnahmefällen angewendet werden, in denen ein funktionales Paradigma am sinnvollsten erscheint.

Dies ist ein Cross-Post von Ajaxer.de.

Thursday, November 18, 2010

Friday, November 12, 2010

Funktionale Vererbung in JavaScript

Funktionale Vererbung ist ein Begriff, der durch Douglas Crockford geprägt wurde.

Funktionen sind Objekte. Wie Objekte können Funktionen auch selbst Eigenschaften (Properties) und Methoden (Funktionen) haben.

Funktionale Verberbung implementiert man, indem man eine Funktion erstellt, die Objekte erzeugt und diese zurückgibt. Innerhalb dieser Objekt-erzeugenden Funktion gibt es private Instanzvariablen. Auf diese kann das zurückgegebene Objekt als Closure zugreifen. Für die Nutzer des zurückgegebenen Objekts sind diese jedoch unsichtbar. Das zurückzugebende Objekt wird um Methoden erweitern. Diese Methoden haben ebenfalls Zugriff auf die privaten Variablen.



function square(width) {
  var squareObject = {};
  squareObject.getArea = function() {
    if (width > 0) {
      return width * width;
    }
   }
   return squareObject;
}

var mySquare = square(5);
print(mySquare.getArea()); // 25
print(mySquare.width); // undefined


Vererbung lässt sich in der funktionalen Vererbung durch das Dekorierer-Entwurfsmuster implementieren. Um einen Subtypen zu implementieren erweitert man den Supertypen um die benötigten Eigenschaften oder Methoden. Zu überschreibende Methoden speichert man in privaten Variablen, um diese auch dann noch verwenden zu können, wenn man die Methoden des Super-Objekts bereits überschrieben hat.



function box(width) {
  var boxObject = square(width);
  var getSquareArea = boxObject.getArea;
  boxObject.getVolume = function() {
    if (width > 0) {
      return getSquareArea() * width; 
    }
  }
  boxObject.getArea = function() {
    if (width > 0) {
      return getSquareArea() * 6;  
    }
  }
  return boxObject;
}

var myBox = box(5);
print(myBox.getVolume());  // 125
print(myBox.getArea()); // 150


Dieser Ansatz ist wohl der einfachste Ansatz zur Objekt-orientierten Programmierung in JavaScript, da er ohne Prototypen und Konstruktor-Funktionen auskommt.

Dies ist ein Cross-Post von Ajaxer.de.

Friday, September 10, 2010

Google V8 installieren

In den letzten Jahren hat sich der Webbrowser zu der wichtigsten Anwendung auf dem Desktop-Rechner und im Smartphone entwickelt. Er ist in die Domäne von ausgewachsenen Applikation wie Mail-Clients und Office-Suiten vorgestoßen und hat scheinbar allgegenwärtige Programme wie Microsoft Outlook und Word teilweise bereits verdrängt. Anwendungslogik wandert zunehmend vom Server zum Client, vom Backend in den Webbrowser.
Wenn immer mehr Anwendungslogik vom Server in den Client verlagert wird, stellt sich für einen Entwickler die Frage, ob der Rest der Anwendungslogik im Backend nicht in der gleichen Sprache entwickelt werden kann, die auch im Webbrowser verwendet wird. Es gibt keinen logischen Grund, warum JavaScript ausschließlich im Webbrowser laufen sollte. Es erscheint zudem wirtschaftlich, wenn man auf eine große Zahl an Entwicklern zurückgreifen kann, die in einer einzigen Sprache entwickeln. JavaScript auf dem Server könnte sicherlich nicht gewachsene SAP- oder SOA-Umgebungen, dafür aber Webframeworks wie ASP.net, Spring MVC / Webflow oder JSF ersetzen.
Inzwischen wird JavaScript also auf dem Server interessant. Sogar Datenbanken wie Couch-DB verwenden JavaScript als native Abfragesprache. Mit ECMAScript 5 gibt es zudem in der Sprache selbst interessante neue Features.
V8 ist die JavaScript-Laufzeitumgebung, die von Google für Googles Chome-Browser entwickelt wurde. V8 implementiert in der aktuellen Version 2.2 ECMA Script 3.
Google entwickelte mit V8 eine Laufzeitumgebung, die auf Mac OS X, Linux und Windows läuft. V8 ist komplett in C++ geschrieben und lässt sich eingebettet in C++-Applikationen verwenden. Außerdem lässt es sich natürlich auch allein stehend ausführen.
V8 gilt als sehr schnell. Verantwortlich für die Entwicklung war Lars Bak. Vor noch etwa zehn Jahren hatte Java den Ruf, langsam und hakelig zu sein. Ein kleines Start-Up um Lars Bak entwickelte jedoch eine neue Java Virtual Machine, die den Code zur Laufzeit kompilierte. Der Java JIT war geboren. Die von Lars Bak entwickelte Virtuelle Maschine ist der Ursprung von Hotspot, der aktuellen Java Virtual Machine von Sun Microsystems (jetzt Oracle). Erst durch Hotspot konnte Java die Popularität erreichen, die es heute hat. Genauso wie HotSpot Java beschleunigt hatte, beschleunigt V8 auch JavaScript.
V8 kompiliert JavaScript zu nativem Microcode anstatt es zu interpretieren. Weitere Beschleunigung erreicht V8, indem es die Rückgabewerte von Methoden cacht. Dieses Optimierungs-Technik wird inline caching genannt. Der Garbage Collector ist zwar ein Stop-the-World-Garbage Collector, allerdings ist er sehr schnell. Mit V8 läuft JavaScript fast so schnell wie ein kompiliertes, natives Programm.
Als V8 veröffentlicht wurde, war es etwa doppelt so schnell wie die Laufzeitumgebungen anderer Hersteller. Dies erst führte dazu, dass ein richtiges Wettrennen zwischen Browsern und deren JavaScript-Laufzeitumgebungen stattgefunden hat. Ohne V8 wären TraceMonkey, JägerMonkey oder SquirrelFish wahrscheinlich nicht so schnell entwickelt worden.
Google betrachtete JavaScript als einen der Treiber des Webs. Google ließ sich sogar dazu verleiten, ein reines Web-Betriebssystem Google Chrome OS anzukündigen, das sich ausschließlich durch Web-Technologien, also HTML und JavaScript, programmieren lässt.
V8 könnte eine - wenn nicht die - Grundlage für JavaScript auf dem Server oder in nativen Anwendungen bilden.
Um V8 zu installieren, benötigt man Subversion und SCons. Subversion ist ein Source-Code-Management-System, SCons ein Build-System. Unter MacOS verwendet man am besten Ports.
$ sudo port install scons subversion
Unter Ubuntu Linux verwendet man dazu den Package-Manger apt.
$ sudo apt-get install zip scons subversion
Unter der 64bit-Version von Ubuntu Linux könnte es notwendig sein, dass man zusätzlich noch die Multi-Lib-Packages installiert.
$ sudo apt-get install g++-multilib gcc-multilib
Anschließend kann man V8 aus dem Subversion-Repository auschecken und bauen.
$ svn checkout http://v8.googlecode.com/svn/trunk/ v8
$ cd v8
$ scons sample=shell
Nun kann man V8 über das Kommando „shell“ starten.
$ ./shell
V8 version 2.2.21
> print("Hello World");
Hello World
> quit();


Thursday, July 01, 2010

Javamagazin - Maskenball - Google Wave (Teil 3)

Maskenball - Clientseitige Erweiterung von Google Wave
Oliver Ochs und Jochen Jörg, JavaMagazin Ausgabe 8.2010

Im ersten Teil der Artikelserie haben wir die grundlegenden Konzepte von Google Wave vorgestellt und Wave ausführlich aus der Produktperspektive betrachtet. Im zweiten Artikel wurden die Funktionsweise sowie das Wave Protokoll näher beleuchtet. Ferner haben wir anhand eines Beispiels gezeigt, wie Wave mit Hilfe der Robot API erweitert werden kann. In diesem dritten Teil werden wir beschreiben, wie sich Wave unter Verwendung der Embed API in eine Website einbinden lässt und wie sich Gadgets - Miniprogramme, die im Client laufen - mit Hilfe der Gadgets API programmieren lassen.

Dies ist ein Cross-Post des Holisticon-Blogs.

Tuesday, June 01, 2010

Javamagazin - Föderierte Agenten - Google Wave (Teil 2)

Föderierte Agenten - Serverseitige Erweiterungen von Google Wave
Oliver Ochs und Jochen Jörg, JavaMagazin Ausgabe 7.2010

Im ersten Teil der Artikelserie haben wir die grundlegende Konzepte von Google Wave vorgestellt und Wave ausführlich aus der Produktperspektive betrachtet. Außerdem haben wir einen eigenen Wave Federation Server aufgesetzt. In diesem zweiten Artikel werden wir die Funktionsweise und das Wave Protokoll näher beleuchten. Ferner wird anhand eines Beispiels gezeigt, wie Wave mit Hilfe der Robot API erweitert werden kann.

Dies ist ein Cross-Post des Holisticon-Blogs.

Thursday, May 13, 2010

Rezept für Spuntenkäse (Spundekäse)

  • 500 g Quark
  • 250 g Butter
  • 2 Pck. Frischkäse, natur
  • 1 Pck. Schmelzkäse (rechteckig)
  • 1-2 Zwiebeln
  • 3 El. Paprikapulver, edelsüß
  • Pfeffer und Salz
  • zusätzlich Paprikapulver, edelsüß zum garnieren
Zutaten mit dem Handrührgerät verrühren, mit Paprikapulver garnieren.

Saturday, May 01, 2010

Javamagazin - Der Schockwellenreiter - Google Wave (Teil 1)

Der Schockwellenreiter - Google Wave
Oliver Ochs und Jochen Jörg, JavaMagazin Ausgabe 6.2010

Google Wave ist ein Werkzeug, mit dem die Kommunikation und Zusammenarbeit über das Internet revolutioniert werden sollte. Dieser radikale Ansatz könnte Wave auch scheitern lassen. In dieser dreiteiligen Artikelserie werden wir zeigen, was Google Wave ist, wie es funktioniert und welche Konzepte sich dahinter verbergen.

Dies ist ein Cross-Post des Holisticon-Blogs.

Wednesday, March 31, 2010

Wednesday, March 24, 2010

Web Performance-Optimierung

Performance-Optimierung

Bei einem Kunde wurde die Performance der Website optimiert. Inspiriert wurde diese Optimierung durch das Buch „High Performance Web Sites: 14 Steps to Faster-Loading Web Sites“ von Steve Souders.

Anhand der Engineering-Taks, die in diesem Buch beschrieben werden, wurden folgende Regeln aufgestellt und implementiert.

Rendering so früh wie möglich erlauben

Wenn ein Benutzer eine Seite aufruft, so muss er warten, bis die Seite aufgebaut ist. Während er wartet, benötigt er ein visuelles Feedback, damit er sicher gehen kann, dass die Seite funktioniert. Durch das Feedback erscheint für den Nutzer die Zeit, die er auf die Seite wartet, subjektiv kürzer.

In Webseiten wird in der Regel kein Fortschrittsbalken eingeblendet, sondern die Seite selbst ist der Fortschrittsbalken. Statt dass der Nutzer vor einer weißen Seite sitzt, die sich nach der Wartezeit in einem einzigen Augenblick aufbaut, sollte sich die Seite im Browser nach und nach aufbauen.

Um dies zu erreichen, sollte es dem Web-Browser technisch ermöglicht werden, herunter geladene Inhalte so früh wie möglich darstellen zu können. Dazu benötigt er einfach möglichst früh alle nötigen Informationen zum Rendering der Seite.

Außerdem sollte man - z.B. nach dem Rendering des Kopfbereichs - den HTTP-Output flushen.

Eine weiße Seite erscheint (vor allem im Internet Explorer) trotzdem dann, wenn

  • man eine Seite in einem neuen Fenster öffnet,
  • der Browser während eines Reloads bewegt wird (z.B. minimiert und wiedergestellt wird),
  • eine Seite als Homepage (als erste Seite überhaupt) geladen wird.
Schlimmer als eine weiße Seite ist ein Flash of Unstyled Content (FUC). Dieser wird hervorgerufen, wenn Stylesheet-Informationen erst dann ausgewertet werden, wenn der Browser bereits mit der Darstellung des Contents begonnen hat.

Dimensionen von Grafiken und Bildern festlegen

Wenn man für Images (Bilder und Grafiken) die Höhe und Breiter schon im Image-Tag vorgibt, kann der Browser mit der Berechnung der Seite schon beginnen, bevor die eigentlichen Binaries der Bilder heruntergeladen wurden.

CSS in den Seitenkopf einbauen

Stylesheets, die bereits im Dokumenten-Kopf (HEAD) eingebunden werden, ermöglichen es dem Browser, möglichst früh mit dem Rendering zu beginnen. Zudem wird durch das Einbinden in den Dokumenten-Kopf der FUC vermieden.

CSS sollte immer über den Link-Tag eingebunden werden. @import-Regeln im CSS werden nämlich erst später, wenn das CSS bereits heruntergeladen wurde, evaluiert.

CSS vor JavaScript einbinden

Browser blockieren JavaScripte bis alle CSS-Dateien geladen sind. Dies liegt daran, dass JavaScripte Informationen aus Style-Attributen verwenden könnten. Darum muss der Browser vor der Ausführung von JavaScript warten, bis die CSS-Dateien geladen sind. Also sollte man, um eine Blockierung zu vermeiden, CSS-Dateien auch im HTML-Code vor JavaScript-Dateien referenzieren.

Reduziere die Anzahl der HTTP-Requests

Je weniger einzelne Ressourcen (Dateien) ein Browser laden muss, desto schneller können diese herunter geladen werden, da der Over-Head durch HTTP bzw. durch TCP-Round-Trips entfällt. Außerdem gilt: je kleiner diese Ressourcen sind, desto schneller sind werden sie übertragen.

Parallelisiere Downloads über verschiedene Hosts

Ein Browser kann pro Host nur eine bestimmte Anzahl an Verbindungen öffnen. Eine Übersicht, welcher Browser wie viele Verbindungen gleichzeitig öffnen kann, bietet die Website browserscope.org. Der Internet Explorer 6 (IE6) kann z.B. nur zwei Verbindungen pro Host öffnen.

Wenn man die Ressourcen einer Site auf mehrere Hosts verteilt, dann kann der Browser eine größere Anzahl an Verbindungen verwenden.

Für statische Inhalte wurden also zwei (virtuelle) Static-Hosts angelegt, über die diese Inhalte ausgeliefert werden.

Reduziere DNS-Lookups

Allerdings lassen sich nicht beliebig viele Host-Namen verwenden. Pro Host-Name ist nämlich ein DNS-Lookup notwendig. Firefox speichert DNS-Lookups nur für eine Minute zwischen. D.h., wenn man zu viele Host für den parallelen Download verwendet, wird der Geschwindigkeitsvorteil, der durch verschiedenen Hosts erreicht wird, durch die DNS-Lookups wieder aufgehoben.

Es sollte also pro Site nicht mehr als 2-4 Hosts zur Parallelisierung verwendet werden. Das Einbinden von Ressourcen sehr vieler Hosts, wie sie bei Zählpixeln (und Mash-Ups) üblich sind, sollte vermieden werden.

Reduziere SSL-Handshakes

Es ist zu im Einzelfall zu überprüfen, ob bei einer Seite, die über HTTPS ausgeliefert wird, es sich überhaupt lohnt, verschiedene parallele Hosts zu verwenden. Evtl. wird die Zeit, die durch parallele Downloads gewonnen wird, durch einen zusätzlichen SSL-Handshake wieder verbraucht.

Verwende ein CDN

Content Delivery Networks bieten ein Netzwerk aus Servern, die an verschiedenen Standpunkten gehostet werden. Über dieses Netzwerk lassen sich Ressourcen wie Videos oder große Downloads räumlich verteilen. Nutzern, die eine Ressource aus dem CDN anfordern, wird diese automatisch über einen räumlich nahe liegenden Server ausgeliefert.

Große Dateien (wie Trailer und andere Flash-Movies) werden über ein CDN ausgeliefert. Flash-Movies sind grundsätzlich so zu gestalten, dass sie von einer beliebigen URL ausgeliefert werden können. Dies ist wegen der Verwendung von ActionScript und der Same Origin Policy nicht selbstvertändlich.

Verwende einen cookie-freien Host für statischen Content

Cookies werden bei jedem Request der Cookie-Domain vom Browser zum Server übertragen. Die Bandbreite vom Browser zum Server ist oft viel kleiner als die Bandbreite vom Server zum Browser. Das Übertragen dieser Cookies ist für viele Inhalte unnützer Overhead. Content, der statisch ist, also keine Session-Informationen o.ä. aus Cookies benötigt, sollte darum von einer zweiten, Cookie-freien Domain ausgeliefert werden. Beispiele für solchen Content sind Stylesheets, Grafiken und Flash-Movies, die nicht über das CDN (siehe oben!) ausgeliefert werden.

Ein URI-Builder in einem CMS, der zwischen statischen und dynamischen Inhalten unterscheiden kann, liefert dynamische Inhalte ohne Hostname, statische Inhalt jedoch über einen konfigurierten Host für statische Inhalte.

Optimiere Stylesheets

Leicht lassen sich HTTP-Requests reduzieren, indem man CSS-Dateien zu wenigen oder zu einer einzigen Ressource zusammenfasst.

Überflüssige White-Spaces lassen sich in den CSS-Dateien über den YUI-Compressor entfernen. Diese Komprimierung sollte automatisiert im Build bzw. im Publikationsprozess stattfinden.

Da CSS-Expressions und lange CSS-Selektoren sich negativ auf die Browser-Performance auswirken, sollten diese vermieden werden.

Background-Grafiken in Stylesheets lassen sich zu CSS-Sprites zusammenfassen. Wenn es auf einer Seite 25 grafische Elemente gleicher Größe gibt, lassen sich diese zu einer Sprite-Map mit 5x5 Elementen zusammenfassen. Statt 25 HTTP-Requests findet dann nur noch ein einziger HTTP-Request statt. Ein gutes Beispiel für CSS-Sprites ist die Sprite-Map, die Google auf der Suchseite verwendet.

Das Bookmarklet auf spriteme.org kann automatisch Sprite-Maps einer Seite generieren.

Optimiere JavaScript

JavaScripts, die auf verschiedenen Seiten benötigt werden, sollten nicht inline im HTML-Dokument sondern als eigene JavaScript-Ressource verwendet werden. So wird die Größe des einzelnen HTML-Dokuments reduziert. JavaScripte, die nur auf einer Seite verwendet werden, sollten inline im HTML-Dokument stehen, um die Anzahl der Requests zu reduzieren.

Viele Browser laden JavaScript – im Gegensatz zu anderen Ressourcen wie Grafiken oder Stylesheets – nicht parallel sondern arbeiten ein JavaScript-Dokument nach dem anderen ab. Der Download von JavaScript blockiert also den parallelen Download von Ressourcen. Darum machen sich Optimierungen in JavaScript-Bibliotheken besonders stark bemerkbar.

Ähnlich wie bei CSS-Dateien lassen sich auch JavaScript-Bibliotheken zu wenigen JavaScript-Ressourcen zusammenfassen.
Dies hat zudem den Vorteil, dass Abhängigkeiten zwischen Bibliotheken explizit gemacht werden. Wenn eine Bibliothek wie jQuery-UI von der Bibliothek jQuery abhängt, dann muss jQuery in der einzigen Bibliotheks-Ressource vor jQuery-UI eingebunden werden. Das Zusammenfassen von JavaScript-Bibliotheken zu einer einzigen JavaScript-Ressource sollte im Build-Prozess durch einen Assembler erfolgen. Durch das Verwenden eines Assemblers im Build-Prozess lässt sich zudem sicher stellen, dass Scripte nicht doppelt eingebunden werden.

Die Größe von JavaScript-Dokumenten lässt sich zudem leicht durch das Minifizieren reduzieren. Durch das Minifizieren werden Kommentare und überflüssige Whitespaces aus dem JavaScript entfernt. Gängige Kompressoren sind Google Closure, JSMin von Douglas Crockford oder YUI-Compress von Yahoo. Auch das Minifizieren von JavaScript sollte automatisiert im Build-Prozess erfolgen. Alternativ lässt sich auch zur Laufzeit JavaScript-Code über den YUI-compressor komprimieren. Dazu lässt sich der Google-HTML-Kompressor (htmlcompressor.googlecode.com) in Verbindung mit dem YUI-Compressor verwenden.

JavaScripte, die nicht schon vor dem Rendern der Seite benötigt werden, lassen sich leicht in den Fuß des Dokuments packen. So werden sichtbare Elemente vor den JavaScript-Ressourcen geladen und die Seite wird bereits dargestellt, während JavaScript-Bibliotheken, die im Fuß der Seite eingebunden wurden, noch nachladen.

JavaScripte sollten so erstellt werden, dass sie nicht über document.write() direkt in den HTML-Strom schreiben sondern das DOM nach dem Laden der Seite manipulieren. So wird verhindert, dass der Browser das Rendering blockiert oder sogar unnötig oft ein Re-Rendering durchführen muss.

Verwende den Expires-Header

Beim ersten Besuch einer Seite muss der Browser alle Inhalte herunter laden. Diese Inhalte lassen sich im Browser-Cache zwischen speichern. Ob und wie lange diese Inhalte gespeichert werden, wird über HTTP-Header wie den Expires-Header gesteuert: Expires: Wed, 14 Oct 2010 09:01:30 GMT

Für statischen Inhalt lässt sich dieser auf beispielsweise ein Jahr in die Zukunft setzen. So wird der Browser veranlasst, Dokumente möglichst lange zwischen zu speichern. Man kann den Header auch weiter in die Zukunft setzen, allerdings wird dies im RFC nicht empfohlen.

Der Expires-Header hat den Nachteil, dass er ein Datum erwartet. Eigentlich müsste also die Zeit zwischen Server und Browser synchronisiert sein. Statt dem Expires-Header wurde in http 1.1 darum die Max-Age-Direktive des Cache-Control-Headers eingeführt, die eine Angabe in Sekunden erwartet: Expires: Wed, 14 Oct 2010 09:01:30 GMT Cache-Control: max-age=31536000

Obwohl der Cache-Control-Header den Expires-Header ablösen sollte, empfiehlt es sich, beide Header übereinstimmend zu setzen. Dies kann das „mod_expires“-Modul des Apaches zu übernehmen.

Wenn nun allerdings statischer Inhalt, der ein Jahr im Cache des Browsers liegen soll, auf dem Server geändert wird, so bekommt der Nutzer dies nicht mehr mit.

Darum muss neben dem Setzen von Expires-Headern auch dafür gesorgt werden, dass die veränderten Inhalte eine neue (eindeutige) URL bekommen. Ein URI-Builder integriert darum das Release-Datum einer Ressource in die URL der Ressource: content/static/5940026/2009-10-12-11-52-39/thumbnail.gif

Wenn eine neue Version der Ressource veröffentlicht wird, dann wird im HTML-Dokument automatisch eine neue URL generiert. Der Browser holt dann die Ressource nicht mehr aus dem Cache sondern holt sich die neue Version der Ressource ab.

Statt einer eindeutigen URL ließe sich auch der/das ETAG verwenden. Ein ETAG kann man sich wie eine Prüfsumme über den Content vorstellen. Der Server überträgt die Prüfsumme (den ETAG) zusammen mit dem Content im HTTP-Header. Wenn der Browser die Ressource erneut benötigt, überträgt er beim GET-Request den ETAG der Ressource mit, die er in seinem Cache findet. Der Server kann nun, wenn sich der ETAG nicht geändert hat, mit einer http-304-Response (Not Modified) antworten.

Gegenüber einer eindeutigen URL hat der ETAG zwei Nachteile. ETAGs lassen sich ein einem Verbund von Apaches (Loadbalancing) nur schwer eindeutig konfigurieren und ETAGs benötigen für jede angeforderte Ressource einen http-Roundtrip.

Darum sind Expires- und Cache-Control-Header dem ETAG vorzuziehen.

ETAGs, die der Tomcat setzt, sollten vom Apache entfernt werden!

Komprimiere Ressourcen

Textuelle Ressourcen (HTML, JavaScript und CSS) lassen sich komprimiert übertragen. Bereits komprimierte Ressourcen wie Grafiken und PDFs sollten nicht zusätzlich noch einmal komprimiert werden. Die gebräuchlichste Komprimierung ist die GZIP-Komprimierung. Browser, die eine komprimierte Übertragung unterstützen, teilen dies beim GET-Request dem Server mit:

Accept-Encoding: gzip, deflate

Wenn der Server dann die Ressource komprimiert ausliefert, antwortet er mit dem Response-Header:

Content-Encoding: gzip

Am Apache einschalten lässt sich GZIP mit dem Modul „mod_gzip“ (Apache 1.3) bzw. “mod_deflate“ (Apache 2.x).

Obwohl der Internet Explorer 6 offiziell Kompression unterstütz, kommt es doch hin und wieder zu Fehlern, z.B. wenn auf dem gleichen Rechner eine alte Version des RealPlayers installiert ist. Um auf Nummer Sicher zu gehen, sollte man darum die Kompression nur für gängige Browser, die neuer als der Internet Explorer 6 sind, aktivieren.

Leider interpretieren einige Proxies, die z.B. in Unternehmen eingesetzt werden, den Content-Encoding-Header falsch. Dies kann dazu führen, dass ein Browser, der GZIP unterstützt, vom Proxy nicht-gezipte Ressourcen ausgeliefert bekommt, oder schlimmer, dass ein Browser, der keine Kompression unterstütz, vom Proxy komprimierte Inhalte bekommt.

Darum müsste dann der Vary-Header entsprechend so gesetzt werden, dass ein Proxy je nach Encoding und Browser (User-Agents) unterschiedliche Ressourcen für unterschiedliche Encodings und Browser (User-Agents) zwischenspeichert:

Vary: Accept-Encoding,User-Agent

Leider landen so sehr viele Artefakte im Cache, weswegen der Vary-Header nicht wie oben beschrieben konfiguriert wurde. Es wird also hingenommen, dass fehlerhafte Proxies und IE6-Versionen eine nicht optimale Darstellung der Seite erhalten.

Vor dem Zippen des HTML-Contents lässt sich dieser über den Code-Kompressor minifizieren. Dadurch lassen sich überflüssige HTML-Kommentare, Whitespaces etc. entfernen:

<%@ taglib uri=„http://htmlcompressor.googlecode.com/taglib/compressor“ prefix=„compress“ %>
<compress:html enabled=„true“ compressJavaScript=„true“>
<!-- here is your JSP code -->
</compress:hmlt>

Ermögliche Proxy-Caching

Obwohl es Probleme mit Proxies geben kann (siehe oben!), solle das Caching durch Proxies trotzdem generell motiviert werden. Dies erreicht man, indem man den Cache-Control-Header auf Public setzt. So teilt man nicht nur dem Browser, sondern auch Proxies explizit mit, dass Inhalte gecacht werden dürfen.

Cache-Control: Public

Einige Browser lassen sich durch diesen Header zusätzlich überreden, Ressourcen, die über HTTPS ausgeliefert werden, zwischen zu speichern, obwohl im Normalfall Ressourcen, die über eine geschützt Verbindung ausgeliefert werden, nicht auf der Festplatte zwischengespeichert werden sollen.

Sunday, October 11, 2009

Hirschpark


Hirschpark
Originally uploaded by SimonOx

Saturday, August 15, 2009

Wasser, wild


Wasser, wild
Originally uploaded by SimonOx

Master of puppets


Master of puppets
Originally uploaded by SimonOx

Hamburger DOM


Hamburger DOM
Originally uploaded by SimonOx