JDOM 2.0.1 freigegeben

http://www.jdom.org/news/index.html. Von der Webseite:

04.28.2012: JDOM 2.0.1 Released!

JDOM 2.0.1 is here!

JDOM 2.0.1 Introduces official support for Android! See the JDOM and Android page. JDOM 2.0.1 also fixes a bug in the ‚Compact‘ output of XML.

Get JDOM 2.0.0 Here! or from the maven-central repository here: Group: org.jdom, Articact: jdom

04.08.2012: JDOM 2.0.0 Released!

JDOM 2.0.0 is here!

JDOM 2.0.0 brings JDOM in to the world of Generics and other Java language items introduced with Java 5. As a result, JDOM 2.0.0 requires Java 5 or later, but is only fully supported on Java 6 and later.

Get JDOM 2.0.0 Here! or from the maven-central repository here: Group: org.jdom, Articact: jdom

In der Insel habe ich meine Programme nun auf JDOM 2 gebracht – ohne große Probleme. Nur die Generics muss ich in der Doku dokumentieren. Das einzige, was ich umschreiben musste, was das Kapitel über XPath, dazu gleich ein eigener Beitrag.

Inselraus: identityHashCode() ist für eine System-eindeutige Objekt-IDs nicht geeignet

Stellen wir uns vor, wir hätten eine Objekthierarchie im Speicher, die zum Beispiel jeder Socke einen Besitzer zuspricht. Wenn wir im Speicher Assoziationen abbilden, dann sollen diese Verweise auch noch nach dem Tod des Programms überleben. Eine Lösung ist die Serialisierung, eine andere eine Objekt-Datenbank oder aber auch eine XML-Datei. Doch überlegen wir selbst, wo bei der Abbildung auf eine Datenbank oder eine Datei das Problem besteht. Zunächst stehen ganz unterschiedliche Objekte mit ihren Eigenschaften im Speicher. Das Speichern der Zustände ist kein Problem, denn nur die Attribute müssten abgespeichert werden. Doch wenn ein Objekt auf ein anderes verweist, muss dieser Verweis gesichert werden. Aber in Java ist ein Verweis durch eine Referenz gegeben, und was sollte es da zu speichern geben? Eine Lösung für das Problem ist, jedem Objekt im Speicher einen Zähler zu geben und beim Speichen etwa zu sagen: »Der Besitzer 2 kennt Socke 5«.

Der Identifizierer für die Objekte muss eindeutig sein, und wir können überlegen, System.identityHashCode() zu nutzen. In der Implementierung der virtuellen Maschine von Oracle geht in den Wert von identityHashCode() die Information über den wahren Ort des Objekts im Speicher ein. Bei einer 64-Bit-Implementierung würden auch 32 Bit abgeschnitten, und die Eindeutigkeit wäre somit automatisch nicht mehr gewährleistet. Ein weiteres Problem besteht darin, dass zwar die Implementierung von Oracles identityHashCode() auf die eindeutige Objektspeicheradresse abbildet, aber dass das nicht jeder Hersteller so machen muss. Damit ist identityHashCode() nicht überall gesichert unterschiedlich. Zudem ist es prinzipiell denkbar, dass die Speicherverwaltung die Objekte verschiebt. Was sollte identityHashCode() dann machen? Wenn die neue Speicheradresse dahinter steckt, würde sich der Hashcode ändern, und das darf nicht sein. Es käme ebenfalls zu einem Problem, wenn mehr als Integer.MAX_INTEGER viele Objekte im Speicher stünden. (Doch wenn wir uns die große Zahl 2^32 = 4.294.967.296 vor Augen halten, dann es ist unwahrscheinlich, dass sich mehr als 4 Milliarden Objekte im Speicher tummeln. Zudem bräuchten wir 4 Gigabyte Speicher, wenn jedes Objekt auch nur 1 Byte kosten würde.)

Es ist gar nicht so schwierig, zwei unterschiedliche Objekte mit gleichen identityHashCode()-Resultat zu bekommen. Wir erzeugen ein paar String-Objekte und testen, jedes mit jedem, ob identityHashCode() den gleichen Wert ergibt:

String[] strings = new String[5000];
for ( int i = 0; i < strings.length; i++ )
  strings[i] = Integer.toString( i );
int cnt = 0;
for ( int i = 0; i < strings.length; i++ ) {
  for ( int j = i + 1; j < strings.length; j++ ) {
    int id1 = System.identityHashCode( strings[i] );
    int id2 = System.identityHashCode( strings[j] );
    if ( id1 == id2 ) {
      out.println( "Zwei Objekte mit identityHashCode() = " + id1 );
      out.println( " Objekt 1: \"" + strings[i] + "\"" );
      out.println( " Objekt 2: \"" + strings[j] + "\"" );
      out.println( " Object1.hashCode(): " + strings[i].hashCode() );
      out.println( " Object2.hashCode(): " + strings[j].hashCode() );
      out.println( " Object1.equals(Object2): " + strings[i].equals( strings[j] ) );
      cnt++;
    }
  }
}
System.out.println( cnt + " Objekte mit gleichem identityHashCode() gefunden." );

Ein Durchlauf bringt schnell Ergebnisse wie:

Zwei Objekte mit identityHashCode() = 9578500
Objekt 1: "541"
Objekt 2: "2066"
Object1.hashCode(): 52594
Object2.hashCode(): 1537406
Object1.equals(Object2): false
Zwei Objekte mit identityHashCode() = 14850080
Objekt 1: "2085"
Objekt 2: "2365"
Object1.hashCode(): 1537467
Object2.hashCode(): 1540288
Object1.equals(Object2): false
2 Objekte mit gleichem identityHashCode() gefunden.

Das Ergebnis ist also, dass identityHashCode() nicht sicher bei der Vergabe von Identifizierern ist. Um wirklich allen Problemen aus dem Weg zu gehen, ist ein Zählerobjekt oder eine ID über zum Beispiel die Klasse java.util.UUID nötig.

JavaFX 2.1 ist fertig

Download unter http://www.oracle.com/technetwork/java/javafx/downloads/index.html. Aus den http://docs.oracle.com/javafx/release-documentation.html:

  • Media H.264 and AAC support

  • Mac OS X support

    Applications must be packaged for the desktop, Web and Web Start applications are not yet supported.

  • LCD text

  • UI enhancements, including controls for Combo Box, Stacked Chart, and application-wide menu bar

  • Webview to support JavaScript to Java method calls

In GWT global auf Tastenkürzel reagieren

Es setzt Strg + F den Fokus auf das Suchfeld:

Event.addNativePreviewHandler( new NativePreviewHandler() {
  @Override public void onPreviewNativeEvent( NativePreviewEvent event ) {
    NativeEvent ne = event.getNativeEvent();
    if ( event.getTypeInt() == Event.ONKEYDOWN && ne.getCtrlKey() && ne.getKeyCode() == ‚F‘) {
      ne.preventDefault();
      searchTextBox.setFocus( true );
    }
  }
} );

Osterrätsel (C, nicht Java)

Linus Torwalds stellt in seinem G+ Stream zwei C-Ausdrücke vor:

/* Modified Carl Chatfield G+ version for 32-bit */
long a = (mask-256) >> 23;
long b = mask & 1;
return a + b + 1;

/* Jan Achrenius on G+ for 64-bit case */
return mask*0x0001020304050608 >> 56;

Die Frage für Rätselfreunde ist (ohne den G+-Beitrag gelesen zu haben): Was berechnen die Ausdrücke?

Jerry: ein jQuery in Java

Jerry ist eine Open-Source-Lib, die eine von jQuery bekannte Funktionalität in Java abbildet. Beispiele von der Webseite: http://jodd.org/doc/jerry/index.html.

import static jodd.lagarto.dom.jerry.Jerry.jerry;

...

Jerry doc = jerry(html);

doc.$("div#jodd p.neat").css("color", "red").addClass("ohmy");

und:

doc.$("select option:selected").each(new JerryFunction() {

public boolean onNode(Jerry $this, int index) {

str.append($this.text()).append(' ');

return true;

}

});

Statt aus einem String kann die Eingabe auch direkt aus der Datei kommen:

File file = new File(SystemUtil.getTempDir(), "allmusic.html");

NetUtil.downloadFile("http://allmusic.com", file);

// create Jerry, i.e. document context

Jerry doc = Jerry.jerry(FileUtil.readString(file));

// parse

doc.$("div#new_releases div.list_item").each(new JerryFunction() {

public boolean onNode(Jerry $this, int index) {

System.out.println("-----");

System.out.println($this.$("div.album_title").text());

System.out.println($this.$("div.album_artist").text().trim());

return true;

}

});

Apache POI 3.8 ist fertig

Nach genau einem Jahr Arbeit an dem Release 3.8 ist die neue Version von Apache POI veröffentlicht. Die Änderungen gibt es unter http://poi.apache.org/changes.html, Download unter http://poi.apache.org/download.html. Links zu den Component APIs:

MVP (Model-View-Presenter) mit GWT

Zum Thema MVP (Model-View-Presenter) gibt es schon einiges an Dokumentation, etwa https://developers.google.com/web-toolkit/doc/latest/DevGuideMvpActivitiesAndPlaces, https://developers.google.com/web-toolkit/articles/mvp-architecture, doch soll dieser Beitrag einen weiteren Zugang zu dem Thema schaffen.

In einer MVP Anwendung macht die Sicht nur das, was sie machen soll: sie zeigt an. Sie ist zudem immer technologieabhängig und es kann verschiedene Implementierungen geben, etwa für GWT, Swing oder als Mock für Tests. Der Presenter ist mit der View assoziiert, aber da die View ja immer anders aussehen kann, kennt der Presenter sie nur über eine Schnittstelle. Damit beginnen wir (alle Programme im Pseudeocode):

interface MyView extends IsWidget {
  void setName( String name );
  String getName();
}

In der Schnittstelle kommen bis auf IsWidet keine GWT-spezifischen Eigenschaften nach außen, wobei es Entwickler gibt, die hier GWT-Schnittstellen wie HasText, IsWidget oder HasClickHandlers verwenden, dazu gleich mehr. Auch werden keine View-Model-Objekte (etwa Person) verwendet, sondern einfache Datentypen.

Die Implementierung für GWT kann den UiBinder nutzen oder nicht. Ohne sieht es etwa so aus:

class MyViewImpl extends Composite implements View {

  private TextBox textbox;

  MyViewImpl() {
    initWidget( textbox );
  }

  @Override public void setName( String name ) { textbox.setValue( name ); }
  @Override public String getName() { return textbox.getValue(); }
}

So einfach ist die View (die wir später noch ausbauen).

Nun kommt der Presenter, der die View initialisiert und auch die Interaktion mit dem Backend übernimmt. Wie stark er mit der View interagiert ist Geschmacksache.

Da der Presenter die View initialisiert und auf die View Einfluss nimmt, muss folglich der Presenter ein Verweis auf die View bekommen. Der Presenter merkt sich die View in einem Attribut:

class MyPresenter
{
  private MyView myView;
  …
}

Es gibt unterschiedliche Wege, wie der Presenter zur View kommt. Wenn man die View nicht austauschen möchte (und auch sonst einige Nachteile in Kauf nicht, siehe später), kann man den Presenter selbst die View erzeugen lassen. Oder man bietet einen Setter oder man lässt den Presenter injizieren. Die letzten beiden Wege erlauben den einfachen Austausch der View, etwa im Testfall. Außerdem muss die View natürlich an höher liegender Stelle irgendwie verfügbar sein, wenn zum Beispiel die MyView auf den Root-Panel gesetzt wird. Wenn man die View injiziert oder sie dem Presenter über einen Setter setzt, gibt es eine zentrale Stelle (ClientFactory in der GWT-Doku genannt) und von dort kann man diesen Teil-View für die jeweils darüber liegende View erfragen. Oder man fragt den Presenter selbst, wenn man sich so eine zentrale Stelle sparen möchte. In meinem Beispiel deklariere ich eine Methode getView() und mein Presenter erzeugt die View auch selbst:

class MyPresenter
{
  private MyView myView = new MyViewImpl();

  MyView getView() { return myView; }
}

Wenn man die View nun auf das Root-Panel setzen möchte, sieht das so aus:

MyPresenter presenter = new MyPresenter(); 
RootLayoutPanel.get().add( new ScrollPanel( presenter.getView() ) );

Einschub: Die Methode getView() kann man natürlich über eine neue Schnittstelle Presenter vorschreiben lassen, die etwa so aussehen kann:

public interface Presenter<T extends View> { 
  T getView(); 
}

In eigenen Projekten hatte ich das ursprünglich so entworfen, darin aber keinen Nutzen gefunden, und diese allgemeine Presenter-Schnittstelle wieder verworfen. Zudem hätte das auch eine View-Schnittstelle nötig gemacht, doch diese Abstraktion brachte mir nichts.

Erzeugt der Presenter die View muss man sich natürlich der Konsequenzen bewusst werden. Damit kann die View nicht mehr so einfach ausgetauscht werden (nur, wenn sie über eine Fabrik kommt), und es bringt auch den Nachteil mit, dass eine neuer Presenter-Instanz immer eine View neu erzeugt. Die View ist dann kein Singleton, die resettet und in nachfolgenden Presenter-Instanzen wiederverwendet wird. So wird jede neue Presenter-Instanz eine neue View-Instanz bilden. In meinem Beispiel soll das Ok sein, doch sollte man sich bei performanten GWT-Anwendungen im Klaren sein, die View nicht immer wieder neu zu erzeugen. Um eine zentrale ClientFactory kommt man nicht drum herum.

Im Grunde haben wir mit den drei Typen schon ein MVP realisiert:

  • MyView: Schnittstelle mit Setter/Gettern zum Verändern/Auslesen der View
  • MyViewImpl: Implementierung der View-Schnittstelle
  • MyPresenter: Kennt die View, initialisiert sind und greift auf die Daten zurück. Kennt das wahre Model

Ist die View rein passiv reichen die drei Typen aus, doch das ist nur im Ausnahmefall so. Es fehlt ein ganz entscheidender Aspekt: Was machen wir, wenn die View Ereignisse auslöst? Drückt der Benutzer einen Button, so muss eine Logik angestoßen werden. Logik ist aber Teil des Presenters, nicht der View. Zwei Realisierungen bieten sich an.

Schaut man sich https://developers.google.com/web-toolkit/articles/mvp-architecture an, so wird ein GWT-spezifischer Typ wie HasClickHandlers von der View nach außen gereicht, sodass der Presenter Logik an diesen Handler/Listener hängen kann. Hat die View einen Button, so sieht das im Code etwa so aus:

interface MyView extends IsWidget {
  void setName( String name );
  String getName();
  HasClickHandlers getOkButton();
}

Und die Implementierung:

class MyViewImpl extends Composite implements View {

  private TextBox textbox;
  private Button okButton;

  @Override public HasClickHandlers getOkButton() { return okButton; }

  …
}

Der Presenter holt sich von der View das HasClickHandlers-Objekt und hängt seine Logik daran:

class MyPresenter
{
  private MyView myView = new MyViewImpl();

  MyPresenter() {
    myView.getOkButton().addClickHandler( new ClickHandler() { … } );
  }

  MyView getView() { return myView; }
}

Den Anmeldevorgang der Listener habe ich hier in den Konstruktor gesetzt, doch ist es empfehlenswert, sie in eine eigene (private) Methode bind() auszulagern.

Dieser Weg ist einfach und kostet verhältnismäßig wenig Code. Allerdings muss man das auch kritisch sehen, denn HasClickHandlers ist eine GWT-Schnittelle, genauso wie com.google.gwt.event.dom.client.ClickHandler. Möchte man eine View total von der Technologie unabhängig machen, so stören diese Typen, wobei es sehr angenehm ist, dass es Schnittstellen sind, und so auch von Swing/SWT/JSF im Prinzip umgesetzt werden können. Eine zweite Sache ist, dass man sich überlegen muss, wo man die Grenze bei den Typen zieht. Ein Textfeld zum Beispiel implementiert im Prinzip HasValue<String>, man kann also so weit gehen, auf View-Schnittstellen-Methoden wie

setValue(String)

String getValue()

zu verzichten und stattdessen so etwas wie

HasValue getValue()

zurückzugeben weil darüber ja ein setValue()/getValue() möglich ist.

Die neuen GWT-Beispiele etwa von https://developers.google.com/web-toolkit/doc/latest/DevGuideMvpActivitiesAndPlaces gehen einen anderen Weg und lassen die View selbst die Listener anhängen. Die View darf aber natürlich immer noch nicht die Logik ausführen, weshalb die View auf Methoden vom Presenter zugreifen kann. Damit gibt es eine bidirektionale Beziehung. Es wird mehr Arbeit als bei einer Lösung wie HasClickHandlers und das, was der View auf dem Presenter aufrufen möchte muss in einen neuen Typ fließen, denn die View soll ja nicht MyPresenter bekommen, sondern einen Basistyp.

Die Beschreibung der Presenter-Methoden kommt als innere Schnittstelle in die View-Schnittstelle und muss einen Presenter annehmen können:

interface MyView {

  void setName( String name );
  String getName();

  void setPresenter( Presenter p );

  interface Presenter {
    ok();
  }
}

Der Button selbst kommt nun nicht mehr nach außen, auf HasClickHandlers getOkButton() können wir verzichten.

Die View muss jetzt setPresenter() implementieren:

class MyViewImpl extends Composite implements View {

  private TextBox textbox;
  private Button okButton;
  private Presenter presenter;

  @Override public setPresenter( Presenter p ) { presenter = p; }
  …
}

Mit dem Verweis auf den Presenter bekommt die View Zugriff auf die Logik von ok(), die immer dann aufgerufen werden soll, wenn der Button gedrückt wird. Die View wird ergänzt um die Ereignisbehandlung:

  MyViewImpl() {
    okButton.addClickHandler( new ClickHandler() {
      presenter.ok();
    } );
  }

Das war es mit der View. Noch netter ist es natürlich, wenn man den UiBinder einsetzt, denn dann wird die Anmeldung noch etwas simpler:

@UiHandler("okButton") void onOkClick( ClickEvent e ) {
  presenter.ok();
}

Der Presenter muss nur noch ok() implementieren, hat aber mit der Anhängen eines Listeneres nichts mehr zu tun:

class MyPresenter implements MyView.Presenter
{
  private MyView myView = new MyViewImpl();

  MyView getView() { return myView; }

  @Override public void ok() { … };
}

Bewertung

Zielt man MVP voll durch, ersteht viel Code. In meinem Projekt hat eine View-Schnittstelle über 100 Methoden, die implementierende Klasse ist voll von kleinen Settern und Gettern. Schön ist das nicht. Wirklich vereinfachen kann man das nur dann, wenn man a) auf diese kleine Mini-Presenter-Schnittstelle in der View verzichtet und somit dem Presenter erlaubt, direkt die Listener anzumelden, und b) wenn man sich von dem schnittstellenorientierten Ansatz verabschiedet:

  • Statt einer Schnittstelle MyView und einer Implementierung MyViewImpl schreibt man nur die eine konkrete View-Klasse und referenziert diese im Presenter direkt. Aus “private MyView myView;” wird also “private MyViewImpl myView;”. Setter/Getter können bleiben.
  • Verschärfte Variante: Man verzichtet in der View auf die vielen Setter/Getter und greift in Presenter direkt auf die GWT-Widget zurück. Dann am Besten über die Schnittstellen um sich relativ unabhängig von den GWT-Klassentypen zu machen.

Wie weit man geht, ist jedem selbst überlassen.

Eigene Linkseiten und Dokumentationen zu Java

Wie jedes System entwickelt sich Java weiter und über zentralen Neuerungen informiert der Blog. Speziell zu den neuen Java-Versionen geben dedizierte Unterseiten detaillierte Updates und eine Fülle von Links:

Nicht nur die Java SE entwickelt sich weiter, auch die Java Enterprise Edition. Einen großen Sprung gab es von J2EE 1.4 auf Java EE 5. Neues in den Enterprise JavaBeans (EJB) 3 listet die zentralen Neuerungen der Enterprise Java Beans 3 im Gegensatz zur EJB 2 auf.

In der professionellen Java-Entwicklung reicht die Java SE/Java EE nicht aus. Zu viele Lücken gibt es, und es wäre ein Fehler, wenn Entwickler-Teams Zeit aufwänden, diese Lücken selbst zu füllen. Die Seiten Open-Source Bibliotheken sowie freie und kommerzielle Swing-Komponenten geben einen Überblick, was quelloffen und kommerziell am Markt verfügbar ist.

Effektive Entwickler schreiben nicht nur wenig Code, sondern minimieren auch ihre Arbeit in Entwicklungsumgebungen, in dem sie zum Beispiel Schritte automatisieren oder Short-Cuts verwenden. Speziell für die IDE Eclipse listet die Seite über beliebte Eclipse-Plugins gute Erweiterungen auf.

Softwareentwicklung bedeutet nicht nur Software neu zu entwickeln, sondern auch bestehende Programme zu lesen und zu verändern. Aus einem misslungenen historisch gewachsenen System soll am Besten ein modernes, leicht zu pflegendes System werden. Die Refactorings von Martin Fowler auf Deutsch geben einen Eindruck, was schlecht riechender Code ist, und wie Transformationen von schlechtem Code in gutem Code aussehen.

Die Anzahl der Java-Bücher ist seit Beginn der Programmiersprache unübersichtlich geworden. Die Seite Java Bücher Hits und Flops gibt eine Übersicht über den Literaturmarkt und gibt eine Einschätzung, welche Werke für Softwareentwickler ein Gewinn darstellen.

Kritiker bemängeln, dass sich Java zu langsam entwickelt. Das ist nicht ganz falsch, doch gilt es zunächst zu unterscheiden, was damit gemeint ist. Java ist ein Dreigespann aus der Sprache, den Bibliotheken und der JVM. Änderungen an der JVM sind von der Kritik ausgenommen, es gibt kaum Eigenschaften, die bemängelt werden (Reified Generics/2 ist gewünscht). Probleme mit der Java API lassen sich durch API-Ergänzungen wie Google Guava oder Apache Commons beheben. Unter der Annahme, dass die die JVM eine solide Basis darstellt, und Interoperabilität mit den existierenden Bibliotheken gewünscht ist, lohnt sich ein Blick auf alternative Sprachen für die JVM.

JSR 357: Social Media API ist in der Entstehung

http://jcp.org/en/jsr/detail?id=357. Aufgabe:

This specification proposes to provide an API for accessing social information networks, both Public (Facebook, Twitter, Google+, LinkedIn, Xing, Yammer,…) and Corporate, e.g. within the Enterprise or Institution (University, Hospital, etc.) The primary API will build upon standards and formats specified by OpenSocial 2.0 or other relevant technologies defined by standard organizations and leading social networks.