Donnerstag, 22. Dezember 2011

JAXB und HashMaps

Die Technik ist zwar schon älter, aber vielleicht ist folgender Tipp trotzdem nützlich – zumal die entsprechende JAXB-Eigenschaft nicht sonderlich gut dokumentiert ist.

Wenn man Schlüssel-Wert-Paare aus einer XML-Datei in eine HashMap einlesen soll, ist das recht einfach, sofern man Einfluss auf die Struktur des XML-Dokuments hat und dieses in etwa so aussehen kann:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<meineEntitaet>
<paarliste>
<entry>
<key>MeinSchlüssel</key>
<value>MeinWert</value>
</entry>
<entry>
<key>...</key>
<value>...</value>
</entry>
...
</paarliste>
</meineEntitaet>

Die zugehörige Klasse muss dann wie folgt aufgebaut und mit JAXB-Annotationen versehen sein:
import java.util.*;
import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class MeineEntitaet {

@XmlElementWrapper(name="paarliste")
private Map<String,String> map
= new HashMap<String,String>();

public Map<String,String> getMap() {
return map;
}

public void setMap(Map<String,String> map) {
this.map = map;
}
}

Die XML-Datei lässt sich dann mit folgender Anweisung in ein Objekt einlesen:
MeineEntitaet obj =
JAXB.unmarshal(
new File("beispiel.xml"),
MeineEntitaet.class);
System.out.println( obj.getMap() );

Genauso einfach lässt sich natürlich auch eine HashMap als XML schreiben:
MeineEntitaet obj = new MeineEntitaet();
obj.getMap().put("MeinSchlüssel", "MeinWert");
// JAXB.marshal(obj, System.out);
JAXB.marshal(obj, new File("beispiel.xml"));

Während man den XML-Element-Namen der Schlüssel-Wert-Paare-Liste mit der Annotation @XmlElementWrapper konfigurieren kann, sind die Elemente entry, key und value fest benannt. Um auch diese Namen zu verändern, muss man einen eigenen XmlAdapter implementieren.

Montag, 28. November 2011

Message Driven Beans anhalten und starten

Message Driven Beans (MDBs) werden meistens schon aktiv (d.h. vom Container aufgerufen, wenn über JMS Nachrichten für die Bean vorhanden sind), während das Deployment der Anwendung noch läuft. Das ist normalerweise unkritisch, aber es gibt Situationen, in denen man sicherstellen möchte/muss, dass die MDBs erst nach vollständigem Deployment (oder irgend einem anderen definierten Zeitpunkt) aktiv werden.

Nahezu jedes Messaging-System (JMS-Implementierung) bietet die Möglichkeit, über produktspezifische API oder externe Kommandos eine Queue anzuhalten und später wieder zu starten. Das betrifft aber die komplette Queue, d.h. es ist dann auch nicht mehr möglich, Nachrichten in die Queue zu schreiben.

Bei einigen Messaging-Systeme kann man daher via JMX die MBean einer MDB ermitteln und damit tatsächlich nur die MDB anhalten – also die Zustellung aus der Queue unterbinden. Das Schreiben in die Queue wäre dann immer noch möglich. (Das Problem, wie damit die MDB gleich zu Beginn des Deployments gestoppt wird, lassen wir mal außen vor.)

Leider sind beide Lösungen produktspezifisch. Eine portable Lösung ist bis inkl. Java EE 6 für Message Driven Beans nicht vorgesehen. Wie könnte eine selbst gebaute, portable Lösung aussehen?

Java SE 5 hat im Paket java.util.concurrent viele neue Klassen rund um Thread-Synchronisation und Sperren in die Java-Welt gebracht. Für unsere Zwecke ist ein CountDownLatch geeignet:

import java.util.concurrent.CountDownLatch;

@Singleton
public class AppStatus {

private static final CountDownLatch startSignal
= new CountDownLatch(1);

public void waitForAppInitialized() {
try {
startSignal.await();
} catch (InterruptedException e) {
throw new RuntimeException(
"Fehler beim Warten auf Sperren-Freigabe", e);
}
}

public void setAppIsInitialized() {
startSignal.countDown();
}

public boolean isAppInitialized() {
return (startSignal.getCount() == 0L);
}
}

Der CountDownLatch ist zu Beginn gesperrt. Zur Freigabe muss die Anwendung zu einem geeigneten Zeitpunkt die Methode setAppIsInitialized() aufrufen, beispielsweise in einem ServletContextListener oder in einem EJB-Timer.

Die zu blockierende MDB wartet dann bei jedem Aufruf zunächst darauf, ob die Sperre schon freigegeben wurde:

@MessageDriven
public class StartbareMDB implements MessageListener {

@EJB
private AppStatus status;

public void onMessage(Message msg) {

status.waitForAppInitialized();

// ... der eigentliche MDB-Code ...
}
}
Eine einfache, funktionierende Lösung mit Java-Bordmitteln.

Nachteil dieser Lösung ist, dass hier Threads des Containers blockiert werden. Man muss aufpassen, dass die Maximalzahl der blockierten MDB-Threads den Threadpool nicht überfordert (was ein Grund ist, warum man in Java EE-Containern nicht mit Threads herumspielen soll).

Außerdem ist der Performanceverlust nicht riesig, aber messbar. Für Höchstlastsysteme ist das also evtl. nicht geeignet. Zumal dort oft Clustering zum Einsatz kommt – und obige Lösung ist nur clusterfähig, wenn man sicherstellen kann, dass die Freigabe-Methode auf jedem Cluster-Knoten aufgerufen wird.

Als weitere (bewusste) Einschränkung ist zu beachten, dass der CountDownLatch nur einmalig nutzbar ist, er dient also wirklich nur als Start-Signal. Wenn man Message Driven Beans beliebig anhalten und starten möchte, muss man andere Sperren verwenden.

Fazit: Die hier vorgestellte Lösung funktioniert in vielen praktischen Situationen gut. Aber gibt es andere Ideen / Lösungen, die ähnlich einfach zu realisieren sind, die gezeigten Einschränkungen aber nicht aufweisen? Oder müssen wir auf Java EE 7 (oder eher 8) warten?

Donnerstag, 24. November 2011

Java für Mac OS X 10.7 "Lion"

Da Apple vor kurzem Java-Updates für Mac OS X veröffentlicht hat, gibt es an dieser Stelle eine kleine Erinnerung an die wichtige Änderung, die mit Mac OS X 10.7 "Lion" eingeführt wurde: Im Gegensatz zu allen anderen Mac OS X-Versionen davor (*) ist keine Java-Laufzeitumgebung mehr vorinstalliert.
(* Von der 10.0-Public Beta, die wir uns 2000 von der Apple Expo Paris mitgenommen haben, mal abgesehen.)

Beim ersten Aufruf einer Java-Anwendung erhält man einen Hinweis, dass die Java-Runtime benötigt wird. Bestätigt man dies, wird Java SE 6 heruntergeladen und installiert. Danach wird die ursprünglich aufgerufene Java-Anwendung gestartet.

Das aktuelle Java-Update von Apple enthält Java 1.6.0_29 und ist damit zeitlich erfreulich nah an Oracles Java 6-Update für andere Systeme. Java 7, für andere Systeme mittlerweile als erstes Update freigegeben, ist leider immer noch nicht final für Mac OS X verfügbar (immerhin gibt es eine Developer Preview von Oracle). Java 8, was sich derzeit noch in der Entwicklung befindet, ist aber schon als Build des OpenJDK 8 für Mac OS X erhältlich.

Dienstag, 25. Oktober 2011

Zeitdauern berechnen mit TimeUnit

Auf java.net ist gerade ein netter Blog-Artikel verlinkt, der das java.util.concurrent-Enum TimeUnit vorstellt. Aus diesem Anlass eine kleine Idee, wofür ich dieses Enum und seine Umwandlung-Methoden schon gut gebrauchen konnte. Wenn man Zeitdifferenzen berechnet, benötigt man häufiger Konstanten von folgender Art:
public static final long MS_PRO_TAG
= 1L * 24L * 60L * 60L * 1000L;
Das funktioniert zwar, ist aber fehleranfällig, weil man sich bei Anzahl und Wert der Faktoren leicht vertun kann. Mit folgendem Ausdruck kann eine beliebige Anzahl Tage (hier: 1) in Millisekunden umgerechnet werden:
public static final long MS_PRO_TAG
= TimeUnit.DAYS.toMillis(1L);
Ebenso lassen sich Sekunden in Stunden umrechnen etc.:
assert TimeUnit.SECONDS.toHours(7250L) == 2;
Wie der andere Blog-Autor schon schreibt: Eigentlich gehört dieses Enum ins Package java.lang (oder vielleicht noch besser nach java.util).

Donnerstag, 13. Oktober 2011

"reschedule-failed-timer" in GlassFish 3.1

In Stateless Session Beans, Singleton Beans und Message Driven Beans kann der EJB-Timer-Service genutzt werden. Ab EJB 3.1 ist es dabei sehr einfach geworden, wiederkehrende Intervall-Timer mit der @Schedule-Annotation zu definieren (eine programmatische Definition ist natürlich weiterhin möglich).

Wenn ein periodischer Timer eine Exception wirft, wird GlassFish sofort noch zweimal versuchen, die Timer-Methode auszuführen. Werden auch diese Aufrufe mit Exceptions abgebrochen, stoppt GlassFish den Timer komplett, d.h. auch nach Ablauf des nächsten Intervall-Zeitraums erfolgt kein Aufruf mehr! Ich bin mir nicht sicher, ob dieses Verhalten von der EJB-Spezifikation gedeckt ist – intuitiv dürfte man aber vermutlich erwarten, dass Intervall-Timer zuverlässig ausgeführt werden.

Seit GlassFish 3.1 gibt es eine Server-Property "reschedule-failed-timer", mit der dieses Verhalten konfiguriert werden kann. Über das Konfigurationsprogramm asadmin kann man mit

asadmin set configs.config.server-config.ejb-container.ejb-timer-service.property.reschedule-failed-timer=true

einstellen, dass auch per Exception abgebrochene Timer nach Ablauf des nächsten Intervall-Zeitraums wieder aufgerufen werden. Durch den Aufruf wird in domain.xml folgender Abschnitt eingetragen:
<ejb-container session-store="${com.sun.aas.instanceRoot}/session-store">
<ejb-timer-service>
<property name="reschedule-failed-timer" value="true"></property>
</ejb-timer-service>
</ejb-container>
Ohne das Setzen der Property bleibt ansonsten nur, den gesamten Timer-Methoden-Rumpf in try { ... } catch (Exception ...) einzufassen, damit garantiert keine Exception den Timer verlässt.

Mittwoch, 5. Oktober 2011

Oracle JDK7 für Mac OS X (Developer Preview)

Nachdem Java SE 7 schon seit einigen Monaten für Windows, Linux und Solaris verfügbar ist und eine Portierung des OpenJDK 7 für Mac OS X begonnen wurde, bietet Oracle nun eine Vorabversion des JDK 7 für Mac OS X an.

Die Installation ist denkbar einfach: Nach dem Download der DMG-Datei zieht (kopiert) man die enthaltene Paket-Datei einfach ins Verzeichnis /Library/Java/JavaVirtualMachines. Fertig! Das Verzeichnis sollte bereits existieren, wenn man das letzte Apple-Java-Update installiert hat, ansonsten muss man es von Hand anlegen.

Die neue Version wird sofort von den Java-Einstellungen erkannt, ein Neustart des Systems ist nicht notwendig. Anwendungen müssen beendet und neu gestartet werden, um das neue Java nutzen zu können.

much$ /usr/libexec/java_home --version 1.7
/Library/Java/JavaVirtualMachines/JDK 1.7.0 Developer Preview.jdk/Contents/Home

much$ /usr/libexec/java_home -v 1.7 --exec java -version
openjdk version "1.7.0-ea"
OpenJDK Runtime Environment (build 1.7.0-ea-b211)
OpenJDK 64-Bit Server VM (build 21.0-b17, mixed mode)

Donnerstag, 12. Mai 2011

Scala 2.9 und Akka 1.1

Ein halbes Jahr nach der letzten Version wurde heute Scala 2.9 veröffenticht.

Wichtigste Neuerung sind sicherlich die parallelen Kollektionen, die z.B. beim Sortieren, Filtern und Durchlaufen automatisch mehrere Prozessorkerne nutzen. Sofern die dabei ausgeführten Funktionen hinreichend komplex sind, lassen sich durch die Parallelisierung deutliche Geschwindigkeitssteigerungen erreichen. "Normale" sequentielle Kollektionen lassen sich mit einem Methodenaufruf in eine parallele Kollektion umwandeln und umgekehrt.

Hilfreich für die alltägliche Programmierung ist auch der neue App-Trait, der den veralteten, nicht so mächtigen und teilweise problematischen Application-Trait ablöst. Mit dem App-Trait lässt sich auf einfache und elegante Weise der Programm-Einstiegspunkt schreiben – er ist also eine Vereinfachung der main-Methode (die man aber immer noch von Hand programmieren kann).

Diese und viele weitere kleine Verbesserungen sind genauer in der Ankündigung der neuen Version 2.9 beschrieben.

Zeitgleich wurde die Gründung der Firma Typesafe bekannt gegeben, in der sich die Gründer und Entwickler von Scala und Akka zusammentun, um Dienstleistungen rund um die Scala-Technologie anzubieten. Als Berater stehen dieser neuen Firma recht bekannte Java-Größen wie James Gosling und Doug Lea zur Seite. Neben Dienstleistungen bietet Typesafe den Typesafe Stack an, ein vorkonfiguriertes Paket mit Scala 2.9 und Akka 1.1.

Akka ist eine Ereignis-gesteuertes Rahmenwerk, mit dem sich verteilte Anwendungen entwickeln lassen. Die Geschäftslogik bleibt dabei frei von den technischen Aspekten der Parallelisierung, Kommunikation, Ausfallsicherheit etc. – ideal für typische Server-Anwendungen. Bekannt geworden ist Akka durch die eigenen, hochskalierbaren und robusten Aktoren (Actors).

Im Typesafe Stack 1.0 sind sowohl die Aktoren von Scala als auch die von Akka mit ihrer bisherigen Funktionalität enthalten. Für zukünftige Scala-Versionen ist aber wohl geplant, diese Funktionalitäten zusammenzuführen.