Mit Version 9 gerät die nächste Major-Release von Java in Sichtweite. Sie lockt mit Neuerungen, die das Entwickeln und Betreiben von Java-Anwendungen erleichtern sollen. Doch auch diesmal rissen die Entwickler wieder alle Abgabetermine um Jahre. Beispiele und Hintergründe dazu liefert der Artikel.
Java[1] steht seit Entwicklergedenken in den einschlägigen Ranglisten der beliebten Programmiersprachen wie festgenagelt ganz weit oben. Kein Wunder, schließlich ermöglicht sie es, die mit ihr entwickelten Programme vom Handy bis hin zum Großrechner quer über alle Betriebssysteme hinweg einzusetzen. Ein Blick auf die ehemaligen Shootingstars Perl und PHP zeigt jedoch auch, dass es keine Garantie auf eine dauerhafte Bestplatzierung gibt.
Regelmäßige Updates sollen die Sprache auf der Höhe der Zeit und die auf neun Millionen geschätzten Java-Programmierer bei der Stange halten. Die müssen sich jedoch bisher in Langmut üben, denn die Major-Release 9 lässt auf sich warten. Ursprünglich war sie nur als Interimsrelease geplant, um die mit Java 1.8 nicht fertig gewordenen Themen schnellstmöglich nachzuliefern.
“Schnellstmöglich” hieß ursprünglich noch Mitte 2015, dann wurde Mitte 2016 daraus, schließlich 2017. Bleibt die Hoffnung, dass der Projektplan diesmal hält, immerhin stehen Test-Builds jetzt unter [2] zur Verfügung.
Mit der Stichsäge
Als Kernfeature von Java 9 schlechthin gilt das als Projekt “Jigsaw” (Stichsäge, [3]) bekannt gewordene Java Plattform Module System (JSR 376). Bisher werfen nämlich Java-Compiler und Laufzeitumgebung alle verfügbaren Bibliotheken aus dem Classpath in einen großen Topf. Ob dabei eine wohlschmeckende Suppe oder Blitz und Rauch entstehen, hängt von teilweise subtilen Änderungen im Classpath ab.
Im Zweikampf gegen den Classloader in der Classpath-Hölle verbrannte schon manche Entwicklerstunde. Das Problem: Nur die richtigen Bibliotheken und Bibliotheksversionen ergeben in der passenden Reihenfolge eine lauffähige Anwendung. Ansätze wie Maven, »ServiceLoader« oder OSGI entzerrten die Situation nur teilweise und mussten um die Standardbibliotheken ohnehin einen großen Bogen schlagen.
Mit Jigsaw soll dies nun anders werden. Das neue Modulsystem zieht sich durch den gesamten Stack vom Compiler über die JRE und die Standardbibliotheken bis hin zu den Anwendungsbibliotheken. Der Entwicklungsaufwand für Jigsaw wurde ursprünglich auf die Konfektionsgröße XL geschätzt, in der Realität kamen allerdings noch ein paar X hinzu. Die ersten Pläne nahmen Java 1.7 ins Visier, letztlich erscheint Jigsaw nun mit Java 9. Alle drei betroffenen Versionen haben sich jeweils um Jahre verzögert.
Total modular
Der Preis für die Mühen ist nicht nur eine verbesserte Handhabung von Bibliotheken. Die Modularisierung erlaubt es Entwicklern auch, die JRE auf die Bedürfnisse einer Anwendung zuzuschneiden. Schließlich schlagen die Standardbibliotheken von Java 8 mit rund 60 MByte und 20000 Klassen zu Buche. Die benötigen nicht nur Platz auf der Festplatte, der Rechner muss sie auch bei jedem Start in den Hauptspeicher laden und auf die darin enthaltenen Klassen und Methoden abklopfen. Schwächere Geräte (Industrie-Automatisierung, IoT, Network Appliance) begrüßen jedes gesparte Megabyte, damit die Anwendung weniger Platz kostet und schneller startet.
Java 9 zerlegt zugleich die monolithische Standardbibliothek in Module. Wer Funktionalitäten wie den Corba-Stack, den Javascript-Interpreter oder verschlüsselte XML-Dokumente nicht braucht, erhält nun erstmals die Chance, die Installation genau auf die eigenen Bedürfnisse zurechtzuschneidern.
Ein Szenario
Zur Illustration des neuen Modulkonzepts dient das Szenario aus Abbildung 1. Die Beispielanwendung besteht aus zwei selbst entwickelten Modulen (»modulea« und »app«). Mit an Bord sind zudem jeweils zwei Module aus Java selbst beziehungsweise zum Logging.
Das Modul »modulea« umfasst alles rund um das Businessobjekt »Kunde«, es enthält je ein Interface »Kunde«, den zuständigen »KundenDienst« zum Suchen und Speichern sowie die zugehörigen Implementationsklassen (»Impl«). Hierfür braucht die Software Klassen aus den Modulen »java.core«, »java.xml« sowie »slf4j.core«. Das Modul »app« enthält die Anwendung selber und benötigt das »modulea« sowie zusätzlich »logback.core«, die in »moda« benötigten Module stehen implizit bereit.
Auf Datei-Ebene sind Module ganz normale Java-Archive im Jar-Format, die zusätzlich eine Modulbeschreibung enthalten. Diese liefern jeweils die »module-info.java«-Dateien, die der Entwickler mit den anderen Klassen kompiliert und in das Jar packt.
Listing 1 zeigt die Modulbeschreibung für »modulea«. Die Deklaration beginnt mit dem »module«-Schlüsselwort, über »require« verweist der Code auf die benötigten Module. Dies nutzen der Compiler und das JRE, um die Module zu laden beziehungsweise gleich beim Start über ihr Fehlen zu informieren. Damit andere Module wie »app« die beiden Interfaces nutzen, gibt die Modulbeschreibung das enthaltende Package über »exports« zur Verwendung frei.
Listing 1
Modulbeschreibung für modulea
01 module de.lm.java9.modulea {
02 // benötigt
03 requires java.base;
04 requires java.xml;
05 requires slf4j.api;
06
07 // exportiert
08 exports de.lm.java9.modulea;
09
10 // stelle eine service-implementation bereit
11 provides de.lm.java9.modulea.KundenDienst with de.lm.java9.modulea.intern.KundenDienstImpl;
12 }
Alle anderen Klassen aus »modulea« fehlen außerhalb des Moduls. Zwar ist die Klasse »KundenImpl« als »public« deklariert, lässt sich dank eines fehlenden »exports« des enthaltenen Package jedoch außerhalb des Moduls nicht erreichen. Auf diesem Wege sorgen Entwickler im Sinne der losen Kopplung dafür, dass die Benutzer einer Bibliothek deren Interfaces sehen, aber keinen Zugriff auf die eigentliche Implementierung oder Hilfsklassen erhalten.
Letztlich müssen sie jedoch irgendwie an Objektinstanzen der exportierten Schnittstellen gelangen. Die Implementierung des »KundenDienst« in das öffentliche Paket zu stellen, widerspräche der Idee der losen Kopplung, denn ein Anwender der Objekte soll sich nicht darum kümmern müssen, wie diese implementiert sind. Hier kommt der altbekannte, aber wenig genutzte »ServiceLoader« als Lösung zum Zuge. Bei ihm lässt sich eine Instanz des »KundenDienst« abfragen, ohne seine Implementation kennen und erreichen zu müssen. Seine Anwendung zeigt Listing 3, in dem die Hauptanwendung einen »KundenDienst« abfragt, ohne etwas über die zugehörige Implementationsklasse zu wissen.
Listing 3
Anwendung des ServiceLoader
01 // ServiceLoader für ein KundenService erzeugen
02 ServiceLoader<KundenDienst> sl
03 = ServiceLoader.load(KundenDienst.class);
04
05 // Erste Implementation nutzen
06 KundenDienst dienst = sl.iterator().next();
07
08 Set<Kunde> kunden = dienst.suche("Tux*");
Der »ServiceLoader« funktioniert also als Mittelsmann zwischen verschiedenen Modulen. Damit er das kann, muss das bereitstellende Modul die Implementierung über das Schlagwort »provides« bekannt machen (Listing 1, Zeile 11) und das nutzende Modul sie über »uses« bekannt geben (Listing 2, Zeile 7).
Listing 2
Modulbeschreibung für Module app
01 module de.lm.java9.app {
02 // benötigt
03 requires de.lm.java9.modulea;
04 requires logback.core;
05 requires logback.classic;
06 // verwendet Dienst
07 uses de.lm.java9.modulea.KundenDienst;
08 }
Java 9 zerlegt die normale Laufzeitbibliothek in 96 kleine Module wie etwa »java.core« oder »java.xml«. Eigene Anwendungen lassen sich vergleichsweise einfach auf das neue Modulkonzept umstellen, dafür genutzte Bibliotheken müssen es in der Regel nicht schlagartig unterstützen. Vielmehr genügt es meist, die normalen Jars in den Modul-Path (hier zum Beispiel »slf4j« oder »logback«) zu legen. Sie stehen dann als Module im Automatic-Modus bereit.
Für sie gelten normale Sichtbarkeitsregeln aus der Klassendefinition (»private«, »package«, »public«). Die Modulnamen leiten sich jeweils aus den Namen der Jar-Dateien ab. Über ihn lassen sie sich von echten Modulen im Named-Modus mit Modulbeschreibung nutzen.
Wer unsaubere Hacks wie »Class.forName« einsetzt oder Ressourcen quer über Jars lädt, wird seine Anwendung unter Java 9 nicht ohne Änderungen zum Laufen bringen. Hier steht eine Umstellung auf das neue Konzept an oder ist zumindest ein Brückenmodul gefragt, das alte und neue Welt verbindet.
Insgesamt scheint sich das Warten auf Jigsaw gelohnt zu haben. So lassen sich die Abhängigkeiten und publizierten Schnittstellen nun sauber definieren und im gesamten Lebenszyklus vom Kompilieren bis hin zur Laufzeit auswerten. Über den »ServiceLoader«-Mechanismus und die unterschiedlichen Sichtbarkeiten innerhalb und außerhalb eines Moduls erzwingt der Entwickler erstmals eine lose Kopplung.
Die modulare Laufzeitumgebung selbst ermöglicht zudem einen schmaleren Speicher-Fußabdruck auf schwacher Hardware. Da schmerzt auch »Class.forName« nur vergleichsweise wenig. Als Wermutstropfen erweist sich der noch fehlende Support für Modulversionen wie ihn Maven bietet. Hier bleibt nur der Ausweg über den Modulnamen wie »modulea_v1«, »modulea_v2« und so weiter.
Werkzeuge
Für die Arbeit mit Modulen bietet Java 9 zwei neue Werkzeuge an: Jdeps und Jlinker. Das erste der beiden kümmert sich um Aufgaben rund um Modulabhängigkeiten. Unter anderem bereitet es diese als Text oder Graph auf oder generiert Modulbeschreibungen für bestehende Jar-Dateien. Mit Jlinker lässt sich die Laufzeitumgebung gemäß einer Modulbeschreibung zuschneiden. Eine minimale Installation schrumpft so von 275 MByte (Java 8) auf 40 MByte, die Startzeit einer einfachen Anwendung reduziert sich auf einem PC um zwei Drittel auf zirka 0,9 Sekunden.
Das Javadoc-Werkzeug erzeugt dank der neuen »-html5«-Option nun HTML mit einem moderneren, barrierefreien Aufbau und Aussehen. Dabei geht es nicht nur um ein geändertes Stylesheet, die generierte Java-Dokumentation glänzt unter anderem mit einer eingebauten Suche für Klassen- und Methodennamen sowie Tooltipps (Abbildung 2). Die angezeigten Klassen lassen sich auf die der ausgewählten Java-Module einschränken beziehungsweise zeigen nur Module für eine bestimmte Klasse an.
Schalwerk
Ein weiterer Neuzugang ist die Jshell. Dank ihr kommt Java unkompiliert als Interpreter-Sprache zum Einsatz. Die Idee selbst ist nicht neu: Patrick Niemeyer präsentierte vor 17 Jahren mit der Bean-Shell eine erste Implementierung [4]. Die unterstützte jedoch nie den vollen Umfang von Java, die Arbeit daran schlief in den letzten Jahren ein. Grund genug für die Java-Compilergruppe unter dem Codenamen Kulla (ein babylonischer Ziegelgott) die Arbeit an einer neuen Shell zu beginnen. Sie ermöglicht als REPL-Tool (Read, Evaluate, Print, Loop) das zeilenweise Ausführen von Javacode und wird mit Jshell auf der Kommandozeile gestartet (Abbildung 3).
Die Jshell evaluiert die Eingabe ähnlich wie bei Python oder TCL nach jedem Zeilenumbruch, egal ob es sich um normalen Javacode oder eines der Jshell-Kommandos handelt. Die beginnen alle mit dem Schrägstrich, »/help« zeigt eine Übersicht der verfügbaren Kommandos. Führt es eine Zeile aus, legt Java 9 Variablen wie im Beispiel entweder automatisch an oder der Entwickler muss sie mit Typ und Namen definieren. Das Kommando »/var« zeigt ihm eine Liste der definierten Variablen und ihrer Werte.
Normale und statische Importe akzeptiert die Jshell an beliebiger Stelle, die bisher definierten listet »/imports«. In der Jshell testen Entwickler nicht nur kleine Codeschnipsel wie hier den »DateTimeFormatter«. Sie unterstützt die volle Syntax von Java 9, vom einfachen Einzeiler über Lambda-Ausdrücke bis hin zur Definition ganzer Methoden und Klassen.
Glücklicherweise muss der Programmierer diese nicht immer fehlerfrei in die Kommandozeile tippen. Bei falsch eingegebenem Code erscheint eine Fehlermeldung des Java-Compilers, und er darf einen neuen Versuch starten. Wie in der Bash holt er vorherige Eingaben über die Pfeiltasten wieder zurück und verändert sie. Auch eine Erweiterung von Klassenpfaden über [Tab] funktioniert analog zur Pfaderweiterung.
Wer lieber im grafischen Editor als auf der Kommandozeile arbeitet, startet einen solchen über das »/edit«-Kommando (Abbildung 4) oder liest extern geschriebenen Code mit »/o« aus einer Datei ein. Anders als bei kompiliertem Code ist das Semikolon am Ende der Zeile optional, und der Java-Entwickler kann sich das Behandeln von Exceptions über »try«- und »catch«-Blöcke ersparen. Eine tiefere Integration in die üblichen Verdächtigen, etwa Netbeans, Eclipse und Intelli-J, ist in den nächsten Monaten mit Sicherheit zu erwarten, IDEs für Python zeigen, was in dieser Richtung alles möglich ist.
Läuft
Für Java 9 waren auch Arbeiten an der Laufzeitbibliothek vorgesehen, davon bleiben aufgrund der Verspätung nicht mehr viele übrig. Am interessantesten bei den Zugängen ist sicher das erweiterte Process-API. Nach Jahren ist es damit erstmals möglich, externe Prozesse nicht nur zu starten, sondern auch zu beenden, an ihre Prozess-ID zu gelangen oder bequem auf ihr Ende zu warten.
Swing erhielt etwas Feinschliff, statt der veralteten GTK+-2-Bibliotheken kommt nun die aktuelle GTK+-3-Bibliothek zum Einsatz, die auf Einstellungen für hochauflösende Displays hört.
Dem Zug der Zeit folgt auch der HTTP-Client mit dem HTTP/2-Support. Das Frame-basierte Protokoll winkt mit weniger Overhead und geringerer Latenz und wird dank der neuen Klassen »HttpRequest« und »HttpResponse« nun vollständig unterstützt. Die »HttpRequest«-Klasse dient als Builder für eine Anfrage, das Fluent-API erlaubt eine bequeme Angabe aller notwendigen Informationen dafür. Die Ergebnisdarstellung nutzt »HttpResponse«, ein Beispiel zeigt Listing 4.
Listing 4
HTTP/2-Client
01 HttpResponse response = HttpRequest
02 .create(new URI("https://www.linux-magazin.de"))
03 .headers("User-Agent", "Java9")
04 .GET()
05 .response();
06
07 LOG.info("status : " + response.statusCode());
08
09 Map<String, List<String>> headers = response.headers().map();
10 for (String key : headers.keySet()) {
11 LOG.info(key + " : " + headers.get(key));
12 }
13
14 String body = response.body(asString());
15 LOG.info("body : " + body);
Fast noch wichtiger als die Neuzugänge ist die Frage, welche Elemente die Java-Entwickler ausrangieren möchten. Bislang haben sie zugunsten der Rückwärtskompatibilität keine einzige Klasse, Methode oder Variable entfernt. Inzwischen tragen immerhin 60 Klassen und 400 Methoden den Stempel »@deprecated«, einige davon lungern bereits seit Java 1.1 herum. Nach 20 Jahren hat Oracle nun endlich ein Einsehen und kündigt an, veraltete Teile zu entfernen.
Prominentestes Opfer ist das Java-Applet, das mit Java 10 sein Leben aushaucht. Es hat der Popularität von Java gerade in den Anfangsjahren gute Dienste erwiesen, ist aus Sicherheitsgründen aber genau wie Flash oder Silverlight nicht mehr im Browser erwünscht. Ironischerweise hinterlässt das scheidende Applet gerade im Bereich rund um die Zertifikatserstellung, den verschlüsselten Datentransfer oder Signaturen die größte Lücke, für das Malen im Browser genügt HTML 5.
Das Aufräumen soll in kleineren Schritten erfolgen, der erste ist bereits getan. Die »@deprecated«-Annotation bringt nun zwei optionale Attribute »forRemoval« und »since« mit. Sie zeichnen in Zukunft die Klassen aus, die final verschwinden sollen.
Bilanz
Neben den erwähnten Punkten gibt es noch eine ganze Reihe von Änderungen, die wahrscheinlich nur wenigen Entwicklern auffallen. So stammt von SAP ein Linux/s390x-Port, während Red Hat einen Linux/Aarch64-Port für die 64-Bit-ARM-Plattform beisteuert. Unicode ist nun in Version 7 statt 6.2 dabei, Java 9 lädt Tiff-Bilder, unterstützt SHA-3 und tut einiges mehr.
Auf technischer Seite findet Java 9 daher allgemeinen Zuspruch, die vorgenommenen Änderungen sind im Rahmen des JDK-Enhancement-Prozesses umfangreich abgestimmt. Auch zehn Jahre nachdem Sun die Quellen für Java freigegeben hat, funktioniert das Entwicklungsmodell ganz passabel, was auch die Beteiligung anderer Firmen zeigt. Wie lange es allerdings noch dauert, bis die wesentlichen Bibliotheken als Module zur Verfügung stehen, wird sich in den kommenden Monaten zeigen. Die Vorteile liegen klar auf der Hand.
Gerade hochkonfigurierbare Bibliotheken nutzen jedoch Konzepte, die mit Projekt Jigsaw nicht mehr laufen. Es bleibt zu hoffen, dass mit dessen Abschluss die ewigen Verspätungen zurückgehen. Laut der Release-Planung von 2014 soll 2017 Java 10 erscheinen. Die ersten Pläne dafür stehen schon fest, unter anderem soll das Projekt Valhalla [5] eine Verbesserung des Typsystems und der Geschwindigkeit mit sich bringen. Der noch fehlende Releasetermin sorgt für Spannung bis zum Schluss.
Infos
-
JDK-Vorabversion: https://jdk9.java.net/download/
-
Bean-Shell: http://www.beanshell.org









