Freitag, 16. November 2012

OS X 10.8, Java und X11

Es gibt Java-Anwendungen, die X11 verwenden. Bei mir ist dies z.B. ein VPN-Client-Applet, was ich allerdings erst durch folgenden Hinweis erfahren habe:
In OS X 10.8 wird nicht nur Java nicht mehr vorinstalliert, auch X11 ist als Komponente, die "normale" Anwender nicht benötigen, nicht mehr vorhanden ...

Klickt man in dem Dialog auf "Fortfahren", wird man auf eine Apple-Support-Web-Seite gebracht, auf der man die Hintergründe zum XQuartz-Open-Source-Projekt erfährt. Dort kann man dann auch eine aktuelle Version (mind. 2.7.2, getestet habe ich 2.7.4) herunterladen, mit der OS X "Mountain Lion" und Java glücklich werden.

Die Installation von X11 löst ein weiteres Problem: Das GlassFish-Installations-Shellskript meldet unter OS X 10.8 folgenden Fehler:

much$ ./glassfish-3.1.2.2-unix.sh 
This program requires DISPLAY environment variable to be set.
Please re-run after assigning an appropriate value to DISPLAY.

Nach der Installation von X11 und einem Aus- und Einloggen ist die Umgebungsvariable "DISPLAY" wieder korrekt gesetzt und das Installations-Skript funktioniert wie gewohnt.

Samstag, 27. Oktober 2012

Apples Java-Update 2012-006 und die Sache mit dem Browser-Applet-Plugin

Zeitgleich haben Apple und Oracle Anfang vergangener Woche die neuesten Java-Updates veröffentlicht (Oracle: JDK und JRE 7u9; Apple: Java 6u37). Dieser Blog-Eintrag fasst zusammen, was ich bereits im Usenet geschrieben habe bzw. was Oracle als Hinweise dazu veröffentlicht hat.

Bisher war es so, dass in den Mac-Web-Browsern immer Apples Java-Applet-Plugin (also Java 6) aktiv war – es sei denn, man hatte Oracles JRE 7 installiert, dann wurde genau dieses Java 7 als Applet-Plugin verwendet.

Mit dem neuen Update "Java for OS X 2012-006" für Mac OS X 10.7 und 10.8 entfernt Apple das Java-6-Applet-Plugin nun explizit aus dem System. D.h. nach der Installation des Java-Updates ist zunächst einmal kein Applet-Plugin mehr verfügbar. Stattdessen wird auf einer Web-Seite, die ein Applet einbindet, die Meldung "Fehlendes Plug-In" angezeigt:


Klickt man auf die Meldung, erscheint ein etwas ausführlicherer Hinweis:
Dort bringt einen der Klick auf "Weitere Infos ..." auf Oracles Mac-JRE-Download-Seite. Apple drängt die Anwender also recht eindeutig zum Upgrade auf Java 7, was angesichts von wichtigen Security-Bugfixes, die es für Java 6 bald nicht mehr geben wird, mehr als verständlich ist.

Leider kommen einige wenige Java-Applets noch nicht mit Java 7 zurecht. Manche verweigern z.B. aufgrund schlechter bzw. zu restriktiver Programmierung (fest kodierte Prüfung einer bestimmten Java-Versionsnummer o.ä.) den Dienst mit der seit über einem Jahr aktuellen Java-Version... Wer auf die Arbeit mit so einem Applet angewiesen ist, kann das alte Plugin manuell im Terminal reaktivieren. Im Wesentlichen läuft das darauf hinaus, den Symlink /Library/Internet Plug-Ins/JavaAppletPlugin.plugin auf das noch vorhandene, aber gut versteckte /System/Library/Java/Support/Deploy.bundle/Contents/Resources/JavaPlugin2_NPAPI.plugin zeigen zu lassen (das ebenfalls vorhandene /System/Library/Java/Support/CoreDeploy.bundle/Contents/JavaAppletPlugin.plugin ist mit dem Update 2012-006 kein vollwertiges Applet-Plugin mehr, sondern zeigt nur noch den Hinweis zum fehlenden JRE7-Plugin an, s.o.).

Apples Java-Update 2012-006 löscht zudem die Java-Einstellungen in den Dienstprogrammen. Das Java Control Panel in den Systemeinstellungen ist damit die einzige Möglichkeit, Laufzeitparameter für Applets festzulegen. Schwerwiegender dürfte aber sein, dass Entwickler damit die bisher unter Mac OS X sehr einfache grafische Möglichkeit verlieren, aus allen installierten JDKs die aktive Version für die Kommandozeile auszuwählen. Stattdessen wird von den Kommandozeilentools in /usr/bin das JDK mit der höchsten Nummer verwendet. Was so einfach klingt, kann durchaus zu Problemen führen, wenn Vorabversionen des JDKs installiert sind:

HeartOfGold:~ much$ which java
/usr/bin/java
HeartOfGold:~ much$ java -version
openjdk version "1.8.0-b50"
OpenJDK Runtime Environment (build 1.8.0-b50-20120801)
OpenJDK 64-Bit Server VM (build 24.0-b16, mixed mode)

Glücklicherweise beachten die Tools die Umgebungsvariable JAVA_HOME, die man in seinen Shell-Skripten mithilfe des – schon längere Zeit verfügbaren – OSX-Tools /usr/libexec/java_home passend setzen kann:

HeartOfGold:~ much$ export JAVA_HOME=`/usr/libexec/java_home -v 1.7`
HeartOfGold:~ much$ printenv JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home
HeartOfGold:~ much$ java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)

Alternativ kann man das java_home-Tool dazu verwenden, ein (Java-)Kommando mit einer bestimmten Java-Version auszuführen:

HeartOfGold:~ much$ /usr/libexec/java_home -v 1.6 --exec java -version
java version "1.6.0_37"
Java(TM) SE Runtime Environment (build 1.6.0_37-b06-434-11M3909)
Java HotSpot(TM) 64-Bit Server VM (build 20.12-b01-434, mixed mode)

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