Mit ‘Apache Commons’ getaggte Artikel

(De-)Serialisation von Objekten in einen String (Base64 encoded)

Dienstag, 23. Juni 2009

Da es in Java keine direkte Möglichkeit gibt Binärdaten in einen String zu schreiben, kann man hierzu auf die Apache Commons Bibliothek codec zurück greifen.

Diese bietet unteranderem auch eine BASE64-Konvertierung an, die die Datenmenge zwar um ca. 33% größer werden, dann aber problemlos als Zeichenkette weiterverarbeiten lässt.

Im folgenden Beispiel wird ein konkreter Anwendungsfall gezeigt, im dem ein Objekt der Klasse SerienTerminForm mittels ObjectOutputStream serialisiert wird, bevor aus den BASE64-kodierten Daten ein neuer String erzeugt wird.

Natürlich lässt sich die Objekt-Serialisierung auch auf jede andere Klasse anpassen, die die Schnittstelle Serializable implementiert.

        /**
         * @param form
         *            the form data to serialize
         * @return the serialized string (Base64 encoded)
         */
        public static String serializeInfo(final SerienTerminForm form) {
            try {
                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
                final ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(form);
                final String info = new String(new Base64().encode(baos.toByteArray()));
                return info;
            } catch (Exception e) {
                LOGGER.warn("SerienTerminForm not serializable");
                return null;
            }
        }

Hier nun auch der Rückweg aus den in der Zeichenkette gespeicherten Objektinformationen wieder eine lauffähige Instanz zu machen. In diesem Beispiel werden auftretende Ausnahmen nur abgefangen und geloggt, aber immer eine Objekt-Instanz erzeugt.

        /**
         * @param info
         *            the serialized string (Base64 encoded)
         * @return a {@link SerienTerminForm} instance (<code>null</code>-safe)
         */
        public static SerienTerminForm deserializeInfo(final String info) {
            SerienTerminForm form;
            try {
                byte[] bytes = new Base64().decode(info.getBytes());
                final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
                form = (SerienTerminForm) ois.readObject();
            } catch (Exception e) {
                LOGGER.warn("SerienTerminForm not deserializable", e);
                form = new SerienTerminForm();
            }
            return form;
        }

Hendrik Lange


DateTime Handling mit apache.commons.lang

Freitag, 23. Januar 2009

Das Arbeiten mit Kalender, Daten und Uhrzeiten ist ein komplexes Thema, dass von der Standard Java API abgedeckt wird. Allerdings sind gängige Aufgaben mit Kalenderdaten etwas umständlich zu lösen.

Das Apache Commons Projekt bietet eine Vielzahl von wiederverwendbaren Komponenten für die Java-Entwicklung. Das Teilprojekt apache.commons.lang bietet Ergänzungen zum Arbeiten mit den Basisklassen von Java. Dieser Eintrag betrachtet die Klassen des Pakets commons.lang.time.

Angenommende Aufgabe: Ermitteln des Datums von morgen, aber ohne Stunden/Minuten/Sekunden Angabe. Folgend nun eine Gegenüberstellung der Standard-Java und der Apache-Commons Lösung:

// Jan 22 10:30:06 CET 2009 -> Jan 23 00:00:00 CET 2009
Calendar calendar = GregorianCalendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
Date tomorrowTrunc = calendar.getTime();
// Jan 22 10:30:06 CET 2009 -> Jan 23 00:00:00 CET 2009
Date d1 = DateUtils.addDays(new Date(), 1);
Date tomorrowTrunc = DateUtils.truncate(d1, Calendar.DATE);

Weiterhin bringt die Klasse DurationFormatUtils simplifizierte statische Methoden zur Formatierung von Laufzeiten.

DurationFormatUtils.formatDurationWords(415486341, false, false); 
// 4 days 19 hours 24 minutes 46 seconds
DurationFormatUtils.formatDuration(415486341, "HH:mm:ss");
// 115:24:46
DurationFormatUtils.formatDurationHMS(415486341);
// 115:24:46.341
DurationFormatUtils.formatPeriod(new Date().getTime(), tomorrowTrunc.getTime(), "HH:mm");
// 10:34

Um eine flexible und schnelle Benutzereingabe eines Datums zuzulassen eignet sich nur ein Textfeld mit freier Texteingabe. Allerdings kann eine solche Eingabe sehr unterschiedlich ausfallen. Das folgende Beispiel demonstriert das Wandeln einer Benutzereingabe(String) in ein Datum(Date).

String trimmedUserInput = "   7.9 ".trim();
String[] datePatterns = {"dd.MM.", "ddMM", "dd.MM"};
String[] datePatternsWithYear = {"EE, dd.MM.yyyy", "dd.MM.yyyy", "ddMMyyyy"};
Date date = null;
try {
   date = DateUtils.parseDate(trimmedUserInput, datePatternsWithYear);
} catch (ParseException e) {
   try {
      date = DateUtils.parseDate(trimmedUserInput, datePatterns);
      date = DateUtils.setYears(date, GregorianCalendar.getInstance().get(Calendar.YEAR));
   } catch (ParseException e1) {
      // Not a parseable date string
      e1.printStackTrace();
   }
}

Als Eingabe sind viele verschiedene Formate gültig, z.B.: 0612, 6.12, 06.12., 06122009, … Alle diese Eingaben werden in ein gültiges Datum gewandelt: Sun Dec 06 00:00:00 CET 2009. Die Unterscheidung in zwei unterschiedliche Patterntypen erfolgt, da die parse-Methode bei fehlender Jahresangabe im Pattern leider nicht das aktuelle Jahr verwendet. Daher muss dieses nachträglich gesetzt werden.
Die Möglichkeit der parse-Methode mehrere Patterns zu übergeben, macht das Parsen von Benutzereingaben sehr komfortabel, besonders wenn verschiedene Locale-Standards, zum Beispiel neben dem deutschen auch das amerikanische Format (MM/dd/), erlaubt seien sollen.


Jan Kuenstler


Apache Commons-IO im praktischen Einsatz

Montag, 08. September 2008

Die Projekte aus der Apache Commons Reihe erleichtern dem Programmierer oft den Alltag, da sie für sehr viele Situationen Standardroutinen anbieten.

Hier wollen wir uns einige Anwendungsbeispiele der Commons IO Util-Klassen anschauen. Die gängigsten sind hier FileUtils, IOUtils und FilenameUtils.

Beim Umgang mit Textdateien z.B. XML ist das Encoding beim einlesen und schreiben von großer Bedeutung. Ansonsten werden Umlaute und Sonderzeichen nicht richtig dargestellt. Commons IO bietet hierfür viele read/write Methoden mit optionaler Angabe des Encoding an.
Zum Thema Encoding wird sicherlich noch ein extra Blog folgen: ein Gebiet, dass einen bei sämtlichen Datei-, Datenbank- usw. Zugriffen verfolgt.

Textdatei in einen String einlesen

Die Methode FileUtils.readFileToString liefert den Textinhalt einer Datei als String oder einen leeren String (nicht null). Im Fehlerfall wird eine java.io.IOException geworfen.

// use system default encoding
String text = FileUtils.readFileToString(
    new File("text.txt"));
// set encoding to UTF-8
String textUTF8 = FileUtils.readFileToString(
    new File("text-utf8.txt"),
    org.apache.commons.lang.CharEncoding.UTF_8);

Binärdatei einlesen

Ebenso einfach lässt sich eine Binärdatei einlesen.

byte[] bytes = FileUtils.readFileToByteArray(
    new File("file.bin"));

String als Textdatei speichern

Das Speichern einer Textdatei wird zum Einzeiler:

String content = "Das ist ein Text";
FileUtils.writeStringToFile(new File("text.txt"), content);

Mit Angabe des Encoding (UTF-8):

String content = "Das ist ein Text mit Umlauten: äöü ÄÖÜ";
FileUtils.writeStringToFile(new File("text.txt"), content,
    org.apache.commons.lang.CharEncoding.UTF_8);

Als Variation kann noch eine Collection in eine Textdatei geschrieben werden:

List lines = new ArrayList();
// befüllen der List ...
FileUtils.writeLines(new File("lines.txt"), lines);

Analog funktioniert das Ganze genau so einfach mit Binärdateien.

Dateiliste anhand der Endung erstellen

Anwendungsfall: In einem Verzeichnis liegen viele XML und PDF Dateien. Nun sollen alle XML Dateien nacheinander geparsed werden. Die Methode FileUtils.listFiles erwartet als Parameter das Verzeichnis, in dem sich die Dateien befinden. Ein Array der Dateiendungen, die eingelesen werden sollen. Ein Flag ob das Verzeichnis rekursiv durchsucht werden soll.

Collection files = FileUtils.listFiles(
    new File("/dir"),
    new String [] {"xml"},
    false);
for (File file : files) {
    // parse file
}

Textdatei Zeilenweise auslesen

Anwendungsfall: Eine csv Datei wird Zeilenweise eingelesen und verarbeitet.

List lines = FileUtils.readLines(
    new File("data.csv"),
    org.apache.commons.lang.CharEncoding.UTF_8);
for (String string : lines) {
    // process single line
}

Streams/Reader/Writer schliessen

Auch ein sehr häufiger Anwendungsfall ist das Schließen von Streams usw.
wobei vorher auf Null geprüft wird und eine Exception gefangen und in der
Regel ignoriert wird.

Vorher:

Writer writer = null;
try {
    writer = new FileWriter("filename.txt");
    // ...
} catch (Exception e) {
    // handle exception
} finally {
    if (writer != null) {
        try {
            writer.close();
        } catch (IOException e) {
            // ignore
        }
    }
}

Nachher:

Writer writer = null;
try {
    writer = new FileWriter("filename.txt");
    // ...
} catch (Exception e) {
    // handle exception
} finally {
    IOUtils.closeQuietly(writer);
}

IOUtils Kurzübersicht

Weitere Methoden der IOUtlis dienen u.A. zum vergleichen und kopieren von Dateien,
erzeugen von Streams und schreiben in Streams.

FilenameUtils Kurzübersicht

Wie der Name erahnen lässt dienen die Methoden dieser Klasse zur Auswertung und Manipulation
von Dateiname. Häufig verwendet:

FilenameUtils.getBaseName(filename);
FilenameUtils.getExtension(filename);
FilenameUtils.removeExtension(filename);

Ein Blick in die Javadocs lohnt sich hier auf alle Fälle.

Viel Spaß und Erfolg beim Ausprobieren und Anwenden.


Christof Aenderl