RichTextFX (GPLv2 with the Classpath Exception)

Seit JavaFX 8 kann JavaFX dank TextFlow problemlos Text mit unterschiedlichen Formatierungen anzeigen. RichTextFX (ehemals CodeAreaFX) greift darauf zurück und bietet mit StyleClassedTextArea eine Komponente, bei der einfach gewisse Teile vom Text ausgezeichnet werden können. Ein kleiner Editor kommt als Demo mit:

Screenshot of the RichText demo

Weiterhin gibt es eine vorgefertigte Editor-Komponente an, die als Basis für eigene Code-Editoren dienen kann:

Screenshot of the JavaKeywords demo

Über Code oder CSS lässt sich die Darstellung ändern. 

Weiter FX-Komponenten unter http://www.tutego.de/java/javafx-komponenten.htm.

ReactFX (https://github.com/TomasMikula/ReactFX)

Thomas Mikula fasst die JavaFX 8 Bibliothek ReactFX mit den Worten "Reactive event streams, inhibitable bindings and more for JavaFX" zusammen. Die Bibliothek veröffentlicht spezifische Ereignisse einer JavaFX-Anwendung auf einer Art lokalen Bus (Typ EventStream) und erlaubt es auf der anderen Seite Klienten an diesem EventStream Bus zu lauschen. Geht es zum Beispiel darum Maus-Klicks auf einem Knoten zu registrieren und dann bei Klicks Code auszuführen sieht es im Code so aus:

EventStream<MouseEvent> clicks = EventStreams.eventsOf( node, MouseEvent.MOUSE_CLICKED );
clicks.subscribe( click -> System.out.println("Klick!") );

So gesehen bietet die API noch keinen Vorteil, spannend wird es, wenn der EventStream gefiltert, mit anderen EventStreams verschmolzen oder die Ereignisse gemappt werden — die Programmierung erinnert an die neue Stream-API aus Java 8.

Weitere FX-Komponenten unter http://www.tutego.de/java/javafx-komponenten.htm.

tutego bietet auch ein JavaFX-Seminar an: http://www.tutego.de/seminare/java-schulung/JavaFX-Seminar-JavaFX-Script-Kurs.html

Official Eclipse Support for Java 8

So ist zu lesen unter https://dev.eclipse.org/mhonarc/lists/eclipse.org-committers/msg00948.html:

The Eclipse top-level project is very proud to announce official support for Java 8. Starting with I20140318-0830 all Luna (4.4) builds contain the Eclipse support for Java™ 8. For Kepler SR2 (4.3.2) a feature patch is available. For future builds visit our downloads page.
The Java™ 8 support contains the following:
– Eclipse compiler implements all the new Java™ 8 language enhancements.
– Significant features, like Search and Refactoring, have been updated to support Java™ 8.
– Quick Assist and Clean Up to migrate anonymous class creations to lambda expressions and back.
– New formatter options for lambdas.
Note that PDE API Tools has not yet adopted the new language constructs. This will be completed in the final Luna release.
Big thanks to everyone who worked on this effort!

Neue Version vom Apache JSPWiki

Das Apache JSPWiki is ein Wiki-System auf der Basis einfacher Java-Technologien Servlets und JSP. Apache JSPWiki gibt es nun in der Version 2.10, Details unter http://jspwiki.apache.org/.

Some of its features include:

  • WikiMarkup/Structured Text
  • File attachments
  • Templates support
  • Data storage through your choice of two WikiPage Providers, with the capability to create and plug in new ones
  • Security: fine grained control over authorization and authentication, yet simple to configure
  • Easy plugin interface
  • UTF-8 support
  • JSP-based
  • Easy-ish installation
  • Page locking to prevent editing conflicts
  • Support for Multiple Wikis

Google Guava Closeables

To shorten things and not to repeat ourselves (usually the closing always looks the same) Google Commons offers the utility class com.google.common.io.Closeables. In this class you can find two static helper methods close(Closeable) and closeQuietly(Closeable). Both take an argument of type Closeable—like a FileInputStream—and call the close() method on this object eventually. If the argument is null, nothing happens; in our hand-coded version we use an extra if (out != null) to prevent a NullPointerException from out.close() if out is null.

The following example uses Closeable.closeQuietly(Closeable) to close a stream. Any potential IOExceptions caused by close() is swallowed by closeQuietly().

package com.tutego.googlecommon.io;

import java.io.*;

import java.util.logging.*;

import com.google.common.io.*;

public class CloseablesDemo {

private static final Logger log = Logger.getLogger( CloseablesDemo.class.getName() );

public static void main( String[] args ) {

InputStream in = null;

try {

in = Resources.getResource( CloseablesDemo.class, „test.txt“ ).openStream();

BufferedReader br = new BufferedReader( new InputStreamReader( in, „utf-8“ ) );

System.out.println( br.readLine() );

} catch ( IOException e ) {

log.log( Level.SEVERE, „IOException thrown while reading line“, e );

} finally {

Closeables.closeQuietly( in );

}

}

}

Using closeQuietly() shortens a program but it does not notify about any exception caused by the inner close() method. A lot of programmers ignore this exception and shortens there closing block to

try { out.close(); } catch ( Exception e ) {}

This style is dangerous, usually not for readers but for all modifying writers. If a writing stream can’t be closed the IOException shows a severe problem that probably the output is not complete and data is missing. For that reason the utility class Closeable offers a second message, close() which, in contrast to closeQuietly() first is denoted by an IOException clause and secondly controls with a boolean parameter if in case of an IOException caused by close() this exception should be swallowed or rethrown.

To summarize these methods:

class Closeables

static void close(Closeable closeable, boolean swallowIOException) throws IOException

Closes the closeable if it is not null. The boolean parameter controls whether an exception will be rethrown (pass false for swallowIOException) or swallowed (pass true).

static void closeQuietly Closeable closeable)

If closeable is not null this method calls closeable.close(). If close() throws an IOException this exception is swallowed by closeQuietly().Because closeQuietly(closeable) swallows the exception it is internally written as close(closeable, true).

Der Report-Generator JasperReport

JasperReport ist eine weit verbreitete Open-Source-Software unter der LGPL-Lizenz zum Erstellen von Reports. Auf der Basis einer Vorlagendatei im XML-Format setzt JasperReports die Eingabe, die etwa aus einer relationalen Datenbank stammt, in eine Ausgabe um. Als Ausgabeformate unterstützt der Generator drei Typen von Ausgaben: Text-/Zeichenbasierte Dokumente HTML, RTF, CSV, TXT und XML, Dokumente in den Binärdateiformaten PDF, XLS und Export auf ein Grafik-Gerät (Graphics2D und Java Print Service). Der Unterschied ist, dass HTML, XLS und CSV nur eingeschränkte Positionierungen erlauben und nicht wie die anderen Formate pixelgenaue Boxen.

JasperReport greift auf weitere Open-Source-Bibliothek zurück, etwa auf iText (http://itextpdf.com/) für die PDF-Ausgabem, JFreeChart (http://www.jfree.org/jfreechart/) für diverse Diagramme oder Apache POI zu Generieren von Excel XLS Dokumenten (dann aber ohne Charts). Rund um JasperReport (http://community.jaspersoft.com/project/jasperreports-library) ist ein Ökosystem mit weiteren Tools entstanden. JasperReports wird zusammen mit anderen Tools von der JasperSoft Corporation (http://www.jaspersoft.com/) entwickelt. Das Unternehmen hat weiterhin die freie Swing-Anwendung iReport (http://community.jaspersoft.com/project/ireport-designer) zur Erstellung von JasperReport-Vorlagendateien im Portfolio. JasperAssistant (http://www.jasperassistant.com/) ist ein Beispiel für eine kommerzielle Software (129 US Dollar für eine Lizenz), um die Vorlagendateien grafisch in Eclipse zu erstellen.

Ein Report besteht bei JasperReport aus so genannten Bands, die auch Report-Sektionen genannt werden. Es lassen sich drei Typen von Sektionen ausmachen:

  • Sektionen, die nur genau einmal im Report auftauchen können, wie der Titel am Anfang, oder eine Zusammenfassung am Ende.
  • Sektionen, die auf jede Seite stehen können, wie die Seitenkopf- und Fußzeile.
  • Eine Detail-Sektion, die einer Tabelle mit Einträgen aus Datenquellen wie Datenbanken ähnelt.

Alle Bands haben eine feste Reihenfolge und eine benutzerdefinierte Höhe und Breite. Die Reihenfolge der Segmente ist: Hintergrund, Titel, Seitenkopf, Spaltenkopf, Gruppierungskopf, Detail, Gruppierungsende, Spaltenende, Seitenende, letztes Seitenende, Zusammenfassung.

Die Elemente dieser Bands nennen sich Report-Objekte. JasperReport definiert zwei Textelemente, vier grafische Objekte und einen Sub-Report – ein Sub-Report ist ein im Report eingebetteter Unter-Report. Dass es als Report-Objekte zweimal Textfelder gibt, liegt daran, dass JasperReport zwischen statischem Text, etwa für einen festen Spaltennamen, und einem dynamischen Text unterscheidet. Der dynamische Text kann sich während der Reporterstellung ändern, wenn etwa ein Feld aus einer Datenbank stammt oder die Seitenzahl angezeigt wird. Zu den grafischen Report-Objekten zählen Linie, Rechteck (auch abgerundet) und Ellipse. Alle weisen eine Vielzahl von Eigenschaften auf, wie Strichtypen, Dicken und Farben. Dazu kommen Bilder, die gewünscht skaliert und positioniert vom Datensystem, Datenbank oder über das Netzwerk kommen können.

Während bei statischen Textelemente sich der Werte nicht ändern, stellen dynamischen Textelemente so genannte Ausdrücke dar. Ein Ausdruck wird zur Laufzeit berechnet und als String in den Report gesetzt. Der Ausdruck kann auf verschiedene Datenspeicher zurückgreifen:

  • Parameter. Die Parameter sind Werte aus einer java.util.Map, die vor dem Aufbau eines Reports initialisiert wurde.
  • Felder. Sie ergeben sich aus Einträgen einer Datenquelle, wie der Datenbank oder einer Sammlung von Hibernate-Objekten.
  • Einträge aus einem Resource-Bundle.
  • Variablen. Während des Reports kann eine neu definierte Variable einen Wert zwischenspeichern. JasperReport bietet einige vorbelegte Variablen, zum Beispiel für die aktuelle Seite oder die aktuelle Spalte in der Verarbeitung.

Die Ausdrücke können mit den bekannten Operatoren aus Java zu komplexeren Ausdrücken zusammengesetzt werden, denn JasperReport erlaubt die gesamte Syntax der Sprache Java. Variablenzugriffe stehen in einer besonderen Notation: $P{name}, $F{name}, $K{name} und $V{name}.

Eine interessante Alternative ist Eclipse BIRT (http://www.eclipse.org/birt/phoenix/).

Dynamisch. Praktisch. Gut Mit dem BeanUtils Properties einfach beherrschen

Die Java-Beans sind ein allgegenwärtiges Gut. Zwar sind die Anforderungen minimal, aber für viele Bereiche wie grafische Oberflächen, JSPs, OR-Mappern sind sie nicht wegzudenken. Damit eine Bean zur Bean wird, muss eine Klasse

  • öffentlich sein
  • einen öffentlichen Standard-Konstruktur besitzen und
  • öffentliche Zugriffsfunktionen anbieten.

Nach dieser Vorgabe ist Folgendes eine gültige Bean:

class Weihnachtsmann
{
  private String name;

  public String getName() {
    return name;
  }

  public void setName( String name ) {
    this.name = name;
  }
}

Die Zugriffsfunktionen kapseln die internen Eigenschaften und sind nach einem festen Muster aufgebaut. Für ein Attribut String name würden die beiden Methoden void setName(String name) und String getName() angelegt. Für boolean-Eigenschaften ist die Vorsilbe is- üblich. Wenn die Beans nicht nach dem Schema aufgebaut sind, kann eine zusätzliche BeanInfo-Beschreibung helfen.

Während auf der einen Seite die Bean als Datencontainer Einsatz findet, sind sie noch aus einen anderen Grund reizvoll: durch ihren festen Aufbau lässt sich leicht mit Reflection/Instrospection auf die Bean und ihre Eigenschaften zugreifen. Nicht umsonst beschreiben Bean grafische Komponenten, so dass ein GUI-Builder völlig automatisch die Eigenschaften auslesen kann und Manipulationen erlaubt. Ein Exemplar einer Bean lässt sich über ihren Namen einfach anlegen; mit einem bekannten Class-Objekt liefert newInstance() ein neues Exemplar.

Class beanClass = Class.forName( "Weihnachtsmann" );
Object santa = beanClass.newInstance(); 

Für das Auslesen der Methodennamen definiert Java Introspection und für den Zugriff Reflection. Vor dem Zugriff auf die Bean-Properties sind jedoch die Präfixe get- und set- zu setzen und Datentypkonvertierungen durchzuführen.

Um den Umgang mit Bean-Eigenschaften zu vereinfachen, hat die Apache-Gruppe in ihren Commons-Paket die BeanUtils aufgenommen. Die aktuelle Version der BeanUtils ist 1.6.1 und ist beim Indianer-Server http://jakarta.apache.org/commons/beanutils/ zuhause. BeanUtils setzt zwei weitere Pakete aus Commons voraus: Collections und Logging. Der prominenteste Nutzer der Bibliothek ist Struts.

Bean-Eigenschaften lesen und setzen

Eine Bean-Property kennzeichnet ähnlich wie ein Servlet/JSP-Attribut zwei Merkmale: Einen Namen (immer ein String) mit einer Belegung (beliebiger Objekttyp). Um bei einer Bean eine Property zu setzen, müssen ihr Name und die neue Belegung natürlich bekannt sein, beim Lesen nur der Name. Auslesen und Setzen von Eigenschaften erlauben zwei statische Funktionen der Utility-Klasse org.apache.commons.beanutils.PropertyUtils: PropertyUtils.getSimpleProperty(Object bean, String name) und PropertyUtils.setSimpleProperty( Object bean, String name, Object value). Um unserem Weihnachtsmann einen Namen zu geben — und diesen gleich wieder auszulesen — ist zu schreiben:

PropertyUtils.setSimpleProperty( santa, "name", "Santa Clause" );
String name = (String) PropertyUtils.getSimpleProperty( santa, "name" );

Die Methoden sind jedoch nicht ohne ausführliche Fehlerbehandlung auszuführen; auftreten kann eine IllegalAccessException, IllegalArgumentException (eine RuntimeException), InvocationTargetException und NoSuchMethodException.

Zu bedenken ist, dass eine Bean-Property ganz spezielle Methoden erzwingt. Das geht so weit, dass BeanUtils nur genau eine get-Methode erwartet. Gibt es neben der erforderlichen get-Methode eine weitere, so liefert BeanUtils beim Zugriff einen Fehler. Der Zugriff auf bounds einer java.awt.Component gelingt daher nicht, da neben der parameterlosen Variante von getBound() auch getBounds(Rectange) existiert — ein Aufruf ähnlich wie der Folgende funktioniert daher nicht und führt zu einer NoSuchMethodException: PropertyUtils.getSimpleProperty(new Label(), "bounds");

PropertyUtils, BeanUtils und Konvertierungen

Neben der Klasse PropertyUtils, die bisher Verwendung fand, gibt es noch zusätzlich die Klasse BeanUtils. Sie erinnert von der Methodenvielfalt an PropertyUtils, doch sie führt für Datentypen mögliche Konvertierungen durch. Das Beispiel zum Weihnachtsmann soll erweitert werden, so dass er ein Attribute int alter besitzt. Der Versuch, das Attribut mit einem Wert vom Datentyp String über PropertyUtils.setProperty() zu setzen scheitert mit einer IllegalArgumentException. Ein int muss mit einem Integer-Objekt belegt werden. Wird jedoch anstelle der Klasse PropertyUtils die Klasse BeanUtils gesetzt, so funktioniert alles, denn BeanUtils führt die notwendigen Konvertierungen von String in int selbst aus.

BeanUtils.setProperty( santa, "alter", "612" );

Ergibt sich bei der Konvertierung ein Fehler, so macht das nichts, denn dann wird die Zuweisung nicht durchgeführt. (Apropos Konvertierungen: Die Firma Lockheed, die die Raumsonde Mars Climate Orbiter in England konstruierte, nutze die englische Einheit Inch, die NASA in Amerika Zentimeter; beim Landeversuch crashte die Sonde 1999 auf den Mars.)

Die Konvertierung aus einem String für die unterschiedlichsten Datentypen nimmt BeanUtils automatisch vor. Im oberen Fall findet eine Konvertierung von String in ein Integer Objekt statt. Die Converter selbst sind Klassen, die die Schnittstelle org.apache.commons.beanutils.Convert implementieren und auf diese Weise von einem String in BigDecimal, BigInteger, boolean, Boolean, byte, Byte, char, Character, Class, Double, float, Float, int, Integer, long, Long, short, Short, String,

java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp und mehr konvertieren. Den passenden Konverter sucht ConvertUtils.lookup(). Falls also BeanUtils.setProperty() vor der Aufgabe steht, einen passenden Typ herauszufinden, so sucht es zuerst nach dem Typ (von alter wäre das int) und anschließend den passenden Konverter. Da ConvertUtils.lookup(int.class) den IntegerConverter liefert, darf er die Zeichenfolgen umwandeln. Diese Konvertierung ist die übliche Konvertierung mit Integer.parseInt().

Falls die Standard-Konverter einen Typ nicht abdeckt, kann mit register() ein neuer Konverter eingehängt werden. Nicht nötig ist das für sprachabhängige Konvertierungen, denn hier bietet das Paket schon Hilfe an. So gibt es den IntegerConverter in der lokalisierten Fassung auch als IntegerLocaleConverter. Zum Nachschlagen dient aber nicht mehr ConvertUtils, sondern LocaleConvertUtils. Die Basisklassen für lokalisierte eigene Konvertiert ist LocaleConverter. Es gibt für alle normalen Konverter auch lokalisierte Konverter. Das lokalisierte Parsen scheint allerdings noch nicht so ganz ausgereift.

Eine Liste aller Eigenschaften

Eine Belegung aller Eigenschaften liefert BeanUtils.describe() beziehungsweise PropertyUtils.describe().

Map description = BeanUtils.describe( new JButton() );

Der Assoziativspeicher unter dem Namen description enthält als Schlüssel alle Properties und als Werte die Belegungen der Eigenschaften.

verifyInputWhenFocusTarget=true
displayedMnemonicIndex=-1
width=0
...

Formular-Parameter übertragen

In JSPs erfreut eine Schreibweise den Entwickler, die alle Parameterwerte eines Requests in eine Bean überträgt. Das Tag <jsp:setProperty> wird dann mit dem Attribut property="*" belegt.


Diese Möglichkeit sieht zwar die JSP-Spezifikation vor, doch in Servlets fehlt diese Möglichkeit. Sie wird aber insbesondere für MVC-Frameworks interessant, in dem der Controller selbständig die Formulardaten in eine Bean überträgt. Das kann schnell nachimplementiert werden. Die Servlet-API bietet in ServletRequest die Funktion getParameterNames(), um mit einer Enumeration über die Parameter zu laufen. Für jeden der Parameter kann setProperty() auf einer passenden Bean aufgerufen werden.

for ( Enumeration params = request.getParameterNames(); params.hasMoreElements(); ) {
  String name = (String) params.next();
  if ( name != null )
    BeanUtils.setProperty( bean, name, request.getParameterValues(name) );
}

getParameterValues() liefert ein Feld, was von der Bean auch verarbeitet werden muss. Sonst tut’s bei eindeutigen Parametern auch getParameter().

Werte einer Map in eine Bean kopieren

Aufgabe: Eine Ini-Datei enthält Schlüssel/Werte-Paare und alle Schlüssel sollen auf Eigenschaften einer Bean übertragen werden. Für Schlüssel soll es also entsprechende set- und get-Methoden geben. Eine einfache Aufgabe mit den passenden Klassen. Zum Laden der Paar-Datei findet Properties und die Objektfunktion load() Einsatz. Ein Properties-Objekt ist eine spezialisierte Hashtable — eine dumme Designentscheidung Begründung ? —, und alle Schlüssel lassen sich aufzählen. An dieser Stelle kommen wieder die BeanUtils ins Spiel. Die statische Funktion setProperty() sieht jeden Schlüssel als Property und überträgt den Wert auf die Bean.

Unter der Annahme, dass alle Eigenschaften in einer Map vorliegen, bietet BeanUtils eine feine Funktion: static void populate(Object bean, Map properties). Die Map schreibt Schlüssel als String vor, die Werte sind beliebige Objekte. populate() iteriert über die Schlüssel von properties und initialisiert über viele setProperty()-Funktionen die Werte der Bean. Die Aufgabe löst sich somit in wenigen Zeilen Quellcode.

Die Funktion populate() ließe sich auch bei Formular-Parametern verwenden, wenn denn die Schlüssel/Werte-Paare in einer Map vorliegen würden. Doch das können sie so ohne weiteres nicht, denn ein Schlüssel darf durchaus mehrmals mit verschiedenen Werten vorkommen — eine Map kann das nicht unterstützen. Gilt, dass überladene Parameter nicht zu erwarten sind, können von Hand die Parameter in eine Map einsortiert werden, so dass doch wieder populate() nutzbar ist. Seit der Servlet 2.3 Spezifikation bietet HttpServletRequest die Methode getParameterMap(), was direkt eine Map liefert. Sie speichert die Werte jedoch immer als Felder; wenn der Schlüssel einmalig ist, dann ist das Feld nur ein Element groß.

Indexierte Properties

Da ein Weihnachtsmann immer Rentiere besitzt, soll ihn eine zusätzliche Klasse Rentier für die Zugtiere bei der Arbeit unterstützen. Er soll in einem internen Feld zugtier die Tiere speichern und dafür auch zwei neue Zugriffsfunktionen definieren.

class Weihnachtsmann
{
  private Rentier[] zugtier = new Rentier[8];
  public Rentier getZugtier( int index ) {
    return zugtier[index];
  }
  public void setZugtier( int index, Rentier zugtier ) {
    this.zugtier[index] = zugtier;
  }
}

Ist ein Weihnachtsmann aufgebaut und mit setZugtier() ganz klassisch ein Rentier zugewiesen kann im nächsten Schritt die Eigenschaft erfragt werden.

Weihnachtsmann w = new Weihnachtsmann();
w.setZugtier( 0, new Rentier() );
Rentier z = (Rentier) PropertyUtils.getIndexedProperty( w, "zugtier", 0 );

Diese indexierten Eigenschaften lassen sich mit getIndexedProperty() erfragen und mit setIndexedProperty() setzen; die Signaturen sind PropertyUtils.getIndexedProperty(Object bean, String name, int index) und PropertyUtils.setIndexedProperty(Object bean, String name, int index, Object value).

Rentier rudolph = new Rentier();
PropertyUtils.setIndexedProperty( w, "zugtier", 1, rudolph );

Eine Alternative zu den Funktionen, die den Index als Parameter erwarten ist eine Schreibweise, die den Index in den Eigenschaftennamen kodiert. Die Schreibweise ist bekannt von Arrays: name[index].

Rentier zt = (Rentier) PropertyUtils.getIndexedProperty( w, "zugtier[1]" )

Mapped Properties

Für indexierte Properties ist es üblich, dass der Index eine positiver ganzzahliger Wert ist. Eine Erweiterung der Java-Beans gehen die BeanUtils mit den Mapped Properties, was indexierte Beans mit Schlüsseln sind — das Konzept erinnert an einen Assoziativspeicher. Genauso wie indexierte Properties mit eckigen Klammern einen Index aufnehmen, gibt es eine Abkürzung auch bei den abgebildeten Eigenschaften. Dabei wird der Schlüssel in runden Klammern hinter die Eigenschaft gesetzt. Die Signaturen sind PropertyUtils.getMappedProperty(Object bean, String name) mit den alternativen Parametern (Object bean, String name, String key), (Object bean, String name, Object value) und (Object bean, String name, String key, Object value).

Ein Beispiel: System.getProperties() liefert ein Properties mit den Systemeigenschaften. Die Objektmethode getProperty() liefert zu einem gegebenen Schlüssel den assoziierten Wert. BeanUtils lässt mit getMappedProperty() die abgebildeten Eigenschaften erfragen.

Properties props = System.getProperties();
System.out.println( PropertyUtils.getMappedProperty( props, "property(user.name)" ) );

Wo ist der Vorteil gegenüber props.get("user.name") ;

Der Schlüssel muss nicht in einfachen oder doppelten Anführungszeichen eingeschlossen werden, wie es etwa die Expression Language unter JSP 2.0 oder Velocity vorschreibt.

Geschachtelte Properties

Um eine Liste aller Zeichensätze des Betriebssystems auszulesen, lässt sich über GraphicsEnvironment getLocalGraphicsEnvironment() aufrufen.

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();

Ausdruck Der Name des ersten Fonts erfragt

String s = ge.getAllFonts()[0].getName();

Wie sieht das nun mit den BeanUtils aus? Sicherlich ist es denkbar, für den indexierten Property einmal getIndexedProperty() und für den Namen setSimpleProperty() aufzurufen, aber commons.beanutils kann es besser, und zwar mit getNestedProperty().

Object o = PropertyUtils.getNestedProperty( ge, "allFonts[0].name" );

Während mit getNestedProperty() eine Eigenschaft ausgelesen werden kann, lässt sie sich mit setNestedProperty() setzen. Die Signaturen für set- und get sind: PropertyUtils.getNestedProperty(Object bean, String name) und PropertyUtils.setNestedProperty(Object bean, String name, Object value).

Nun sind drei Möglichkeiten bekannt, auf Bean-Eigenschaften zuzugreifen: Einfache Properties (getSimpleProperty(), setSimpleProperty()), indexierte Properties (getIndexedProperty()/setIndexedProperty()), mapped Properites und geschachtelte Properites (getNestedProperty()/setNestedProperty()). Um den Umgang zu vereinfachen, hilft die allgemeine Funktion getProperty(), die sich den passenden Typ heraussucht. Damit lässt sich alles mit nur einer Art von Funktion beschreiben: PropertyUtils.getProperty(Object bean, String name) und PropertyUtils.setProperty(Object bean, String name, Object value).

Kopien von Bean-Eigenschaften

Stehen zwei Beans gegenüber, die sich Properties teilen, so kopiert BeanUtils. copyProperties() die Eigenschaften. Die Klasse Point definiert genauso wie Rectangle das Property x und y. Die Belegungen der Eigenschaften lassen sich übertragen, so dass das Rectangle alles annimmt, was in Point definiert wird:

Point p = new Point( 10, 20 );
Rectangle rec = new Rectangle(); 
BeanUtils.copyProperties( rec /* = */, p );
System.out.println( rec ); // java.awt.Rectangle[x=10,y=20,width=0,height=0]

Intern geht copyProperties() alle Eigenschaften vom Punkt durch, findet x, y und auch location. Für diese drei Eigenschaften werden die entsprechenden set-Methoden beim Rectangle-Objekt gesucht; sie werden nur für x und y gefunden, denn setLocation() ist überladen und so ungültig.

Ein Beispiel, unter dem Bean-Attribute zu kopiert sind, liefert Struts. Die Action Form Beans werden zwar allzu oft als Model eingesetzt und weiterverarbeitet, das ist aber falsch. Die Form Beans sind lediglich eine Hilfs-Struktur, die nur für die Formulardaten eine Berechtigung haben. Demnach sind sie auch nur in einem Struts-Durchlauf bekannt und werden nicht als Datencontainer weitergegeben. Trennt man allerdings Form-Bean von tatsächlicher Model-Bean (falls eine Model-Bean benötigt wird) sieht das Model sehr ähnlich der Form-Bean aus und entsteht leider doppelter Quellcode, insbesondere für die set- und get-Methoden. Weiterhin ist lästig, dass bei einer gültigen Formularbelegung alle Form-Eigenschaften auf die Model-Bean übertragen werden. Das artet aus zu einer Reihe von Aufrufen der Art

modelBean.setXXX( formBean.getXXX() );

An diese Stellt helfen die BeanUtils und die praktische Funktion copyProperties(). Sie überträgt automatisch die Eigenschaften der Form-Bean in die Model-Bean.

BeanUtils.copyProperties( modelBean, formBean );

Bean-Objekte klonen

Auf copyProperties() aufbauend lassen sich Kopien von Beans anlegen lassen. Da das Kopieren auf Grund der Eigenschaften und set/get-Methoden geschieht, ist eine Implementierung der Schnittstelle Clonable nicht nötig. Mit der clone()-Methode teilt cloneBean() jedoch mit, dass die Kopie wieder eine flache und keine tiefe ist: static Object cloneBean(Object bean).

Dynamische Beans

Bisher waren es existierende Beans, die über wirkliche set- und get-Methoden verfügten. Mit den BeanUtils lassen sich jedoch auch "virtuelle" Beans erzeugen, also Beans, die mit den BeanUtils verwaltet werden können, aber keine passenden set- und get-Funktionen besitzen. Diese Spezial-Beans implementieren die Schnittstelle DynaBean und verwalten über diese Schnittstelle Schlüssel/Werte-Paare. Genauso wie eine Klasse den Bauplan für Objekte beschreibt, beschreibt die Schnittstelle DynaClass den Bauplan für DynaBean-Ojekte. Exemplare der virtuellen Beans führen zuerst über die DynaClass. Die BeanUtils bringen eine Basisimplementierung in Form der Klasse BasicDynaClass mit; ihre Eigenschaften werden als Array von DynaProperty-Objekten übergeben.

DynaProperty[] dynaProp = new DynaProperty[] {
  new DynaProperty( "schenker", String[].class ),
  new DynaProperty( "bringer", Weihnachtsmann.class ),
  new DynaProperty( "volumen", int.class )
};

BasicDynaClass dynaClass = new BasicDynaClass( "geschenk", null, dynaProp ); 

Die BasicDynaClass beschreibt für ein Geschenk ein Feld von Schenkern (wie "Mama", "Papa"), den Weihnachtsmann und die Größe des Geschenks.

Nach der Beschreibung der Klasse bildet die Objektfunktion newInstance() ein Exemplar der dynamischen Bean:

DynaBean paket = dynaClass.newInstance();

Alternativ zu dieser Schreibweise lässt sich ein Exemplar BasicDynaBean bilden, der im Konstruktor die beschreibende Klasse annimmt: paket = new BasicDynaBean(dynaClass);

Diese DynaBean besitzt über die Schnittstelle Methoden zum Erfragen, Setzen oder Löschen von Attributbelegungen. Die Methode set() setzt zum Beispiel eine Eigenschaft.

paket.set( "schenker", new String[1] );
paket.set( "bringer", new Weihnachtsmann() );

Die set()-Funktion ist überladen, um auch indexierte und abgebildete Eigenschaften zu unterstützten. Das gleiche gilt für die get-Funktion. Während bei den herkömmlichen Beans der Zugriff get<Property>() steht, so heißt der nun get("<Property>").

System.out.println( paket.get("volumen") );

Da das BeanUtils-Paket bei den bekannten Funktionen wie PropertyUtils.getProperty() neben den "normalen" Beans auch die DynaBean unterstützen, müssen die über die Schnittstelle DynaBean vorgeschriebenen Funktionen nicht verwendet werden.

PropertyUtils.setSimpleProperty( paket, "volumen", new Integer(123) );
Integer volumen = (Integer) PropertyUtils.getSimpleProperty( paket, "volumen" );

Da im Beispiel vorher schon das Feld der Schenker mit einem String-Array der Länge eins initialisiert wurde, lässt sich ein konkreten Element einfach mit der allgemeinen Funktion setProperty() setzen.

PropertyUtils.setProperty( paket, "schenker[0]", "Ulli" );
System.out.println( PropertyUtils.getProperty(paket, "schenker[0]") ); // Ulli

Mantel um existierende Beans

Die DynaBeans bietet einen einheitlichen Standard für einfache, indexierte oder abgebildeten Eigenschaften über setXXX() und getXXX()-Funktionen. Um auch existierende Beans als DynaBeans zu verwaltet, sind die Klasse WrapDynaBean und WrapDynaClass definiert. Eine WrapDynaBean legt sich um eine bekannten Bean, so dass diese sich als DynaBean verwalten lässt.

Weihnachtsmann myWeih = new Weihnachtsmann();
DynaBean wrapper = new WrapDynaBean( myWeih );
wrapper.set( "name", "Schlittenheld" );
String nameW = (String) wrapper.get( "name" );
System.out.println( "Wrapper: " + nameW + ", Bean: " + myWeih.getName() );
wrapper.set( "alter", new Integer(891) );
int alterW = ((Integer) wrapper.get( "alter" )).intValue();
System.out.println( "Wrapper: " + alterW + ", Bean: " + myWeih.getAlter() );

DynaBeans in Struts *

In Struts führt die Implementierung der Form-Beans für die Abbildung der Formulardaten zu lästigen Aufwand. Die DynaBeans helfen, diesen Aufwand zu minimieren, da keine wirkliche Bean-Klasse mehr zu implementieren ist, sondern die "virtuelle" Bean Verwendung findet. In der struts-config.xml wird zunächst unter form-bean wie üblich die Bean eingetragen. Allerdings ist der Typ nun nicht der Form-Bean Typ, sondern DynaActionForm. In den folgenden Elementen der XML-Datei wird genau die schon bekannten DynaProperty für eine DynaClass eingetragen — nur nicht in Java, sondern in einer Konfigurationsdatei.

<form-bean name="dynaWeihnachtsmannForm"
           type="org.apache.struts.action.DynaActionForm">
  <form-property name="name" type="java.lang.String"/>
  <form-property name="alter" type="java.lang.Integer"/>
</form-bean>

Da Struts mit den HTML-Tags die DynaBeans berücksichtigt, muss an der View (JSP-Seite) keine Veränderung vorgenommen werden. Nur die Action muss eine Veränderung erfahren, da ja ohne DynaBeans das ActionForm aus der execute()-Funktion auf die konkrete Bean typangepasst würde, um die entsprechenden set- und get-Funktionen zu nutzen. Dieser Programmcode muss nun umgebaut werden. Statt dessen gibt Struts eine Klasse DynaActionForm vor, zu der eine Typanpassung vorgenommen wird. Über diese Klasse lässt sich mit get- und set-Funktionen die Bean nutzen.

public ActionForward execute(
 ActionMapping mapping, ActionForm form,
 HttpServletRequest request,
 HttpServletResponse response )
  throws ServletException, IOException
{
  DynaActionForm weihForm = (DynaActionForm) form;
  String name = weihForm.get( "name" );
  ...

Nicht umsonst sieht der Zugriff auf die Attribute über DynaActionForm so aus wie schon im vorangehenden Beispiel beschrieben: DynaActionForm implementiert die Schnittstelle org.apache.commons.beanutils.DynaBean.

Die DynaActionForm ersetzt vollständig die Form-Bean, wobei eine Sache auf der Streckte bleibt: die Validierung. Für die Überprüfung der Formularbelegungen wurde in der Unterklasse von ActionForm die Methode validate() überschrieben. Bei den DynaBean gibt es aber keine eigene Bean-Klasse! Eine Lösung ist eine Validierung über das Validator-Framework (auch eine Jakarta Commons), also über eine XML-Datei, eine andere ist doch wieder eine Unterklasse. Diese spezielle Bean-Klasse leitet von der schon bekannten DynaActionForm ab und implementiert wie bekannt die validate()-Funktion. In struts-config.xml muss natürlich DynaActionForm wieder verschwinden und der Name der Unterklasse eingesetzt werden.

DynaBeans für Datenbanken

Die DynaBeans finden auch an anderer Stelle Verwendung: bei Datenbanken. Ein DynaBeans-Objekt kann eine Zeile eines ResultSet repräsentieren. Um das zu erreichen, legt sich ein ResultSetDynaClass-Objekt um ein ergebnislieferndes ResultSet.

ResultSetDynaClass rsdc = new ResultSetDynaClass( rs, true );

ResultSetDynaClass implementiert die Schnittstelle DynaClass. Das ist wichtig, denn DynaClass verwaltet die Beschreibung der Eigenschaften über DynaProperty-Objekte. Bei der ResultSetDynaClass werden die Namen der Spalten zu den Namen der Eigenschaften. Der Konstruktor von ResultSetDynaClass ist überladen und verträgt ein zusätzliches boolean, damit die Namen der Eigenschaften nicht exakt in Groß- und Kleinbuchstaben übereinstimmen müssen. Ist die Belegung true, so reichen alle Namen in Kleinbuchstaben. Wichtiger ist aber die Funktion iterator() von ResultSetDynaClass, die der Reihe nach die Zeilen als DynaBean liefert; die Beschreibung der Klassen-Informationen war ja wichtiges Teil in den DynaProperty-Objekten.

for ( Iterator rows = rsdc.iterator(); rows.hasNext(); ) {
  DynaBean row = (DynaBean) rows.next();
  System.out.println( row.get("bla") );
}

Nach dem Durchlaufen kann das ResultSet geschlossen werden, das bedeutet, dass zur Zeit des Durchlaufens eine Verbindung zur Datenbank bestehen muss.

Die Frage ist nun, was ein ResultSetDynaClass bringt; zum Durchlaufen tut es auch das ResultSet. Ein Vorteil ist, dass die Daten, die über die DyaBean veröffentlicht werden, leicht auf andere Beans kopiert werden können; man erinnere sich an BeanUtils.copyProperties(ziel, quelle). Oftmals müssen die Datenbankinhalte auf das Model einer Applikation übertragen werden, so dass ein ResultSetDynaClass dazu gut geeignet ist.

Eine Eigenschaft der Klasse BasicDynaBean (und BasicDynaClass) ist ihre Serialisierungs-Möglichkeit, da die Klasse die Schnittstelle java.io.Serializable implementiert, und somit persistent gemacht werden kann. Eine normale DynaBean implementiert diese Schnittstelle nicht, so dass man an dieser Stelle die Serialisierung nicht erzwingt. Aber natürlich kann eine andere Klasse DynaBean und Serializable gleichzeitig implementieren und das wäre sogar praktisch, denn könnte die Daten gleich abgespeichert werden. Wurde zum Beispiel eine BasicDynaBean angelegt, können Daten auf diese Bean übertragen werden und sie lässt sich später abspeichern. Es wäre doch praktisch, wenn auch die Zeilen vom ResultSetDynaClass serialisierbar wären. Dann wäre das Problem gelöst, wie sich Datenbankinhalte einfach übertragen lassen — O.K., ein RowSet ist auch eine Lösung. Die Antwort aus den BeanUtils ist die Klasse RowSetDynaClass, die Serializable implementiert. RowSetDynaClass liest aus einem ResultSet alle Zeilen aus und speichert sie, so dass sie serialisierbar werden.

RowSetDynaClass rsdc = new RowSetDynaClass( rs );

Nach dieser Zeile kann das ResultSet rs schon geschlossen werden. Der Zugriff auf die Zeilen geschieht anschließend mit getRows(), was eine java.util.List mit den bekannten DynaBean-Objekten liefert. Mit dem serialisier-fähigen RowSetDynaClass lassen sich die Inhalte über RMI leicht transportieren, ohne dass viel Programmcode entwickelt werden muss.

Links

Google Annotations Gallery: Immer wieder ein Schmunzler

Siehe https://code.google.com/p/gag/:

Disclaimer

  • @AhaMoment
  • @BossMadeMeDoIt
  • @HandsOff
  • @IAmAwesome
  • @LegacySucks

Enforceable

  • @CantTouchThis
  • @ImaLetYouFinishBut

Literary Verse (new subcategory)

  • @Burma Shave
  • @Clerihew
  • @DoubleDactyl
  • @Haiku (moved to this subcategory)
  • @Limerick
  • @Sonnet

Remarks

  • @Fail
  • @OhNoYouDidnt
  • @RTFM
  • @Win

Team (new category)

  • @Blame
  • @Channeling
  • @Fired
  • @HonorableMention
  • @Visionary

JMapViewer, einfache Komponenten für OpenStreepMap Karte

Siehe http://wiki.openstreetmap.org/wiki/JMapViewer.

  • Provides integrated zoom controls (slider and buttons) in the left upper corner (can be hidden)
  • Switch between different tile sources: Mapnik, Tiles@Home, Cyclemap, … (other tiled maps can be used, too)
  • Configurable in-memory and file-based caching of loaded map tiles
  • A list of map markers (the yellow circles in the screenshot) can be added. Map markers of different shape can be easily added by implementing the MapMarker interface.
  • Configurable/Extentable/Replaceable controller (code part that manages mouse interaction and how the map reacts to it)
  • Requirement: Java 1.6
  • License: GPL

 

JMapViewer demo app screenshot

Apache ODF Toolkit in Release 0.6

Aus dem Changelog https://www.apache.org/dist/incubator/odftoolkit/CHANGES-0.6-incubating.txt:

* Added document encryption support

* Added metadata support

* Support for OpenDocument-v1.2

* Additional APIs for Simple API

Zum Projekt selbst sagt die Homepage:

The Apache ODF Toolkit is a set of Java modules that allow programmatic creation, scanning and manipulation of Open Document Format (ISO/IEC 26300 == ODF) documents. Unlike other approaches which rely on runtime manipulation of heavy-weight editors via an automation interface, the ODF Toolkit is lightweight and ideal for server use.

Dokumente sind leicht erstellt, siehe https://incubator.apache.org/odftoolkit/simple/gettingstartguide.html:

import java.net.URI;

import org.odftoolkit.simple.TextDocument;
import org.odftoolkit.simple.table.Cell;
import org.odftoolkit.simple.table.Table;
import org.odftoolkit.simple.text.list.List;

public class HelloWorld {
    public static void main(String[] args) {
        TextDocument outputOdt;
        try {
            outputOdt = TextDocument.newTextDocument();

            // add image
            outputOdt.newImage(new URI("odf-logo.png"));

            // add paragraph
            outputOdt.addParagraph("Hello World, Hello Simple ODF!");

            // add list
            outputOdt.addParagraph("The following is a list.");
            List list = outputOdt.addList();
            String[] items = {"item1", "item2", "item3"};
            list.addItems(items);

            // add table
            Table table = outputOdt.addTable(2, 2);
            Cell cell = table.getCellByPosition(0, 0);
            cell.setStringValue("Hello World!");

            outputOdt.save("HelloWorld.odt");
        } catch (Exception e) {
            System.err.println("ERROR: unable to create output file.");
        }
    }
}