Hallo,
Zeit für einen neuen Newsletter. Wir bringen heute den zweiten Teil zu Jakarta Commons, hier mit dem Beitrag zu Commons Lang, einer Bibliothek, die Schwächen der Java Standard-API ausgleicht. Dazu haben wir wieder zwei Buchbesprechungen, die diesmal weniger direkt mit JSPs zu tun haben, sondern vielmehr mit Hibernate und CSS die Bereiche Persistenz und Frontend behandeln.
Selbstverständlich hat sich in den letzten Wochen einiges in der Java-Welt getan. Unsere Auswahl der wichtigsten Nachrichten:
- Am entscheidendsten für die Webentwicklung mit Java dürfte der neueste Proposed Final Draft für die JSP 2.1-Spezifikation und die weiteren Änderungen in den JSF- und Servlet-Specs sein. Auf die Neuerungen, die sich daraus für die javabasierte Web-Entwicklung ergeben, werden wir in einem späteren Newsletter ausgiebig eingehen. Der neue, zweite Proposed Final Draft der JSP 2.1-Spezifikation kann hier heruntergeladen werden:
http://jcp.org/en/jsr/detail?id=245 - Das Netbeans-Projekt hat inzwischen die Version 5.0 der freien IDE freigegeben. Wir arbeiten gerade an einem Kapitel zur Einrichtung von Arbeitsumgebungen und werden dabei neben Eclipse auch auf Netbeans eingehen. Soviel schon mal vorne weg: Für die Entwicklung von Webanwendungen hat Netbeans Eclipse einiges voraus und bringt Projektunterstützung für JavaServer Faces und Struts-Projekte wie auch einen integrierten Tomcat 5.5 mit. Für Einsteigerinnen in die JSP-Entwicklung auf jeden Fall eine Überlegung wert (auch wenn die Sun-Promotion-Maschine beim Rühren der Werbetrommel für Netbeans deutlich zu weit geht):
http://www.netbeans.org - Seit ein paar Wochen ist endlich der freie Portlet-Container Jetspeed mit einer neuen Version draußen. Jetspeed 2 ist eine völlige Neuimplementierung des Jetspeed-Portlet-Containers. Neben der inzwischen obligatorischen Unterstützung der Standard-Portlet-API bietet Jetspeed 2 eine AJAX-API, um bspw. Portlets auf einer Portal-Seite zu verschieben, und nutzt Apaches Portal-Bridges, um Struts, JavaServer Faces oder auch PHP innerhalb einer Portal-Anwendung einsetzen zu können. Mehr zu Jetspeed 2 auf der Projektseite:
http://portals.apache.org/jetspeed-2 - Tapestry, ein komponentenbasiertes Java-Webframework der Apache Software Foundation, ist zum Top-Level-Projekt aufgestiegen (bisher Sub-Projekt der Projektsammlung unter http://jakarta.apche.org). Noch wird nicht die neue URL http://tapestry.apache.org genutzt, aber das wird sich nach den entsprechenden Anpassungen durch das Projekt ändern. Die Ankündigung findet man hier:
http://howardlewisship.com/blog/2006/02/tapestry-promoted-to-apache-top-level.html - Oracle hat seine JavaServer Faces-Komponenten-Bibliothek "ADF Faces" an das MyFaces-Projekt der Apache Software Foundation übergeben. Diese durchläuft nun den Inkubator-Prozess, in dessen Rahmen geprüft wird, ob diese Bibliothek den Apache-Bestimmungen entspricht und in das Projekt aufgenommen werden kann. Mentor in diesem Prozess ist übrigens Craig McClanahan, bekannt als Begründer des Struts-Projektes und als Leiter der Spezifikationsgruppe für die JavaServer Faces-Spec. Zum ADF-Proposal geht's hier:
http://wiki.apache.org/myfaces/adfproposal - Sicher lohnt sich auch ein Blick auf Yahoos unter der freien BSD-Lizenz veröffentlichte UI-Bibliothek, auch wenn diese nichts mit Java zu tun hat. Aber Tree-, Kalender- oder Drag und Drop-Controls für Web-Oberflächen werden natürlich auch in javabasierten Webprojekten benötigt. Auf den hier vorhandenen HTML- und JavaScript-Code zurückgreifen zu können, erleichtert die Webentwicklung wesentlich. Die UI-Bibliothek und ein Link zu den UI-Pattern von Yahoo gibt's hier:
http://developer.yahoo.net/yui - Last but not least kommt Mustang (Codename für Java 6) weiter voran. Nachdem SUN das Entwicklungsmodell umgestellt und bereits seit einiger Zeit wöchentliche Snapshots der jeweiligen Mustang-Codebasis veröffentlicht hat, ist nun die erste Beta veröffentlicht worden. Die Highlights betreffen vor allem die Desktop-Entwicklung mit Swing, aber auch Verbesserungen bei den Java Management Extensions und bei Webservices. Interessierte finden die Beta hier:
http://java.sun.com/javase/6/download.jsp
Die wöchentlichen Snapshots und die Projektseite zu Mustang hier:
https://mustang.dev.java.net
Nun aber erstmal viel Spaß mit dem zweiten Teil unserer Artikel-Serie zu Jakarta Commons.
Wolfram Rittmeyer
Commons Lang
Nachdem ich im letzten Newsletter in die Geschichte und Motivation des Jakarta Commons-Projektes eingeführt habe, werde ich in diesem Newsletter "Commons Lang" vorstellen, das im wesentlichen einige Lücken des JDK abdeckt. In erster Linie werden im Folgenden nützliche Ergänzungen zu den APIs aus java.lang und java.util besprochen.
In den nächsten Newslettern folgen dann weitere Beiträge zu anderen im Webumfeld wichtigen Commons-Subprojekten, wie bspw. BeanUtils, FileUpload, Logging oder Validator.
Da hier nur Ausschnitte der gesamten API vorgestellt werden können, lohnt sich unbedingt ein zusätzlicher Blick auf die Website des Projekts und die Javadoc-Seite zu Commons Lang.
Builder-Klassen
Eine der langweiligsten Routine-Aufgaben in jedem Projekt, ja in nahezu jeder Klasse ist das Überschreiben der MethodentoString(), hashCode() und equals(Object). Dabei wird toString() zumeist für Logging-Ausgaben verwendet, ein Überschreiben ist somit optional, wohingegen man hashCode() und equals(Object) nahezu immer überschreiben sollte. Für diese drei Methoden kann man nun die Klassen ToStringBuilder, HashCodeBuilder und EqualsBuilder verwenden. Alle drei Klassen sind ähnlich aufgebaut, hier soll daher am Beispiel der EqualsBuilder-Klasse kurz das Vorgehen skizziert werden. Als jeweils einfachste Möglichkeit bietet ToEqualsBuilder statische Methoden, die mittels Reflection alle Attribute einer Klasse berücksichtigen und zum Vergleich zweier Objekte heranziehen.
Dazu verwendet man einfach:
return EqualsBuilder.reflectionEquals(this, obj);
}
Diese Methode
reflectionEquals() ist zudem überlagert, damit man angeben kann, ob auch transiente Felder und bis zu welcher Super-Klasse Attribute per Reflection berücksichtigt werden sollen. In der oben abgebildeten Methode, die die entsprechenden Parameter nicht nutzt, werden alle Attribute aller Super-Klassen berücksichtigt, aber keinerlei transienten Attribute. Bei equals(Object) und hashCode() reicht dies meistens aus, beim ToStringBuilder möchte man hingegen häufig feingranularer angeben, welche Attribute in der Ausgabe berücksichtigt werden sollen. Dazu gibt es überlagerte
append()-Methoden, mit denen einzelne Attribute einem ToStringBuilder-Objekt hinzugefügt werden können. Dazu nehmen wir ein imaginäres Mail-Objekt und geben nur die Werte für die Empfänger-E-Mail-Adresse und den Empfängernamen aus: ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
builder.append(senderName);
builder.append(senderMail);
return builder.toString();
}
Wie man sieht, kann man bei der ToStringBuilder-Klasse auch noch das Ausgabeformat mithilfe eines ToStringStyle-Objektes definieren. Diese Klasse kommt mit einigen vordefinierten Styles als statische Konstanten, die für die meisten Fälle ausreichen dürften.
Im Package
org.apache.commons.lang.builder gibt es zusätzlich noch eine Klasse CompareToBuilder, mit deren Hilfe ebenfalls sehr einfach und nach dem gleichen Schema Implementierungen der Methode compareTo(Object) des java.lang.Comparable-Interfaces vorgenommen werden können. Utils-Klassen
Dasorg.apache.commons.lang-Package enthält eine Menge Utils-Klassen, wie bspw. ArrayUtils, BooleanUtils , StringUtils oder WordUtils. Hier können nicht alle besprochen werden und auch nicht alle Methoden dieser Klassen. Für Webanwendungen sind v.a. Methoden aus StringUtils und StringEscapeUtils nützlich, weswegen wir uns hier hauptsächlich mit diesen beiden Klassen beschäftigen. Mit StringEscapeUtils kann man v.a. Strings, die HTML-Zeichen enthalten, für die Darstellung innerhalb von HTML aufarbeiten - entsprechendes gilt für XML. S. dazu folgendes Code-Beispiel:
String name = "O'Reilly";
// notice the small difference with special characters
System.out.println(StringEscapeUtils.escapeHtml(someHtml));
System.out.println(StringEscapeUtils.escapeXml(someHtml));
// you should always SQL-escape any user-input before
// querying the database; otherwise you might be susceptible to
// SQL-injection.
// Of course PreparedStatements are another (and even better) way to be on the safe side...
System.out.println("SELECT * from users WHERE lastname = '" + StringEscapeUtils.escapeSql(name) + "'");
StringUtils bietet wiederum etliche Methoden, die auch in der Klasse
java.lang.String enthalten sind, allerdings auf eine null-sichere Art und Weise. Zusätzlich finden sich einige nützliche Methoden, um bspw. herauszufinden, ob ein String numerisch ist, oder um Strings auf eine angegebene Länge aufzufüllen. Häufig nutzbar ist der Test isEmpty(String), der prüft, ob der übergebene String ein Leerstring oder null ist, ebenso die selbsterklärenden Prüfungen isNumeric(String), isAlphanumeric(String), isAlpha(String) oder isBlank(String), wobei bei der letzteren Methode noch die Besonderheit gilt, dass null hier true ergeben würde. Ein paar Beispiele:
System.out.println(StringUtils.difference(testString1, "this is no") + " - difference");
System.out.println(StringUtils.contains(testString1, null) + " - contains");
System.out.println(StringUtils.contains(testString1, "is") + " - contains");
System.out.println(StringUtils.contains(null, testString1) + " - contains");
System.out.println();
System.out.println(StringUtils.isNumeric("123") + " - isNumeric(\"123\")");
System.out.println(StringUtils.isNotEmpty("123") + " - isNotEmpty(\"123\")");
System.out.println(StringUtils.isAlpha("123") + " - isAlpha(\"123\")");
System.out.println(StringUtils.isAlphanumeric("a2c") + " - isAlphanumeric(\"a2c\")");
System.out.println();
System.out.println(StringUtils.center("shortString", 20, '.') + " - center(\"shortString\")");
System.out.println(StringUtils.leftPad("shortString", 20, '.') + " - leftPad(\"shortString\")");
System.out.println();
String[] testArray1 = {"the", "restaurant", "at", "the", "end", "of", "the", "universe"};
System.out.println(StringUtils.join(testArray1, ' ') + " - join");
Eine weitere nützliche Klasse ist die Klasse RandomStringUtils. Wenn wir bspw. eine Website erstellen, bei der einem Nutzer nach der Registrierung erstmalig ein zufälliges Einmal-Passwort zugesendet werden soll, können wir dieses sehr bequem mit dieser Klasse erzeugen. Dabei kann man die gewünschte Länge des Zufall-Strings ebenso angeben wie, ob es sich um einen rein alphanumerischen, numerischen oder alphabetischen String handeln soll. Das folgende Beispiel spricht dabei für sich:
// of characters (true) but not of numbers (false) and has a length of 10
System.out.println(RandomStringUtils.random(10, true, false));
// the second method generates a String consisting of ASCII
// characters only - including special characters
System.out.println(RandomStringUtils.randomAscii(10));
// the next one generates a plain alphanumeric String of given length
System.out.println(RandomStringUtils.randomAlphanumeric(10));
Weitere Klassen in dem Bereich sind ArrayUtils, BooleanUtils, CharSetUtils, ClassUtils, ObjectUtils, SystemUtils und WordUtils. Mit Ausnahme von ArrayUtils halte ich diese aber für weniger nützlich. Mehr dazu - wie immer - in der API.
Range-Klassen
Die verschiedenen Range-Klassen aus dem Packageorg.apache.commons.lang.math (plus CharRange aus org.apache.commons.lang) bieten alle nahezu die gleiche Funktionalität. Ein Range-Objekt umfasst immer ein geschlossenes Intervall zwischen einem unteren und einem oberen Wert. Mittels Test-Methoden kann man nun prüfen, ob Werte innerhalb des Intervalls liegen, ob ein anderes Intervall innerhalb dieses Intervalls liegt oder ob es Überlappungen gibt. Ein kurzes Beispiel macht am ehesten deutlich, worum es geht:
Range intRange = new IntRange(-3, 6);
// should print true:
System.out.println("is 6 contained in intRange: "
+ intRange.containsInteger(6));
// should print false:
System.out.println("is 6 contained in doubleRange: "
+ doubleRange.containsInteger(7));
// should print false:
System.out.println("doubleRange contains intRange: "
+ doubleRange.containsRange(intRange));
// should print true:
System.out.println("intRange overlaps doubleRange: "
+ intRange.overlapsRange(doubleRange));
// this test should print the same result again:
System.out.println("doubleRange overlaps intRange: "
+ doubleRange.overlapsRange(intRange));
System.out.println("doubleRange: " + doubleRange);
System.out.println("intRange: " + intRange);
CharRange ist vom Konzept her ebenfalls ein geschlossenes Intervall, bietet allerdings weniger Methoden.
Die Nützlichkeit dieser Klassen liegt eher bspw. bei der Prüfung von Messreihen-Ergebnissen etc. als in Webanwendungen, könnte aber u.U. auch bei der Validierung von Eingabedaten herangezogen werden.
Datums- und Zeit-Hilfsklassen
Das Arbeiten mit den Standard-Datums- und Zeit-Objekten ausjava.util, seien es nun Calendar- oder Date-Objekte, ist schlicht und ergreifend ein Graus. In Date sind nahezu alle Methoden als "deprecated" gekennzeichnet, und der unschöne Zugriff auf den Status der Calendar-Objekte mittels Konstanten macht das Arbeiten mit Datums- und Zeitwerten auch nicht gerade angenehmer. Konzepte wie Zeitperioden, Intervalle oder die Dauer zwischen zwei Ereignissen fehlen völlig. Ein paar Schwächen werden mithilfe der Klassen des Pakets
org.apache.commons.lang.time gemildert. So enthält die Klasse DateUtils praktische Methoden zum Vergleich zweier Datumswerte und zum Runden und Abschneiden von Werten. Das Runden und Abschneiden macht sich dabei an den Konstanten der Calendar-Klasse fest, wie im folgenden Beispiel gezeigt: FastDateFormat formatter = FastDateFormat.
getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM, Locale.GERMAN);
// round to four minutes to the turn of the year
Calendar beforeTurnOfYear = DateUtils.round(cal, Calendar.MINUTE);
// round exactly to the turn of the year
Calendar afterTurnOfYear = DateUtils.round(cal, Calendar.YEAR);
System.out.println(formatter.format(beforeTurnOfYear.getTime()));
System.out.println(formatter.format(afterTurnOfYear.getTime()));
Nützlich sind auch die Konstanten für bestimmte Perioden wie bspw.
MILLIS_PER_DAY, die ein guter Ersatz für unschöne Schreibweisen in der Art von 24 * 60 * 60 * 1000 sind. Die weiteren Methoden sind hingegen eher seltener von Nutzen. Die Klasse FastDateformat des Packages ist im wesentlichen eine threadsichere Reimplementierung der nicht threadsicheren Klasse SimpleDateFormat des JDKs. Interessanter ist die Klasse DateFormatUtils, die statische Methoden zur schnellen String-Formatierung von Date-Objekten ermöglicht. Hierzu wieder ein knappes Beispiel:
Date date = cal.getTime();
Locale locale = Locale.GERMAN;
System.out.println("ISO-Formatted: " +
DateFormatUtils.format(date,
DateFormatUtils.ISO_DATETIME_FORMAT.getPattern(),
locale));
System.out.println("Custom-Formatted: " +
DateFormatUtils.format(date, "dd.MM.yyyy hh:mm ZZ", locale));
Interessant sind die Konstanten für verschiedene ISO-konforme Formatierungen von Datums- und Zeitwerten. Das "ZZ" im selbstformatierten Datum steht übrigens für die ISO-konforme Formatierung der Zeitzonen-Angabe und ist angelehnt an das "Z", das auch das JDK versteht. Die JDK-Formatierung von Zeitzonen-Angaben ist allerdings nicht ISO-konform.
Mit der Version 2.1 von Commons Lang wurde auch eine Klasse DurationFormatUtils neu aufgenommen. Wer hofft, diese würde für Perioden und Dauern Hilfestellungen leisten, wird letztendlich vermutlich enttäuscht sein. M.E ist diese Klasse in ihrer gegenwärtigen Form nicht zu gebrauchen.
Auch wenn Commons Lang einige Schwächen der Datums- und Zeitbehandlung im JDK lindert, bleiben die Grundprobleme dennoch bestehen. Als echte Alternative zum Chaos der Datums- und Zeitumsetzung im JDK erweist sich die Bibliothek Joda-Time, die wir in einem späteren Newsletter vorstellen werden.
Wrapper-Klassen
Abschließend soll noch kurz darauf hingewiesen werden, dass in Commons Lang Neu-Implementierungen der Wrapper-Klassen existieren, die im Unterschied zu den Wrappern ausjava.lang veränderlich sind. Daher auch der Name dieser Wrapper, wie bspw. MutableDouble. Allerdings sind diese Wrapper in ihrer Funktionalität nicht so umfangreich wie die Wrapper der Standard-Java-Bibliothek. Sie machen also v.a. dann Sinn, wenn man bspw. eine Datenstruktur hat, die nur mit Objekten umgehen kann, wie bspw. eine List. Wenn es nun innerhalb dieser List zu Veränderungen kommt, ist ein solcher MutableWrapper besser. Mit dem klassischen Wrapper Double müsste man erst das Objekt aus der Liste entfernen und dann ein neues Double-Objekt an der geeigneten Stelle wieder einfügen. Je nach Implementierung der Liste kann dies bspw. eine kostspielige Operation (ArrayList) oder vergleichsweise kostengünstig realisierbar sein (LinkedList). Meiner Meinung nach ist hier besser die LinkedList mit dem klassischen Wrapper (bzw. ab Java 5 dem Autoboxing) zu verwenden als die Wrapper aus Commons Lang. Dennoch ist deren Existenz gelegentlich hilfreich, und man sollte das Wissen darüber durchaus im Hinterkopf behalten. Mit dem hier Besprochenen habe ich die Klassen aus Commons Lang vorgestellt, die meiner Meinung nach besonders hilfreich sind. Diese Auswahl ist natürlich unvollständig. Zudem geht die Entwicklung im Commons-Projekt zügig vonstatten. Daher sei nochmals darauf hingewiesen, dass es sich unbedingt lohnt, sich die API anzuschauen.
Buchbesprechung
Bei den beiden Büchern, die wir dieses mal besprechen, geht es um zwei Themen, die zentraler Bestandteil jeder webbasierten Anwendung sind. In dem Buch "Hibernate in Action" von Christian Bauer und Gavin King geht es um die Realisierung der Persistenzschicht mithilfe des objektrelationalen Frameworks Hibernate. Auf der anderen Seite behandelt das Buch "Der CSS Problemlöser" von Rachel Andrew die Oberflächenschicht. Sie liefert knappe Tipps zur Lösung immer wiederkehrender Probleme, auf die Webentwicklerinnen bei der Formatierung der Oberfläche mithilfe von CSS stoßen.

