Wie funktioniert eigentlich invokedynamic?

Bei invokedynamic sind viel weniger Typinformationen nötig wie bei den anderen vier existierenden Bytecodes für Methodenaufrufe. Generiert wird der Bytecode zum Beispiel von Skriptsprachen, wenn Typinformationen fehlen. Die Compiler der Skriptsprachen nutzen Bytecode-Bibliotheken wie ASM (http://asm.ow2.org/) und umgehen den Java-Compiler zur Erstellung der Klassendateien.

Der neue Bytecode ist die eine Seite. Aber wenn die Typinformationen fehlen, insbesondere der wichtige Empfänger (wie PrintStream bei println()), wie kommt die JVM zur wirklichen Implementierung? Etwa bei unserem Beispiel von isEmpty(s), in dem es ein invokedynamic-Aufruf von s.length() gibt. Der Aufruf muss ja irgendwo landen.

function isEmpty(s) { return s == null || s.length() == 0; }

Zwei Dinge sind hier zusätzlich nötig. Das erste ist, dass es neben dem Aufruf von invokedynamic noch eine zusätzliche Information im Bytecode gibt, nämlich von einer Bootstrap-Methode. Findet die JVM zum ersten Mal ein invokedynamic, dann weiß sie nicht, an wen der Aufruf geht und wendet sich an die Bootstrap-Methode. Zur passenden Auswahl der Zielmethode übergibt die JVM an die Bootstrap-Methode Informationen über Aufrufer und Name, sodass alle wichtigen Informationen zur Auswahl des Ziels vorhanden sind. Hier sind wir nun in der zweiten Hälfte, denn zusätzlich zum neuen Bytecode gibt es ein neues Paket java.lang.invoke. Die Bootstrap-Methode spezifiziert mit der API des neuen Pakets die Zielmethode und gibt diese an die JVM weiter. Wenn das einmal geschehen ist, ist der Aufruf gebunden und die JVM ruft beim dynamischen Aufruf direkt die vom Bootstrap gelieferte Methode auf. Der genaue Ablauf bei den invokedynamic-Aufrufen dokumentiert das Paket.

Der neue Bytecode muss von einer Java 7-JVM unterstützt werden und wird von dynamischen Skriptsprachen generiert, um die Aufrufe schnell von der JVM ausführen zu lassen. Normale Entwickler werden den neuen Bytecode kaum brauchen, und im Quellcode ist er eh nicht sichtbar.

Welche Suppress-Warnings gibt es?

Neben den von Generics kommenden Kennungen rawtype und unchecked gibt es weitere, die allerdings nicht sonderlich gut dokumentiert sind. Das liegt auch daran, dass Meldungen während der Programmübersetzung zur Compilerinfrastruktur gehören und nicht zur Laufzeitumgebung, und damit nicht zur traditionellen Java-API. Der Compiler kann im Prinzip beliebige Codeanalysen beliebiger Komplexität vornehmen und bei vermuteten Fehlern Alarm schlagen. Um wir dürfen auch nicht vergessen, dass es nur Warnungen sind: wer als Programmierer alles richtig macht, wird die Meldungen nicht zu Gesicht bekommen. Dennoch ist es relevant, sie zu kennen, denn der Compiler wird manches Mal etwas anmerken, was Entwickler bewusst nutzen wollen, und dann gilt es, die Meldungen abzuschalten.

Die Macher vom Eclipse-Compiler (JDT) dokumentieren die unterstützten Warnungen unter http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-suppress_warnings.htm. Neben den aufgeführten Meldungen all, rawtype und unchecked sind folgende noch interessant:

@SuppressWarnings

Unterdrückt Meldungen für

deprecation

veraltete Elemente, wie new java.util.Date(2012-1970, 3, 3).

incomplete-switch

ausgelassene Aufzählungen in swich-case-Anweisungen.

resource

ein nicht geschlossenes AutoClosable, wie new java.util.Scanner(System.in).nextLine().

serial

eine serialisierbare Klasse, die keine Serialisierung-ID besitzt.

unused

nicht benutzte Elemente, etwa nicht aufgerufene private Methoden.

Einige Werte von @SuppressWarnings

Eclipse-Plugin: Shell-Script-Editor (ShellEd) und Remote System Explorer (RSE)

ShellEd (Bild) ist ein Shell-Script-Editor für Unix-Skripte (also ash, bsh, bash, csh, ksh, sh, zsh). Mit Manual und Vervollständigung. Interessant dazu ist das relativ unbekannte Target Management Project, wo man remote, etwa über SSH oder FTP auf einem Server arbeiten und zum Beispiel Dokumente editieren kann.

Mehr Eclipse-Plugins gibt’s unter http://www.tutego.de/java/eclipse/plugin/eclipse-plugins.html.

Was ist aus Apache Click geworden?

Anfang 2009 schrieb ich im Blog:

Darauf hat die Welt gewartet: Wieder ein neues/altes Web-Framework

Apache Click http://incubator.apache.org/click/ heißt es und läuft ab Java 1.4. Super. Kommt damit 5 Jahre zu spät. Also nix mit Annotationen. Schön XML und so. (Click 0.3 gab es auch schon im März 2005, also ist das Projekt nicht wirklich jung.) Das Wort Ajax taucht auf den ganzen Webseiten nicht auf. Immerhin gibt es eine http://incubator.apache.org/click/docs/click-ide.html auf Eclipse 3.4 Basis. Toll die Antwort auf die Frage "Why develop a new Web Application Framework?" Antwort: "Because the existing frameworks did not meet my needs. Struts doesn’t really do much, while Tapestry is too complicated. For a more comprehensive answer please see Why Click." Klar, als ob es nur Struts und Tapestry gibt. Vor 10 Jahren vielleicht. Bei Struts 1.x ist die Zeit schon lange abgelaufen und bei Tapestry 5 hält man sich noch ein wenig mit DI/IoC über Wasser.

Heute, im Jahr 2012, hat es Click zu einem vollständigen Web-Framework geschafft, die Demos (http://click.avoka.com/click-examples/home.htm) sehen von der Funktionalität OK aus, nur etwas altbacken, etwa so wie Windows 3.1 im Vergleich zu Windows 7.

Das letzte Update von Click ist von März 2011, also 1 1/2 Jahr alt. Das war’s dann wohl.

Snippet: NETSTAT in Java with command line call

import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

public class Netstat
{
  public static class Protocoll
  {
    public String protocoll;
    public String localAddress;
    public String remoteAddress;
    public String status;

    public Protocoll( String protocoll, String localAddress, String remoteAddress, String status )
    {
      this.protocoll = protocoll;
      this.localAddress = localAddress;
      this.remoteAddress = remoteAddress;
      this.status = status;
    }

    @Override
    public String toString()
    {
      return String.format( "%-6s %-22s %-22s %s", protocoll, localAddress, remoteAddress, status );
    }
  }

  public static void main( String[] args ) throws IOException
  {
    for ( Protocoll p : netStat() )
      System.out.println( p );
  }

  public static Iterable<Protocoll> netStat() throws IOException
  {
    Collection<Protocoll> result = new ArrayList<>();
    ProcessBuilder builder = new ProcessBuilder( "netstat", "-n" );
    Process p = builder.start();
    try ( Scanner scanner = new Scanner( p.getInputStream() ) ) {
      Pattern pattern = Pattern.compile( "(TCP|UDP)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)" );
      while ( scanner.findWithinHorizon( pattern, 0 ) != null )
        result.add( new Protocoll( scanner.match().group( 1 ), scanner.match().group( 2 ), scanner.match().group( 3 ), scanner.match().group( 4 ) ) );
    }
    return result;
  }
}

This leads to something like

TCP    127.0.0.1:16709        127.0.0.1:49159        HERGESTELLT

TCP    127.0.0.1:19872        127.0.0.1:49176        HERGESTELLT

TCP    127.0.0.1:49159        127.0.0.1:16709        HERGESTELLT

TCP    127.0.0.1:49176        127.0.0.1:19872        HERGESTELLT

TCP    127.0.0.1:49189        127.0.0.1:49190        HERGESTELLT

TCP    127.0.0.1:49190        127.0.0.1:49189        HERGESTELLT

BridJ

BridJ verfolgt den gleichen Ansatz wie JNA, kann also auch aus Java heraus nativen Code von C(++) und auch Objective-C ansprechen. Nur steht es als quelloffene Bibliothek nicht unter der LGPL, sondern unter der BSD/Apache-Lizenz. Zudem ist es aktueller, nutzt Generics (was es abhängig von Java 5 macht, JNA benötigt nur 1.4) und diverse Tricks, um noch performantere native Aufrufe zu realisieren. Auch kann es besser mit den Eigenschaften von C++ umgehen, wie virtuellen Methodenaufrufen, Vererbung und Templates „verstehen“. Regelmäßige Updates gibt es für die Systeme Windows (x86, x64), Linux (x86, x64, arm32), Mac OS X (x86, x64, PPC), Solaris 10 (x86, bald x64 und SPARC), Android 3.0 (ARMv5) und experimentell auf jailbroken iOS iPhones (ARM). Ob das ältere JNA oder BridJ besser ist, muss im Einzelfall evaluiert werden. JNAerator unterstützt ebenfalls BidJ zur Generierung der typisierten Schnittstellen und Übergabeobjekten.

JNA (Java Native Access)

Eine Bibliothek zum Ansprechen dynamischer Bibliotheken ohne JNA-Kontakt und damit ohne C(++)-Compiler ist JNA (Java Native Access). Beheimatet ist das Projekt unter https://github.com/twall/jna/  und es steht unter der Lizenzform LGPL. JNA benötigt nur das Archiv jna.jar im Klassenpfad und geht dann fast von selbst zur dynamischen Bibliotheken. Verschiedene Plattformen werden unterstützt, dazu zählen Windows, Linux und Mac OS X.

Zur dem Zugriff müssen allerdings Java-Schnittstellen als typisierte Versionen der nativen Funktionen deklariert werden, damit auch die Datentypen von der Java-Seite automatisch auf Datentypen der nativen Seite gemappt werden können. Die Dokumentation unter https://github.com/twall/jna/blob/master/www/GettingStarted.md  eigt ein Beispiel (hier etwas gekürzt):

import com.sun.jna.*;

public class HelloWorld {
  public interface CLibrary extends Library {
    CLibrary INSTANCE = (CLibrary) Native.loadLibrary( Platform.isWindows() ? "msvcrt" : "c",
                                                       CLibrary.class );
    void printf( String format, Object... args );
  }

  public static void main( String[] args ) {
    CLibrary.INSTANCE.printf( "Hello, World\n" );
    for ( int i = 0; i < args.length; i++ )
      CLibrary.INSTANCE.printf( "Argument %d: %s\n", i, args[ i ] );
  }
}

Die Übertragung der Funktionen von der C-Seite in Java-Schnittstellen – wie CLibrary in dem Beispiel – kann auch mit JNAerator (http://code.google.com/p/jnaerator/) automatisiert werden.

Snippet: List all MessageDigest provider

Pattern digestPattern = Pattern.compile( "^(Alg\\.Alias\\.)?MessageDigest\\.(?<alg>(\\w|-)+)" );
for ( Provider p : Security.getProviders() ) {
  for ( Object o : Collections.list( p.keys() ) ) {
    for ( Matcher m = digestPattern.matcher( o.toString() ); m.find(); )
      System.out.println( m.group("alg") );
  }
}

The result is:

SHA-1 SHA MD5 SHA-384 SHA-512 SHA1 SHA MD5 SHA-256 MD2

Also look at http://download.java.net/jdk8/docs/technotes/guides/security/StandardNames.html.

Unicode-Skripte

Neben den Unicode-Blöcken gibt es noch ein anderen Konzept, was vielleicht noch wichtiger als die Blöcke selbst sind: Unicode-Skripte. Darunter ist eine Gruppe von Zeichen für eine Sprache (in der Regel) zu verstehen.[1] Um den Unterschied zu Blöcken noch einmal zusammenzufassen:

· Ein Unicode-Block ist ein einfacher von-bis-Bereich, und Stellen können für die spätere Belegung leer sein.

· Ein Unicode-Skript enthält keine freien Positionen.

· Zeichen eines Unicode-Skripts können aus verschiedenen Unicode-Blöcken stammen.

· Zeichen aus einem Block können in verschiedenen Unicode-Skripten auftauchen.

Auch für Skripte bietet Java in der Klasse Character ein öffentliches statische innere Attribut, wobei das erst seit Java 7 existiert und Oracle hier ein moderneres enum gewählt hat. Character.UnicodeScript besteht aus einer Reihe von Konstanten, wobei die Namen nach ISO 15924[2] gewählt sind. Auch gibt es eine of(int) Methode um das Skript für ein Zeichen zu erfragen und ein UnicodeScript-Objekt kann über forName(String) mit einem Namen aufgebaut werden.

Auch wenn die Character und String-Klasse arm an weiteren Methoden zu den Unicode-Blöcken und Unicode-Skripten ist, gibt es doch Unterstützung von ganz anderer Stellen: Von den regulären Ausdrücken. Sie können testen, ob Zeichen in gewissen Skripten oder Blöcken sind und damit Zeichen in Teilstrings aufspüren, sie löschen und entfernen. (Wir springen nun thematisch etwas vor.) Die Syntax in den regulären Ausdrücken ist \p{script=Skriptname} bzw. \p{block=Blockname} (auch der Präfix Is oder In ist erlaubt ohne Nutzung vom script=/block=-Konstrukt). Achtung, Nicht alle Unicode-Skripts werden unterstützt!

Beispiel

Teste drei Zeichen, ob sie arabisch sind:

System.out.println( "ح".matches( "\\p{script=Arabic}" ) ); // true

System.out.println( "ح".matches( "\\p{IsArabic}" ) ); // true

System.out.println( "ש".matches( "\\p{IsArabic}" ) ); // false

System.out.println( "1".matches( "\\p{IsArabic}" ) ); // false


[1] Siehe auch http://www.unicode.org/reports/tr24/.

[2] http://unicode.org/iso15924/iso15924-codes.html

Unicode-Blöcke

Unicode-Zeichen gehören immer Blöcken an und bei denen ist teilweise immer etwa Platz, um nachrückende Zeichen noch aufnehmen zu können. Beim lateinischen Alphabet ist das nicht so wichtig, wohl aber bei mathematischen Sonderzeichen oder anderen Symbolen.

Die Klasse Character deklariert eine öffentliche statische finale Klasse UnicodeBlock mit einer Vielzahl von Unicode-Blöcken, die als öffentliche statische Variablen in UnicodeBlock deklariert sind und selbst vom Typ UnicodeBlock sind. Character.UnicodeBlock.BASIC_LATIN ergibt zum Beispiel so einen Block, allerdings ist der Typ nicht so ausdrucksstark, nur der Name kommt bei einem toString() dabei raus, aber nicht etwa in welchem Bereich die Zeichen liegen. Auch fehlt die Möglichkeit alle Zeichen aufzuzählen oder zu testen, ob ein Zeichen im Block liegt. Was jedoch der Typ UnicodeBlock bietet sind zwei statische Methoden of(int) und of(char), die als Fabrikfunktionen einen UnicodeBlock für ein gewisses Zeichen geben. Der ist-ein-Element-von-Test lässt sich also damit indirekt realisieren.

Beispiel. Gib die Namen der Unicode-Blöcke für einige Zeichen aus:

UnicodeBlock basicLatin = Character.UnicodeBlock.BASIC_LATIN;

System.out.println( basicLatin );

System.out.println( Character.UnicodeBlock.of( ‚ß‘ ) );

System.out.println( Character.UnicodeBlock.of( ‚\u263A‘ ) );

System.out.println( Character.UnicodeBlock.of( ‚\u20ac‘ ) );

System.out.println( Character.UnicodeBlock.of( 0x1D15E ) );

Das liefert BASIC_LATIN LATIN_1_SUPPLEMENT MISCELLANEOUS_SYMBOLS

CURRENCY_SYMBOLS MUSICAL_SYMBOLS.

Das Wissen um den Bereich ist immer hilfreich dann, wenn ein unbekannter Text zugeordnet werden soll, denn auf diese Weise lässt sich erahnen, ob der Text zum Beispiel auf lateinischen Buchstaben basiert, er arabisch, chinesisch oder japanisch (Kanji/Kana) ist.

JDK 8 Milestones

Nach http://openjdk.java.net/projects/jdk8/milestones sind das nun:

M1    2012/04/26    (b36)
117 Remove the Annotation-Processing Tool (apt)
M2    2012/06/14    (b43)
133 Unicode 6.1

M3    2012/08/02    (b50)
124 Enhance the Certificate Revocation-Checking API
130 SHA-224 Message Digests
131 PKCS#11 Crypto Provider for 64-bit Windows

Hier stehen wir, das soll kommen:

M4    2012/09/13   
105 DocTree API
121 Stronger Algorithms for Password-Based Encryption
129 NSA Suite B Cryptographic Algorithms

M5    2012/11/29   
106 Add Javadoc to javax.tools
110 New HTTP Client
111 Additional Unicode Constructs for Regular Expressions
112 Charset Implementation Improvements
113 MS-SFU Kerberos 5 Extensions
114 TLS Server Name Indication (SNI) Extension
119 javax.lang.model Implementation Backed by Core Reflection
122 Remove the Permanent Generation
128 BCP 47 Locale Matching
140 Limited doPrivileged
153 Launch JavaFX Applications

M6    2013/01/31        Feature Complete
101 Generalized Target-Type Inference
104 Annotations on Java Types
107 Bulk Data Operations for Collections
108 Collections Enhancements from Third-Party Libraries
109 Enhance Core Libraries with Lambda
115 AEAD CipherSuites
118 Access to Parameter Names at Runtime
120 Repeating Annotations
123 Configurable Secure Random-Number Generation
126 Lambda Expressions and Virtual Extension Methods
135 Base64 Encoding and Decoding
156 G1 GC: Reduce need for full GCs
160 Lambda-Form Representation for Method Handles

M7    2013/02/21        Developer Preview
2013/03/18        All Tests Run
2013/04/04        Rampdown start
2013/05/02        API/Interface Freeze
2013/05/16        Zero Bug Bounce
2013/06/13        Rampdown phase 2
M8    2013/07/05        Final Release Candidate
GA    2013/09/09        General Availability

Von Date-Time API nichts zu sehen!

Thema der Woche: @CheckForNull, @Nonnull

Null-Pointer-Exceptions sind eine Qual, da oftmals eine Referenzvariable null ist, die nicht null sein darf. Das kommt oft erst zur Laufzeit bei ganz besonderen Ausführungspfaden raus.  Mit Annotationen kann man dem ein wenig entgegentreten, da man zum Einen gut dokumentiert was erlaubt ist und was nicht, und zum Anderen Analysetools erlaubt, sich die Ausführungspfade etwas genauer anzuschauen.

Snippet: Kalender ausdrucken, Teil 2: von-bis

Die erste Version meines Kalenderprogramms druckte ein Kalender für ein Jahr. In Anwendungen dürfte häufiger vorkommen, dass es ein Start- und Enddatum gibt, das auch über Jahresgrenzen liegt. Das macht dieses Programm:

public static class CalLine
{
  public int year;
  public int weekOfYear;
  public int month = -1;  // 0 <= month <= 11
  public int[] day = { -1, -1, -1, -1, -1, -1, -1 };
}

public static List<CalLine> calenderOfTheYear( Date start, Date end )
{
  Calendar startCal = new GregorianCalendar();
  startCal.setTime( start );
  Calendar endCal = new GregorianCalendar();
  endCal.setTime( end );
  return calenderOfTheYear( startCal, endCal );
}

public static List<CalLine> calenderOfTheYear( Calendar start, Calendar end )
{
  List<CalLine> lines = new ArrayList<>();

  // Calender instances are mutable, so copy them
  Calendar startCal = (Calendar) start.clone(); 
  Calendar endCal   = (Calendar) end.clone(); 

  // For start date: first go backwards to the beginning of the month
  // then find monday of this week
  while ( startCal.get( Calendar.DAY_OF_MONTH ) != 1 )
    startCal.add( Calendar.DAY_OF_YEAR, -1 );
  while ( startCal.get( Calendar.DAY_OF_WEEK ) != Calendar.MONDAY )
    startCal.add( Calendar.DAY_OF_YEAR, -1 );

  // For end date: go forwards and find end of month
  // then find sunday of this week
  while ( endCal.get( Calendar.DAY_OF_MONTH ) != startCal.getActualMaximum( Calendar.DAY_OF_MONTH ) )
    endCal.add( Calendar.DAY_OF_YEAR, 1 );
  while ( endCal.get( Calendar.DAY_OF_WEEK ) != Calendar.SUNDAY )
    endCal.add( Calendar.DAY_OF_YEAR, 1 );
  endCal.add( Calendar.DAY_OF_YEAR, 1 );  // add 1 to test with < not <=

  CalLine line = new CalLine();

  while ( startCal.before( endCal ) ) {
    if ( line.year == 0 )
      line.year = startCal.get( Calendar.YEAR );
    if ( line.weekOfYear == 0 )
      line.weekOfYear = startCal.get( Calendar.WEEK_OF_YEAR );

    int dayOfMonth = startCal.get( Calendar.DAY_OF_MONTH );
    int dayOfWeek  = startCal.get( Calendar.DAY_OF_WEEK );

    if ( dayOfMonth == 1 )
      line.month = startCal.get( Calendar.MONTH );

    line.day[dayOfWeek - 1] = dayOfMonth;

    if ( dayOfWeek == Calendar.SUNDAY ) {
      // Days are Sun, Mon, ..., Sat. Rearange to Mon, ..., Sun 
      int first = line.day[ 0 ]; // This is faster then System.arraycopy()
      line.day[ 0 ] = line.day[ 1 ]; line.day[ 1 ] = line.day[ 2 ]; line.day[ 2 ] = line.day[ 3 ];
      line.day[ 3 ] = line.day[ 4 ]; line.day[ 4 ] = line.day[ 5 ]; line.day[ 5 ] = line.day[ 6 ];
      line.day[ 6 ] = first;

      lines.add( line );
      line = new CalLine();   // it ends always with SUN, last line is not added
    }

    startCal.add( Calendar.DAY_OF_YEAR, 1 );
  }

  return lines;
}

Beispielaufruf:

List<CalLine> lines = DateUtils.calenderOfTheYear( new GregorianCalendar( 2011, Calendar.NOVEMBER, 12 ), new GregorianCalendar( 2012, Calendar.JANUARY, 22 ) );

String[] monthNames = { "Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez" };
System.out.println( "KW        Mo Do Mi Do Fr Sa So" ); // to lazy for DateFormatSymbols here...

for ( CalLine l : lines ) {
  String monthStr = (l.month == -1) ? "   " : monthNames[ l.month ];
  String s = String.format( "%2d  %s   %(2d %(2d %(2d %(2d %(2d %(2d %(2d",
                            l.weekOfYear, monthStr,
                            l.day[0], l.day[1], l.day[2], l.day[3], l.day[4], l.day[5], l.day[6] ).replace( "(1)", "  " );
  System.out.println( s );
}

Das führt zu

KW        Mo Do Mi Do Fr Sa So
44  Nov   31  1  2  3  4  5  6
45         7  8  9 10 11 12 13
46        14 15 16 17 18 19 20
47        21 22 23 24 25 26 27
48  Dez   28 29 30  1  2  3  4
49         5  6  7  8  9 10 11
50        12 13 14 15 16 17 18
51        19 20 21 22 23 24 25
52  Jan   26 27 28 29 30 31  1
 1         2  3  4  5  6  7  8
 2         9 10 11 12 13 14 15
 3        16 17 18 19 20 21 22
 4        23 24 25 26 27 28 29
 5  Feb   30 31  1  2  3  4  5