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

Supergau? Apache zieht sich vollständig aus dem JCP zurück

Aus https://blogs.apache.org/foundation/entry/the_asf_resigns_from_the:

The Apache Software Foundation has resigned its seat on the Java SE/EE Executive Committee.  Apache has served on the EC for the past 10 years, winning the JCP "Member of the Year" award 4 times, and recently was ratified for another term with support from 95% of the voting community.  Further, the project communities of the ASF, home to Apache Tomcat, Ant, Xerces, Geronimo, Velocity and nearly a 100 mainstay java components have implemented countless JSRs and serve on and contribute to many of the JCPs technical expert groups. 
We’d like to provide some explanation to the community as to why we’re taking this significant step.
The recent Java SE 7 vote was the last chance for the JCP EC to demonstrate that the EC has any intent to defend the JCP as an open specification process, and demonstrate that the letter and spirit of the law matter.   To sum up the issues at stake in the vote, we believe that while continuing to fail to uphold their responsibilities under the JSPA, Oracle provided the EC with a Java SE 7 specification request and license that are self-contradictory, severely restrict distribution of independent implementations of the spec, and most importantly, prohibit the distribution of independent open source implementations of the spec.  Oracle has refused to answer any reasonable and responsible questions from the EC regarding these problems.
In the phrase "fail to uphold their responsibilities under the JSPA", we are referring to Oracle’s refusal to provide the ASF’s Harmony project with a TCK license for Java SE that complies with Oracle’s obligations under the JSPA as well as public promises made to the Java community by officers of Sun Microsystems (recently acquired by Oracle.)  This breach of the JSPA was begun by Sun Microsystems in August of 2006 and is a policy that Oracle explicitly continues today.  For more information on this dispute, see our open letter to Sun Microsystems.
This vote was the only real power the Executive Committee has as the governing body of the Java specification ecosystem, and as we indicated previously we were looking for the EC to protect the rights of implementers to the degree they are able, as well as preserve the integrity of the JCP licensing structure by ensuring that JCP specifications are able to be freely implemented and distributed.  We don’t believe this is an unreasonable position – it should be noted that the majority of the EC members, including Oracle, have publicly stated that restrictions on distribution such as those found in the Java SE 7 license have no place in the JCP – and two distinguished individual members of the EC, Doug Lea and Tim Peierls, both have resigned in protest over the same issue.
By approving Java SE 7, the EC has failed on both counts : the members of the EC refused to stand up for the rights of implementers, and by accepting Oracle’s TCK license terms for Java SE 7, they let the integrity of the JCP’s licensing structure be broken.
The Apache Software Foundation concludes that that JCP is not an open specification process – that Java specifications are proprietary technology that must be licensed directly from the spec lead under whatever terms the spec lead chooses; that the commercial concerns of a single entity, Oracle, will continue to seriously interfere with and bias the transparent governance of the ecosystem;  that it is impossible to distribute independent implementations of JSRs under open source licenses such that users are protected from IP litigation by expert group members or the spec lead; and finally, the EC is unwilling or unable to assert the basic power of their role in the JCP governance process.
In short, the EC and the Java Community Process are neither.
To that end, our representative has informed the JCP’s Program Management Office of our resignation, effective immediately.  As such, the ASF is removing all official representatives from any and all JSRs. In addition, we will refuse any renewal of our JCP membership and, of course, our EC position.

Die letzte Aussage ist besonders hart: “As such, the ASF is removing all official representatives from any and all JSRs. In addition, we will refuse any renewal of our JCP membership and, of course, our EC position.”