JOpenDocument API

Zum Zugriff auf Dokumente und Spreadsheets von OpenOffice (for OASIS Open Document ) ist JOpenDocument (http://www.jopendocument.org/) eine GPL-Bibliothek, die das mit einer einfachen API unterstützt.

Etwa das Laden und Verändern von Spreadsheets:

File file = new File( "c:/in.ods" );
SpreadSheet createFromFile = SpreadSheet.createFromFile( file );
Sheet sheet = createFromFile.getSheet( 0 );
sheet.setValueAt( "Filling test", 1, 1 );
sheet.getCellAt( "A1" ).setValue( "On site support" );
sheet.getCellAt( "I10" ).setValue( new Date() );
sheet.getCellAt( "F24" ).setValue( 3 );
File outputFile = new File( "c:/out.ods" );
sheet.getSpreadSheet().saveAs( outputFile );

Weitere Beispiele gibt http://www.jopendocument.org/documentation.html.

Interessant ein ein purer Java-Viewer, und damit die Möglichkeit in PDF zu exportieren, ohne dass man OO dazu fernsteuern muss.

Beim Testen der SpreadSheet-API sind mir leider einige Nachteile aufgefallen:

  • Es gibt keine Named References
  • Die API ist sehr Datei-orientiert. Nur im Speicher Dokumente anzulesen und zu verarbeiten ist nicht möglich. Ich sehe erst einmal keine Methode, wie ein Servlet z.B. sich den InputStream auf ein OO-Dokuments holen und als OutputStream an den Client verschicken kann, ohne dass man vorher das OO-Dokument in eine Datei schreibt.
  • Soll der eingebauter Viewer verwendet werden, können TIFF-Bilder nicht angezeigt werden.
  • GPL könnte für einige Bereiche ein Problem sein. Es werden aber kommerzielle Lizenzen verkauft.
  • Nur das Anzeigen von einfachen Dokumenten ist möglich.

Community-Aufruf: Testet eure Software mit Java 8

Das OpenJDK 8-Team spielt gerade das Release mit unterschiedlichen OS-Produkten durch, um Inkompatibilitäten festzustellen. Bitte testet eure Software mit aktuellen Builds von Java 8 und meldet Fehler, etwa hier im Blog oder noch besser auf https://groups.google.com/forum/#!forum/adopt-openjdk. Falls eure Software über ein CI-System bebaut und getestet wird, nehmt Java 8 jetzt schon mit auf.

Die aktuelle Matrix gibt es hier: https://java.net/projects/adoptopenjdk/pages/TestingJava8. Man sieht, dass die wichtigen Projekte, wie GlassFish/Groovvy noch nicht funktionieren.

 

Project

Open Issue(s)?

Closed Issue(s)?

Testing Status
None / CI / Manual / Unknown

Adopt OpenJDK Volunteer

Project Contact Details

ActiveMQ

Test failed under Java 8

 

Manual

Greg Bakos

Greg Bakos

Camel

   

None

   

Glassfish 4.0

Error on startup

 

Unknown

   

Guava-libraries

Build error

 

Manual

Helio Frota

Mailing list

Gudeg7

Prototype phase

 

Manual

Yosi Pramajaya

Yosi Pramajaya

gs-collections

Java 8 Support

 

Manual

Mani Sarkar

Contacts on Wiki Communicate via the github issue tracker

Hawkshaw

 

Test failed under Java 8 (race condition)

CI

Martijn Verburg

Martijn Verburg

Jackrabbit

Test failed under Java 8

 

Manual

Greg Bakos

Greg Bakos

Jacoco

ASM5 support
static line nos.

ASM5 fix

CI

 

Mailing List

Java-Chronicle

 

Java 8 support

Manual

Mani Sarkar

Mailing list

JBehave

See UISpec4j

 

Manual

Helio Frota

Quick Subscribe using dev team

JSR-353

Javadoc compilation

 

Manual

Helio Frota

Mailing list

JUnit

   

Manual

Mani Sarkar

dev team

Maven

   

CI

Helio Frota

Mailing list

MongoDB Java driver

   

CI

Trisha Gee

Trisha Gee

MutabilityDetector
ClientOfMutabilityDetector
MutabilityDetector4FindBugs

 

All three projects support Java 8

Manual

Graham Allan
Mani Sarkar

Mailing list

Play Framework

See Scala

 

Manual

Helio Frota

Mailing List

RESTAPIUnifier

   

Manual

Mani Sarkar

Neomatrix

Scala

build does not support JDK 8 yet

 

Manual

Helio Frota

Scala Project

TestNG

   

Manual

Mani Sarkar

Mailing list

UISpec4j

UISPEC4J #5

 

CI

Helio Frota

Uispec4j

Yougi

 

Java 8 support

CI

Hildeberto

Hildeberto

Groovy

GROOVY-6279
GROOVY-6246
GROOVY-6156

 

Manual

Andres Almiray

Andres Almiray

Griffon

 

GRIFFON-642

Manual

Andres Almiray

Andres Almiray

Thema der Woche: Werte und Operatoren erkennen

Bean-Bean Mapping mit Dozer

Dozer ist eine kleine Open-Source Bibliothek (Apache-Lizenz) zum Mappen von JavaBeans auf JavaBeans. Motivationen gibt es genug — die Webseite zählt gleich einige Argumente auf:

  • A mapping framework is useful in a layered architecture where you are creating layers of abstraction by encapsulating changes to particular data objects vs. propagating these objects to other layers (i.e. external service data objects, domain objects, data transfer objects, internal service data objects). A mapping framework is ideal for using within Assembler type classes that are responsible for mapping from one data object to another.
  • For SOA/ESB systems, a side effect is the passing of domain objects between different systems. Typically, you won’t want internal domain objects exposed externally and won’t allow for external domain objects to bleed into your system.
  • Mapping between data objects has been traditionally addressed by hand coding value object assemblers (or converters) that copy data between the objects. Most programmers will develop some sort of custom mapping framework and spend countless hours and thousands of lines of code mapping to and from their many transfer objects.
  • A generic mapping framework solves these problems. Dozer is an open source mapping framework that is robust, generic, flexible, reusable, and configurable.
  • Data object mapping is an important part of layered service oriented architectures. Pick and choose the layers you use mapping carefully. Do not go overboard as there is maintenance and performance costs associated with mapping data objects between layers.

Basis des Mappings sind XML-Dokumente; im einfachsten Fall:

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">
  <mapping>
<class-a>d.e.i.n.p.a.k.e.t.TestObject</class-a>
<class-b>d.e.i.n.p.a.k.e.t.TestObjectPrime</class-b>
<field> <A>yourSourceFieldName</A> <B>yourDestinationFieldName</B> </field>
</mapping>
</mappings>

Angestoßen wird er über ein Mapping-Objekt, welches auch leicht von Spring injiziert werden kann:

Mapper mapper = new DozerBeanMapper();

DestinationObject destObject = mapper.map(sourceObject, DestinationObject.class);

oder

DestinationObject destObject = new DestinationObject();
mapper.map(sourceObject, destObject);

Interessant finde ich die JMX-Integration. Der Mapper gibt Statistik-Informationen etwa über die Anzahl gemappter Objekte.

Mikroformate mit JAXB in XML abbilden

Die Informationen von Mikroformaten lassen sich mit JAXB relativ leicht in XML konvertieren:

import java.util.*;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;

public class MicroformatViaJaxb
{
  public static void main( String[] args ) throws JAXBException
  {
    Microformat mf = new Microformat();
    mf.elements.add( new Element( "name", "Christian Ullenboom" ) );
    mf.elements.add( new Element( "address", "tutego Allee" ) );
    mf.elements.add( new Element( "phone", "2873568956928" ) );

    JAXBContext context = JAXBContext.newInstance( Microformat.class );
    Marshaller m = context.createMarshaller();
    m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
    m.marshal( mf, System.out );
  }
}
@XmlRootElement( name = "div" )
class Microformat
{
  @XmlAttribute( name = "class" )
  public String key = "person";
  @XmlElement( name = "span" )
  public List<Element> elements = new ArrayList<Element>();
}
class Element
{
  @XmlAttribute( name = "class" )
  public String key;
  @XmlValue
  public String value;
  public Element() { }
  public Element( String key, String value )
  {
    this.key = key;
    this.value = value;
  }
}

Got all these abbreviations?

XML HK2 WSIT ESB JMS SSO SAO JSR JAX-RS JCP DOJO AJAX WSDP jBMP RIFE RAILS MC4J CAPS SAML WS-* WSRP SAW JXTA HTTP URL IIOP ORB JMX TCP/IP WSDL MEX XWSS MTOM XSD SMTP WSA SOAP JBI JCA CMT LDAP BMT CMPI VJM CDDL GPL BSD API JAX-B StAX JSP JSTL EJB JPA TCK JDBC JDK JRE JAXP XOP REST POJO SQL GUI URI XMLDSig JAAS GSSAPI META-INF NIO ACL JNL CORBA CDC CLDC MIDP ACID SSL DRDA UML SAX DOM XOM XML-RPC MVC XSLT DAO SVG COM FTP PDF JNA ANTLR SLF4J WiX

Alles klar?

Thema der Woche: N+1 SELECT Problem

Bei SQL-Anfragen über mehrere Tabellen trifft man schnell über das N+1 SELECT Problem.

  • Finde mehr zu dem Problem und entwerfe ein Szenario, was das Problem deutlich macht.
  • Welche Lösung gibt es für das Problem in SQL?
  • Wie löst man das Problem in einer Anfragesprache wie JPAQL?
  • Lässt sich das Problem durch geschickte Modellierung entschärfen? Warum ist das insbesondere für NoSQL-Datenbanken essenziell? Welchen Ansatz verfolgt man?

Meine Freundinnen

Mit 10 oder so hatte ich meine erste, ich erinnere mich noch genau, ich hatte sie in einem Buch kennengelernt. Basic war ihr Name. Immer was verträumt, aber eine treue Seele. Ich war jung, sie auch, wir stellen keine wilden Sachen an. Ein Jahr später lerne ich Assembline kennen, WOW, das war eine zackige Braut, der war ich lange treu. Ganz schmutzige Sachen haben wir gemacht, sie war auch ein bisschen devot. Bin dann umgezogen nach Amiga-City und hatte mich in die ältere Schwerster von Assembline verguckt, die war auch heiß und ging ganz schön ran. Da war ich um die 14. Aber ich wurde älter und mit Assembline war es immer so anstrengend, sie war total nachtragend, jeden kleinen Fehler nahm sie mir übel und wollte dann erst mal nicht mehr mit mir sprechen. Also habe ich Schluss gemacht. Zu der Zeit hatte ich auch Pascaline und Zee kennengelernt und viel erlebt, was man eben zu Dritt so macht. Das ging dann so ein paar Jahre, aber jetzt habe ich fast seit 20 Jahren nur noch eine: mein Java-Mäuschen. Die hat mir auch den g-Punkt und fillRect gezeigt, hui. Manches Mal zeigt sie mir den roten Kringel, aber mit ihr kann ich fast alles machen. Und was mich früher an Assembline scharf machte (ich sage nur „bare metal“), interessiert mich heute überhaupt mehr. Passt also, eine glückliche Beziehung.

Expression Language 3.0 (JSR-341), wow, aber komplett an mir vorbeigezogen

Schau http://www.youtube.com/watch?v=Uyx7fLXXSS4, http://www.youtube.com/watch?v=JEKpRjXL06w und http://rijkswatch.blogspot.de/2012/06/expression-language-30-is-in-public.html. Aus der Spezifikation http://download.oracle.com/otn-pub/jcp/el-3_0-fr-eval-spec/EL3.0.FR.pdf.

  • Lambdas: fact = n -> n==0? 1: n*fact(n-1); fact(5) oder employees.where(e->e.firstName == ‘Bob’)
  • Collection-Syntax: employees.where(e->e.firstName == ‘Bob’) oder {„one“:1, „two“:2, „three“:3}
  • Streaming wie in Java 8: books.stream().filter(b->b.category == ‘history’) .map(b->b.title) .toList() oder [1,2,3,4,5].stream().substream(2,4).toArray()

Insgesamt:

■ Added Chapter 2 “Operations on Collection Objects”.
■ Added 1.8, String concatenation operator.
■ Added 1.13, Assignment operator.
■ Added 1.14, Semi-colon operator.
■ Added 1.20 Lambda Expression.
■ Added 1.22 Static Field and Methods.
■ Added T and cat to 1.17 Reserved words.
■ Modified 1.16 Operator precedence.
■ Modified coercion rule from nulls to non-primitive types.
■ Many changes to the javadoc API.

Schön ist, dass man das auch eigenständig außerhalb vom Web-Container verwenden kann. Das macht die EL interessant für Template-Lösungen.

Siehe auch YouTube Video: https://www.youtube.com/watch?v=JEKpRjXL06w

Überblick: Aktuelle Versionen der Java EE 7 Technologien

Von https://blogs.oracle.com/theaquarium/entry/java_ee_7_platform_completes:

JSRs:

  • Java Platform, Enterprise Edition 7 (JSR 342)
  • Concurrency Utilities for Java EE 1.0 (JSR 236)
  • Java Persistence 2.1 (JSR 338)
  • JAX-RS: The Java API for RESTful Web Services 2.0 (JSR 339)
  • Java Servlet 3.1 (JSR 340)
  • Expression Language 3.0 (JSR 341)
  • Java Message Service 2.0 (JSR 343)
  • JavaServer Faces 2.2 (JSR 344)
  • Enterprise JavaBeans 3.2 (JSR 345)
  • Contexts and Dependency Injection for Java EE 1.1 (JSR 346)
  • Bean Validation 1.1 (JSR 349)
  • Batch Applications for the Java Platform 1.0 (JSR 352)
  • Java API for JSON Processing 1.0 (JSR 353)
  • Java API for WebSocket 1.0 (JSR 356)

MRs:

  • Web Services for Java EE 1.4 (JSR 109)
  • Java Authorization Service Provider Contract for Containers 1.5 (JACC 1.5) (JSR 115)
  • Java Authentication Service Provider Interface for Containers 1.1 (JASPIC 1.1) (JSR 196)
  • JavaServer Pages 2.3 (JSR 245)
  • Common Annotations for the Java Platform 1.2 (JSR 250)
  • Interceptors 1.2 (JSR 318)
  • Java EE Connector Architecture 1.7 (JSR 322)
  • Java Transaction API 1.2 (JSR 907)
  • JavaMail 1.5 (JSR 919)

MR steht für “Maintenance Releases”.

Kommandozeilenprogramm jcmd für Diagnosekommandos

Mit dem Kommandozeilenprogramm jcmd lassen sich Diagnosekommandos zu einer laufenden JVM schicken. Die Java-Programme werden wieder über eine PID identifiziert, die jcmd auch anzeigen kann:

$ jcmd.exe
2484 C:\Users\Christian\eclipse\\plugins/org.eclipse.equinox…
18868 sun.tools.jcmd.JCmd

Eclipse hat die PID 2484 und das Tool selbst – das bei jedem Starten natürlich eine neue PID bekommt – 18868.

Interessant wird jcmd dadurch, dass sich Diagnose-Kommandos senden lassen. Als erstes steht die PID, dann folgt das Kommando. Um ein Übersicht über die Häufigkeit von geladen Klassen zu bekommen ist GC.class_histogram zu nutzen:

$ jcmd.exe 2484 GC.class_histogram
2484:
num #instances #bytes class name
----------------------------------------------
1: 600676 40906520 [C
2: 549996 13199904 java.lang.String
3: 121570 4862800 java.util.WeakHashMap$Entry
4: 117120 3747840 java.lang.ref.WeakReference
5: 63926 2730616 [Ljava.lang.String;

Auf der Hilfeseite sind die Kommandos nicht aufgeführt, weil sie abhängig von der jeweiligen JVM sind und nicht im Tool jcmd selbst verankert sind. Daher müssen sie dynamisch von einem laufenden Java-Programm erfragt werden. Unser Elcipse-Prozess hatte die PID 2484, und dann kommt die Option help zum Einsatz:

$ jcmd.exe 2484 help
2484:
The following commands are available:
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.run_finalization
GC.run
VM.uptime
VM.flags
VM.system_properties
VM.command_line
VM.version
help
For more information about a specific command use 'help <command>'.

Wie die letzte Zeile verrät, gibt ein angehängtes Kommando weitere Informationen, etwa

$ jcmd.exe 2484 help GC.heap_dump
2484:
GC.heap_dump
Generate a HPROF format dump of the Java heap.
Impact: High: Depends on Java heap size and content. Request a full GC unless th
e '-all' option is specified.
Permission: java.lang.management.ManagementPermission(monitor)
Syntax : GC.heap_dump [options] <filename>
Arguments:
filename : Name of the dump file (STRING, no default value)
Options: (options must be specified using the <key> or <key>=<value> syntax)
-all : [optional] Dump all objects, including unreachable objects (BOOLE
AN, false)

Was ist eine Oracle JDK/JRE Certified System Configuration?

Eine ideale, perfekt getestete und unterstützte Umgebung gilt als Oracle JDK/JRE Certified System Configuration. Das ist eine Kombination aus Betriebssystem mit installierte Service-Packs; das beschreibt das Unternehmen unter http://www.oracle.com/technetwork/java/javase/config-417990.html für Java 7. Bei gemeldete Bugs auf nicht zertifizierten Plattformen kann dann schnell ein „Sorry, das ist eine nicht-unterstützte Plattform, das schauen wir uns nicht weiter an“ folgen. Bei Linux ist zum Beispiel Ubuntu-Linux markiert als „Not certified on Oracle VM“. Das heißt nicht, dass es dort nicht zu 100% läuft, nur, dass im Fehlerfall eben kein Fix geben muss.

JavaScript, Rhino und Nashorn

Java ist zwar eine tolle Allround-Programmiersprache, aber die explizite Typisierung und der Zwang, Klassen und Methoden zu deklarieren, machen Java nicht wirklich attraktiv für Skripte, wo eher die Kompaktheit zählt und wo keine lang zu pflegenden Programme entstehen. Um die JVM und die Java-Bibliotheken auch zur Automatisierung von Abläufen einzusetzen, lässt sich neben der Java-Syntax auch alternative Programmiersprachen einsetzen.

JavaScript ist eine flexible interessante Programmiersprache, die mittlerweile auch außerhalb von Browsern populär ist, serverseitig eingesetzt wird und auch für die Entwicklung von Desktop-Anwendung – siehe Gnome 3, WinRT, Chrome Apps oder FirefoxOS.

Das Oracle JDK/OpenJDK bzw. das JRE[1] bringt ab Java 6 neben dem Java-Compiler eine JavaScript-Engine und ein Kommandozeilentool mit, das Sprite ausführt. So lässt sich JavaScript als alternative Programmiersprache einsetzen – die Sprache ist also auswechselbar und steht versinnbildlicht auf den Bibliotheken und der Laufzeitumgebung. Auch lässt sich die JavaScript-Umgebung in eigene Java-Programme einbetten und integrieren.

Die Java-Distribution liefert eine JavaScript-Engine aus, die sich allerdings von Java 6 auf Java 8 verändert hat.

· Java 6, Java 7: Rhino (https://developer.mozilla.org/en-US/docs/Rhino). Die JavaScript-Engine – in Java programmiert – kommt von Mozilla und basiert auf einer Implementierung von Netscape, die ihren Browser damals komplett in Java schreiben wollten. Die Browser-Implementierung „Javagator“ wurde zwar eingestellt, doch die JavaScript-Umgebung lebte weiter und Sun lizenzierte die Technologie und bettete sie (minimal verändert) in Java 6 und Java 7 ein.

· Ab Java 8: Nashorn (http://openjdk.java.net/projects/nashorn/, http://openjdk.java.net/jeps/174). Oracle entwickelte von Grund auf eine ganz neue JavaScript-Engine, die den Code ohne Interpretation direkt in Bytecode übersetzt und eine exzellente Performance und Kompatibilität mit ECMAScript-262 Edition 5.1 zeigt. Obwohl die JavaScript Edition 6 noch keine Rolle spielt, unterstützt Nashorn einige Spracherweiterungen, etwa Abkürzungen für Lambda-Ausdrücke. Da Nashorn nur JavaScript selbst unterstützt, Rhino aber noch einige Mozilla-Bibliotheken, kommt es zu Inkompatibilitäten, falls Nashorn unreines JavaScript ausführen soll.


[1] Bei anderen Java SE-Implementierungen muss das nicht zwingend gegeben sein.

Java SE 7 Update 25

https://blogs.oracle.com/java/entry/java_se_7_update_25. Mit den üblichen Security-Udates und sonst noch ein paar Änderungen: http://www.oracle.com/technetwork/java/javase/7u25-relnotes-1955741.html. Nicht uninteressant finde ich folgendes, was die Kompatibilität von existierenden Anwendungen beeinflussen könnte:

Changes to Runtime.exec

On the Windows platform, the decoding of command strings specified tojava.lang.ProcessBuilder and the exec methods defined by java.lang.Runtime, has been made stricter since JDK 7u21. This may cause problems for applications that are using one or more of these methods with commands that contain spaces in the program name, or are invoking these methods with commands that are not quoted correctly. For more information see JDK 7u21 Release Notes.

In JDK 7u25, the system property jdk.lang.Process.allowAmbigousCommands can be used to relax the checking process and may be used as a workaround for some applications that are impacted by the stricter validation. The workaround is only effective for applications that are run without a security manager. To use this workaround, either the command line should be updated to include -Djdk.lang.Process.allowAmbigousCommands=true or the java application should set the system property jdk.lang.Process.allowAmbigousCommands to true.

Quoting and escaping commands on Windows platform is complicated. The following examples may be useful to developers if they are impacted by the stricter validation.

Example 1: The application needs to be launched with C:\Program Files\foo.exe.

Here are 3 possible ways:

Process p = new ProcessBuilder("c:\\Program File\\foo.exe").start();
Process p = Runtime.getRuntime().exec(new String[] { "c:\\Program File\\foo.exe" });
Process p = Runtime.getRuntime().exec("\"c:\\Program File\\foo.exe\"");

Where it is not possible to change the application to use one of the above approaches, then the system property jdk.lang.Process.allowAmbigousCommands may be used as a workaround.

Example 2: The application needs to launch "dir > dir.out".
This case requires launching cmd.exe, and also it needs the output to be redirected to a file. The best approach is to use the ProcessBuilder as shown in the following example:

Process p = new ProcessBuilder("cmd", "/C", "dir").redirectOutput(new File("dir.out")).start();

Where it not possible to change code to use ProcessBuilder or redirectOutput, then the following approaches can also be used:

Process p = new ProcessBuilder("cmd", "/C", "dir > dir.out").start();
Process p = Runtime.getRuntime().exec("cmd /C \"dir > dir.out\"");

Example 3: The application wants to launch a command with parameters that require special quoting; for example "log.bat \">error<\"".
Here are 3 possible ways to do this:

Process p = new ProcessBuilder("cmd", "/C", "log.bat", ">error<").start();
Process p = Runtime.getRuntime().exec(new String[] { "cmd", "/C", "log.bat", ">error<" })
Process p = Runtime.getRuntime().exec("cmd /C log.bat \">error<\"");

Mit RoboVM geht’s für Java in das iOS-Land

Normalerweise nimmt eine JVM den Bytecode der Hauptklasse, lädt zur Laufzeit Klassendateien nach, interpretiert diese und übersetzt sie Zwecks Optimierung in Maschinencode. Dem gegenüber steht ein anderer Ansatz, der sonst eher typisch für klassische Programmiersprachen ist, genannt ahead-of-time-Compilation. Bei diesem Verfahren ermittelt ein Werkzeug zunächst alle abhängigen Typen und generiert dann direkt Maschinencode für eine Plattform — kein Interpreter ist somit nötig. Für Java sind solche Compiler selten, aber wer Java auf iOS-Geräten nutzen möchte, kommt darum nicht herum, denn Apple gestattet keine interpretierten Sprachen auf den kleinen Geräten. Dafür springt RoboVM (http://www.robovm.org/) ein, eine Open-Source-Software unter der GPL- und Apache-Lizenz, die Java-Programme direkt in Maschinencode (ARM bzw. x86 Code) übersetzt. Zum Bauen der Zieldateien nutzt RoboVM unterschiedliche Tools, um eine Kette von Schritten zu realisieren: Zunächst übersetzt ein installierter LLVM den Bytecode in Assembler-Code, dann folgt ein Tool, was Assembler-Code in Binärcode umsetzt, und dann ein Linker für den letzten Schritt, der das Ergebnis zusammensetzt; Assembler und Linker kommen vom GCC. Die Übersetzung ist kein großer Akt, denn für RoboVM ist ein Eclipse-Plugin verfügbar, in dem die App auch – zumindest beim Mac – im Simulator gestartet werden kann.

RoboVM bietet den Entwicklern die Standard-Klassen der Java-Bibliothek sowie sie auch bei Android verfügbar sind und bildet die Klassen der iOS-Bibliothek (genannt Cocoa) auf Java ab. Weiterhin integriert es die Typen vom Android-Paket und die OpenGL ES API, sodass Android-Anwendungen auch unter iOS abgebildet werden können. Interessant wird das Projekt auch durch eine Umsetzung von JavaFX, sodass die proprietären Cocoa-Bibliotheken nicht verwendet werden müssen, und es im Prinzip möglich ist, die gleiche Java-Anwendung auf iOS oder Windows, Linux, … zum Laufen zu bringen. Bisher ist RoboVM in einer frühen Phase.

Die Java 8 JVM wird keine Tail Call Recursion Optimierung bekommen

Endrekursion (engl. Tail Call Recursion) ist eine Besonderheit bei Methoden, dass sie mit einem rekursiven Aufruf enden. Zum Einstieg: Ist die bekannte rekursive Implementierung der Fakultät endrekursiv?

int factorial(int n) {
  if ( n == 0 ) return 1;
  return n * factorial( n – 1 );
}

Zwar sieht es optisch so aus, als ob die factorial(int) mit einem Methodenaufruf an factorial(…) endet, doch findet hier keine Endrekursion statt, da nach dem Methodenaufruf noch eine Multiplikation stattfindet – etwas umgeschrieben ist es besser zu erkennen:

int factorial(int n) {
  if ( n == 0 ) return 1;
  int fac = factorial( n – 1 );
  return n * fac;
}

Die Berechnung der Fakultät lässt sich umschreiben, so dass tatsächlich eine Endrekursion sattfindet, und zwar durch Einführung eines Containers für Zwischenergebnisse, genannt Akkumulator:

int factorial( int n )
{
  return factorialTailrec( n, 1 );
}

private int factorialTailrec( int n, int accumulator )
{
  if (n == 0) return accumulator;
  return factorialTailrec( n – 1, n * accumulator );
}

Die umgeschriebene Version büßt gegenüber der ursprünglichen Version an Schönheit ein. Doch endrekursive Aufrufe sind attraktiv, da eine schlaue Übersetzungseinheit sie so optimieren kann, dass der rekursive Methodenaufruf durch einen Sprung ersetzt wird. In der Java Sprache haben wir keine direkten Sprünge, doch im Bytecode schon, sodass die Basis der Optimierung im Grunde so aussehen kann:

private int factorialTailrec( int n, int accumulator ) {
start:
  if ( n == 0 ) return accumulator;
  accumulator *= n;
  n--;
  goto start;
}

Die Rekursion ist durch eine ordinäre Schleife ersetzt, was Stack einspart und eine sehr gute Performance ergibt.

In funktionalen Programmen ergeben sich eher Situationen, in denen Endrekursion vorkommt, sodass es attraktiv ist, diese zu optimieren. Die Standard-JVM kann das bisher nicht, weil Java traditionell keine funktionale Programmiersprache ist, und Endrekursion eher selten vorkommt. Zwar wird die Optimierung von Endrekursion (engl. tail call optimization) immer wieder diskutiert und auch schon in Prototypen ausprobiert, aber nie  von der Oracle JVM implementiert. Für Entwickler heißt dass, rekursive Aufrufe nicht umzuschreiben in endrekursive Varianten, da sie sowieso nicht optimiert werden und nur unleserlicher würden, und bei großen Datenvolumen, sprich Stack-Tiefe, auf die übliche nicht-rekursive iterative Version umzustellen. Im Fall von factorialTailrec(..) kann dies nämlich auch vom Entwickler gemacht werden und sieht so aus:

private int factorialTailrec( int n, int accumulator ) {

while (n != 0) {

accumulator *= n;

n–;

}

return accumulator;

}

 

Einmal Finalizer, vielleicht mehrmals die automatischen Speicherbereinigung

Objekte von Klassen, die eine finalize()-Methode besitzen, kann Oracles JVM nicht so schnell erzeugen und entfernen wie Klassen ohne finalize(). Das liegt auch daran, dass die automatische Speicherbereinigung vielleicht mehrmals laufen muss, um das Objekt zu löschen. Es gilt zwar, dass der Garbage-Collector aus dem Grund finalize() aufruft, weil das Objekt nicht mehr benötigt wird, es kann aber sein, dass die finalize()-Methode die this-Referenz nach außen gibt, sodass das Objekt wegen einer bestehenden Referenz nicht gelöscht werden kann und so zurück von den Toten geholt wird. Das Objekt wird zwar irgendwann entfernt, aber der Finalizer läuft nur einmal und nicht immer pro GC-Versuch. Einige Hintergründe erfährt der Leser unter http://www.iecc.com/gclist/GC-lang.html#Finalization.

Löst eine Anweisung in finalize() eine Ausnahme aus, so wird diese ignoriert. Das bedeutet aber, dass die Finalisierung des Objekts stehen bleibt. Die automatische Speicherbereinigung beeinflusst das in ihrer Arbeit aber nicht.