Autorenarchiv

Schnellere Web-Anwendungen

Montag, 04. Januar 2010

Bei der Entwicklung von Web-Anwendungen ist die Performance in der Regel die letzte Frage, die gestellt wird. Der Fokus liegt im Regelfall auf der Umsetzung der Anforderungen.

Die Geschwindigkeit der Anwendung sollte aber früh mit eingeplant werden, da die Antwortzeiten darüber entscheiden, ob eine Anwendung als gut oder schlecht wahrgenommen wird. Dauert das Laden des gewünschten Inhalts zu lange, wird die Anwendung nie die Akzeptanz der Benutzer erfahren. Spontane Benutzer können sogar die Anwendung verlassen, weil sie die Anwendung / den Link als tot betrachten. Wer wartet schon 15 Sekunden auf eine Web-Seite!?

Die Geschwindigkeit einer Anwendung wird durch viele Parameter bestimmt. Während Anwendungen serverseitig meistens aufgrund einer hohen Anzahl von Datenbankabfragen lahmen, ist das Problem clientseitig komplexer, lässt sich aber auf 2 grundlegende Aussagen zusammenfassen:

  • Größe der geladenen Objekte (HTML, Bilder, CSS, JavaScript)
  • Anzahl der eingebundenen Objekte (Bilder, CSS, JavaScript)

Die Größe der Objekte bedingt eine längere Ladezeit und die Anzahl der Objekte führt zu einer Vielzahl von Anfragen an den Server. Jede Anfrage an den Server erzeugt einen eigenen Overhead.
Erst wenn alle Objekte vollständig geladen sind, kann der Inhalt vollständig und korrekt angezeigt werden.

Schritt 1: Komprimierung
Die Größe fast jeden Inhalts kann durch eine Komprimierung verringert werden. Die Komprimierung erfolgt am besten durch den Webserver. Beim Apache kann dies das Modul mod_deflate übernehmen.

Schritt 2: Caching
Das Meiste, was für die Anzeige der Oberfläche einer Web-Anwendung benötigt wird, kann gecached werden. Einzig die dynamisch generierten Objekte (im Regelfall nur das HTML), dürfen nicht gecached werden.
Der Server kann den Client durch durchgängiges Setzen von Header-Informationen unterstützen, die angeben wie lange das Objekt gültig ist. Auch hierfür gibt es beim Apache ein entsprechendes Modul: mod_expires

Mit diesen beiden rudimentären Schritten läßt sich die Performance schon spürbar verbessern, da weniger Objekte vom Server übertragen werden und diese nicht größer als notwendig sind.
Dies ist natürlich noch lange nicht alles, sollte aber bei geringem Aufwand den größten Nutzen erzielen.

Zur Unterstützung der Optimierung einer Webseite kann ich Firebug und YSlow (2 Plugins für Firefox) empfehlen.
Während man mit Firebug die einzelnen Anfragen an den Server mit den Headern für den Serverrequest und -response einsehen kann, gibt YSlow weitergehende Tipps zur Optimierung einer HTML-Seite.

Geschwindigkeit ist nicht alles, aber: “Performance rules!”


Thomas Bader


PostgreSQL-Funktionen mit ANT einspielen

Mittwoch, 12. August 2009

Um eine Datenbank auf dem aktuellen Stand zu halten, bietet sich ANT mit dem Task sql an. Hiermit ist es ein leichtes viele SQL-Skripte effektiv in die Datenbank einzuspielen:

<sql
    driver="org.database.jdbcDriver"
    url="jdbc:database-url"
    userid="sa"
    password="pass"
    src="data.sql"/>

Der Lösung so nah

Dies funktioniert soweit sehr gut, es sei denn man versucht Funktionen in die Datenbank einzuspielen. ANT kann leider nicht mit den Semikoli (;) in der Funktion richtig umgehen; so wird davon ausgegegangen das jedes Semikolon ein Statement beendet. Dies trifft beim Einspielen einer Funktion natürlich nicht zu. Hierfür bietet ANT eine schnelle Lösung mit dem Attribute “delimiter” am sql-Task. In dem folgenden Beispiel wird der Default-Delimiter (;) durch “$$” ersetzt.

<sql
    driver="org.database.jdbcDriver"
    url="jdbc:database-url"
    userid="sa"
    password="pass"
    src="data.sql"
    delimiter="$$"/>

Einfacher Fall

Das SQL der einzuspielenden Funktion für PostgreSQL sollte in etwa wie folgt aussehen:

CREATE OR REPLACE FUNCTION hello_world() RETURNS void AS '
BEGIN
    -- Aktionen der Funktion
END;
' LANGUAGE 'plpgsql' $$

Das SQL-Statement erstellt eine Funktion ohne Rückgabewerte, definiert danach die verwendete Sprache und beschliesst das Statement mit dem Delimiter ($$).
Ein kleines aber sehr wichtiges Detail sind hierbei die Hochkomma (’) hinter dem AS und vor dem LANGUAGE. Auch wenn bei der Verwendung von PgAdmin fast jede beliebiege Zeichenkette dort stehen kann sollte bei der Verwendung von ANT nur das Hochkomma verwendet werden, so das der JDBC den Inhalt der Funktion als Text auffasst und nicht versucht den Inhalt zu interpretieren.

Komplexer Fall

Doch was tun, wenn innerhalb der Funktion Texte verwendet werden, die selbst mit einem Hochkomma (’) eingefasst sind. In diesem Fall muss man darauf achten, das alle Hochkommas innerhalb der Funktion mit einem Hochkomma “escaped” werden.

CREATE OR REPLACE FUNCTION hello_world() RETURNS void AS '
BEGIN
    insert into my_table (text) values (''hello'');
END;
' LANGUAGE 'plpgsql' $$

Thomas Bader


Diagramme mit JFreeChart

Freitag, 10. Juli 2009

Jeder wird früher oder später der Aufgabe gegenübergestellt Daten grafisch aufzubereiten: Ein Diagramm muss her um einen Erfolg oder eine Verteilung zu visualisieren. Dann sollte man einen Plan in der Tasche haben, das Diagramm effektiv zu erstellen; mein Plan heisst JFreeChart.

“Hello Chart”

Für die schnelle und einfache Erstellung von Diagrammen bietet sich die ChartFactory an. Die Klasse bietet einige häufig verwendete Diagramme zur Auswahl.
Die Factory-Methoden erwarten die rudimentären Informationen, die für ein Diagramm benötigt werden, wie z.B.:

  • Titel des Diagramms
  • Beschriftung der X- / Y-Achse
  • Diagrammdaten
  • Flag, ob eine Legende erstellt werden soll

Natürlich gibt es hier leichte Variationen, je nachdem welchen Diagrammtyp man erstellen möchte. Heraus kommt ein Chart-Objekt, das man dann weiter nutzen, verändern oder einfach nur in eine Grafik wandeln kann, um es in eine Datei zu schreiben.

DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(100.0, "Datenreihe", "eins");
dataset.addValue(50.0,  "Datenreihe", "zwei");
dataset.addValue(75.0,  "Datenreihe", "drei");
dataset.addValue(37.5,  "Datenreihe", "vier");
dataset.addValue(56.25, "Datenreihe", "fünf");
 
JFreeChart chart = ChartFactory.createBarChart(
		"Hello Chart", 
		"X-Achse", 
		"Y-Achse", 
		dataset, 
		PlotOrientation.VERTICAL, 
		true, 
		true, 
		true);
 
BufferedImage image = chart.createBufferedImage(300, 200);

Wie man in dem folgenden Diagramm erkennen kann, kann man mit dem vorstehenden, kurzen Code-Fragment ein ansprechendes Diagramm einfach erzeugen

linechart

Die Qual der Wahl

Wenn man sich die ChartFactory anschaut findet man dort über 30 create-Methoden; doch welche mag die Richtige für das eigene Ziel sein? Der einfachste Weg geht natürlich über das Diagramm das ich erstellen möchte:

  • Liniendiagramm
  • Balkendiagramm
  • Tortendiagramm
  • … und vieles mehr

Aber es gibt mehr als das Erscheinungsbild. Daher möchte ich hier 3 verschieden Arten von Datensätzen exemplarisch vorstellen, da die Datasets über die Verwendung und die Visualisierung im Detail entscheiden:

  • CategoryDataset: ein 2-dimensionaler Datensatz, mit beliebigen X-Werten (u.a. auch Texte). Dieser Datensatz kann gut zur Erstellung von Grafiken mit wenigen individuellen Spalten verwendet werden und findet bei vielen Diagrammen Verwendung.
  • XYDataset: ein 2-dimensionaler Datensatz mit numerischen X-Werten. Dieser Datensatz ist optimal wenn viele X-Werte genutzt werden. (Anmerkung des Authors: mein persönlicher Favorit)
  • PieDataset: ein 1-dimensionaler Datensatz zur Verwendung in Tortendiagrammen.

Kleider machen Leute

Die erstellten Diagramme müssen sich natürlich in das UI oder die CI integrieren. Daher ist es nicht nur eine Kunst eine Diagramm zu erstellen, sondern auch es gemäß den Vorgaben anzuzeigen: Schriften, Farben, Rahmenstärken, etc. Auch hier finden wir bei JFreeChart Unterstützung.

  1. 3D-Diagramm: Wenn es ein wenig extravaganter sein muss, bieten sich spezielle 3D-Diagrammtypen an. Einige sind sogar über die ChartFactory verfügbar.
  2. Themes: Man kann ein ChartTheme erzeugen, mit dem man alle Diagramme programmatisch im selben Design formatieren kann. Man sollte sich der Themes früh annehmen, um manuelle Formatierungen zu vermeiden.

Unendliche Weiten

JFreeChart kann aber mehr als nur einfache Diagramm erstellen. So sind auch Diagramme mit mehreren Datenreihen ebenso möglich wie interaktive Grafiken. Dem Interessierten sei hier die Demo (Webstart) ans Herz gelegt.

Ausnahmslos glücklich?

Ich bin sehr angetan von JFreeChart. Die Bibliothek ist sehr umfassend und lässt kaum einen Wunsch offen. Die JavaDocs sind sehr gut und helfen einem durch viele grosse und kleine Probleme. Jedoch ist es bei der Größe der Bibliothek manchmal sehr schwierig sich einen vollständigen Überblick zu verschaffen. Hier bietet das Team von JFreechart jedoch einen kostenpflichtigen Developer Guide an. Der abgeneigte Käufer wird zwar viele Beispiele zu Diagrammen und Lösungsstrategien im Internet finden, hier jedoch so manche Stunde in die Recherche stecken müssen.


Thomas Bader


Nicht mehr oder noch nicht angemeldet?

Montag, 30. März 2009

In Webanwendungen ist ein Teil der Funktionen häufig nur für angemeldete Benutzer verfügbar.
So kann es passieren, dass die Session verfällt, wenn der Benutzer zu lange nichts in der Anwendung getan hat. Da der Benutzer jedoch vielfach noch seinen Browser und auch die Anwendung geöffnet hat, werden wir häufig damit konfrontiert den Benutzer darüber zu informieren, dass seine Session nicht mehr gültig ist.
Der direkte Aufruf von URLs führt uns schliesslich zu der Frage: Bin ich nicht mehr oder noch nicht angemeldet?

Was wollen wir sagen?
Was sagen wir dem Benutzer, wenn er auf eine Seite zugreifen will, für die eine Anmeldung erforderlich ist? Hierfür kann es verschiedene Gründe geben:

  • die Session ist verfallen
  • der Benutzer folgt einem Deeplink oder hat sich ein Lesezeichen gesetzt

Für die Anwendung sehen beide Fälle fast identisch aus, jedoch ist es für eine gute Benutzerführung eine aussagekräftige Fehlermeldung erforderlich.

Entscheidungskriterien
Die Lösung hierfür liegt in zwei kleinen Funktionen, die wir uns durch Kombination zu Nutzen machen können:

  • getRequestedSessionId() in javax.servlet.http.HttpServletRequest
  • isNew() in javax.servlet.http.HttpSession

Eine Sitzung ist dann verfallen, wenn der Client eine Session-ID mitschickt, der Server dennoch eine neue Session erzeugt. In Java können wir den Zustand wie folgt prüfen:

if (request.getRequestedSessionId() != null 
    && request.getSession().isNew()) {
    // session invalidated
} else {
    // login required
}

Ausnahmen
Die oben beschriebene Lösung funktioniert gut wenn die Sessions mittels Cookies verwaltet werden. Probleme macht der Fall, wenn der Benutzer keine Cookies zulässt und ein URL-Rewriting genutzt werden muss. Hierbei wird die URL mit der Session-ID erweitert; der einzelne Link wird Session-abhängig. Lesezeichen einer solchen URL würden dann immer eine Session-ID mitsenden: eine falsche Fehlermeldung wäre vorprogrammiert.
Glücklicherweise dürfte dieser Fall eher selten vorkommen, da der Benutzer schon generell die Verwendung von Cookies ablehnen müsste.


Thomas Bader