Mittwoch, 29. April 2009

@SuppressWarnings("unchecked")

Seit Version 5.0 kennt Java das Konzept der Generics, mit denen u.a. Kollektionen mit Typen parametrisiert und typsicherer gemacht werden können.

Leider lassen sich damit nicht alle Stellen perfekt absichern – insbesondere bei Hibernate bzw. JPA wird man beim Ausführen von Abfragen (Queries) immer wieder auf "Type safety"-Compiler-Warnungen stoßen:

An sich ist das kein Problem, denn mit der bestehenden API gibt es für den Compiler keine sinnvolle Möglichkeit, vom Query-String auf den Ergebnis-Typ zu schließen. Und Entwicklungsumgebungen wie Eclipse bieten die einfache Lösung an, diese unumgängliche Warnung mit der @SuppressWarnings-Annotation zu unterdrücken.

Nur leider schreibt Eclipse die Annotation immer vor die umgebende Methode, was alle weiteren – vielleicht wichtigen – Typ-Warnungen in der Methode ebenfalls unterdrückt:
@SuppressWarnings("unchecked")
public List<Benutzer> findAllBenutzer() {
Query q = session.createQuery("from Benutzer");
return q.list();
}
Das muss nicht sein! Sehen wir uns dazu den Quelltext der @SuppressWarnings-Annotation an:
@Target({TYPE, FIELD, METHOD, PARAMETER,
CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
LOCAL_VARIABLE bedeutet, dass wir die Annotation auch an eine lokale Variablendeklaration schreiben dürfen. Wenn wir also in der Methode eine temporäre Variable einführen, können wir das Unterdrücken der Warnung auf die eine relevante Zeile beschränken:
public List<Benutzer> findAllBenutzer() {
Query q = session.createQuery("from Benutzer");

@SuppressWarnings("unchecked")
List<Benutzer> alle = q.list();

return alle;
}
Für die JPA-Query-Methoden getResultList() und getSingleResult() gilt dies entsprechend.

Dienstag, 21. April 2009

Oracle kauft Sun

Nun ist es also geschehen: Sun wird verkauft, und nicht etwa an IBM (die Übernahmeverhandlungen waren erst kürzlich gescheitert), sondern an Oracle.

Was dies für die Java-Technologie und die diversen Sun-Java-Produkte konkret bedeutet, bleibt abzuwarten. Immerhin ist Java seit langem gut in bestehende Oracle-Produkte integriert und dürfte für Oracle auch weiterhin von strategischer Bedeutung sein.

Spannend wird es für Suns Application Server GlassFish, der sich seit über einem Jahr deutlich steigender Beliebtheit erfreut. Hier ist zu hoffen, dass Oracle das Potential dieses nun dritten Application Servers im Produktportfolio kennt und nutzt. Berechtigt ist diese Hoffnung allemal, schließlich hat Oracle TopLink Essentials als JPA-Referenzimplementation zu GlassFish beigesteuert.

Mittwoch, 8. April 2009

Serialisierbarer ByteArrayOutputStream

Ein typisches Problem in einer mehrschichtigen Web-Anwendung besteht darin, eine Datei (beispielsweise ein auf dem Server generiertes PDF-Dokument) zum Client/Web-Frontend zu übertragen (oder nach einem Datei-Upload entsprechend umgekehrt). Die einfache Lösung mit Streams schlägt fehl, sobald Sender und Empfänger nicht mehr lokal in derselben Java Virtual Machine laufen, sondern entfernt in verschiedenen JVMs. Die Streams müssten nun entweder entfernt aufrufbar (Remote) oder aber serialisierbar (Serializable) sein – beides sind sie nicht. Die Serialisierbarkeit ist standardmäßig nicht vorgesehen, da Streams geräteabhängig sind.

Eine einfache Lösung für das Versenden kleinerer Binärdateien (wenige hundert KB) macht den Standard-ByteArrayOutputStream serialisierbar, was kein Problem ist, da der ByteArrayOutputStream eine reine Java-Speicher-Implementierung und somit geräteunabhängig ist. Der Sender kann den Stream wie gewohnt mit write() füllen, und der Empfänger kann den Stream mit den üblichen Methoden auswerten. In einem Servlet kann man den Stream z.B. mit baos.writeTo(response.getOutputStream()) direkt zum Client schicken.

Für die Implementierung des serialisierbaren ByteArrayOutputStreams reicht es nicht aus, die neue Unterklasse das Serializable-Interface implementieren zu lassen. Man muss zusätzlich noch die Methoden writeObject() und readObject() implementieren, die den internen Stream-Puffer explizit serialisieren:

public class SerializableByteArrayOutputStream
extends ByteArrayOutputStream
implements Serializable {

private void writeObject(ObjectOutputStream stream)
throws IOException {
stream.writeInt( count );
stream.writeObject( buf );
}

private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
count = stream.readInt();
buf = (byte[]) stream.readObject();
}
}

Der vollständige Quelltext der Klasse
SerializableByteArrayOutputStream kann hier abgerufen werden.

Wer eine umfassendere Lösung sucht oder große Dateien übertragen muss, sollte sich die RMIIO-Bibliothek anschauen.

Mittwoch, 1. April 2009

Proxy-Konfiguration für VisualVM

Wenn man mit VisualVM in einem Netzwerk arbeitet, das über einen Proxy ans Internet angebunden ist, hat VisualVM Probleme mit dem Nachladen seiner Plugins – was schade ist, denn dadurch verschenkt man die Hälfte der Möglichkeiten.

Der Proxy kann leider nicht in VisualVM selbst konfiguriert werden, sondern muss mit Aufrufparametern übergeben werden:

visualvm -J-DproxyHost=mein.proxy.com -J-DproxyPort=1234

Aber Achtung: Dies sind nicht die üblichen Java-Proxy-Parameter http.proxyHost und http.proxyPort! Das "http." fehlt jeweils.

Außerdem scheint die Konfiguration mit dem im JDK 6 mitgelieferten VisualVM nicht zu funktionieren. Mit dem separat herunterladbaren VisualVM 1.1.1 klappt es aber wie gewünscht.