September 2007

Großartiges Spring Tutorial

Jeder, der schon mal etwas im Web- und Enterprise-Bereich programmiert hat, weiß, dass es ab einer gewissen Komplexität viele Probleme auftreten - wie unsauberer Code mit Seiteneffekten, Fehleranfälligkeit, schlechte Wartbarkeit und zu starke Kopplung von Präsentation und Model. Gleichzeitig ist aber auch klar, dass es eine riesige Anzahl von konkurrierende Technologien gibt, die alle vorgeben, diese Probleme zu lösen.

Ein sehr erfolgreicher Ansatz ist der des Spring Frameworks und ein fantastisches Tutorial findet sich hier. Viele Frameworks im Web- und auch dem UI-Bereich geben vor, das MVC-Pattern zu unterstützen, aber dabei stellt sich dann oft heraus, dass man das Pattern nur mit viel Programmier-Disziplin oder aber nur durch selbstgemachte Konventionen umsetzen kann ohne dabei auf die Hilfe oder die Architektur des Frameworks setzen zu können. Solche Szenarien ergeben sich insbesondere bei PHP und ASP.NET. Allerdings haben diese beiden Technologien Vorzüge gegenüber Spring, da man produktiver ist und mit weniger Code auskommt. Zwei kurze Code-Beispiel dazu:

Additions-Website in PHP

index.php (in die Seite integriert)

<div class="ergebnis">
<?php
$erg = $_GET["a"] + $_GET["b"];
echo "Ergebnis = $erg.";
?>
</div>

Additions-Website in Spring

AdditionController.java

public class AdditionController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request,
                                      HttpServletResponse response)
                                      throws ServletException, IoException {
        int a = Integer.parseInt(request.getParameter("a"));
        int b = Integer.parseInt(request.getParameter("b"));
        int erg = a + b;
        return new ModelAndView("add.jsp", "erg", erg);
    }
}

add.jsp

<div class="ergebnis">
Ergebnis = <c:out value="${erg}"/>
</div>

(originally posted on 2007-09-24)

Mit Java SE 7 keine JARs mehr

Im Java Community Process gibt es viele interessante Vorschläge für Java 7. Wie bereits in diesem Artikel beschrieben, sind Closured für Java SE 7 im Gespräch - genauso wie auch die Properties, die es in C# gibt.

Richtig interessant sind aber vor allem die Java Specification Requests JSR277 und JSR294. Die elementare Code-Einheit in Java ist die Klasse und da diese gemeinsam mit dem Namespace auch immer den Dateipfad bestimmt, ergeben sich normalerweise wenig Probleme bei Projekten, die dann externe Resourcen mithilfe einer JAR-Datei benutzen. Eine JAR-Datei ist prinzipiell nichts anderes als ein Archiv, in dem wiederum alle Namespaces als Ordner und alle Klassen als Dateien gemeinsam mit einem Manifest oder sonstigen Meta-Daten verpackt ist.

In C# setzt man auf Assemblies. So werden alle Typen (Klassen, Schnittstellen-Definitionen, etc.) gemeinsam in ein Assembly - normalerweise eine DLL oder eine EXE Datei verpackt. Diese kann dann in anderen Projekten als Referenz hinzugefügt werden oder abern im Global Assembly Cache (GAC) gefunden werden. Dieses Konzept ist zwar schon etwas fortschrittlicher als die JAR-Dateien bei Java, allerdings kann es bei größeren Anwendungen sehr mühsam werden, wenn eine refenrenziertes Assembly auf eine neue Version geupdates wird. Außerdem ist das Konzept der Assemblies auch nicht grobgranular genug, so kann es bei einer großen ASP.Net Anwendung dazu kommen, dass Dutzende oder Hunderte von DLLs bei der Auslieferung der Website mitausgeliefert werden müssen.

Lange Rede, kurzer Sinn: Mit Java SE 7 soll es nun ein Konzept für 'Module' in Java geben, sodass Bibliotheken nicht mehr als JAR-Dateien, sondern als Module ausgeliefert werden. Ein solches Modul hat einen eindeutigen Namen (Vergleich: Strong Name bei .Net) und eine Version. Desweiteren kann ein Modul zwar weitere Module enthalten oder auch andere externe Module referenzieren. Dabei kann dann optional auch eine genaue Version verlangt werden oder sogar eine gewisse Versions-Spanne. Die Modul-Verwaltung erfolgt durch die JRE, die dann auch ähnlich wie das OSGi-Framework von Eclipse die benötigten Module dynamisch aus dem Internet nachladen kann, Abhängigkeiten auflöst und mit dem Betriebssystem kooperiert. Bisher musste ein Java-Entwickler (fast) immer eine Batch-Datei mitliefern, wenn es seine Java Programme von normalen Benutzern auf Windows ausführen lassen möchte. Man wird sich bei der Implementierung des Java(tm) Modul Systems sicherlich auch bei Maven etwas abgucken. Eine erfolgreiche Umsetzung wird Java sicherlich auch wieder für einige C# Entwickler interessant machen, die mit der 90er Jahre Architektur von großen Java Anwendungen unzufrieden sind.

(originally posted on 2007-09-19)

Delegates vs. Anonyme Klassen vs. Blöcke

Im Moment wird heftig diskutiert, ob Properties und die sogenannte Closures in Java SE 7 aufgenommen werden sollen. Befürworter meinen, dass dieses Konzept in fast allen Scriptsprachen und auch in C# vorkommt und Java hinterherhinkt. Gegner meinen, dass Java glücklicherweise eher konservativ ist, wenn es um neue Programmier-Trends geht, was die Sprache einfach hält und schließlich hat man bisher ja auch keine Closures gebraucht.

Ein typischer Smalltalk-Programmierer würde für eine Suche in einer Collection aufgrund der "Blöcke" nicht mehr als drei Zeilen brauchen:

| s t |
s := (OrderedCollection newFrom: #('Hallo' 'du' 'wie' 'geht' 'es' 'dir')).
t := s select: [:each | each beginsWith: 'ge'].
Transcript show: (t at: 1).

Bei C# bedient man sich in solchen Fällen der Delegates. Das sind Typdefintionen für Methoden, sodass diese dann als Parameter oder als Variable gehalten und auch aufgerufen werden können. Damit ist eine event-getriebene Programmierung besondern einfach und kurz. In diesem Fall muss man also erst sein generisches TestCondition-Delegate definieren und dann erweitert man seine Kollektion um einen Suchaufruf und kann dann eine Methoden-Implementierung als Parameter übergeben.

public delegate bool TestCondition<E>(E entity);
 
class ListWithFinding<E> : List<E> {
    public E Find(TestCondition<E> test) {
        foreach(E e in this) {
            if (test(e)) {
                return e;
            }
        }
        return default(E);
    }
}
 
public bool TestString(string entity) {
    return entity.StartsWith("ge");
}
 
public void Main() {
    ListWithFinding<string> l = new ListWithFinding<string>();
    l.Add("Hallo");  l.Add("du");  l.Add("wie");
    l.Add("geht");    l.Add("es");  l.Add("dir");
    string res = l.Find(TestString);
    Console.WriteLine(res);
}

In Java gibt es weder Blöcle noch Delegates. Daher gibt es eine Nachfrage nach einen entsprechenden Konzept. Dennoch sind die vorgeschlagenen Closures gar nicht notwendig, da Java ein anderes Konzept, dass es in C# und Smalltalk nicht gibt: Anonyme Klassen. Wenn man nun eine Suche braucht, dann kann man diese Methode als Interface definieren und dann seine Collection um eine Suche bereichern. Im Code selbst kann man dann dieser Suche seinen Test als anonyme Klasse, die das TestInterface implementiert, übergeben. Abgesehen von den etwas überflüssigen und unnötig aufwändigen Code, ist also in Java alles möglich, was man auch mit Blöcken und Delegates erreichen kann.

interface TestCondition<T> {
    public boolean test(T type);
}
 
class ArrayListWithFinding<E> extends ArrayList<E> {
    public E find(TestCondition<E> tester) {
        for (E entity : this) {
            if (tester.test(entity)) {
                return entity;
            }
        }
        return null;
    }
}
 
public static void main(String[] args) {
    ArrayListWithFinding<String> l = new ArrayListWithFinding<String>();
    l.add("Hallo");   l.add("du");  l.add("wie");
    l.add("geht");    l.add("es");  l.add("dir");
    String res = l.find(new TestCondition<String>() {
        public boolean test(String type) {
            return type.startsWith("ge");
        }
    });
    System.out.println(res);
}

(originally posted on 2007-09-15)

junit.framework.AssertionFailedError - No tests found

Auf diese oder eine ähnliche Fehlermeldung stößt man, wenn JUnit keine Tests findet. Das kann durchaus auch daran liegen, dass man seine Testfälle mit Annotationen gekennzeichnet hat.

@Test
public void testMethod() {
    Assert.assertEquals(2, 1 + 1);
}

Leider kann es sein, dass ein Ant-Task , der die Tests ausführen soll, oder auch die IDE selbst, wie beispielsweise Eclipse intern immer noch mit einem älteren JUnit arbeiten, also noch nicht mit JUnit 4 oder höher.

Eine sehr einfache Lösung für dieses Problem ist das hinzufügen folgender simpler Methode - dann werden die Tests auch gefunden !

public static junit.framework.Test suite() { 
    return new JUnit4TestAdapter(MyTestClass.class); 
}

(originally posted on 2007-09-13)

WebParts dynamisch aus Assemblies laden

Ein nettes Feature bei dem freien ASP.NET Portal mojoPortal ist das dynamische hinzufügen von Assemblies, bei denen es sich meistens um kompilierte Klassenbibliotheken als DLL handelt. In diesen Assemblies können sich dann WebParts befinden, die automatisch erkannt und auch auf der Website erkannt werden können.

Der Code zum Laden von WebParts aus einer DLL könnte dann beispielsweise so aussehen:

Assembly assembly = Assembly.LoadFrom(assemblyFileName);
Type type = assembly.GetType("MyWebPart", true);
WebPart webPart = Activator.CreateInstance(type) as WebPart;
contentPanel.Controls.Add(webPart);

Wenn man sich allerdings eines WebPartManagers bedient. Dann wirft dieser nach dem Hinzufügen eines WebParts folgende Fehlermeldung:

The Type must be loadable by BuildManager.GetType(string typeName)

Wenn man ein Assembly erst zur Laufzeit hinzufügt, dann zählt es nicht mehr zum so genannten "Top_Level_Assembly" wo beispielsweise der kompilierte App_Code-Ordner drin liegt. Da der WebPartManager allerdings auch später noch immer an die Klassendefinitionen des WebParts herankommen muss, verbietet er alle WebParts, die in anderen Assemblies liegen, es sei denn, sie sind in der web.config eingetragen, doch aus diese kann man zur Laufzeit auch nur lesend zugreifen.

Die Fehlermeldung kommt ja aus der Methode WebPartManager.VerifyType() . Die Lösung ist also relativ einfach: Man überschreibt diese Methode und verhindert so eine Exception. Da diese Methode aber leider privat und nicht protected ist, muss man die aufrufende Methode überschreiben. Dabei handelt es sich um WebPartManager.CopyWebPart(WebPart). Ein modifizierter WebPartManager ohne Exception bei externen Assemblies könnte nun so aussehen:

using System;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
 
public class ModifiedWebPartManager : WebPartManager
{
    protected override WebPart CopyWebPart(WebPart webPart)
    {
        WebPart newPart;
        if (webPart is GenericWebPart) {
            Type childType = ((GenericWebPart)webPart).ChildControl.GetType();
            Control newControl = (Control)Internals.CreateObjectFromType(childType);
            newControl.ID = CreateDynamicWebPartID(childType);
            newPart = CreateWebPart(newControl);
        } else {
            newPart = (WebPart)Internals.CreateObjectFromType(webPart.GetType());
        }
        newPart.ID = CreateDynamicWebPartID(webPart.GetType());
        return newPart;
    }
}

(originally posted on 2007-09-12)

Gefahrlose Typumwandlung

Wenn aus irgendeinen Grund der Typ eines Objekts zu 'schwach' ist, weil beispielsweise aus einem einfachen Cache ein Objekt geholt wurde, dann ist es oft nötig, das man eine Klassenzugehörigkeit explizit hinzufügt durch eine Typumwandlung - einen so genannten Typecast.

Person p = (Person)cache.getSavedObject();

Typecasts sollten nur im Notfall eingesetzt werden, da bei einer strengen und konsequenten Verwendung von Typen gar keine Typecasts gebraucht werden und somit gar keine Gefahr für derartige Laufzeitfehler entsteht.

Wenn man dann aber doch einen Typecasts braucht, dann sollte man eine ClassCastException abfangen, oder aber vorher den Typ abfragen und bei ungültigen Casts dann null zurückgeben..

Object o = cache.getSavedObject(); 
Person p = null;
if (o instanceof Person) {
    p = (Person)o;
}

Das geht zwar auch in C#, doch da gibt es sogar noch einen viel einfacheren Weg, der exakt dasselbe leistet:

Person p = cache.getSavedObject() as Person; 

(originally posted on 2007-09-10)

Frühe Bindung ist schlecht für Schnittstellen

Traditionell wird bei dem Methodenaufruf zwischen früher und später bzw. statischer und dynamischer Bindung unterschieden. Bei der späten Bindung stellt sich erst zur Laufzeit heraus, welche Methode wirklich aufgerufen wird, während bei der frühen Bindung bereits beim Kompilieren die aufzurufende Methode bekannt ist. Bei Code, der direkt in Maschinensprache übersetzt wird, wie beispielsweise C, C++ und Delphi, kann man diesen Effekt gut nachvollziehen.

Bei einer modernen objektorientierten Programmiersprache, die interpretiert wird oder auf Bytecode basiert, lässt sich aber an der Sinnhaftigkeit und Notwendigkeit zweifeln, die aufzurufende Methode bereits vorher zu kennen. Vielmehr sieht man die Objekte als eigene Autoritäten, die selbst ihr Verhalten über die Methoden steuern und fast immer in Oberklassen oder Interfaces geboxt werden. Somit ist bei einem Objekt die Klassenzugehörigkeit entscheidend und nicht mehr der deklarierte Datentyp der Variable.

Ein sehr wichtiges Prinzip bei der Objektorientierung ist das Programmieren auf Schnittstellen und nicht auf konkrete Implementierungen. Durch eine Schnittstelle soll im Prinzip nur eine Funktionalität von einer Klasse gewährleistet sein - unabhängig davon welche konkrete Klasse die Schnittstelle implementiert. Methoden einer Schnittstelle sind also eher als Nachrichten an Objekte zu verstehen und somit immer virtuell - setzen also immer eine späte Bindung voraus.

Im folgenden Beispiel wird die zweite Methode nicht als virtuell deklariert und das Interface IMyInterface wird vererbt. Das führt dann aber dazu, dass der restliche Code, also in diesem Fall die Main-Methode, durch Frühe Bindung zwei Methoden zweier verschiedener Klassen aufruft, ohne dass es (aus dem Interface) klar wird, welche Methoden welcher Klassen das sind.

Fazit: Durch Frühe Bindung und Interface-Vererbung wird also eine neu (keyword new) deklarierte Methode in der Unterklasse nicht mehr aufrufbar !

interface IMeinInterface
{
    void ErsteMethode();
    void ZweiteMethode();
}
 
class MeineKlasse : IMeinInterface
{
    public virtual void ErsteMethode()
    {
        Console.WriteLine("Oberklasse - Erste Methode");
    }
 
    public void ZweiteMethode()
    {
        Console.WriteLine("Oberklasse - Zweite Methode");
    }
}
 
class MeineUnterklasse : MeineKlasse
{
    public override void ErsteMethode()
    {
        Console.WriteLine("Unterklasse - Erste Methode");
    }
 
    public new void ZweiteMethode()
    {
        Console.WriteLine("Unterklasse - Zweite Methode");
    }
}
 
public static void Main(string[] args) {
    IMeinInterface mi = new MeineUnterklasse();
    mi.ErsteMethode(); // Unterklasse - Erste Methode
    mi.ZweiteMethode(); // Oberklasse - Zweite Methode (!!!)
}

(originally posted on 2007-09-09)

Polymorphie mit C# und Java

Obwohl die Syntax von Java und C# sehr ähnlich sind, gibt es doch auch sehr gravierende Unterschiede. Denn während in Java alle Methode implizit als virtuell gelten, so muss dies bei C# extra mit dem keyword virtual gekennzeichnet werden wie auch bei C++. Um diesen Unterschied zu veranschaulichen, habe ich hier einmal den entsprechenden Code in C# und Java gegenübergestellt.

Polymorphie in C#

using System;
 
class Haus {
    public virtual String getBewohner() {
        return "niemand";
    }
    public String getBesitzer() {
        return getBewohner();
    }
}
 
class Hotel : Haus {
    public override String getBewohner() {
        return "Gäste";
    }
    public new String getBesitzer() {
        return "Hotelier";
    }
}
 
class Polymorphie {
    static void Main(string[] args) {
        Haus haus = new Haus();
        Console.WriteLine(haus.getBewohner()); // niemand
        Console.WriteLine(haus.getBesitzer()); // niemand
        Hotel hotel = new Hotel();
        Console.WriteLine(hotel.getBewohner()); // Gäste
        Console.WriteLine(hotel.getBesitzer()); // Hotelier
        haus = hotel;
        Console.WriteLine(haus.getBewohner()); // Gäste
        Console.WriteLine(haus.getBesitzer()); // Gäste (!!!)
    }
}

Polymorphie in Java

public class Haus {
    public String getBewohner() {
        return "niemand";
    }
    public String getBesitzer() {
        return getBewohner();
    }
}
 
public class Hotel extends Haus {
    public String getBewohner() {
        return "Gäste";
    }
    public String getBesitzer() {
        return "Hotelier";
    }
}
 
public static void main(String[] args) {
    Haus haus = new Haus();
    System.out.println(haus.getBewohner()); // niemand
    System.out.println(haus.getBesitzer()); // niemand
    Hotel hotel = new Hotel();
    System.out.println(hotel.getBewohner()); // Gäste
    System.out.println(hotel.getBesitzer()); // Hotelier
    haus = hotel;
    System.out.println(haus.getBewohner()); // Gäste
    System.out.println(haus.getBesitzer()); // Hotelier
}

(originally posted 2007-09-07)