Montag, 20. August 2012

Oracle JRE 7 für Mac OS X 10.7 und OS X 10.8

Mit dem letzte Woche veröffentlichten JRE 7u6 hat Oracle nach dem Mac-JDK im Frühjahr nun erstmals ein reines JRE für Mac OS X ab Version 10.7.3 (d.h. auch für OS X 10.8.x) freigegeben. Endanwender können die Software nun endlich vereinfacht von java.com herunterladen und müssen sich nicht mehr durch die Entwicklerseiten klicken. Entsprechend einfach ist die Installation – das JRE wird als DMG ausgeliefert:
Das übliche Mac OS X-Installationsprogramm führt dann durch die Installation:
Im Unterschied zum JDK werden keine Entwickler-Werkzeuge und -Dokumentation mitgeliefert. Genau genommen liefert Oracle mit dem Mac-JRE im Wesentlichen nur das Applet-Plugin für Web-Browser aus, d.h. Anwendungen, die sich auf das java-Kommandozeilentool verlassen, laufen damit nicht. Mit dem Oracle-Applet-Plugin wird Java 7 auch zur Standard-Version im Browser, unabhängig davon, ob eine Apple-Java-6-Umgebung installiert ist.

Weil Oracles Java nicht mehr über die Softwareaktualisierung von Mac OS X auf den neuesten Stand gebracht wird, ist es gut, dass das JRE eine Auto-Update-Funktion mitbringt. Oracle hat dafür das Rad nicht neu erfunden, sondern greift auf das bekannte Sparkle-Framework zurück.

Zusätzlich installiert das JRE in den Mac-Systemeinstellungen das von anderen Betriebssystemen bekannte "Java Control Panel":
Leider ist das Control Panel noch nicht wirklich gut in OS X integriert, denn nach dem Klick auf das Java-Symbol öffnet sich das Panel in einem separaten Fenster:
Immerhin findet sich dadurch jeder, der das Java Control Panel von anderen Systemen kennt, sofort bzgl. Netzwerk-Proxies, Zertifikaten etc. zurecht.

Für Entwickler ist klar, dass sie sich das JDK 7 installieren (in dem u.a. das JRE enthalten ist), wahrscheinlich zusätzlich zu Apples Java-6-Implementierung. Welches Java aber sollte sich ein normaler Endanwender auf seinem Mac-System installieren? Java 7 ist seit über einem Jahr verfügbar, Java 6 wird von Oracle nur noch bis Februar 2013 öffentlich gepflegt werden (dann wird ziemlich sicher auch Apple keine Java-6-Updates mehr anbieten) – das spricht für Java 7. Weil aber viele Mac-Java-Anwendungen durch ihren Start-Code noch an Java 6 gebunden sind, muss man zur Zeit eigentlich noch zwingend Apples Java 6 installieren. Also: Am besten installiert man derzeit beide Java-Versionen – und von Java 7 am besten das JDK 7, damit alle Tools mitinstalliert werden, die eventuell zum Starten einer Anwendung benötigt werden.

Wenn man bereits ein JDK 7 auf dem Rechner hat und eine neue Version davon installiert, wird die alte Version – anders als beim JRE – nicht vom Rechner gelöscht, sondern steht zusätzlich zur Verfügung (Entwickler wollen ja durchaus mal unterschiedliche Java-7-Versionen im Vergleich testen). Man sollte daher nach der Installation unbedingt in den Java-Einstellungen (im Ordner "Dienstprogramme") prüfen (und ggfs. korrigieren), welches Java-7-Update aktiv ist:

Und ein Hinweis zum Schluss: Wenn man nur das JRE installiert hat, taucht dies in den Java-Einstellungen nicht auf; dort werden nur die auf dem Rechner vorhandenen JDKs aufgelistet.

Mittwoch, 1. August 2012

OS X 10.8 "Mountain Lion", Java und der Gatekeeper

Vor einer Woche wurde (Mac) OS X 10.8 "Mountain Lion" veröffentlicht. Wie schon bei der Vorgängerversion liefert Apple keine Java-Laufzeitumgebung mehr mit dem Berglöwen/Puma aus – bei einem Update von 10.7.x wird eine bestehende Java 6-Installation sogar entfernt und muss erneut installiert werden:
Der Vergleich mit der Java-Installation unter 10.7 zeigt einen kleinen, aber wichtigen Unterschied: Es wird nun explizit eine Java SE 6-Runtime erwähnt. D.h. selbst wenn man Java 7 installiert hat, muss man zum Ausführen eines Java Application Bundles zusätzlich das alte Java 6 herunterladen. Dies ist das derzeit aktuelle Apple-Java-Update, das ursprünglich für Mac OS X 10.7 veröffentlicht wurde:
Ein Problem für Java-Anwendungen entsteht durch den Gatekeeper, der ab OS X 10.8 dafür sorgt, dass normalerweise nur Anwendungen aus dem App Store bzw. von zertifizierten Entwicklern gestartet werden können. Der Java Application Stub, mit dem die meisten Mac OS X-Java Application Bundles gestartet werden, enthält aber kein oder kein passendes Zertifikat (je nachdem, wie alt der Stub ist), was zu einer ziemlich unpassenden Fehlermeldung führt:
Mit ein bisschen Trickserei (Austausch des Application Stubs bzw. Entfernen der Prüfsumme / des Zertifikats) kann man die Fehlermeldung zwar etwas verständlicher bekommen ...
... aber das ist nicht im Sinne der Erfinder / nicht ganz einfach / zu instabil für künftige OS X-Versionen. Kurzum: Tun Sie's nicht. Eine Möglichkeit für den Anwender, Java-Anwendungen trotzdem noch starten zu können, besteht darin, den Gatekeeper in den Systemeinstellungen zu deaktivieren (auf "Keine Einschränkungen" stellen):
Das ist nun allerdings auch nicht im Sinne der zusätzlichen Sicherheit, die OS X 10.8 bringen soll, weil dann wirklich alle Anwendungen (auch die nativen Mac-Anwendungen) ungeprüft ausgeführt werden. Besser ist es, gezielt nur die gewünschte Java-Anwendung ohne Gatekeeper-Prüfung aufzurufen, was man über den "Öffnen"-Menüpunkt im Kontextmenü erreicht:


OS X verbietet das Starten nun nicht mehr pauschal, sondern fragt nach, ob man die vermeintlich unsichere Anwendung wirklich ausführen möchte:

Leider klappt das – je nach verwendetem Application Stub – nicht mit allen Java-Anwendungen. JD-GUI kann man so aufrufen, ArgoUML und TV-Browser beispielsweise nicht... Für letztere muss der Gatekeeper wie oben beschrieben deaktiviert werden. Kleiner Trost: Wenn man einmal eine Java-Anwendung gestartet hat, merkt sich der OS X dies, man kann den Gatekeeper anschließend also wieder aktivieren.

Am besten wäre es allerdings, wenn die Entwickler von Java-Anwendungen den Gatekeeper aktiv unterstützten und ihre Applikation signierten. Bei Oracle gibt es dazu kompakte Informationen. Und vor kurzem hat sich jemand die Mühe gemacht, ausführlich zu beschreiben, wie man seine Java-Programme im Mac App Store veröffentlichen kann.

Samstag, 26. Mai 2012

Oracle JDK 7 für Mac OS X

Vor genau einem Monat hat Oracle nun endlich  das offizielle JDK 7 für Mac OS X freigegeben, zeitgleich mit Java SE 7u4 für alle anderen Systeme. (Wir erinnern uns: Ab Version 7 wird Java für Mac OS X von Oracle und nicht mehr von Apple bereitgestellt.) Die genauen Versionsinformationen habe ich wie immer in meiner Liste der Mac OS X- und Java-Versionen zusammengetragen.
Die DMG-Datei enthält ein Installationsprogramm, das das JDK 7 ohne großen Aufwand ins Verzeichnis /Library/Java/JavaVirtualMachines/1.7.0.jdk schreibt, d.h. das JDK steht für alle Benutzer auf dem Rechner zur Verfügung.
Nach der Installation muss man das JDK 7 in den "Java-Einstellungen" in den Dienstprogrammen aktivieren, ansonsten bleibt das nach wie vor von Apple gepflegte Java 6 die Standard-Version. Entwickler – gerade im Bereich Server-/Enterprise-Programmierung – können das JDK 7 bereits gut verwenden. Für Endanwender mag es im Moment noch besser sein, Java 6 als die aktive (d.h. in der Liste oberste) Java-Version einzustellen, da das JDK 7 noch das ein oder andere Problem mit Applets bzw. der GUI zu haben scheint. Das ist wohl auch der Grund, warum Oracle das Mac-JRE für Endanwender erst später im Jahr veröffentlichen wird.

Abschließend noch ein paar Hinweise zu Problemen und deren mögliche Lösung.

Falls im Terminal das JDK 7 nicht gefunden wird (d.h. java -version und /usr/libexec/java_home liefern noch Java 6), obwohl es in den Java-Einstellungen als System-Default konfiguriert wurde, sollte man prüfen, ob die Umgebungsvariable JAVA_HOME (falsch) gesetzt ist. Das kann beispielsweise grafisch im RCEnvironment geschehen sein, falls man diese Erweiterung der Systemeinstellungen installiert hat. Häufiger ist dies wohl aber in der Shell-Konfigurationsdatei (z.B. .bash_profile) erfolgt. Entweder setzt man den Eintrag für JAVA_HOME auf

export JAVA_HOME=`/usr/libexec/java_home`

(Wichtig: Mit den "Backticks" um den Programmaufruf), oder aber man entfernt den Eintrag für JAVA_HOME komplett. Nach einem Rechner-Neustart (aus- und einloggen hat bei mir nicht geholfen) sollte dann alles funktionieren.

In Eclipse 3.7.2 sollte das JDK 7 bei einem neuen Workspace automatisch erkannt werden, wenn Java 7 die System-Voreinstellung ist. Ansonsten muss man das JDK manuell in den Eclipse-Preferences auf der Seite "Installed JREs" hinzufügen. Mit dem "Add"-Button fügt man eine "Standard VM" hinzu und trägt als "JRE Home" den Pfad

/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home

ein – fertig.

In NetBeans 7.1.2 sollte ein installiertes JDK 7 automatisch erkannt werden. Ansonsten muss man das JDK auch hier manuell ergänzen, und zwar im Menü "Tools / Java-Platforms". Mit dem Button "Add Platform" kann man das gewünschte Basisverzeichnis eingeben (/Library/Java/JavaVirtualMachines oder /System/Library/Java/JavaVirtualMachines) und anschließend eine der dort installierten Java-Laufzeitumgebungen auswählen.

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.