Sonntag, 27. Dezember 2009

Scala-Servlets

Ein schönes Beispiel dafür, wie sich gewohnte Java-Programmierung in Scala "anfühlt", sind Servlets:
package com.muchsoft.scala.servlets

import javax.servlet.http.HttpServlet
import javax.servlet.http.{HttpServletRequest => Request,
HttpServletResponse => Response}

class MeinErstesScalaServlet extends HttpServlet {

override def doGet(request: Request, response: Response) {

val ausgabe =
<html>
<head><title>Hallo Scala</title></head>
<body>
<h1>Hallo Scala-Welt!</h1>
</body>
</html>

response.getWriter().print(ausgabe)
}
}
Das sieht doch fast wie Java aus, oder? Nicht ganz: Beim Import können wir Umdefinitionen vornehmen, das override vor der Methodendefinition ist zwingend notwendig, zudem ein Schlüsselwort und keine Annotation. Der Rückgabetyp der Methode doGet() ist nicht angegeben und entspricht dadurch dem Java-void. Für Scala hätten wir vor die öffnende geschweifte Methodenklammer :Unit= schreiben können.

Innerhalb von doGet() legen wir das XML-Literal ausgabe an (als nicht änderbaren Wert mit val; mit var angelegte Variablen versucht man in funktionalen Sprachen zu vermeiden). Dieses XML-Literal schreiben wir ganz normal in den Ausgabestrom des Response-Objekts. Der Typ von ausgabe ist scala.xml.Elem.

Die Struktur der Web-Applikation entspricht dem JavaEE-Standard:


Der vom Scala-Compiler erzeugte Java-Bytecode landet in WEB-INF/classes. Die Scala-Standardbibliothek scala-library.jar (aus dem Scala-Installationsverzeichnis) muss in WEB-INF/lib mit ausgeliefert werden. In WEB-INF/web.xml mappen wir die Klasse com.muchsoft.scala.servlets.MeinErstesScalaServlet auf die URL "hallo.scala". Nach dem Bereitstellen (Deployment) z.B. in Tomcat oder GlassFish kann das Servlet im Web-Browser dann mit der URL http://localhost:8080/halloscala/hallo.scala aufgerufen werden.

Zum Übersetzen der Servlet-Klasse muss sich noch die Servlet-API auf dem Build-Path (Classpath) befinden. Dazu kann man z.B. aus dem Tomcat-Installationsverzeichnis servlet-api.jar oder aus dem GlassFish-Verzeichnis javaee.jar als externe Bibliothek einbinden.

Nun verbessern wir das erste Scala-Servlet und trennen Darstellung und Steuerung etwas besser voneinander:
class MeinZweitesScalaServlet extends HttpServlet {

def ausgabe =
<html>
<head><title>Hallo Scala</title></head>
<body>
<h1>Hallo Scala-Welt!</h1>
<p>Zeit auf dem Server: {jetzt} </p>
</body>
</html>

def jetzt = new java.util.Date

override def doGet(request: Request, response: Response) {
response.getWriter().print(ausgabe)
}
}
ausgabe ist nun eine Funktion außerhalb von doGet(), die auf die Funktion jetzt zurückgreift, um einen aktuellen Zeitstempel auszugeben. Beide Funktionen sind ohne Parameterklammern definiert und müssen daher ohne Klammern aufgerufen werden. Hätten wir die Funktionen mit leeren Parameterklammern definiert, hätten wir es uns beim Aufruf aussuchen können, ob wir die Klammern hinschreiben oder nicht. Es hat sich die Konvention herausgebildet, die Klammern bei der Definition wegzulassen, wenn eine Funktion keine Nebenwirkungen (Seiteneffekte) hat.

Für eine "vollständige" Web-Applikation fehlt noch ein Formular. Bitteschön:
class MeinDrittesScalaServlet extends HttpServlet {

def ausgabe(benutzername: String) =
<html>
<head><title>Hallo Scala-Welt</title></head>
<body>
<h1>Hallo {benutzername}</h1>
<p>Zeit auf dem Server: {jetzt}</p>
<form method="GET" action="hallo3.scala">
<p>Wie heißt Du?
<input name="benutzer" />
<input type="submit" value="Abschicken" />
</p>
</form>
</body>
</html>

def jetzt = new java.util.Date

override def doGet(request: Request, response: Response) {

val param = request getParameter "benutzer"

val benutzername =
if ((param == null) || (param.isEmpty))
"Scala"
else
param capitalize

val antwort = ausgabe(benutzername)

response.getWriter().print(antwort)
}
}
ausgabe() bekommt nun einen String-Parameter übergeben, der in doGet() anhand des Request-Parameters "benutzer" ermittelt wird. Statt des ?:-Operators von Java, den es in Scala nicht gibt, verwenden wir ein besser lesbares if-else, das in Scala ein Ausdruck ist und entsprechend ein Ergebnis liefert.

Wenn diese dritte Servlet-Klasse in web.xml auf die URL "hallo3.scala" gemappt wird, kann der Aufruf im lokalen Applikations-Server mit http://localhost:8080/halloscala/hallo3.scala?benutzer=Thomas erfolgen.

In der Praxis wird man Servlets wohl eher nicht direkt programmieren, sondern ein Framework wie Struts, JSF, Wicket o.ä. einsetzen. Man sieht hier aber gut, wie problemlos man Scala in bestehende Java-(Web-)Anwendungen integrieren kann. Und wenn man Web-Applikationen so richtig funktional entwickeln möchte, kann man das Scala-Lift-Framework nutzen.

Samstag, 12. Dezember 2009

ECMAScript 5

Bis vor ein paar Jahren musste man regelmäßig darauf hinweisen, dass Java nicht JavaScript ist, und JavaScript wurde als Programmiersprache – trotz Objektbasierung und funktionaler Aspekte – nicht ernst genommen. Mit Web 2.0 und Ajax ist dies seit einiger Zeit definitiv anders. JavaScript ist im Frontend akzeptiert, und es gibt keine ernsthaften Diskussionen mehr, ob man es im Browser deaktivieren soll. RIA kommt ohne JavaScript nicht aus.

Vor einer Woche wurde nun die Spezifikation von ECMAScript 5, dem offiziellen Standard für JavaScript, freigegeben. Die neue ECMAScript-Version wurde ziemlich genau zehn Jahre nach der letzten Version, ECMAScript 3, veröffentlicht (Version 4 wurde ausgelassen). Neben vielen kleinen Verbesserungen und einem "strict"-Modus ist nun auch JSON im Standard enthalten. Mit diesem Datenformat können JavaScript-Objekte kompakt durch das Netz übertragen werden.

Für mich ist ECMAScript insofern interessant, als es nun über zehn Jahre her ist, dass ich für den Web-Browser iCab eine JavaScript-Engine (Interpreter) zunächst auf Basis von ECMAScript 2, nach 1999 dann aufbauend auf ECMAScript 3 entwickelt hatte. iCab hat seine Rendering Engine (und damit auch die JavaScript-Laufzeitumgebung) zwar mittlerweile auf Apples WebKit umgestellt, aber die Implementierung einer so zentralen Browser-Komponente gab einen tollen Einblick hinter die technischen Kulissen auf das, wie das Web bis (und gerade) heute funktioniert.

Wie die Zeit vergeht...

Donnerstag, 10. Dezember 2009

Hallo Java EE 6!

Dreieinhalb Jahre nach Java EE 5 hat Sun heute die endgültige Spezifikation von Java EE 6 freigegeben. Bestandteil davon sind u.a. die Spezifikationen von EJB 3.1, JPA 2.0 und JSF 2.0.

Der Download des Java EE 6 SDK enthält den Application Server GlassFish v3 als Referenzimplementation (neben Beispielen samt Dokumentation).

Ebenfalls verfügbar ist die an JEE 6 und GlassFish v3 angepasste Entwicklungsumgebung NetBeans 6.8. Eclipse-Anwender finden hier das entsprechende Plugin.

Wer bereits jetzt Java-Enterprise-Projekte auf Basis von JEE 5 entwickelt, dem dürfte die Umstellung auf die neue Version nicht allzu schwer fallen – sofern der eingesetzte Application Server kompatibel zur neuen Spezifikation ist.

Eine dreiteilige Einführung in Java EE 6 hat Sun hier veröffentlicht.

Dienstag, 8. Dezember 2009

Eclipse 3.5 Galileo, Software-Updates und Proxy-Verbindungen

Je nach Netzwerkumgebung gibt es mit Eclipse immer wieder Probleme beim Installieren von Plugins (über das Menü Help > Install New Software...) bzw. beim automatischen Aktualisieren bereits installierter Erweiterungen (über Help > Check for Updates). Grund sind meistens Proxies, die von Eclipse nicht unterstützte Protokolle verwenden, sowie Firewalls, die eigene Zertifikate für https-Verbindungen ausgeben.

Sofern man das Zertifikat der Firewall besitzt (eine Datei üblicherweise mit der Endung *.cer), kann man dieses in den Keystore der Java-Installation importieren. Welche Java-Installation von Eclipse verwendet wird, kann man unter About Eclipse > Installation Details > Configuration herausfinden. Unter Windows lautet der Aufruf des keytool-Kommandozeilenbefehls beispielsweise
C:\Programme\Java\jre6\bin\keytool -import -v
-file PfadZumZertifikat
-keystore C:\Programme\Java\jre6\lib\security\cacerts
Geben Sie das Keystore-Passwort ein: changeit
"changeit" ist das Standard-Passwort des KeyStores, sofern Sie es nicht geändert haben.


Ein anderes Problem stellen NTLMv2-Proxy-Server dar, die seit Eclipse 3.5 nicht mehr direkt unterstützt werden. Neben der korrekten Einrichtung der Proxy-Adresse und der Authentifizierungsdaten unter Preferences > General > Network Connections muss in der Datei eclipse.ini (im Eclipse-Programmverzeichnis) am Ende folgende Zeile hinzugefügt werden:
-Dorg.eclipse.ecf.provider.filetransfer.excludeContributors=org.eclipse.ecf.provider.filetransfer.httpclient
In Eclipse 3.5 war es in der o.g. Preferences-Seite zudem notwendig, den "Active Provider" auf "Native" zu stellen. In Eclipse 3.5.1 funktioniert auch die Einstellung "Manual".

Montag, 16. November 2009

Scala-Code als und in einer Java-Anwendung ausführen

Compilierter Scala-Bytecode lässt sich problemlos in bestehende Java-Anwendungen integrieren. Möglich ist dies, weil der Scala-Compiler ganz normalen Java-Bytecode erzeugt. Als Laufzeitumgebung sollte dabei Java 5.0 oder neuer zum Einsatz kommen.

Dazu wird eine Scala-Installation benötigt. Am besten nimmt man den IzPack-Installer. Das Installationsverzeichnis heißt im folgenden Beispiel "scala-2.7.7".
object HalloScalaWelt extends Application {
println("Hallo Scala!")
}
Dieses ausführbare Singleton-Objekt speichert man als Datei "HalloScalaWelt.scala", die nun übersetzt und ausgeführt wird:
HeartOfGold:~ much$ scalac HalloScalaWelt.scala
HeartOfGold:~ much$ ls Hallo*.class
HalloScalaWelt$.class HalloScalaWelt.class
HeartOfGold:~ much$ scala HalloScalaWelt
Hallo Scala!
Bereits jedem Java-Einsteiger sollten diese Kommandos irgendwie bekannt vorkommen. Entsprechend führen wir die erzeugten class-Dateien ganz einfach als Java-Anwendung aus:
HeartOfGold:~ much$
java -cp scala-2.7.7/lib/scala-library.jar:. HalloScalaWelt
Hallo Scala!
Unter Windows gibt man beim Klassenpfad (Option -cp oder -classpath) vor dem Punkt am Ende (= aktuelles Verzeichnis) ein Semikolon anstelle des Doppelpunkts an.

Ebenso einfach kann man Scala-Code in bestehende Java-Anwendungen integrieren. Man übernimmt einfach die compilierten class-Dateien und setzt scala-library.jar auf den Klassenpfad – fertig!

Die Benutzung von Klassen zwischen Java und Scala besteht meistens ganz einfach darin, wie gewohnt den (vollqualifizierten oder importierten) Klassennamen zu verwenden.

Freitag, 13. November 2009

Java ME SDK 3.0 für Mac OS X

Die Entwicklung von Applikationen für Java ME, der Java-Plattform für mobile Geräte, war unter Mac OS X ein eher schwieriges Thema. Wichtige Bestandteile des SDKs fehlten oder wurden nicht offiziell unterstützt (Anleitungen für "inoffizielle" Lösungen gibt es z.B. hier, hier oder hier).

Vor drei Wochen hat Sun nun endlich das Java ME SDK 3.0 als Early Access (EA) für Mac OS X 10.5.8 und neuer veröffentlicht. Das DMG enthält das komplette SDK als eine Applikation, die einfach per Drag&Drop in den Programme-Ordner installiert wird.

Die SDK-Entwicklungsumgebung basiert auf der NetBeans-Plattform und ermöglicht erstmalig CLDC-Entwicklung auf Mac OS X. Enthalten ist außerdem der JavaFX 1.2.1 Mobile Emulator.

VisualVM 1.2

Nachdem im Oktober Version 1.2 des Monitoring- und Profiling-Werkzeugs VisualVM veröffentlicht wurde, folgte Anfang dieser Woche das Bugfix-Release 1.2.1. Beide Versionen basieren auf dem Profiler von NetBeans 6.7.1.

Neu ist u.a. die stabile Version des "Sampler"-Plugins, mit dem schnell ein einfaches (aber oft schon vollkommen ausreichendes) CPU- und Speicher-Profiling durchgeführt werden kann. Wie die übrigens Plugins muss auch dieses im Menü Tools > Plugins installiert werden:

Außerdem kann man nun endlich eventuell notwendige Proxy-Einstellungen innerhalb von VisualVM vornehmen. Weitere Änderungen und Ergänzungen sind hier aufgelistet.

Dienstag, 3. November 2009

Java EE SDK unter Snow Leopard installieren

Mac OS X 10.6 "Snow Leopard" enthält Java SE 6 als einzige Java-Laufzeitumgebung (und nicht mehr Java 1.4, 5.0 und 6 wie Mac OS X 10.5). Das bringt ein Problem mit sich, wenn man Suns aktuelles Java EE 5 SDK Update 7 installieren möchte, denn das Installationsskript ist fest verdrahtet auf ein installiertes Java 5 angewiesen. Im System sind zwar die Ordner für Java 5.0 vorhanden, die verweisen aber auf die Java 6-Installation – und das reicht dem Skript leider nicht aus.

Im konkreten Fall haben wir das Archiv java_ee_sdk-5_07-mac-nojdk.zip (Download "GlassFish Java EE") ausgepackt und zu installieren versucht:

HeartOfGold:java_ee_sdk-5_07-mac-nojdk much$ chmod a+rx setup
HeartOfGold:java_ee_sdk-5_07-mac-nojdk much$ ./setup
Installer cannot find a suitable 1.5.x version of JDK under the
following directories.

1. /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home
2. /Library/Java/Home

Please rerun with -javahome argument pointing to a valid 1.5 version of JDK.

Zum Glück ist die Lösung recht einfach (man muss dafür kein altes Java 5.0 aus Mac OS X 10.5 installieren). In der Skript-Datei setup ersetzt man mit einem Texteditor in Zeile 54 einfach am Ende der Zeile "1.5" durch "1.6":
54: javaVersion=`${TIGER_JAVAHOME} ... ${GREP} 1.6`
Das war es dann auch schon! Der Installer lässt sich nun auch mit Java 6 problemlos ausführen, und GlassFish läuft auch (bzw. gerade) mit dem aktuellen JRE hervorragend.

Donnerstag, 15. Oktober 2009

IntelliJ IDEA 9: Community Edition und Open Source

Die Entwicklungsumgebung IntelliJ IDEA ist seit längerem der Geheimtipp für Java-Entwickler. Doch während Eclipse und NetBeans kostenlos erhältlich und weit verbreitet sind, schreckten viele vor dem dreistelligen Kaufpreis für eine IDE(A) zurück.

Mit der kommenden Version 9.0 wird IntelliJ IDEA nun Open Source und steht seit heute auch in einer kostenlosen Community Edition zur Verfügung. Mit dieser Edition kann Java SE, Groovy und Scala programmiert werden (inkl. Debugging, Refactoring, Testen, Versionsverwaltung etc.).

Für die Entwicklung von Web- und Enterprise-Applikationen wird die weiterhin kostenpflichtige Ultimate Edition benötigt, die neben aktuellen und künftigen Standards (Java EE 6 etc.) zahlreiche Programmiersprachen, Frameworks und Versionsverwaltungssysteme zusätzlich unterstützt.

Fazit: Für professionelle JEE-Entwicklung ist zwar nach wie vor die kostenpflichtige Ultimate Edition notwendig, aber zumindest kann sich nun jeder einen ausführlichen Eindruck von IntelliJ verschaffen. Und das hat diese Entwicklungsumgebung durchaus verdient.

Dienstag, 13. Oktober 2009

Snow Leopard und Rosetta

Schon seit den ersten Mac OS X-Versionen können Java-Applikationen in einer speziellen Verzeichnisstruktur als "normale" Mac OS X-Anwendung verpackt werden, so dass sie u.a. mit einem eigenen Programmsymbol dargestellt werden. Damit solche Application Bundles Java-Bytecode ausführen können, enthalten sie als nativen Mach-O-Startcode im Verzeichnis /Contents/MacOS/ die Datei JavaApplicationStub:

Führt man ältere, noch unter PowerPC (PPC) Mac OS X zusammengebaute Java-Application-Bundles unter Mac OS X 10.6 "Snow Leopard" aus, erhält man eventuell den Hinweis, dass man den auf der Snow-Leopard-Installations-DVD enthaltenen PPC-Emulator "Rosetta" installieren muss, um die Anwendung starten zu können. Obwohl Apple seit Mac OS X 10.4 alte PPC-JavaApplicationStubs erkennt und als 32-Bit-Intel-Prozess neu startet, muss dafür Rosetta installiert sein – und genau das ist seit Snow Leopard nicht mehr standardmäßig der Fall.

Die einfachste Lösung, die zudem Rosetta nicht benötigt, ist, den alten Stub im Application Bundle durch eine Kopie der aktuelle Version /System/Library/Frameworks/JavaVM.framework/Resources/MacOS/JavaApplicationStub zu ersetzen. Dieser neue Stub enthält Code für die drei derzeit unterstützten Prozessorarchitekturen:
Straylight:~ much$ file JavaApplicationStub
JavaApplicationStub: Mach-O universal binary with 3 architectures
JavaApplicationStub (for architecture x86_64): Mach-O 64-bit executable x86_64
JavaApplicationStub (for architecture i386): Mach-O executable i386
JavaApplicationStub (for architecture ppc7400): Mach-O executable ppc
Wenn eine Java-Anwendung eine bestimmte Architektur nicht benötigt (z.B. weil nachgeladene JNI-Bibliotheken dafür nicht compiliert wurden), kann und sollte der entsprechende Code mit /usr/bin/lipo entfernt werden.

Dienstag, 1. September 2009

Java in Mac OS X 10.6 "Snow Leopard"

Der Schneeleopard ist also aus dem Käfig... Und während das nun "alte" Mac OS X 10.5 noch drei Implementierungen für Java 1.4, 5.0 und 6 mitbrachte, enthält Mac OS X 10.6 ausschließlich Java SE 6 (1.6.0_15) in einer 32-Bit- und einer 64-Bit-Version. Detaillierte Informationen zu beseitigten und bekannten Fehlern enthalten die Release Notes.

Damit auch Java 5-Anwendungen weitestgehend problemlos laufen, sind im Ordner /System/Library/Frameworks/JavaVM.framework/Versions noch die Unterordner 1.5 und 1.5.0 enthalten, aber beide sind Aliase (Symlinks) auf die Java 6-Implementierung.

Und obwohl QuickTime for Java schon seit längerem nicht mehr offiziell unterstützt wird, sind die QTJava-Bibliotheken immer noch vorhanden und funktionieren auch mit Java 6.

Mal abwarten, was die praktischen Erfahrungen in den kommenden Tagen zeigen.

Mittwoch, 26. August 2009

Closures in JavaFX Script

JavaFX Script (die Programmiersprache für Suns neue RIA-Technologie JavaFX) ist eine objektorientierte, deklarative und funktionale Programmiersprache.

Als funktionale Sprache unterstützt sie Funktionen höherer Ordnung (first-class functions) und Funktionsabschlüsse (Closures). Ersteres bedeutet, dass Funktionen selber Werte sind – sie können also als Parameter übergeben und zurückgegeben werden. Letzteres erlaubt den Funktionswerten, dass sie sich den Zustand ihrer Umgebung (die lokalen Variablen) bei ihrer Erzeugung merken können.

Für viele sind Closures seltsame Konstrukte und ihre Einsatzmöglichkeiten nicht offensichtlich. Daher im Folgenden ein kleines, relativ leicht verständliches Beispiel, in dem mehrere Zähler mit individuellen Startwerten erzeugt und benutzt werden:
// closure.fx

function zaehlerMitStartwert(startwert: Integer) {
var n = startwert;
return function(): Integer { return n++ }
}

var next1 = zaehlerMitStartwert(10);
var next2 = zaehlerMitStartwert(20);

println( next1() ); // 10
println( next2() ); // 20
println( next2() ); // 21
println( next1() ); // 11
Wer das Beispiel ausprobieren möchte, installiert sich das JavaFX 1.2 SDK und nutzt die Kommandozeile (oder NetBeans oder Eclipse mit dem JavaFX-Plugin):
Straylight:~ much$ javafxc closure.fx
Straylight:~ much$ javafx closure
10
20
21
11
Die ausgegrauten Elemente sind nicht wirklich notwendig und sollen nur das Skript für Java-Programmierer leichter verständlich machen. "return" ist an beiden Stellen optional, da der letzte Ausdruck in einer Funktion gleichzeitig deren Ergebnis definiert. Der Rückgabetyp ":Integer" der inneren, anonymen Funktion ist optional, da aus dem Datentyp des Ergebnisses der Rückgabetyp durch Typinferenz ermittelt werden kann.

Trotz (bzw. gerade wegen) dieser impliziten Typisierung ist JavaFX Script im Gegensatz zu Skriptsprachen wie JavaScript statisch und streng typisiert. Bereits der Compiler ermittelt also die Datentypen und Signaturen von Variablen und Funktionen, sofern sie nicht explizit angegeben sind. Im Beispiel hat die Variable n also den Datentyp Integer, die Variablen next1 und next2 haben den Typ function():Integer und zaehlerMitStartwert hat den Typ function(:Integer):function():Integer :-)

Wie funktioniert das Skript? Beim Aufruf der Funktion zaehlerMitStartwert wird eine Funktion zurückgegeben, die bei jedem Aufruf die Variable n um eins erhöht zurückgibt. Obwohl die Variable n zur äußeren Funktion gehört und damit beim Aufruf der zurückgegebenen Funktion mit next1() bzw. next2() eigentlich gar nicht mehr vorhanden ist, merkt sich die innere Funktion die lokale Variable separat für jede zurückgegebene innere Funktion – und genau das macht sie zur Closure.

Man kann sich Closures als Objekte vorstellen, die aus einer einzigen Methode/Funktion bestehen und wie Objekte zusätzlich einen Zustand haben. Schön sehen kann man dies an den generierten Java-Bytecode-Klassendateien: closure.class und closure$1.class. Die innere, anonyme Funktion wird für die JVM also einfach als innere, anonyme Java-Klasse closure$1.class abgebildet.

Closures sind prima geeignet, um damit z.B. Event-Handler zu definieren, denen man zusätzliche Informationen mitgeben kann (bei Farb-Setz-Buttons in einem Grafikprogramm beispielsweise die zu setzende Farbe). Dadurch kann man häufig auf das Schreiben von Event-Handler-Klassen verzichten, auch wenn dies in JavaFX Script ebenfalls möglich ist.

Vielleicht hat man es gemerkt: Ich halte JavaFX Script trotz des Namens ganz und gar nicht für eine Skriptsprache. Meines Erachtens wurde die Sprache vor allem deshalb so genannt, um zu zeigen, dass man damit in Wettbewerb zu Skriptsprachen wie Ruby tritt, die als agil und produktiv empfunden werden. JavaFX Script zeigt dabei sehr schön, dass sich Produktivität und statische, strenge Typisierung überhaupt nicht ausschließen müssen.

Dienstag, 25. August 2009

Hibernate 3.5 und JPA 2.0

Vor wenigen Tagen wurde die erste Betaversion von Hibernate 3.5 veröffentlicht. Sofern der Download von SourceForge klappt, kann man damit viele der Neuerungen von JPA 2.0 ausprobieren.

Hibernate 3.5 bietet keine wesentlichen neuen Hibernate-Features, und auch für die Unterstützung der neuen JPA-Version wurden vor allem standardkonforme Schnittstellen für bereits vorhandene Hibernate-Eigenschaften bereitgestellt. Dies ist wohl auch der Grund, warum die Hibernate-Version nicht gleich auf 4.0 heraufgesetzt wurde.

Schön ist meines Erachtens, dass ab Version 3.5 die JARs für die Hibernate-Annotationen und den JPA-EntityManager Bestandteil vom Hibernate-Kern (Core) geworden sind. Und die Einbindung von Envers dürfte diese Versionierungs-Bibliothek zum heißen Kandidaten für den De-Facto-Standard bezüglich Versionierung/Historisierung/Auditierung mit JPA machen.

Mittwoch, 22. Juli 2009

Lokaler Aufruf von @Remote in GlassFish

GlassFish muss als Java EE 5-konformer Application-Server beim Aufruf von EJB-@Remote-Interfaces die Übergabeparameter und Rückgabewerte kopieren (serialisieren). Der Aufruf erfolgt also mit Wert-Semantik ("pass-by-value"), selbst wenn der Aufrufer und die aufgerufene Session-Bean innerhalb derselben JVM laufen ("co-located") und der Aufruf damit prinzipiell lokal erfolgen könnte (durch Über-/Rückgabe von Referenzen, entsprechend "pass-by-reference" genannt).

Obwohl GlassFish den eigentlichen Remote-Aufruf bereits gut optimiert, wenn sich Aufrufer und aufgerufene Bean auf demselben Server befinden, kostet das Kopieren der Parameter Zeit und Speicher. Wenn die Parameter große Datenmengen beinhalten, kann es sich daher als Performanz-Optimierung lohnen, die Parameter des lokalen Remote-Aufrufs entgegen der Spezifikation als Referenz übergeben zu lassen. Man muss dann aber bedenken, dass Änderungen an den Parameterdaten – wie bei @Local-Aufrufen – an allen Stellen sichtbar sind, die eine Referenz darauf halten.

Aktivieren lässt sich der lokale Aufruf von @Remote-Methoden in GlassFish für eine komplette Enterprise-Applikation (EAR) im sun-application.xml-Deskriptor wie folgt:
<sun-application>
<pass-by-reference>true</pass-by-reference>
</sun-application>
Oder für einzelne EJBs in sun-ejb-jar.xml:
<sun-ejb-jar>
<enterprise-beans>
<ejb>
<pass-by-reference>true</pass-by-reference>
</ejb>
</enterprise-beans>
</sun-ejb-jar>
Achtung: Zumindest GlassFish 2.1 scheint einen Bug zu haben, dass die Einstellung in sun-application.xml ignoriert wird, wenn sich in einem EJB-Modul (EJB-JAR) kein sun-ejb-jar.xml befindet. Abhilfe: Einfach einen (abgesehen vom Wurzel-Element) leeren Deskriptor ergänzen.

Und nochmal Achtung: Offenbar klappt die Optimierung nur innerhalb einer Anwendung (EAR), nicht aber zwischen mehreren separaten Anwendungen bzw. EJB-Modulen auf demselben Server.

Warum will man überhaupt eine nicht standardkonforme Referenz-Übergabe aktivieren? Kann man nicht besser @Local-Interfaces verwenden, die die erwähnten Performanz-Vorteile sowieso mit sich bringen? Ja, man sollte @Local-Interfaces nach Möglichkeit bevorzugen.
Es gibt aber Fälle, bei denen man nicht schon bei der Programmierung entscheiden kann bzw. möchte, wie die Bean-Aufrufe durchgeführt werden, beispielsweise weil man sich die Option offen halten möchte, ob Frontend (WAR) und Backend (EJB-JAR) gemeinsam in einem EAR oder aber separat bereitgestellt ("deployed") werden. Im Quelltext wird man dann die @Remote-Variante programmieren, hat aber mit dem pass-by-reference-Schalter später per Konfiguration die Möglichkeit, die Aufrufart einfach und schnell umzustellen.

Sinnvoll erscheint dies vor allem dann, wenn man viele EJBs entfernt aufruft und alle Aufrufe auf einen Schlag optimieren will. Wenn man dagegen nur wenige EJBs entfernt nutzt oder nur einzelne Aufrufe optimieren möchte, kann man die Aufrufart – wenn auch mit etwas mehr Aufwand – über die Standard-XML-Deskriptoren ändern. Eine mögliche Realisierung ist hier beschrieben.

Donnerstag, 16. Juli 2009

Sein oder !sein, das ist hier die Frage

Echte C-Freaks werden mich dafür verachten, aber ich finde ausgeschriebene boolesche Operatoren wie not, and und or einfach lesbarer als !, && und || (und ja, ich habe jahrelang C und C++ programmiert und weiß, wovon ich rede). Ein Beispiel:
  if (!liste.isEmpty()) { ... }

if (not(liste.isEmpty())) { ... }
M.E. ist das Ausrufungszeichen beim Durchsehen des Quelltextes viel zu leicht zu übersehen (und Code wird normalerweise viel häufiger gelesen als geschrieben).

Während beispielsweise die EL beide Schreibweisen erlaubt, kennt Java leider nur die Symbol-Operatoren. Was tun? Zumindest für den Präfix-Operator not kann man statische Methoden definieren, die sich dann mittels statischer Imports wie in obigem Beispiel nutzen lassen:
  public static boolean not(boolean bval) {
return !bval;
}

public static Boolean not(Boolean bval) {
if (bval == null) {
return null;
}
else {
return !bval;
}
}
(Den kompletten Quelltext gibt es hier.)

Nur: Muss man sich diese Methoden selber schreiben, oder gibt es dafür bereits fertige Bibliotheken? Im Netz habe ich leider nichts passendes gefunden.

Donnerstag, 9. Juli 2009

java_home oder /Library/Java/Home?

Es ist immer wieder eine spannende Frage, wie man in Mac OS X die Standard-Java-Version einstellt bzw. wie man die für eine bestimmte Anwendung "richtige" Java-Version herausbekommt. Mac OS X 10.5 unterstützt derzeit bis zu drei verschiedene Hauptversionen (1.4.2, 1.5.0, 1.6.0), teils als 32-Bit-, teils als 64-Bit-Variante.

Bisher konnten Anwender die bevorzugte Java-Version getrennt für Applets und für andere Anwendungen (Web Start, Kommandozeile) mit /Programme/Dienstprogramme/Java/Java-Einstellungen konfigurieren. Alternativ konnte man natürlich auch die Umgebungsvariable JAVA_HOME setzen oder sich auf den festen Pfad /Library/Java/Home verlassen. Und weil diese Möglichkeiten immer noch nicht bei allen Anwendungen funktionierten, verbogen die ganz Wagemutigen diverse Symlinks im JavaVM-Framework (was aber auch nicht immer klappte und zu diversen Fehlern führte).

Bevor wir uns um die empfohlene Lösung kümmern, hier zunächst eine Warnung: Basteln Sie niemals an den Symlinks in /System/Library/Frameworks/JavaVM.framework (und in Unterordnern davon) herum! Auch dann nicht, wenn Sie viele gutgemeinte Tipps finden, wie man Current und CurrentJDK verbiegen kann... Apple weist seit langem darauf hin, dass diese Symlinks interne Implementationsdetails sind, die sich jederzeit ändern können.

Mit dem aktuellen Java-Update für Mac OS X 10.5 liefert Apple nun ein Kommandozeilenprogramm aus, mit dem sich die Benutzereinstellungen aus /Programme/Dienstprogramme/Java-Einstellungen auslesen lassen (beachten Sie, dass der Java-Unterordner in dem Pfad mit diesem Update verschwunden ist):

HeartOfGold:~ much$ /usr/libexec/java_home
/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home

Ein Aufruf mit der Option -h zeigt alle erlaubten Optionen an. Eine Liste aller verfügbaren JVMs – in der in den Java-Einstellungen festgelegten Reihenfolge – erhalten wir mit --verbose:

HeartOfGold:~ much$ /usr/libexec/java_home --verbose
Matching Java Virtual Machines (4):
1.5.0_19 (i386): /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home
1.6.0_13 (x86_64): /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home
1.5.0_19 (x86_64): /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home
1.4.2_21 (i386): /System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Home

Man kann auch ganz gezielt nach einer bestimmten Java-Version und/oder Prozessorarchitektur fragen:

HeartOfGold:~ much$ /usr/libexec/java_home --version 1.6 --arch x86_64
/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home

Sofern die gewünschte JVM nicht vorhanden ist, erhält man eine Fehlermeldung und eine hoffentliche passende alternative JVM:

HeartOfGold:~ much$ /usr/libexec/java_home --version 1.6 --arch i386
Unable to find any JVMs matching architecture "i386".
/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home


Zurück zur Ausgangsfrage. Wie finden wir damit z.B. in einem Shell-Skript die passende Java-Version für eine Anwendung? Apple empfiehlt folgendes Vorgehen:
  1. Prüfen Sie, ob /usr/libexec/java_home vorhanden ist, übergeben Sie optional die gewünschte Java-Version und werten Sie die Rückgabe aus.
  2. Wenn das nicht erfolgreich war, verwenden Sie als Java-Home-Verzeichnis fest kodiert (!) das Verzeichnis /System/Library/Frameworks/JavaVM.framework/Versions/1.X/Home, wobei Sie "1.X" durch die gewünschte Versionsnummer (1.4, 1.5, 1.6) ersetzen.
  3. Sollte das Verzeichnis nicht existieren, verwenden Sie /Library/Java/Home (und beten, das alles klappt).
100% perfekt ist das m.E. zwar nicht, denn Punkt 2 geht schief, wenn das Verzeichnis für Java 6 existiert, Java 6 aber nicht ausgeführt werden kann (das betrifft vor allem 32-Bit CoreDuo-Macs). Aber immerhin hat Apple nun dokumentiert, wie man künftig – und in den meisten Fällen auch bisher – zum korrekten Ergebnis gelangt.


Und was ist mit der Umgebungsvariable JAVA_HOME? Offenbar werten diverse Systemprogramme (wie /usr/bin/java) nicht nur die Rückgabe von /usr/libexec/java_home aus, sondern auch den Wert von JAVA_HOME. Wenn sich die Werte unterscheiden, kann das zu unerwartetem Verhalten führen... Insofern sollte mittelfristig auf das Setzen von JAVA_HOME besser verzichtet werden.

Da sich aber kurzfristig viele Anwendungen noch auf die Umgebungsvariable JAVA_HOME verlassen, ist es derzeit m.E. am besten, die Variable auf die Java-Version zu setzen, die auch in den Java-Einstellungen an oberster Stelle steht (auch wenn das erst einmal doppelten Konfigurationsaufwand bedeutet). Das Setzen der Variablen geschieht am einfachsten mit RCEnvironment, wodurch die Variable sowohl Shell-Skripten als auch GUI-Anwendungen bekannt ist:

Eine ausführliche Diskussion rund um java_home findet sich auf Apples Java-Mailingliste.

Donnerstag, 4. Juni 2009

VisualVM 1.1.1 im JDK

Letzte Woche hat Sun das JDK 6 Update 14 veröffentlicht. Darin enthalten ist unter anderem die aktuelle Version 1.1.1 von VisualVM, dem Werkzeug für Monitoring und einfaches Profiling. Derzeit ist damit also kein separater Download mehr erforderlich, um die vielen kleinen Verbesserungen und Bugfixes gegenüber der Version 1.0.1 zu erhalten, die seit dem JDK 6 Update 11 mitgeliefert wurde.

Sun hat übrigens vor einigen Wochen einen Blog-Wettbewerb zu VisualVM gestartet, der noch bis zum 24. Juni läuft. Dieses Blog läuft allerdings außer Konkurrenz, da es nicht auf Englisch geschrieben ist...

Sonntag, 31. Mai 2009

NetBeans 6.7 im nächsten Monat

Die JavaOne startet in nicht einmal zwei Tagen – ein prima Termin, um NetBeans 6.7 zu veröffentlichen, das sich derzeit noch im Beta-Stadium befindet.

Die neue Version von Suns Entwicklungsumgebung bietet viele Neuerungen und Verbesserungen, u.a. einen erweiterten Profiler, die Unterstützung von GlassFish v3 sowie die Integration von Suns Entwickler-Portal Kenai.

Für Entwickler, die NetBeans unter Mac OS X nutzen, wird dies ein besonders schönes Release. Das Projektfenster wurde an das native Aussehen des Systems angepasst und erinnert nun deutlich an Xcode:

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.

Samstag, 28. März 2009

Scala ist auch eine Oper

... aber natürlich vor allem eine funktionale, objektorientierte Programmiersprache für die Java Virtual Machine :-)

Scala hat das Zeug dazu, die nächste große Sprache nach bzw. neben Java zu werden. Viele Ausdrücke lassen sich in Scala einfacher und kürzer als in Java schreiben, dennoch bleibt die statische und starke Typisierung wie bei Java erhalten. Ein großes Plus ist zudem die nahtlose Integration mit den unzähligen bestehenden Java-Bibliotheken. Und Scala ist nicht wirklich neu und nur irgendwie gerade cool, sondern bereits acht Jahre alt und stabil.

Zum Einstieg verwendet man am besten Eclipse 3.4 (das "Classic"- oder "Java EE"-Paket). Ein JDK 5.0 oder neuer sollte bereits installiert sein. Nun fehlt nur noch das Scala-Plugin, das man in Eclipse im Menü Help > Software Updates... mit der Update-URL

http://www.scala-lang.org/scala-eclipse-plugin

herunterladen lässt.

Wechselt man in die Scala-Perspektive, kann man unter File > New ein neues "Scala Project" anlegen. Wie aus Java gewohnt legt man ein Paket an und in diesem Paket dann ein "Scala Object" (!). Richtig gelesen, Scala unterstützt nicht nur Klassen, sondern auch einzelne Objekte. Wenn wir dieses Objekt von Application erben lassen, entspricht das der main-Methode in Java:


Scala bietet Typinferenz, d.h. man muss beim Deklarieren einer Variablen nicht zwingend den Typ angeben, wenn Scala diesen aus dem Kontext eindeutig ableiten kann. Die Variable ist dann trotzdem zur Übersetzungszeit typsicher, man kann also keine inkompatiblen Werte zuweisen:


Wie bei funktionalen Sprachen üblich, liegt eine der Stärken in der Listenverarbeitung. Listen können mit einer Java-ähnlichen Syntax angelegt werden (es geht auch funktionaler, siehe erster Screenshot):

val früchte = List("Äpfel", "Birnen", "Orangen")

Entweder kann man die Liste dann mit einer Java 5-ähnlichen for-each-Schleife ausgeben:

for (frucht <- früchte) { println(frucht) }

Oder man nutzt ein anonymes Funktionsliteral, eine sogenannte Funktion höherer Ordnung, die man als Wert an die foreach-Methode der Liste übergibt:

früchte.foreach(f => println(f))

Da man den Punktoperator beim Methodenaufruf weglassen darf und println nur ein Argument erwartet, geht das auch noch kürzer:

früchte foreach println

Und immer daran denken: Auch wenn das so langsam nach einer Skriptsprache aussieht (und tatsächlich ähnlich schnell programmiert werden kann), haben wir hierbei statische, starke, vom Compiler geprüfte Typisierung!

Soweit fürs Erste, das sollte als Appetitanreger reichen :-)

Mittwoch, 25. März 2009

Monitoring und Profiling mit VisualVM

Am Wochenende habe ich einen kleinen Vortrag über VisualVM gehalten. Dieses nützliche Werkzeug ist seit dem Update 7 fester Bestandteil des JDK 6. Es bietet gute Überwachungsmöglichkeiten für laufende Java-Anwendungen (sowohl lokal als auch auf entfernten Rechnern). Beim Profiling kann es zwar nicht ganz mit kommerziellen Lösungen mithalten, bietet aber dennoch für viele Situationen vollkommen ausreichende Auswertungen (z.B. die Aufrufstellen einer Methode).

VisualVM benötigt Java 6 als Laufzeitumgebung, kann aber Anwendungen schon ab Java 1.4 überwachen. Faustregel: Je neuer die Laufzeitumgebung des auszuwertenden Programms ist und je lokaler sie läuft, desto bessere Informationen liefert VisualVM.

Leider liefert Apple VisualVM auch mit dem neuesten Update ihrer Java SE 6-Implementation nicht mit. Sofern man dieses Update eingespielt hat, kann man VisualVM 1.1.1 aber problemlos separat herunterladen und (durch Auspacken des ZIP-Archivs) installieren.

Da es gute Gründe gibt, als Standard-Java-Version unter Mac OS X derzeit noch 5.0 einzusetzen, muss man Java 6 beim Aufruf im Terminal explizit als Parameter angeben:

HeartOfGold:visualvm-1.1.1 much$ bin/visualvm --jdkhome /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/ &


Das Profiling funktioniert mit der Apple-Java-Implementierung noch nicht wirklich zuverlässig... Eventuell ist ein älterer Bug immer noch nicht vollständig korrigiert. Immerhin stehen die Monitoring-Möglichkeiten komplett und stabil zur Verfügung.

Mit dem neuen "Memory Sampler"-Plugin können aber zumindest Live-Speichervergleiche (Deltas) ermittelt werden, was einen wichtigen Aspekt des Memory-Profilings abdeckt. Und der Memory Sampler läuft zum Glück auch unter Mac OS X.

Mittwoch, 18. März 2009

WebObjects-Anwendungen in Eclipse entwickeln

Still und heimlich hat Apple vor ein paar Wochen eine Anleitung veröffentlicht, wie man WebObjects-Anwendungen in Eclipse mit den WOLips-Plugins entwickelt.

Zur Erinnerung: WebObjects ist Apples Enterprise-Framework zur Entwicklung skalierbarer Web-Applikationen. Von NeXT bereits 1996 veröffentlicht, ist es heute die Software-Basis u.a. vom iTunes Store. 2001 wurde das bis dahin in Objective-C entwickelte Framework komplett neu geschrieben – in Java. Entsprechend kann WebObjects 5.x problemlos mit Java SE oder innerhalb eines Java EE-kompatiblen Servers genutzt werden.

Nachdem Apple 2006 die Weiterentwicklung der eigenen, auf Xcode basierenden Entwicklerwerkzeuge eingestellt hat, unterstützt und verwendet Apple nun WOLips, eine OpenSource-Plugin-Sammlung für Eclipse, die die WebObjects-Entwicklertools nachbildet.

Montag, 16. März 2009

Java EE und JavaFX für Mac OS X

Während nach wie vor Apple für das JDK (Java SE) unter Mac OS X verantwortlich ist und sich dabei nicht besonders gut um ältere Macs kümmert, sind andere Java-Technologien direkt von Sun für Mac OS X erhältlich:
Beide Produkte können ab Mac OS X 10.4 genutzt werden, sowohl auf Intel- als auch auf PowerPC-G5-Macs – sofern Apples aktuellstes Java 5.0-Update installiert ist.

Als JEE-Umgebung eignet sich natürlich auch der JBoss AS 5.0, der ebenfalls problemlos unter Mac OS X betrieben werden kann.

Donnerstag, 12. März 2009

Aktuelle Java-Versionen für Mac OS X

Im Netz taucht immer wieder die Frage auf, welche Java-Versionen aktuell unter Apples Mac OS X genutzt werden können – gerade wenn Java-Entwickler ihre Programme nicht selber auf einem Mac testen können.

Für Mac OS X 10.5 sind die Java-Versionen 1.4, 5.0 und 6 direkt von Apple verfügbar und können über die Softwareaktualisierung ins System eingebunden werden (als JDKs, es gibt von Apple keine separaten JREs). Unter Mac OS X 10.4 war zusätzlich noch Java 1.3 nutzbar, dafür hat Apple für diese ältere Betriebssystemversion kein Java SE 6 herausgebracht.

Aber Achtung: Gerade Java SE 6, also die einzige Version, die Ende dieses Jahres noch nicht EOL sein wird, steht nur für 64-Bit-Intel-Macs zur Verfügung. Das sind solche mit Core2Duo-Prozessor. PPC-Macs und ältere Intel-Macs mit CoreDuos bleiben leider außen vor...

Standardmäßig ist für Applets und Java Web Start-Anwendungen so oder so Java 5.0 eingestellt. Mit dem System-Utility /Programme/Dienstprogramme/Java/Java-Einstellungen kann man dies ändern (siehe Bildschirmfoto).

Fazit: Java 6 ist in der Apple-Welt für Endbenutzer leider immer noch nicht wirklich angekommen. Wenn Ihre Java-Applikationen von möglichst vielen Apple-Nutzern eingesetzt werden sollen, sollten Sie derzeit noch Java 5.0 als Mindestanforderung voraussetzen.

Und nun noch ein paar technische Details:

Die aktuellen JDKs für Mac OS X haben die Versionsnummern 1.4.2_18, 1.5.0_16 und 1.6.0_07. Sie können mit den Software-Updates Java for Mac OS X 10.4 Release 7 bzw. Java for Mac OS X 10.5 Update 2 installiert werden (es gibt mittlerweile auch Release 8 und Update 3, aber dadurch wird nur Java Web Start aktualisiert und nicht nicht JDK-Version verändert).

Das Java-Update für Mac OS X 10.5 installiert übrigens auch auf CoreDuo-Macs Java SE 6, nur leider lässt sich das auf diesen Rechnern trotzdem nicht ausführen...

Seit einigen Jahren pflege ich eine detaillierte Liste von Mac OS X- und deren Java-Versionen inkl. der entsprechenden Download-Links.

Unabhängig von Apples Java-Versionen gibt es mit SoyLatte seit einiger Zeit auch eine Portierung vom OpenJDK für Mac OS X. Damit kann Java SE 6 nicht nur unter Mac OS X 10.5, sondern auch unter 10.4 genutzt werden. Allerdings ist auch SoyLatte nur für Intel-Macs verfügbar, und für grafische Ausgaben wird das für Mac-Oberflächen unübliche X11 anstelle von Cocoa genutzt.

Bleibt zu hoffen, dass Sun es (mit Apples Hilfe?) ab Java 7 schafft, selber wieder ein vernünftiges JDK für Mac OS X herauszubringen.

Mittwoch, 4. März 2009

Das Swing Application Framework lebt!

Desktop-Anwendungen mit Java zu entwickeln, war schon immer so eine Sache... Zwar gibt es die Swing-Bibliothek seit Ewigkeiten und deren GUI-Komponenten sind auch recht brauchbar. Was aber fehlte, war ein Framework, mit dem man den ganzen Lebenszyklus einer Desktop-Applikation einfacher handhaben konnte.

Auch wenn im Laufe der Zeit mit der NetBeans Platform und Eclipse RCP Alternativen entstanden sind, wollte Sun mit dem Swing Application Framework (JSR 296) eine reine Swing-Lösung anbieten. Der Ansatz war gut, doch leider wurde es um diesen JSR in den vergangenen Monaten sehr still - so still, dass der JSR vor kurzem in den Status "inaktiv" versetzt wurde.

Nun hat sich einer der Sun-Entwickler zu Wort gemeldet und verkündet, dass nun wieder aktiv an Swing und dem Swing Application Framework (SAF) gearbeitet wird. In der letzten Zeit gab es offenbar einfach wichtigere Dinge (vermutlich JavaFX?) zu erledigen.

Bei den neuerlichen Aktivitäten ist interessant, dass nicht mehr nur MDI-Anwendungen unterstützt werden sollen, sondern explizit auch die vor allem unter Mac OS X verbreiteten SDI-Applikationen (und auch die beim Mac üblichen Menüzeilen ganz ohne offenes Dokument-Fenster). Das lässt hoffen, denn bisher war für solche GUI-Anpassungen ein nicht unerheblicher Mehraufwand nötig.

Man darf gespannt sein, ob das Swing Application Framework Bestandteil von Java 7 sein wird, dessen Veröffentlichung mittlerweile für 2010 vorgesehen ist.

Montag, 23. Februar 2009

@EJB Dependency Injection für Struts 1.2/1.3 Action-Klassen

Auch wenn das Web-Framework Struts - gerade in den Versionen 1.x - schon etwas in die Jahre gekommen ist und mittlerweile zahlreiche Alternativen zur Verfügung stehen (JSF, Struts 2, Wicket, Stripes, GWT, Eclipse RAP...), wird es immer noch häufig eingesetzt, gerade wenn in diesem Bereich viel Entwickler-Know-How vorhanden ist.

Unschön ist dabei allerdings die unnötig komplizierte Einbindung in aktuelle Java EE-Umgebungen, da zum Abfragen von Enterprise-Ressourcen (z.B. Session Beans) explizite JNDI-ServiceLocator-Aufrufe nötig sind.

In JEE-Standard-Klassen ("managed classes") wie Servlets und JSF Managed Beans steht dagegen Dependecy Injection (DI) zur Verfügung, wodurch man sich Referenzen auf Enterprise-Ressourcen nicht mehr aktiv holen muss, sondern automatisch bereitgestellt bekommt.

Mit meiner kleinen Bibliothek Struts-DI funktioniert dies auch innerhalb von Struts-Actions:

public class MyAction extends org.apache.struts.action.Action {

@EJB
private MySessionBean msb;


@Override
public ActionForward execute(...) throws Exception {

String backendResponse = msb.myCall(...);
...
}
}

Die dazu notwendige Konfiguration ist denkbar einfach. Anstelle des Struts-RequestProcessors wird in der struts-config.xml ein neuer RequestProcessor eingetragen:

<controller nocache="true"
processorClass=
"com.muchsoft.struts.annotations.RequestProcessor"/>

Dies ist unter Struts 1.2 das übliche Vorgehen, um sich in die Action-Verarbeitung einzuklinken. Mit Struts 1.3 funktioniert dies ebenso (der dort vorhandene Chain-Mechanismus wird nicht verwendet).

Freitag, 20. Februar 2009

Neues Jahr, neues Blog

Nachdem ich bisher unter http://www.snailshell.de/blog/ auf Englisch geschrieben habe, starte ich nun mein neues Java-Blog auf Deutsch. Viel Spaß damit!