<jsptutorial />

Newsletter vom 19.02.2006


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:


Nun aber erstmal viel Spaß mit dem zweiten Teil unserer Artikel-Serie zu Jakarta Commons.

Wolfram Rittmeyer

Zum SeitenanfangZum Seitenanfang

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 Methoden toString(), 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:
public boolean equals(Object obj) {
   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:
public String toString() {
   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

Das org.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 someHtml = "<html><body>Über Umlaute</body></html>";
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:
String testString1 = "this is a simple test string";
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:
// the next method call generates a randomized String which consists
// 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 Package org.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 doubleRange = new DoubleRange(-2, 5);
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 aus java.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:
Calendar cal = new GregorianCalendar(2005, 11, 31, 23, 55, 31);
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:
Calendar cal = new GregorianCalendar(2005, 11, 31, 23, 55, 31);
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 aus java.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.

Zum SeitenanfangZum Seitenanfang

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.

Zum SeitenanfangZum Seitenanfang


www.jsptutorial.org
© 2005, 2006, 2007