Wicket 1.5 jetzt raus

Siehe http://wicket.apache.org/2011/09/07/wicket-1.5-released.html.

Most notable changes

With this release the Wicket team has revised many of its internals. A short list:

  • HTML5 components added: EmailTextField, NumberTextField, UrlTextField and RangeTextField
  • New inter-component events (explained below)
  • Minimum required servlet API is servlet-api 2.5
  • All standard validators now extend Behavior to allow for client side validations
  • IBehavior has been removed and AbstractBehavior has been deprecated, you should now extend Behaviorinstead
  • Simplified the request cycle processing and made it more extensible
  • URL handling is now in one place
  • Wicket’s rendering code has been greatly simplified
  • Improved browser caching support
  • ClientSideImageMap replaces old ImageMap
  • Better support for running behind proxies with x-forwarded-for header
  • Request cycle listeners make it easier to integrate frameworks in your Wicket application
  • Consistent naming: methods with Javascript in the name have been renamed to use proper capitalization:JavaScript
  • Switching to HTTPS is as simple as configuring a new root mapper to make Wicket HTTPS aware and annotating a page with @RequireHttps

A longer list of changes and improvements can be found in our migration guide.

Gastbeitrag: AWT-Schwergewichte nach vorne, bitte!

Der Code hinter Container.add(Component comp) hat sich immer wieder gewandelt, und lange Zeit war es problematisch, AWT- und Swing-Komponenten zu mischen. Seit Java 7 stellt das AWT jedoch jede mögliche Kombination von beliebigen Komponenten zuverlässig dar – den zusätzlichen Rechenaufwand bekommt jeder zu spüren, der etwa versucht, eine JScrollPane mit 50.000 JLabels zu bestücken.

Weiterlesen

Was ist JavaFX?

AWT und Swing sind bisher die Standardlösungen für grafische Anwendungen unter Java. AWT bildet das Fundament mit Ereignisbehandlung, Fenster-Management und einer mächtigen 2D-API. Swing sitzt auf dem AWT und ist eng mit ihm verbunden. Es realisiert die Komponenten, die zum Teil selbst in Java implementiert sind, manch ein Look and Feel – wie im Fall von Windows – lässt die Komponenten nativ vom Betriebssystem zeichnen.

Swing und AWT sind mächtig, aber es hat sich in den Letzen Jahren nicht großartig weiterentwickelt. Insbesondere gibt es Lücken im Bereich Medien und Animation, etwas, was bei modernen grafischen Oberflächen heutzutage gefragt ist. Statt das Sun/Oracle in die Weiterentwicklung investiert, hat sich das Unternehmen für eine komplette Neuentwicklung der GUI-Ebene entschieden, die nichts mehr mit Swing/AWT gemeinsam hat: JavaFX.

JavaFX ist eine Komplettlösung mit einer API für

· GUI-Komponenten

· HMTL/CSS/JavaScript mit eingebetteten Web-Brower

· Animationen

· Video

· Audio

· 2D und 3D

Da JavaFX komplett alle APIs für moderne Oberflächen anbietet, und auch nicht von AWT/Swing abhängig ist, bildet JavaFX einen kompletten Media-Stack. Die Betonung liegt auf Media, denn die AWT/Swing-API im Java SE kann keine Medien einbinden oder abspielen. Zwar ist JavaFX auch noch kein Teil der Java SE, doch das kann sich ändern. Über Profile sollte JavaFX auch auf mobilen Endgeräten und im Internet wie Applets laufen, allerdings ist eher davon auszugehen, das JavaFX es bei Rich-Client-Anwendungen Einzug hält und dort AWT/Swing verdrängt. Es ist nicht abzusehen, dass JavaFX im Internet als Flash-Ersatz oder auf mobilen Endgeräten punkten kann, dafür ist die Kombination HTML5 + CSS3 + JavaScript zu attraktiv.

Anders als AWT ist die JavaFX-Implementierung auf der Höhe der Zeit und greift direkt auf alle 2D/3D-Fähigkeiten moderner Grafikkarten zurück. So kann mit JavaFX alles das programmiert werden, was bisher eher mit Flash gemacht wurde, wohl aber fehlen noch die tollen Entwicklertools. Es gibt Plugins für Adobe Photoshop und Illustrator, mit denen Grafiken und Pfade exportiert werden können, aber eben keine ganzen Animationen, die etwa mit Adobe Flash erzeugt wurden. Und seit dem Adobe Flash auch HTML5 exportiert, öffnet sich eine ganz neue Welt.

 

Geschichte

JavaFX ist schon sehr lange in Entwicklung und viele interne Swing-Entwickler wurden auf das Projekt angesetzt – daran liegt es wohl auch, dass bei Swing nicht mehr passierte. Im Jahr 2003 wurde JavaFX dann auf der SunOne Konferenz vorgestellt, zusammen mit der Programmiersprache Java FX Script. Die Sprache macht es einfach möglich hierarchische Objektgrafen aufzubauen, und bot eine nette Syntax für Objekt-Bindung, doch wurde sie für die aktuelle Version JavaFX 2.0 fallen gelassen. Oracle wollte keine weitere Programmiersprache, sondern eine pure Java API, die sich von unterschiedlichen existierenden Skriptsprachen dann ansprechen kann. Das ist sicherlich eine gute Entscheidung, denn unter Groovy sieht das sehr schlank aus, fast wie mit JavaFX Script auch (http://groovy.codehaus.org/GroovyFX).

Microsoft Office-Dokumente in Java verarbeiten

Auch wenn sich die Welt nach freien und quelloffenen Lösungen sehnt, Microsoft Office ist immer noch eines der bestverkauftesten Produkte auf diesen Planeten mit dem MS-Dokumentenformat als Sonne im Mittepunkt. Da die Dokumentation von Microsofts Dateiformaten mittlerweile zugänglich[1] sind, gibt es auch Java-Bibliotheken, die MS-Office-Dokumente einlesen, modifizieren und schreiben. Bekannt dafür ist Apache POI[2] (http://poi.apache.org/), was APIs für folgende Formate (Komponenten genannt) bietet:

POI-Komponete

Aufgabe

Grad der Unterstützung

HSSF, XSSF

Excel XLS, Excel XLSX

Gut

HSLF, XSLF

PowerPoint PPT, PowerPoint PPTX

Ausreichend

HWPF, XWPF

Word DOC, Word DOCX

Befriedigend

HDGF

Visio VSD

Rudimentär, nur lesen

HPBF

Publisher PUB

Rudimentär, nur lesen

HMEF/HSMF

Outlook MSG/ Microsoft TNEF (Transport Neutral Encoding Format)

Rudimentär, nur lesen

OpenXML4J

Open Packaging Conventions (OPC)/OOXML

Gut

POIFS

OLE 2 Compound Document (OLE2 Filesystem)

Sehr gut

HPSF

OLE2 Property Sets

Sehr gut

POI-Komponenten nach http://poi.apache.org/overview.html#components

OpenXML4J und POIFS sind keine üblichen Dokumentenformate, aber Archivformate für Microsoft-Dokumenten (analog zu Zip). HPSF erlaubt Zugriff auf Datei-Metadaten wie Autor, Titel, usw.

Neben POI gibt es nicht mehr so viele Alternativen, eher kleinere Bibliotheken, die sich auf ein Formate spezialisiert haben. So etwa http://jexcelapi.sourceforge.net/ für Excel, was eine sehr einfache und intuitive API hat, aber beim Lesen oft Probleme bereitet. Nur wurde es bisher seit 2 Jahren nicht mehr aktualisiert. Eine kommerzielle Lösung bietet Aspose (http://www.aspose.com/categories/java-components/aspose.total-for-java/default.aspx). Für den Zugriff auf MS Access Datenbanken gibt es die quelloffene Bibliothek http://jackcess.sourceforge.net/, die auch eine sehr aktuelle fluent API hat. Auf MS Access lässt sich aber auch per ODBC zurückgreifen, das Datenbankkapitel gibt eine kleine Übersicht.

Das neue Office-Format basiert auf XML und so auch deutlich einfacher zu verarbeiten als das binäre Format. Es ist im Office Open XML beschrieben. Für Word-Dokumente gibt es zum Beispiel das relativ neue Projekt java2word (http://code.google.com/p/java2word/).

Neben dem Office Open gibt es das OASIS Open Document Format – mit Dateiformaten wie OpenDocument (ODF) für Textdokumente – die etwa von OpenOffice verarbeitet werden. Eine Bibliothek zum Verarbeiten bietet das ODF Toolkit (http://odftoolkit.org/).

 

Fehlt noch was?


[1] http://www.microsoft.com/interop/docs/officebinaryformats.mspx

[2] POI steht für „Poor Obfuscation Implementation“, weil das Dateiformat so kryptisch ist. Die Kürzel der Komponenten beginnen in der Regel mit „H“ was für „Horrible“ steht .

Java™ Platform, Standard Edition 7 Update 2 Binary Snapshot Releases

http://jdk7.java.net/download.html

Änderungen:

http://hg.openjdk.java.net/jdk7u/jdk7u/ws-b02-2011-08-12_39

Changeset

Bug ID

Synopsys

831f1dadcc35

7057705

can’t generate api docs for JDK7 updates

http://hg.openjdk.java.net/jdk7u/jdk7u/jdk

Changeset

Bug ID

Synopsys

f01230bea4aa

7043737

klist does not detect non-existing keytab

9847e43556fb

7029903

Splash screen is not shown in 64-bit Linux with 16-bit color depth

175f98d43a12

7062969

java -help still shows http://java.sun.com/javase/reference

440d5a3cfdf8

7067922

(launcher) java -jar throws NPE if JAR file does not contain Main-Class attribute

a5ea1f537169

7039182

PPC: NIO: java.io.IOException: Invalid argument in sun.nio.ch.FileDispatcherImpl.read0

http://hg.openjdk.java.net/jdk7u/jdk7u/langtools

Changeset

Bug ID

Synopsys

ceb7ca04b7eb

7060926

Attr.PostAttrAnalyzer misses a case

2f2ac80b6836

7061125

Proposed javac argument processing performance improvement

130154dbafc8

7068902

(javac) allow enabling or disabling of String folding

8b6f8a4bc8b8

7060642

(javadoc) improve performance on accessing inlinedTags

fda5571c663a

6735320

StringIndexOutOfBoundsException for empty @serialField tag

add40922e84d

7059905

(javadoc) promote method visibility for netbeans usage

Deklarative JavaFX-Oberflächen mit FXML

Den Szene-Graph über Java Programmcode aufzubauen ist eine Möglichkeit, doch JavaFX erlaubt es auch, die Objekte über XML zu konfigurieren. Das erlaubt es viel einfacher, grafische Oberflächen über Gui-Builder aufzubauen und sauber das Layout (was) vom Code (wie) zu trennen.

Die hierarchische Struktur von XML passt natürlich prima zu der Hierarchie, die es bei grafischen Oberflächen gibt: Ein Fenster enthält Container, die wiederum Elemente enthalten, usw. Für unser kleines Beispiel soll eine Oberfläche drei Elemente bieten: Eine Beschriftung, ein Textfeld und eine Schaltfläche. Drückt der Anwender die Schaltfläche, soll der Text im Textfeld in Großbuchstaben konvertiert werden.

Die XML-Datei covert2UpperCase.fxml sieht so aus:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>

<HBox xmlns:fx="http://javafx.com/fxml"
      fx:controller="com.tutego.insel.javafx.ButtonController">
  <children>
    <Label text="Eingabe: " />
    <TextField fx:id="input" />
    <Button text="Konvertiere" onAction="#convertAction" />
  </children>
</HBox>

Die Hierarchie ist gut zu erkennen. Die interessanten Dinge sind andere:

  1. Zu Beginn gibt es eine Art import-Anweisung um Typnamen nicht voll qualifizieren zu müssen. Für den Rückgriff auf Grafiken muss <?import javafx.scene.image.*?> eingebunden werden und <?import javafx.scene.*?> für Group, Node oder ähnliches, was wir im Programm aber alles nicht nutzen.
  2. HBox hat zwei Attribute: Ein Attribut deklariert den Namensraum fx, ein anderes eine Klasse, den sogenannten Controller, der später die Ereignisbehandlung für den Klick übernimmt. Die Typen in FXML heißen genauso wie die Klassennamen. Natürlich könne auch eigene Klassen eingebaut werden, sofern sie mit <?import> bekannt gemacht wurden.
  3. Das TextField bekommt mit dem Attribut fx:id eine ID zugewiesen, unter der das Textfeld später erfragt werden kann. JavaFX geht noch einen Schritt weiter, und bildet das Objekt mit der ID automatisch auf ein Attribut der Controller-Klasse ab. Label und Schaltfläche brauchen keine IDs, da sie nicht erfragt werden müssen.
  4. Das Attribut onAction der Schaltfläche referenziert Programmcode, der immer aufgerufen wird, wenn die Schaltfläche gedrückt wird. Hier kann direkt Java-Quellcode stehen, oder, wie in unserem Fall, ein # und der Name einer Methode, die in einem Controller deklariert werden muss. Den Klassenamen vom Controller haben wir am Wurzelelement deklariert.

Die Ereignisbehandlung ist komplett aus der FXML-Datei rausgezogen und wandert in Controller-Klassen. Die eigene Klasse ButtonController, die voll qualifiziert bei fx:controller genannt wurde, enthält:

com/tutego/insel/javafx/ButtonController.java

package com.tutego.insel.javafx;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;

public class ButtonController
{
  @FXML
  private TextField input;

  @FXML
  protected void convertAction( ActionEvent event )
  {
    input.setText( input.getText().trim().toUpperCase() );
  }
}

Drei Dinge fallen ins Auge:

  1. Die Controller-Klasse erweitert keine Schnittstelle.
  2. Die Annotation @FXML sagt, dass der Verweis auf das TextField-Objekt aus dem Szene-Graphen in die Variable input injiziert werden soll.
  3. Da in der FXML-Datei an der Schaltfläche ein onAction="#convertAction" steht, muss das zu einer Methode zugeordnet werden. Die Annotation @FXML an der Methode unter dem Namen convertAction stellt diese Beziehung her.

Das Hauptprogramm ist nun relativ einfach:

com/tutego/insel/javafx/FXMLDemo.java, start()

@Override
public void start( Stage stage ) throws Exception
{
  Parent p = FXMLLoader.load( getClass().getResource( "covert2UpperCase.fxml" ) );
  stage.setScene( new Scene( new Group( p ), 500, 400 ) );
  stage.setVisible( true );
}

Was noch alles mit FXML möglich ist, beschreibt ein Dokument von Oracle: http://fxexperience.com/wp-content/uploads/2011/08/Introducing-FXML.pdf.

Neuer JavaFX b40 Build

Änderungen: Kein Zip mehr zum Auspacken, sondern eine exe. Keine Demos mehr dabei. APIs ändern sich, viele Beispiele muss ich anpassen. Bei einer Beta ist das natürlich OK, dennoch überlege ich mir, ob ich JavaFX unter diesen Umständen überhaupt in die Insel setzen kann. An großen FX-Apps kann man so nicht denken, die Änderungen machen einen verrückt.

Mein Buchbeispiel ist falsch (und nur einem fällt es auf und sagt es)

Das folgende Code für mein Semaphoren-Beispiel (http://openbook.galileodesign.de/javainsel7/javainsel_10_006.htm) habe ich vereinfacht und ist so nicht wirklich korrekt:

  1: static Runnable r = new Runnable() { 
  2:     public void run() { 
  3:       while ( true ) { 
  4:         try 
  5:         { 
  6:           semaphore.acquire(); 
  7:  
  8:           System.out.println( "Thread=" + Thread.currentThread().getName() + 
  9:                        ", Available Permits=" + semaphore.availablePermits() ); 
 10:           Thread.sleep( 2000 ); 
 11:         } 
 12:         catch ( InterruptedException e ) { 
 13:           e.printStackTrace(); 
 14:         } 
 15:         finally { 
 16:           semaphore.release(); 
 17:         } 
 18:       } 
 19:     } 
 20:   };

Wer findet den Fehler noch?

Nun, in der 10. Auflage ist der Fehler jedenfalls korrigiert und ein Dank geht an Marco Völz.

c’t Artikel über Java 7: “Was lange währt …” – eine kurze Nachkritik

In der c’t hat Michael Tamm in der Ausgabe 17 (August) einen Beitrag über Java 7 geschrieben. Der ist ganz gut gelungen und gibt einen netten Einblick in die Neuerungen von Java 7 (präzisiertes rethrow hat er irgendwie verschwiegen, weiß nicht warum).

Ein paar Kleinigkeiten kann man noch anfügen:

  • Bei switch mit Strings: “Für jeden case-Zweig wird die in der switch-Anweisung aufgeführte String-Variable mit Hilfe ihrer Methode equals() mit der Konstanten hinter case verglichen”. Hier könnte man meinen, dass eine switch-Anweisung zu einer if-else-Kaskade mit einer linearen Laufzeit führt. Das stimmt aber nicht. equals() kommt erst relativ spät dazu. Erst gibt es einen switch auf dem Hashwert, wenn der passt, kommt equals(), um mit Hashwert-Kollisionen umzugehen.
  • “… dass man beim Anlegen von Objekten mit generischen Parametern diese auf der rechten Seiten der Zuweisung nicht mehr wiederholen muss…”. Das könnte man so lesen, dass <> ausschließlich bei Zuweisungen oder Initialisierungen gültig ist. Doch <> ist flexibler; es kann durchaus return new ArrayList<>(); heißen. (Dass der Begriff “generischer Parameter” unsauber ist, ist eine andere Sache … )
  • Die Einleitung vom multi-catch beginnt mit “… Seit Java 1.0 gibt es geprüfte (checked exceptions), die immer behandelt oder in der Methodensignatur aufgeführt werden müssen, sowie die ungeprüfte Ausnahmen […].“ Ob multi-catch nun checked oder unchecked Exception fängt ist egal, daher ist die Information an diese Stelle eigentlich überflüssig.
  • Beim Thema try-mit-Ressourcen “Die VM schließt alle hinter dem try in runden Klammern aufgeführten Ressourcen[…]”. Irgendwie macht alles schon die JVM, aber man könnte den Eindruck bekommen, hier ist Magie im Spiel. Das stimmt aber nicht, denn der Compiler erzeugt nur etwas, was wir hätten auch schreiben können, nur haben wir mit dem neuen try weniger Schreibarbeit. Die JVM weiß nicht, ob der tolle Schließ-Code von uns kommt oder nicht.
  • “Das Pendant zur alten Klasse File ist die neue Klasse Path”. Pendant? Klingt, als ob die gleich sind, ist aber absolut nicht so. Wenn das ein Pendant, also eine Kopie wäre, warum dann NIO.2?

Die beschriebenen Dinge sind nur missverständlich, aber leider gibt es auch zwei dickere Falschaussagen:

  • “Mit den in Java 7 eingeführten Typannotationen lassen sich nun auch Typen näher beschreiben”. Es folgt das Beispiel “@NonNull Set<@NonNull Edges> edges;” und er verweist auf den Link von JSR-308 (http://types.cs.washington.edu/jsr308/). Wäre nett, ist aber Blödsinn! Es gibt in Java 7 keine Änderung. Das verlinkten Dokument sagt: “Type annotations are planned to be part of the Java language and are supported by Oracle’s OpenJDK (for JDK 7, as of build M4).” Das JST-308 nicht von Java 7 ist hatte ich auch schon vor einem halben Jahr geschrieben: http://www.tutego.de/blog/javainsel/2011/02/java-se-7-developer-preview-release-verfgbar/, aber ich erwarte nicht, dass Herr Tamm mein Blog liest.
  • “[…] Nimbus Look & Feel […] ist standardmäßig aktiv”. Hätte Michael nur ein Swing-Programm gestartet, wäre ihm aufgefallen, das immer noch Metal aktiv ist und nicht Nimbus. (Grund: Inkompatibilitäten http://blogs.oracle.com/henrik/entry/nimbus_look-and-feel_in_jdk_7). Das SwingSet Demo wählt nur automatisch Nimbus aus.

App Engine 1.5.3 Update

Siehe http://googleappengine.blogspot.com/2011/08/app-engine-153-sdk-released.html, http://code.google.com/p/googleappengine/wiki/SdkForJavaReleaseNotes:

  • Blobstore API – We’ve removed the limits on the size of blob uploads. You can now upload files of any size, allowing your app to serve images, video, or anything your internet connection can handle.
  • Index retrieval – We’ve added the ability for you to programmatically retrieve the list of indexes you’ve currently defined in the datastore, as well as their statuses.
  • Datastore Admin – You can now enable the Datastore Admin function from the Admin Console. This will allow Java users to make use of this functionality, like deleting all entities of a certain kind, without having to upload a Python version of their application. And for Python developers, you no longer need to enable this in your app.yaml file.
  • HRD Migration Trusted Testers – We are seeking early adopters to try out an improved HRD migration tool that requires a read-only period relative to your datastore write rate (as opposed to your datastore size, which is how the current version behaves). Please see the release notes for more information.
  • Download app – Using the AppCfg download_app command, you can download any files that were uploaded from your war directory when you last updated the app version.

Sehr sehr cool: Swing-Anwendungen im Browser

Schaut euch

an.

Interessant für Anwender mobiler Endgeräte, die kein Swing können (Android) oder dürfen (iPad).

Die HTML-Anwendung besteht aus einem bisschen JavaScript, die Interaktionen zum Server schickt, also Mausklicks, Tastendrücke, usw. Dabei entstehen Events wie

MM_984_567_MM_986_567_

die an http://icedrobot.de:9091/EventReceiver per POST gesendet werden. Die Swing-Anwendung (die werden dafür richtig viel Speicher brauchen denke ich, das kommt vielleicht noch mal raus…) läuft nur auf dem Server und der reagiert mit geänderten Bildschirmausschnitten, die wieder auf den Brower Canvas gezeichnet werden. Ist so wie ein remote VNC-Server (http://code.google.com/p/jsvnc/), schöne Sache das. HTML5 wir lieben dich Smile

 

“Schön” im Sourcecode der Kommentar:

//Exclude WebKit for now, as it will trigger a memory leak.

//TODO: should be version dependent, as its fixed in Chrome 14

Interessantes Mathe/Logik-Buch – Fehler in der Didaktik einsetzen

Attila Furdek ist Mathelehrer und hat ein interessantes Buch veröffentlicht: http://www.amazon.de/Fehler-Beschw%C3%B6rer-Typische-Fehler-L%C3%B6sen-Mathematikaufgaben/dp/3831121109 (http://www.bod.de/index.php?id=296&objk_id=51857). Ich finde den Ansatz toll: Er stellt eine Aufgabe vor und suggeriert richtige Lösungen, die aber falsch sind. (Bei Amazon kann man sich ein paar Sachen anschauen.) Der Lernende muss zeigen, warum der Lösungsweg falsch ist. Mit gefällt die Idee gut. In meinen Java-Kursen mache ich am Anfang etwas ähnliches: ich gebe syntaktisch falschen Programmcode vor (Klammern fehlen, Variablendeklarationen fehlen, Initialisierungen fehlen, Semikolon fehlt, usw.), und die Lernenden müssen ohne Compilermeldungen die Fehler finden. Wer die meisten Fehler findet, ist Bug-König. Danach wird aufgelöst und die die Fehler mit dem Compiler abgeglichen, um die Meldungen des Compilers zu lernen und mit den Fehlertypen in Verbindung zu bringen.

Aneinanderreihung von Comparatoren

Oftmals ist das Ordnungskriterium aus mehreren Bedingungen zusammengesetzt, wie die Sortierung in einem Telefonbuch zeigt. Erst gibt es eine Sortierung nach dem Nachnamen, dann folgt der Vorname. Um diese mit einem Compartor-Objekt zu lösen, müssen entweder alle Einzelvergleiche in ein neues Compartor-Objekt verpackt werden, oder einzelne Comparatoren zu einem „Super“-Comparator zusammengebunden werden – die zweite Lösung ist natürlich schöner, denn das erhöht die Wiederverwendbarkeit, denn einzelne Comparatoren können dann leicht für andere Zusammenhänge genutzt werden.

Comparatoren in eine Vergleichskette setzen

Am Anfang steht ein besonderer Comparator, der sich aus mehreren Comparatoren zusammensetzt. Immer dann, wenn ein Teil-Compartor bei zwei Objekten aussagt, dass sie gleich sind (der Vergleich liefert 0 ist), so soll der nächste Comparator die Endscheidung fällen – kann er das auch nicht, weil das Ergebnis wieder 0 ist, geht es zum nächsten Vergleicher.

Den Programmcode wollen wir einen neue Hilfsklasse ComparatorChain setzen:

package com.tutego.insel.util;

import java.util.*;

/**
 * A {@link Comparator} that puts one or more {@code Comparator}s in a sequence.
 * If a {@code Comparator} returns zero the next {@code Comparator} is taken.
 */
public class ComparatorChain<E> implements Comparator<E>
{
  private List<Comparator<E>> comparatorChain = new ArrayList<Comparator<E>>();
  
  /**
   * Construct a new comparator chain from the given {@code Comparator}s.
   * The argument is not allowed to be {@code null}.
   * @param comparators Sequence of {@code Comparator}s
   */
  @SafeVarargs  // ab Java 7
  public ComparatorChain( Comparator<E>... comparators )
  {
    if ( comparators == null )
      throw new IllegalArgumentException( "Argument is not allowed to be null" );

    Collections.addAll( comparatorChain, comparators );
  }

  /**
   * Adds a {@link Comparator} to the end of the chain.
   * The argument is not allowed to be {@code null}.
   * @param comparator {@code Comparator} to add
   */
  public void addComparator( Comparator<E> comparator )
  {
    if ( comparator == null )
      throw new IllegalArgumentException( "Argument is not allowed to be null" );

    comparatorChain.add( comparator );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int compare( E o1, E o2 )
  {
    if ( comparatorChain.isEmpty() )
      throw new UnsupportedOperationException(
                  "Unable to compare without a Comparator in the chain" );

    for ( Comparator<E> comparator : comparatorChain )
    {
      int order = comparator.compare( o1, o2 );
      if ( order != 0 )
        return order;
    }

    return 0;
  }
}

Die ComparatorChain können wir auf zwei Weisen mit den Comparator-Gliedern füttern: einmal zu Initialisierungszeit im Konstruktor, und dann später noch über die addComparator()-Methode. Beim Weg über den Konstruktor ist ab Java 7 die Annotation @SafeVarargs zu nutzen, da sonst die Kombination eines Varargs und Generics auf der Nutzerseite zu einer Warnung führt.

Ist kein Comparator intern in der Liste, wird das compare() eine Ausnahme auslösen. Der erste Comparator in der Liste ist auch das Vergleichsobjekt was zuerst gefragt wird. Liefert er ein Ergebnis ungleich 0 liefert das die Rückgabe der compare()-Methode. Ein Ergebnis gleich 0 führt zur Anfrage des nächstes Comparators in der Liste.

Wir wollen diese ComparatorChain für ein Beispiel nutzen, dass eine Liste nach Nach- und Vornamen sortiert.

package com.tutego.insel.util;

import java.util.*;

public class ComparatorChainDemo
{
  public static class Person
  {
    public String firstname, lastname;

    public Person( String firstname, String lastname )
    {
      this.firstname = firstname;
      this.lastname  = lastname;
    }

    @Override public String toString()
    {
      return firstname + " " + lastname;
    }
  }

  public final static Comparator<Person>
    PERSON_FIRSTNAME_COMPARATOR = new Comparator<Person>() {
      @Override public int compare( Person p1, Person p2 ) {
        return p1.firstname.compareTo( p2.firstname );
      }
    };

  public final static Comparator<Person>
    PERSON_LASTNAME_COMPARATOR = new Comparator<Person>() {
      @Override public int compare( Person p1, Person p2 ) {
        return p1.lastname.compareTo( p2.lastname );
      }
    };

  public static void main( String[] args )
  {
    List<Person> persons = Arrays.asList(
      new Person( "Onkel", "Ogar" ), new Person( "Olga", "Ogar" ),
      new Person( "Peter", "Lustig" ), new Person( "Lara", "Lustig" ) );

    Collections.sort( persons, PERSON_LASTNAME_COMPARATOR );
    System.out.println( persons );

    Collections.sort( persons, PERSON_FIRSTNAME_COMPARATOR );
    System.out.println( persons );

    Collections.sort( persons, new ComparatorChain<Person>(
        PERSON_LASTNAME_COMPARATOR, PERSON_FIRSTNAME_COMPARATOR ) );
    System.out.println( persons );
  }
}

Die Ausgabe ist:

[Peter Lustig, Lara Lustig, Onkel Ogar, Olga Ogar]

[Lara Lustig, Olga Ogar, Onkel Ogar, Peter Lustig]

[Lara Lustig, Peter Lustig, Olga Ogar, Onkel Ogar]

Bug des Tages im Java-Compiler: case

package insel;

public class Insel {

    public static void main(String[] args) {

        switch (1) {
            case (1):
        }
        switch ("") {
            case (""):
        }

    }
}

Eclipse und NetBeans kommen damit zurecht, nicht aber javac. Der Fehler ist bekannt und für das nächste Update gefixt.

An exception has occurred in the compiler (1.7.0). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport)  after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report.  Thank you.
java.lang.NullPointerException
    at com.sun.tools.javac.comp.Lower.visitStringSwitch(Lower.java:3456)
    at com.sun.tools.javac.comp.Lower.visitSwitch(Lower.java:3357)
    at com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:959)
    at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
    at com.sun.tools.javac.comp.Lower.translate(Lower.java:2160)

NIO.2 Umstellung. Mein Fazit (nach einem Tag)

Um zu testen, wie sich die neue NIO.2-Bibliothek so in der Praxis macht, habe ich unsere tutego-Software auf NIO.2 gebracht. Als erstes habe ich File durch Path/Paths/Files-Aufrufe ersetzt. Dabei sind mir schon ein paar Stellen aufgefallen, die ich gerne noch verbessert sehen würde für >= Java 8.

  1. Von den File-Konstrukturen gibt es: File(File parent, String child) und File(String parent, String child). Es ist praktisch, dass der erste Teil entweder File oder String sein kann. Ich habe bei mir eine Menge von Aufrufen der Art new File(path, filename). Mal ist path ein String, mal ein File (in der Regel File). Bei der Konvertierung zu Path wird es ungemütlich, denn es gibt nur get(String first, String… more) aber kein get(Path first, String… more). Also läuft es auf ein path.resove(child) raus, was ich aber nicht so schön finde wie ein get(path, child). Aber alles Geschmacksache.
  2. File wird öfter als Parametertyp akzeptiert als Path. So muss ich schreiben:
    PrintWriter out = new PrintWriter( testimonalsPath.toFile() );
    Schöner wäre ein Konstruktur PrintWriter(Path).
  3. Die Methode getFileName() liefert keinen String, sondern ein Path-Objekt nur mit dem Dateinamen. Daher führt folgendes nicht zum Ziel: path.getFileName().endsWith(".xml"). Und path.getFileName().toString()endsWith(".xml") ist etwas lang.
  4. Files.readAllBytes() ist zwar schön, aber Files.readAllChars(Path,CharSet) wäre auch nett.

Inselupdate: Ein Wort zu Microsoft, Java und zu J++, J#

In der Anfangszeit verursachte Microsoft einigen Wirbel um Java. Mit Visual J++ (gesprochen „Jay Plus Plus“) bot Microsoft schon früh einen eigenen Java-Compiler (Teil vom Microsoft Development Kit) und mit der Microsoft Java Virtual Machine (MSJVM) eine eigene schnelle Laufzeitumgebung. Das Problem war nur, dass Dinge wie RMI und JNI am absichtlich fehlten[1] – JNI wurde 1998 nachgereicht. Entgegen alle Standards führte der J++-Compiler neue Schlüsselwörter multicast und delegate ein. Weiterhin fügte Microsoft einige neue Methoden und Eigenschaften hinzu, zum Beispiel J/Direct, um der plattformunabhängigen Programmiersprache den Windows-Stempel zu verpassen. Mit J/Direct konnten Programmierer aus Java heraus direkt auf Funktionen aus dem Win32-API zugreifen und damit reine Windows-Programme in Java programmieren. Durch Integration von DirectX soll die Internet-Programmiersprache Java multimediafähig gemacht werden. Das führte natürlich zu dem Problem, dass Applikationen, die mit J++ erstellt wurden, nicht zwangsläufig auf anderen Plattformen lauffähig sind waren. Sun klagte gegen Microsoft.

Da es Sun in der Vergangenheit finanziell nicht besonders gut ging, pumpte Microsoft im April 2004 satte 1,6 Milliarden US$ in die Firma. Microsoft erkaufte sich damit das Ende der Kartellprobleme und Patentstreitigkeiten. Dass es bis zu dieser Einigung nicht einfach gewesen war, zeigen Aussagen von Microsoft-Projektleiter Ben Slivka über das JDK beziehungsweise die Java Foundation Classes, man müsse sie »bei jeder sich bietenden Gelegenheit anpissen« (»pissing on at every opportunity«).[2]

Im Januar 2004 beendete die Arbeit an Microsoft J++, denn die Energie floss in das .NET-Framework und der .NET-Sprachen. Am Anfang gab es mit J# eine Java-Version, die Java-Programme auf der Microsoft .NET-Laufzeitumgebungen CLR ausführt, doch Anfang 2007 wurde auch J# eingestellt. Das freie IKVM.NET (http://www.ikvm.net/) ist eine JVM für .NET und kommt mit einem Übersetzter von Java-Bytecode nach .NET-Bytecode, was es möglich macht, Java-Programme unter .NET zu nutzen. Das ist praktisch, denn für Java gibt es eine riesige Anzahl von Programmen, die somit auch für .NET-Entwickler zugänglich sind.

Microsoft hat sich aus der Java-Entwicklung nahezu vollständig zurückgezogen. Es gibt zum Beispiel noch den Microsoft JDBC Driver for SQL Server und Microsoft unterstützt eine API für Office-Dokumente. Das Verhältnis ist heute auch deutlich entspannter und vielleicht gratuliert Microsoft wie es auch Linux zum 20 Geburtstag gratuliert hat[3] irgendwann einmal Oracle.


[1] http://www.microsoft.com/presspass/legal/charles.mspx

[2] Würden wir nicht gerade im westlichen Kulturkreis leben, wäre diese Geste auch nicht zwangsläufig unappetitlich. Im alten Mesopotamien steht »pissing on« für »anbeten«. Da jedoch die E-Mail nicht aus dem Zweistromland kam, bleibt die wahre Bedeutung wohl unserer Fantasie überlassen.

[3] http://www.youtube.com/watch?v=ZA2kqAIOoZM