Freitag, 27. April 2012

Flashback und Apples Java-Updates

Apples (späte) Antwort auf den "Flashback" Virus/Trojaner waren diverse Java-Updates in schneller Folge (bzw. ein Hilfsprogramm zum Entfernen des Virus für Mac OS X-Systeme ohne installiertes Java, was seit Mac OS X 10.7 "Lion" möglich ist). Sowohl die Java-Updates als auch das Removal-Tool entfernen Flashback, wenn es auf einem System gefunden wird.

Weniger offensichtlich ist, dass durch das letzte der Java-Updates die Ausführung von Java-Applets und Java Web Start generell deaktiviert wird... Der Anwender muss Applets und Web-Start-Anwendungen in den Java-Einstellungen (im Ordner /Programme/Dienstprogramme) explizit wieder aktivieren:
Dies ist ebenfalls notwendig, wenn der Anwender mehr als 35 Tage weder mit Applets noch mit Java Web Start gearbeitet hat – dann wird beides wiederum automatisch deaktiviert.

Damit diese Änderungen nun nicht gleich das Ende vom Desktop-Java auf Mac OS X bedeuten, zeigt Apples Java-Plugin immerhin einen Hinweis an, warum eine Webseite nicht ganz so aussieht bzw. funktioniert wie erwartet:

Der Hinweistext ist gleichzeitig ein Link, mit dem sich das Java-Plugin wieder aktivieren lässt, ohne dass man die Java-Einstellungen aufrufen muss:
Das funktioniert soweit auch ganz prima – nur leider wird vermutlich die Hälfte der Anwender den kleingedruckten Text übersehen, dass der Browser anschließend neu gestartet werden muss... Abgesehen von der Frage, warum überhaupt ein Neustart notwendig ist, wäre es sicherlich gut, wenn sich der Hinweis-Link nach dem Aktivieren in einen statischen Text "Inaktives Plug-In (bitte Browser neu starten)" o.ä. verwandeln würde.

Donnerstag, 15. März 2012

Typinferenz in Java

Durch das verstärkte Interesse an funktionalen Sprachen wird auch immer häufiger von Typinferenz gesprochen, also vom automatischen Ermitteln eines passenden Datentyps durch den Compiler.

Beispielsweise legt man in Scala eine Liste ganzer Zahlen mit folgendem Ausdruck an:

val zahlen = List[Int](1,2,3)

Oder noch kürzer:

val zahlen = List(1,2,3)

Der Compiler ermittelt also einen möglichst spezifischen Typen für die rechte Seite des Ausdrucks (hier eine Liste von Ints) und verwendet ihn automatisch als Datentyp für die Wertdeklaration auf der linken Seite.

Seit Version 7 beherrscht nun auch Java eine offensichtliche Form der Typinferenz:

List<Integer> liste = new ArrayList<>();

Man kann auf der rechten Seite die Typangabe bei der generischen ArrayList weglassen (die Angabe der leeren spitzen Klammern, des "Diamanten", ist aber notwendig), und der passende Typ (Integer) wird automatisch von der linken Seite übernommen. Die Inferenz von links nach rechts ist auch der wichtigste Unterschied zu der üblichen Typinferenz funktionaler Sprachen – in der Praxis ist deren Typinferenz von rechts nach links praktischer, da sie mehr Schreibarbeit spart.

Was nicht so offensichtlich ist: Bereits seit Java 5 gibt es eine ähnliche Form der Typinferenz. Definiert man eine Methode mit lokalem generischen Parameter:

public static <T> T typecast(Object obj) {
@SuppressWarnings("unchecked")
T gecastet = (T) obj;
return gecastet;
}

kann man sie wie folgt aufrufen:

String s = typecast("Hallo");

Die Methode "typecast" hat in diesem Fall tatsächlich den Rückgabetyp "String", abhängig vom Typ der deklarierten Variablen auf der linken Seite. Ob diese Methode in der Praxis sinnvoll ist, sei mal dahingestellt – bei Übergabe eines Nicht-String-Objekts geht der Typecast in der Methode zur Laufzeit mit einer ClassCastException schief. Aber sie demonstriert sehr gut die seit langem in Java vorhandene (wenn auch sehr eingeschränkte) Typinferenz.

Donnerstag, 16. Februar 2012

Konstanten für JPA-NamedQueries

NamedQueries sind ein wichtiges Mittel zur JPA-Performance-Optimierung. Im letzten Workshop kam die Frage auf, wie man String-Literale für den Namen einer NamedQuery vermeidet:
@Entity
@NamedQueries({
@NamedQuery(
name = "findBenutzerByName",
query = "select b from Benutzer b where b.name=:name")
})
public class Benutzer {
...
}
Denn in einem Fall wie oben müsste derselbe String an den Stellen geschrieben werden, an denen die Abfrage aufgerufen werden soll:

EntityManager manager = ...;

TypedQuery<Benutzer> query =
manager.createNamedQuery(
"findBenutzerByName",
Benutzer.class);

query.setParameter("name", ...);

List<Benutzer> ergebnisse = query.getResultList();

Natürlich möchten wir dafür Konstanten verwenden – was auch bei Annotationen erlaubt ist. Die Frage ist nur, wo legen wir die Konstanten ab? Mein Vorschlag ist, dies direkt in der Entity zu tun:

@Entity
@NamedQueries({
@NamedQuery(
name = Benutzer.QUERY_FINDBYNAME,
query = "select b from Benutzer b where b.name=:"
+ Benutzer.QUERYPARAM_NAME)
})
public class Benutzer {

public static final String QUERY_FINDBYNAME = "findBenutzerByName";

public static final String QUERYPARAM_NAME = "name";

@Id @GeneratedValue(strategy=GenerationType.AUTO)
private Long id;

@Column
private String name;

...
}
Dadurch wird an der Aufrufstelle klar, dass man passende Query-Strings und -Parameter verwendet:

TypedQuery<Benutzer> query =
manager.createNamedQuery(
Benutzer.QUERY_FINDBYNAME,
Benutzer.class);

query.setParameter(Benutzer.QUERYPARAM_NAME, ...);

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).