Testdatenerstellung: Permutationen

Beim Testen mit DataProvidern kann es manchmal sinnvoll sein, dass man viele
verschieden Eingabeparameter testen möchte. Z.B. wenn man eine Methode tested,
die als einen Parameter eine Buffergröße hat, macht es Sinn dieselben Testdaten
mit verschiedenen Buffereinstellungen durchzuprobieren. Da die Ergebnisse
nicht von der Puffergröße abhängen kann man einfach alle Tests mit allen
Puffergrößen laufen lassen.

/**
 * <p>
 * Creates a permutation of the given 2-dimensional array
 * and the given 1-dimensional array.
 * </p>
 * <code>
 * <pre>
 * permutate(new Object[][] { {"foo", "bar"}, {"alice","bob"}},
 *           new Object[] {1,2},
 *           1)
 * </pre>
 * </code> returns <code>
 * <pre>
 * new Object[][] {
 *      {"foo", 1, "bar"},
 *      {"alice", 2,"bob"},
 *      {"foo", 1, "bar"},
 *      {"alice", 2, "bob"}
 * }
 * </pre>
 * </code>
 * 
 * @param aData a 2-dimensional data object
 * @param aAdditionalColumn the additional column's data
 * @param aIndex the index where the additional column
 *               is added to <code>aData</code>
 * @return the permutated array
 */
public Object[][] permutate(
                            Object[][] aData,
                            Object[] aAdditionalColumn,
                            int aIndex)
{
    final List<Object[]> result = new ArrayList<Object[]>();

    for (Object additionalValue : aAdditionalColumn)
    {
        for (Object[] data : aData)
        {
            final Object[] newData = new Object[data.length + 1];

            if (aIndex > 0)
            {
                System.arraycopy(data, 0, newData, 0, aIndex);
            }
            newData[aIndex] = additionalValue;

            if (aIndex + 1 < newData.length)
            {
                System.arraycopy(data,
                                 aIndex,
                                 newData,
                                 aIndex + 1,
                                 newData.length - aIndex - 1);
            }
            result.add(newData);
        }
    }

    return result.toArray(new Object[0][]);
}

Note: The code above is released to public domain.

TestNG: mit DataProvidern auf Exceptions testen

Das Testframework TestNG bietet DataProvider an, mit deren Hilfe es leicht möglich ist viele Kombinationen von Eingabeparametern zu überprüfen. Aber was macht man, wenn man bei bestimmten Eingaben eine Exception erwartet? Einen zweiten Test mit DataProvidern, so dass man einen Test für valide Eingaben und einen für invalide Eingaben hat? Oder sollte man die invaliden Eingaben lieber in separaten Testcases prüfen, damit man auch prüfen kann, dass die korrekte Exception fliegt?

Ich finde beide Lösungen etwas unübersichtlich. Aber zum Glück gibt es Abhilfe.

public String append(String s, String t)
{
  if (s == null || t == null) throw new NullPointerException();
  return s + t;
}

@DataProvider(name = "providerAppend")
public Object[][] providerAppend()
{
  return new Object[][] {
    { null, null, new NullPointerException() },
    { "", null, new NullPointerException() },
    { null, "", new NullPointerException() },
    {"abc", "def", "abcdef" },
    {"abc", "", "abc" },
    {"", "def", "def" }
  };
}

@Test(dataProvider = "providerAppend")
public void testAppend(String s, String t, Object expected)
{
  if (expected instanceof Exception)
  {
    try
    {
      append(s, t);
      Assert.fail("Expected an exception: " + expected.getClass());
    }
    catch (Exception e)
    {
      Assert.assertEquals(e.getClass(), expected.getClass());
    }
  }
  else
  {
    Assert.assertEquals(append(s,t), expected);
  }
}

In obiger Lösung gehen wir einfach davon aus, dass es für den DataProvider egal ist, ob der Rückgabewert nun eine Wert oder eine Exception ist. Das handling wird dann in der Testmethode übernommen, und zwar dadurch, dass wir unterschiedliche Testimplementierungen wählen, je nachdem ob der erwartete Wert vom Typ Exception ist oder nicht. Wenn er vom Typ Exception ist, dann stellen wir sicher, dass eine Exception geworfen wird, und das die Klasse identisch mit der Erwartung ist. Manchmal kann es sinnvoll sein, denn Klassenvergleich gegen eine instanceof Prüfung zu ersetzen.


vServer oder dedicated Server was ist besser?

Wer mit dem Gedanken spielt eine eigene Webseite aufzusetzen die einige tausend Besucher am Tag anlocken soll, der wird sich irgendwann die Frage stellen, ob er mit einem eigenen Server nicht besser bedient ist, als mit einem traditionellen Webhosting. Wenn man dann anfängt sich schlauer zu machen, dann findet man dutzende Angebote von vServern ab 7-8 Euro im Monat, bis zu dedicated Servern für hundert oder mehr Euro im Monat.

Die Frage was für wen das richtige ist lässt sich nicht so einfach beantworten und verlangt, dass man sich erstmal klar darüber wird, was man braucht und vielleicht mal brauchen wird. Ich will hier einige Stolperfallen und Wehrmutstropfen ansprechen, über die man sich im Klaren sein sollte.

Also erstmal: Wer die kompromisslose Performance braucht, der sollte einen Hoster finden, bei dem er sich beliebige Hardware aussuchen kann und die direkt an einen der Backbone Switches (z.B. ein Port am DE-CIX Knoten in Frankfurt gibts für schlappe 2000 € im Monat) anschließen kann. Aber sowas wird für die meisten etwas zu teuer sein.

Dedicated Server

Preiswerter geht es da schon mit den dedicated Servern, also ein (physikalischer) Rechner, der irgendwo in einem Rechenzentrum steht und nur von dir benutzt wird. Solche Kisten gibt es in zwei Ausführungen: 1. Managed Server und 2. die Anderen. Bei Managed Servern kümmert sich der Hoster um Installation, Wartung usw.. Das bedeutet auch, dass man keine Admin/Root Rechte hat. Bei den nicht Managed Servern bekommt man meistens eine nackte Installation hingestellt und Zugangsdaten für SSH bzw. RemoteDesktop. Was man dann auf der Kiste installiert bleibt einem selbst überlassen, d.h. auch Betriebsystem updates und Sicherheitspatches liegen dann in der eigenen Verantwortung.

Auch wenn sich Managed Server ersteinmal wunderbar anhört, so hat er doch einige Nachteile. Zum einen bezahlt man mindestens einen hunderter mehr im Monat, oder man bezahlt jedesmal, wenn man etwas installiert haben möchte. Ein zweiter Nachteil ist, dass man immer erst einem Mitarbeiter des Hosters erklären muss, was man haben will. Reibungsverluste sind vorprogrammiert. Auf der anderen Seite ist natürlich nicht jeder in der Lage einen Server selbst zu administrieren. Und auch wenn man aktuell einen fähigen Studenten eingestellt hat, kann es sinnvoll sein, sich nicht von dem abhängig zu machen.

Die Hardwareausstattung ist eine reine Preisfrage. Es gibt Angebote für so ziemlich alles.

vServer

vServer sind Schnäppchen unter den Servern. Bei diesen Angeboten teilt man sich den Rechner mit anderen Kunden. Wie viele das sind kann man pauschal nicht sagen. Geboten bekommt man einen virtuellen Rechner, der sich so anfühlt, wie ein eigener Server, d.h. man sieht von den anderen Kunden nichts.Virtuelle Server gibt es ebenfalls in Managed und Unmanaged Varianten.

Die Hardware des Wirtsystems kann ebenfalls sehr unterschiedlich ausfallen. Ich hab schon Maschinen gesehen mit modernsten CPUs drin (in dem Fall zwei 8 Core Xeons), die darauf schließen lassen, dass an der restlichen Hardware auch nicht gespart wurde. Viel wichtiger als die CPUs ist allerdings, wie die vServer eingestellt wurden. D.h. wie viel RAM bekommt man garantiert, wie viele Programme darf man öffnen, wie viele Filehandles und ähnliches.

Beim RAM werden meist zwei Zahlen veröffentlicht: garantiertes RAM und dynamisches RAM. Hat man 512MB garantiertes RAM, dann kann man sich darauf verlassen für die eigenen Prozesse mindesten 512 MB zu haben. Dynamisches RAM bedeutet im Grunde folgendes. Wenn der Rechner noch RAM übrig hat, dass nicht von anderen Kunden benutzt wird, dann kann man dies ebenfalls nutzen. Ich für meinen Teil ignoriere dieses dynamische RAM vollkommen und richtige mich einzig und allein nach dem garantierten.

Eine Einschränkung, die man leider nicht aus der Reklame herauslesen kann ist, wie viele Programme man gleichzeitig laufen haben darf bzw. wie viele Dateien man gleichzeitig geöffnet haben darf. Diese Einstellungen werden sehr unterschiedlich gesetzt und können ganz leicht dazu führen, dass man die Kiste nicht benutzen kann.

Max open files: Auf Linux Systemen meistens mit dem Befehl cat /proc/user_beancounters  | grep numfile nachzuschlagen. Diese Zahl regelt wie viele Files gleichzeitig geöffnet sein dürfen, und dazu zählen auch alle referenzierten Libraries und Sockets. Bei meinem vServer steht die Zahl z.B. aktuell bei 2334 und ich hab lediglich einen apache, mysql-server, tomcat, exim4, dovecot, svnserve, sshd laufen, ohne dass die viel tun (die Kiste ist gerade frisch neugestartet). Wenn der Hoster das Limit nun auf 4000 setzt (das hab ich schon mehrfach gesehen), dann können einen schon ein paar Besucher auf dem Apachen und einige ankommende und abgehende Mails ins Limit laufen lassen. Und dann hagelt es Fehlermeldungen.

Ein weiteres Problemchen sind Wartungsarbeiten am Wirtsystem.  Wenn der vServer-Betreiber seine Hausaufgaben macht, dann wird es regelmäßig (alle paar Monate) zu einem Neustart es Wirtsystems und damit auch des vServers kommen. Auf den Zeitpunkt hat man keinen Einfluss. Und wehe dem, der die Maschine nicht so konfiguriert hat, dass die das unbeschadet übersteht und auch alle Dienste wieder hochfährt. Es gibt nichts ärgerlicheres, als auf den Bahamas zu sitzen und der Webshop startet nicht mehr.

Natürlich sind vServer echt preiswert.  Wer auf Ausfallsicherheit setzen will, und in der Lage ist das selbst einzurichten, der sollte darüber nachdenken sich 2 bis n vServer zulegen. So kann man sich für den Preis eines dedicated Servers eine kleine Farm an vServern zulegen, die über verschiedene Provider verteilt sind und in verschiedenen Netzen hängen.


In PHP geschriebene Templatesprachen sind eine Sackgasse der Evolution

Wer sich mit PHP beschäftigt wird früher oder später auf Personen treffen, die Befürworter von sogenannten Templatesystemen sind (z.B. Smarty, Fluid, uvm.). Es gibt auch viele Personen, die eigene Templateengines schreiben. Nahezu jedes in PHP geschriebene Content Management System (CMS) verfügt über eine eigene Implementierung oder nutzt ein Templatesystem.

Es will mit einfach nicht in den Kopf, warum die das machen. Ich meine PHP ist eine Templatesprache. Warum zur Hölle sollte man eine Templatesprache in einer Templatesprache schreiben? OK, man versucht eine Trennung zwischen Businesslogik und Darstellung zu erreichen, und behauptet dann auch noch das Model-View-Controller Pattern zu verwenden, was ja State-Of-The-Art ist. (Wer das nicht benutzt ist ein Höhlenmensch, der seit dem Altpaläolithikum jeglichen Fortschritt verschlafen hat!)

Merken diese Leute denn nicht, was für einen riesigen Ballast sie sich damit an Board holen? Man muss nämlich eine weitere Sprache lernen, die in den meisten Fällen zwar an PHP angelehnt ist, aber gerade für Schleifen und If-Then-Else Konstrukte meist unbenutzbar ist. Die Templates werden mit Hilfe von PHP geparst, um dann mühsam wieder HTML-Code daraus zu generieren. Das ist langsam und ineffizient. PHP kann das seit Version 1.0 und macht es zuverlässig und schnell. Um Logik und Darstellung zu trennen braucht es nicht einer künstlichen Barriere, die jedesmal mit viel Aufwand überwunden werden muss, sondern einfach nur etwas Disziplin beim Programmieren. PHP kennt nämlich so tolle Konzepte wie Funkionen und Klassen. Damit kann man ganz prima verschiedene Codebereiche voneinander trennen.

“Aber die Sicherheit”, schreien dann einige, “mit PHP kann ich ja alles machen. Bei einer eigenen Templatesprache kann ich mir sicher sein, dass ein Template keinen Unfug anstellen kann.” Zugegeben, das klingt auf den ersten Blick einleuchtend. Wenn ich eine eigene Templatesprache habe, die nur Variablen ausgeben, aber keine beliebigen Befehle ausführen kann, dann reisst mir das keine Sicherheitslöcher in die Applikation.

Aber das ist nur die halbe Wahrheit. Nahezu alle CMSs können per Plug-In (Add-On, Extension, …) erweitert werden. Diese sind sowieso in PHP geschrieben und können beliebigen Scheiß anstellen. Gelten die Sicherheitsbedenken dafür denn nicht? Und mal ganz ehrlich: Alle Templates die ich bislang genutzt habe waren entweder die Beispieltemplates der Applikation, oder ich hab sie selbst geschrieben. Und wenn ich die Dinger selbst schreibe, dann bin ich ja auch selbst dafür verantwortlich, oder?

Ich hab jetzt viel geschrieben. Manchmal hilft es einen Standpunkt nieder zu machen, um ihn besser zu verstehen. Aber in diesem Fall bin ich überzeugter denn je, dass in PHP geschriebene Templatesprachen unsinnig sind. Eine Sackgasse der Evolution, sozusagen.


jQuery: The ‘charCode’ property of a keydown event should not be used.

Warning: The ‘charCode’ property of a keydown event should not be used. The value is meaningless.
Source File: http://example.com/
Line: 0

Wenn du diese Meldung bekommst, dann hast du vermutlich Code geschrieben, der event.keyCode oder event.charCode benutzt:

$(document).keydown(function(event) {
     if  (event.keyCode == 37)  {
         // do something
     }
     if (event.charCode == 37) {
         // do something
     }
}

Diese beiden Variablen sind unschön. KeyCode zum Beispiel gibt nur den Code der Taste an, aber man kann nicht zwischen a und A unterscheiden, weil beide auf derselben Taste liegen.

Lösung

Du solltest das von jQuery hinzugefügte event.which nehmen, das versucht diese beiden zu normalisieren.

$(document).keydown(function(event) {
    if  (event.which == 37)  {
        // do something
    }
}

JavaScript Dateien effizient übertragen mit GZIP, PHP und Apache

Wenn man diese schönen JavaScript Bibliotheken, wie z.B. jQuery, nutzt, dann steht man schnell vor dem Problem, dass man ein Dutzend JavaScript Dateien laden muss, um die Seite korrekt angezeigt zu bekommen. Wenn man dann den Webserver falsch konfiguriert hat werden die Dateien alle unkomprimiert übertragen. Ausserdem muss der Browser für jede Datei den Webserver fragen. Und wenn man dann auch noch das Caching falsch konfiguriert hat, dann werden für jeden Seitenaufbau alle JS Dateien neu geholt.

Ob man sein System richtig Konfiguriert hat lässt sich sehr schön mit dem Firefox Plugin Firebug beobachten. Wie das geht bleibt dem geneigten Leser als Übung überlassen, denn mir geht es hier hauptsächlich um die Lösung dieses Problems.

Nach Monaten intensiver Recherchen und langen Versuchsreihen bin ich auf folgende Lösung gestoßen: Anstatt die JS Dateien direkt herunterzuladen, kopiere ich einfach den Inhalt von allen Dateien on-the-fly in eine Große. Diese wird dann hübsch komprimiert und mit den notwendigen HTTP Headern ausgestattet, damit das Caching auch funktioniert. Irgendwann hat sich folgendes PHP Script als optimal herauskristallisiert:

<?php 
// License: none (public domain) 
/*
 * Dieses Script concateniert mehrere Dateien und schickt sie gzip
 * komprimiert an den Browser. 
 * Ausserdem wird ein Etag gesetzt, der nötig ist, um das Cachen der 
 * Javascript Datei zu ermöglichen. Der Etag ändert sich jedesmal, wenn 
 * eine der Dateien geändert wird, oder eine hinzu-/weggenommen wird. 
 */ 
header('Content-Type: text/javascript');
header("Expires: ".gmdate("D, d M Y H:i:s", time() + 3600*24)." GMT");

/*
 * Hier die JavaScript Dateien angeben:
 */
$files = array(
    "jquery-1.4.2.min.js",
    "ui.core.js",
    "ui.dialog.js",
    "ui.draggable.js",
    "ui.resizable.js",
    "jquery.textarearesizer.compressed.js"
);

$md5 = '';
foreach ($files as $file)
{
    $md5 .= md5_file($file);
}

$etag = md5($md5);
header("Etag: $etag");

if ( isset($_SERVER['HTTP_IF_NONE_MATCH'])
  && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag)
{
    header('HTTP/1.1 304 Not Modified');
}
else
{
    if ( isset($_SERVER['HTTP_ACCEPT_ENCODING'])
      && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], "gzip") !== false )
    {
        ob_start("ob_gzhandler");
    }
    else
    {
        ob_start();
    }

    foreach ($files as $file)
    {
        echo file_get_contents($file);
    }
}
?>

Vorgehensweise:

Lege eine Datei ‘js.php‘ in dem Ordner mit den JavaScript-Dateien an. Kopiere obigen Code in die Datei. Ändere das $files Array so, dass deine JavaScript-Dateien da drin stehen. Füge folgenden Script-Tag in deinen HTML-Code ein:

<script src="/path/js.php" type="text/javascript"></script>

Das ganze funktioniert natürlich auch mit CSS-Dateien, wobei man dann den Content-Type auf text/css ändern muss.


Javascript: delayed method call

Sometimes you want to delay an action (e.g. an AJAX call) until the user finishes or pauses her input. This is usefull for any typeahead actions. So you want to call a function when the user types into a field, but you only want to call that function once even if the user types two characters at once, or a hole word within a short period of time.

AFAIK Javascript does not provide a method that handles this use case conveniently.

Here is a function that can be called on every keystroke. It sets a timeout to call the function which handles the input. If the method is called again while the timeout has not been elapsed, then the first timeout will be cancelled and a new timeout is set.

var timeout = null;
function delayTask(methodToCall, delayInMilliseconds)
{
  if (timeout != null)
  {
    clearTimeout(timeout);
  }
  timeout = window.setTimeout(methodToCall, delayInMilliseconds);
}

Caused by: java.rmi.RemoteException: -1

sun-java-logo

Caused by: java.rmi.RemoteException: -1

Dieser Fehler ist meist die Folge einer ArrayIndexOutOfBoundsException die während eines RMI calls auftrat. Irgendwo auf der Gegenseite ist die geflogen und wurde dann in eine RemoteException umgewandelt.


No enclosing instance of type InnerClassExample is accessible.

sun-java-logo

Die Fehlermeldung No enclosing instance of type InnerClassExample is accessible. Must qualify the allocation with an enclosing instance of type InnerClassExample (e.g. x.new A() where x is an instance of InnerClassExample). bekommt man immer dann, wenn man eine Klasse instantieren möchte, die als nicht statische innere Klasse definiert wurde. Wie zum Beispiel hier:

public class InnerClassExample
{
    private static void aFunction()
    {
        InnerClass privateClass = new InnerClass();
    }

    private class InnerClass {}
}

Die Ursache ist, dass InnerClass nicht static definiert wurde. Wenn man InnerClass static macht funktionierts wie man das erwarten würde. Ich behaupte einfach mal, dass man in 95,3% der Fälle auch wirklich eine statische innere Klasse haben will, man kann das static also einfach dazuschreiben.

Und so siehts dann aus:

public class InnerClassExample
{
    private static void aFunction()
    {
        InnerClass privateClass = new InnerClass();
    }

    private static class InnerClass {}
}

Hintergrund: Innere Klassen sind an ihre äußeren Klassen gebunden. So ist es möglich von der inneren Klasse auf Member der äußeren zuzugreifen. Damit das funktioniert muss klar sein von welcher Instanz der Member gelesen werden soll. Daher braucht es eine eins-zu-eins Beziehung zwischen äußerer und innerer Klasse. Diese Beziehung ist gegeben, wenn die innere Klasse in einer nicht statischen Methode erzeugt wird. Will man aber die innere Klasse aus einer statischen Methode, oder von einer anderen Klasse aus erzeugen, dann weiss Java nicht zu welcher Instanz der äußeren Klasse die Innere gehört.

Man hat also eigentlich mehrere Möglichkeiten:

1. Man macht die innere Klasse statisch. Dann kann man nicht mehr auf Member der äußeren Klasse zugreifen, aber man kann die Innere überall erzeugen.

2. Man zieht die innere Klasse in eine eigene Java Datei raus. Hat im Prinzip die gleichen Nachteile wie 1.

3. Man erzeugt eine extra Instanz der äußeren Klasse und benutzt deren new Operator. Das sieht dann so aus:

new InnerClassExample().new InnerClass();

Jep, das sieht etwas gewöhnungsbedürftig aus und ich hab das bislang noch nie in Code von erfahrenen Java-Programmierern gesehen.

4. Eine Variante von 3. ist, wenn man bereits eine Instanz von der äußeren Klasse hat, dann kann man die auch nehmen, z.B. so:

outclassInstance.new InnerClass();

Hab ich noch nie in der freien Natur gesehen. (Und das obwohl die Fehlermeldung genau diese Lösung vorschlägt.)

5. Man sieht ein, dass man eine innere Klasse nur innerhalb der äußeren Klasse benutzen sollte. Also macht man die Innere private (oder wenigstens protected) und baut seinen Code so um, dass er ohne die innere Klasse auskommt.


Hall Of Fame: Die schönsten Methoden-Signaturen

02 Eine der schönsten Methoden-Signaturen, die ich bisher gesehen habe:

SomeException runCommand(Command aCommand, boolean aRunSilent) throws SomeException;

Follow

Bekomme jeden neuen Artikel in deinen Posteingang.