Thema der Woche: Externe Programme starten, URL Protokoll unter Windows registrieren

Lies http://openbook.galileocomputing.de/javainsel/javainsel_11_008.html#dodtpd29fe557-8d1f-4a1a-a23e-ce87eda2454b

Schreibe ein Java-Programm, welches per Kommandozeile eine Bug-ID annimmt, und dann die entsprechende Seite unter http://bugs.sun.com/ aufruft. Beispiel: Zur Bug-ID 6787890 soll der Browser http://bugs.sun.com/view_bug.do?bug_id=6787890 ansteuern.

Lies http://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx

Kopiere folgendes in eine temporäre Datei, ersetzte dabei YourApp mit etwas Eigenem und passe den Pfad korrekt auf unser Bug-ID-Öffnen-Programm an. Das Protokoll kann zum Beispiel “sunbugid” sein.

REGEDIT4

[HKEY_CLASSES_ROOT\YourApp]
@=“URL:YourApp Protocol“
„URL Protocol“=““

[HKEY_CLASSES_ROOT\YourApp\DefaultIcon]
@=“\“C:\\Program Files\\YourApp\\YourApp.exe\““

[HKEY_CLASSES_ROOT\YourApp\shell]

[HKEY_CLASSES_ROOT\YourApp\shell\open]

[HKEY_CLASSES_ROOT\YourApp\shell\open\command]
@=“\“C:\\Program Files\\YourApp\\YourApp.exe\“ \“%1\“ \“%2\“ \“%3\“ \“%4\“ \“%5\“ \“%6\“ \“%7\“ \“%8\“ \“%9\““

(Quelle http://stackoverflow.com/questions/389204/how-do-i-create-my-own-url-protocol-e-g-so)

Führe die Datei mit regedit aus.

Steht dann auf einer Webseite die URL sunbugid:6787890 sollte bei der Aktivierung des Links das eigene Java-Programm ein neues Browserfenster mit der Bug-Beschreibung aufkommen.

Über eine Reihe von Werten laufen, Ist-Element-von-Test

Rechts vom Doppelpunkt lässt sich auf die Schnelle ein Feld aufbauen, über welches das erweiterte for dann laufen kann.

for ( int prime : new int[]{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 } )
  System.out.println( prime );

Das ist praktisch, um über eine feste Menge von Werten zu laufen. Das funktioniert auch für Objekte, etwa Strings:

for ( String name : new String[]{ "Krissy", "Morris", "Dan" } )
  System.out.println( name );

Einige Programmierer verstecken die Objekterzeugung auch in einen Methodenaufruf:

for ( String name : Arrays.asList( "Krissy", "Morris", "Dan" ) )
  System.out.println( name );

Arrays.asList(…) erzeugt kein Array als Rückgabe, sondern baut aus der variablen Argumentliste eine Sammlung auf, die von einem speziellen Typ Iterable ist – das kann auch die erweiterte for-Schleife ablaufen.

Unabhängig vom erweiterten for hat die Nutzung von Arrays.asList(…)noch einen anderen Vorteil, etwa bei ist-Element-von-Anfragen, etwa so:

if ( Arrays.asList( 1, 2, 3, 4, 5, 6, 7, 8, 10 ).contains( number ) )

  …

java.net.ConnectException? Was tun?

Falls der Computer keinen direkten Internetzugriff hat, kommt es zu einem Timeout. Der übliche Fehler ist eine „java.net.ConnectException: Operation timed out“. Hier gilt zu prüfen, woher der Fehler kommt. Eine Fehlerquelle sind Proxies, die zwischen dem eigenen Rechner und dem Internet hängen. Proxy-Einstellungen können in Java gesetzt werden, wie im im Kapitel über Proxies in der Insel beschrieben. Eine gute Idee ist, mithilfe des Kommandozeilenprogramms telnet[1] die Erreichbarkeit eines Servers zu überprüfen. Es kann auch sein, dass die Verbindung grundsätzlich besteht, sie jedoch langsam ist, und Java aus Ungeduld aufgibt. Die Lösung ist dann, den Timeout hochzusetzen, etwa bei dem URLConnection-Objekt über setConnectTimeout(millis).


[1] Muss in einem aktuellen Windows erst aktiviert werden.

Snippet: Kalender des Jahres ausgeben

Angeregt durch den Compact Calendar (http://davidseah.com/compact-calendar/) wollte ich etwas ähnliches in meine Webanwendung einbauen, sodass das Ergebnis tabellenartig wie https://docs.google.com/spreadsheet/ccc?key=0AkyxK00VLnSidEl1SS1sZjZiVlpuRnJIY1JmUW9IRHc#gid=0 formatiert wird.

import java.text.DateFormatSymbols;
import java.util.*;

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

  public static List<CalLine> calenderOfTheYear( int year )
  {
    Calendar cal = new GregorianCalendar( year, 1, 1 );

    List<CalLine> lines = new ArrayList<>();
    CalLine line = new CalLine();

    for ( int dayOfYear = 1; dayOfYear <= cal.getActualMaximum( Calendar.DAY_OF_YEAR ); dayOfYear++ ) {
      cal.set( Calendar.DAY_OF_YEAR, dayOfYear );
      line.weekOfYear = cal.get( Calendar.WEEK_OF_YEAR ); // Set several times, thats ok

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

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

      line.day[dayOfWeek - 1] = dayOfMonth;

      if ( dayOfWeek == Calendar.SUNDAY ) {
        lines.add( line );
        line = new CalLine();
      }
    }
    lines.add( line );
    return lines;
  }
  
  public static void main( String[] args )
  {
    List<CalLine> lines = calenderOfTheYear( 2012 );

    String[] monthNames = new DateFormatSymbols( Locale.GERMANY ).getMonths();
    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 ].substring( 0, 3 );
      // Days are Sun, Mon, ..., Sat. Rearange to Mon, ..., Sun
      String s = String.format( "%2d  %s   %(2d %(2d %(2d %(2d %(2d %(2d %(2d",
                                l.weekOfYear, monthStr,
                                l.day[1], l.day[2], l.day[3], l.day[4], l.day[5], l.day[6], l.day[0] ).replace( "(1)", "  " );
      System.out.println( s );
    }
  }
}

Zur Demo gibt eine Textausgabe (mit einem Hack). Das Ergebnis für dieses Jahr:

kw        mo do mi do fr sa so
52  jan                      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
 6         6  7  8  9 10 11 12
 7        13 14 15 16 17 18 19
 8        20 21 22 23 24 25 26
 9  mär   27 28 29  1  2  3  4
10         5  6  7  8  9 10 11
11        12 13 14 15 16 17 18
12        19 20 21 22 23 24 25
13  apr   26 27 28 29 30 31  1
14         2  3  4  5  6  7  8
15         9 10 11 12 13 14 15
16        16 17 18 19 20 21 22
17        23 24 25 26 27 28 29
18  mai   30  1  2  3  4  5  6
19         7  8  9 10 11 12 13
20        14 15 16 17 18 19 20
21        21 22 23 24 25 26 27
22  jun   28 29 30 31  1  2  3
23         4  5  6  7  8  9 10
24        11 12 13 14 15 16 17
25        18 19 20 21 22 23 24
26  jul   25 26 27 28 29 30  1
27         2  3  4  5  6  7  8
28         9 10 11 12 13 14 15
29        16 17 18 19 20 21 22
30        23 24 25 26 27 28 29
31  aug   30 31  1  2  3  4  5
32         6  7  8  9 10 11 12
33        13 14 15 16 17 18 19
34        20 21 22 23 24 25 26
35  sep   27 28 29 30 31  1  2
36         3  4  5  6  7  8  9
37        10 11 12 13 14 15 16
38        17 18 19 20 21 22 23
39        24 25 26 27 28 29 30
40  okt    1  2  3  4  5  6  7
41         8  9 10 11 12 13 14
42        15 16 17 18 19 20 21
43        22 23 24 25 26 27 28
44  nov   29 30 31  1  2  3  4
45         5  6  7  8  9 10 11
46        12 13 14 15 16 17 18
47        19 20 21 22 23 24 25
48  dez   26 27 28 29 30  1  2
49         3  4  5  6  7  8  9
50        10 11 12 13 14 15 16
51        17 18 19 20 21 22 23
52        24 25 26 27 28 29 30
 1        31                  

			

Thema der Woche: Excel API

Abbruch-Signale

Das Betriebssystem sendet beim Abbruch eines Programms Signale. Für Windows/Unix-Systeme sind das zum Beispiel INT (Abbruch z. B. mit Strg-C), TERM (Aufforderung zur Terminierung), BREAK (Break-Anforderung vom Termin), usw.

Java kann diese unterschiedlichen Feinheiten des Programmabbruchs nicht auswerten, jedenfalls nicht mit einer erlaubten API. Es gibt aber zwei Klassen sun.misc.Signal und sun.misc.SignalHandler, die verwendet werden können, wenn Entwickler sich der Konsequenzen bewusst sind, dass diese API in Zukunft verschwinden könnte. Die Nutzung sieht so aus:

class MySignalHandler implements SignalHandler {

public void handle( Signal signal ) { … }

}

String signalName = "TERM";

handler = Signal.handle( new Signal( signalName ), new MySignalHandler() );

Weitere Informationen auch zu den unterschiedlichen Signaltypen liefern http://stackoverflow.com/questions/5023520/sending-signals-to-a-running-jvm und die referenzierten Unterseiten.

Gruppen in regulären Ausdrücken

In regulären Ausdrücken lassen sich Teilausdrücke in runde Klammen setzen und diese bilden dann Gruppen; im Englischen heißen sie capturing groups. Gruppen haben zwei Vorteile:

· An die Gruppe gesetzte Operationen wirken für die Gruppe. Beispiel: Der Ausdruck (\d\d)+ steht für gerade Anzahl von Ziffern.

· Auf eine Gruppe lässt sich gezielt zurückgreifen.

Beispiel 1

Ein String enthält zwei Zahlen, die mit einem Doppelpunkt getrennt sind. Wie interessieren uns für die erste und zweite Zahl:

Matcher matcher = Pattern.compile( "(\\d+):(\\d+)").matcher( "1:123" );

if ( matcher.find() )

System.out.println( matcher.group(1) + "," + matcher.group(2) ); // 1,123

Der Trick ist also, bei group(int) einen Index anzugeben; der beginnt bei 1.

Beispiel 2

Ein String aus einer Datei enthält einen Block in geschweiften Klammen. Wir interessieren uns für den Block.

String input = "line1\n{line2\nline3}\nline4";

Matcher matcher = Pattern.compile( "\\{(.*)\\}", Pattern.MULTILINE | Pattern.DOTALL ).matcher( input );

if ( matcher.find() )

  System.out.println( matcher.group( 1 ) ); // line1, dann Umbruch, line2

Bei der Angabe ohne Index, also group(), ist die Rückgabe alles was find() gefunden hat. Die Fundstellen sind natürlich Teilstrings der gesamten Gruppe.

Welche Jars braucht man mindestens für ein JPA-Hibernate-Beispiel?

  • javax.persistence-2.0.0.jar
  • hibernate-commons-annotations-4.0.1.Final.jar
  • hibernate-core-4.1.4.Final.jar
  • hibernate-entitymanager-4.1.4.Final.jar
  • hibernate-validator-4.3.0.Final.jar
  • javassist-3.7.ga.jar
  • jboss-logging-3.1.1.GA.jar
  • jta-1.1.jar
  • antlr-2.7.5H3.jar
  • dom4j-1.6.1.jar

Zu beziehen in einem Maven-Repository des Vertrauens oder gleich über Maven auflösen lassen.

Inselupdate: Property-Dateien mit java.util.Properties lesen und schreiben

Dateien, die Schlüssel-Werte-Paare als String repräsentieren und die Schlüssel und Wert durch ein Gleichheitszeichen trennen, nennen sich Property-Dateien. Sie kommen zur Programmkonfiguration häufig vor, und Java bietet mit der Klasse Properties die Möglichkeit, die Property-Dateien einzulesen und zu schreiben.

store() und load()-Methoden vom Properties Objekt

Die Methode store(…) dient zum Speichern der Zustände und load(…) zum Initialisieren eines Properties-Objekts aus einem Datenstrom. Die Schlüssel und Werte trennt ein Gleichheitszeichen. Die Lade-/Speicher-Methoden sind:

class java.util.Properties
extends Hashtable<Object,Object>

§ void store(OutputStream out, String comments)

§ void store(Writer writer, String comments)
Schreibt die Properties-Liste in des Ausgabestroms. Am Kopf der Datei wird eine Kennung geschrieben, die im zweiten Argument steht. Die Kennung darf null sein.

§ void load(InputStream inStream)

§ void load(Reader reader) throws IOException
Liest eine Properties-Liste aus einem Eingabestrom.

Ist der Typ ein Binärstrom also OutputStream/InputStream, so behandeln die Methoden die Zeichen in der ISO 8859-1 Kodierung. Reader/Writer erlauben eine freie Kodierung. Eine ähnliche Methode list(…) ist nur für Testausgaben gedacht ist, sie sollte nicht mit store(…) verwechselt werden.

Das folgende Beispiel initialisiert ein Properties-Objekt mit den Systemeigenschaften und fügt dann einen Wert hinzu. Anschließend macht store(…) die Daten persistent, load(…) liest sie wieder, und list(…) gibt die Eigenschaften auf dem Bildschirm aus:

Path path = Paths.get( "properties.txt" );

try ( Writer writer = Files.newBufferedWriter( path, StandardCharsets.UTF_8 ) ) {

Properties prop1 = new Properties( System.getProperties() );

prop1.setProperty( "MeinNameIst", "Forrest Gump" );

prop1.store( writer, "Eine Insel mit zwei Bergen" );

try ( Reader reader = Files.newBufferedReader( path, StandardCharsets.UTF_8 ) ) {

Properties prop2 = new Properties();

prop2.load( reader );

prop2.list( System.out );

}

}

catch ( IOException e ) {

e.printStackTrace();

}

Besonderheiten des Formats

Beginnt eine Zeile mit einem „#“ oder „!“ gilt sie als Kommentar und wird überlesen. Da der Schlüssel selbst aus einem Gleichheitszeichen bestehen kann, steht in dem Fall ein „\“ voran, folglich liefert Properties p = new Properties(); p.setProperty("=", "="); p.store(System.out, null); neben dem Kommentar die Zeile „\==\=“. Beim Einlesen berücksichtigen die Lesemethoden auch Zeilenumbrüche: eine Zeile darf mit \ enden und dann führt die folgende Zeile die vorangehende fort. Die Property „cars“ ist also “Honda, Mazda, BMW”, wenn steht:

cars = \

Honda, Mazda, \

BMW

Mit der internen Compiler-API auf den AST einer Klasse zugreifen

Der Zugriff zum Java-Compiler ist über die Java-Compiler-API standardisiert, jedoch sind alle Interna, wie die tatsächliche Repräsentation des Programmcodes verborgen. Die Compiler-API abstrahiert alles über Schnittstellen, und so kommen Entwickler nur mit JavaCompiler, StandardJavaFileManager und CompilationTask in Kontakt – alles Schnittstellen aus dem Paket javax.tools. Um etwas tiefer einzusteigen, lässt sich zum einem Trick greifen: Klassen implementieren Schnittstellen und wenn ein Programm den Schnittstellentyp auf den konkreten Klassentyp anpasst, dann stehen in der Regel mehr Methoden zur Verfügung. So lässt sich der CompilationTask auf eine com.sun.tools.javac.api.JavacTaskImpl casten und dann steht eine parse()-Methode für Verfügung. Die parse()-Methode liefert als Rückgabe eine Aufzählung von CompilationUnitTree. Um diesen Baum nun abzulaufen, lässt sich das Besuchermuster einsetzen. CompilationUnitTree bietet eine accept(…)-Methode; der übergeben wir einen TreeScanner. Die accept(…)-Methode ruft dann beim Ablaufen jedes Knotens unseren Besucher auf.

package com.tutego.tools.javac;

import java.io.*;
import java.net.*;
import javax.tools.*;
import javax.tools.JavaCompiler.CompilationTask;
import com.sun.source.tree.*;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.api.JavacTaskImpl;

public class PrintAllMethodNames {

  final static TreeScanner<?, ?> methodPrintingTreeVisitor = new TreeScanner<Void, Void>() {
    @Override public Void visitCompilationUnit( CompilationUnitTree unit, Void arg ) {
      System.out.println( "Paket: " + unit.getPackageName() );
      return super.visitCompilationUnit( unit, arg );
    };
    @Override public Void visitClass( ClassTree classTree, Void arg ) {
      System.out.println( "Klasse: " + classTree.getSimpleName() );
      return super.visitClass( classTree, arg );
    }
    @Override public Void visitMethod( MethodTree methodTree, Void arg ) {
      System.out.println( "Methode: " + methodTree.getName() );
      return super.visitMethod( methodTree, arg );
    }
  };

  public static void main( String[] args ) throws IOException, URISyntaxException {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
    URI filename = PrintAllMethodNames.class.getResource( "PrintAllMethodNames.java" ).toURI();
    Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects( new File( filename ) );
    CompilationTask task = compiler.getTask( null, null, null, null, null, fileObjects );

    JavacTaskImpl javacTask = (JavacTaskImpl) task;

    for ( CompilationUnitTree tree : javacTask.parse() )
      tree.accept( methodPrintingTreeVisitor, null );
  }
}

Ein TreeScanner hat viele Methoden, wir interessieren uns nur für den Start einer Compilationseinheit für den Paketnamen, für alle Klassen und Methoden. Wir könnten uns aber auch über alle Annotationen oder do-while-Schleifen informieren lassen. Die Ausgabe ist:

Paket: com.tutego.tools.javac

Klasse: PrintAllMethodNames

Klasse:

Methode: visitCompilationUnit

Methode: visitClass

Methode: visitMethod

Methode: main

Die zweite Angabe für den Klassennamen ist leer, da die anonyme Klasse eben keinen Namen hat.

Inselupdate: Konstruktoren der Formatter Klasse

Die String.format(…)-Methode und prinf(…)-Methoden der Ein-/Ausgabeklassen übernehmen die Aufbereitung nicht selbst, sondern delegieren sie an die Klasse java.util.Formatter. Das ist auch der Grund, warum die Dokumentation für die Formatspezifizierer nicht etwa an String.format(…) hängt, sondern an Formatter.

Konstruktor vom Formatter

Die Klasse Formatter hat eine beeindruckende Anzahl von Konstruktoren:

· Formatter()

· Formatter(Appendable a)

· Formatter(Appendable a, Locale l)

· Formatter(File file)

· Formatter(File file, String csn)

· Formatter(File file, String csn, Locale l)

· Formatter(Locale l)

· Formatter(OutputStream os)

· Formatter(OutputStream os, String csn)

· Formatter(OutputStream os, String csn, Locale l)

· Formatter(PrintStream ps)

· Formatter(String fileName)

· Formatter(String fileName, String csn)

· Formatter(String fileName, String csn, Locale l)

Wird nicht der Standardkonstruktur eingesetzt, schreibt der Formatter in die angegebene Quelle. Daher ist die Klasse schön für das Schreiben von Texten in Dateien geeignet. Formatter implementiert Closeable, ist also auch AutoCloseable. Ein Beispiel zum Schreiben in Dateien:

try ( Formatter out = new Formatter( "ausgabe.txt", StandardCharsets.ISO_8859_1.name() ) ) {

for ( int i = 0; i < 10; i++ )

  out.format( "%02d%n", i );

}

catch ( FileNotFoundException | UnsupportedEncodingException e ) {

e.printStackTrace();

}

Inselupdate: Methode mit variabler Argumentanzahl (Vararg)

Bei vielen Methoden ist es klar, wie viele Argumente sie haben; eine Sinus-Methode bekommt nur ein Argument, equals(Object) ein Objekt, println(…) nichts oder genau ein Argument, usw. Es gibt jedoch Methoden, bei denen die Zahl mehr oder weniger frei ist. Ein paar Beispiele:

· Wenn der Aufruf System.out.printf(formatierungsstring, arg1, args2, arg3, …) etwas auf dem Bildschirm ausgibt, ist erst einmal nicht bekannt, wie viele Argumente die Methode besitzt, denn sie sind abhängig vom Formatierungsstring.

· Fügt Collections.addAll(sammlung, elem1, elem2, elem3, …) etwas einer Sammlung hin, ist frei, wie viele Elemente es sind.

· Wird ein Pfad für das Dateisystem zusammengebaut, ist vorher unbekannt, wie viele Segmente Paths.get( anfang, segment1, segment2, … ) besitzt.

· Startet new ProcessBuilder("kommando", "arg1", "arg1", …).start() ein Hintergrundprogramm, ist der Methode unbekannt, wie viele Argumente dem externen Programm übergeben werden.

Um die Anzahl der Parameter beliebig zu gestalten, sieht Java Methoden mit variabler Argumentanzahl, auch Varargs genannt, vor.

Eine Methode mit variabler Argumentanzahl nutzt die Ellipse (»…«) zur Verdeutlichung, dass eine beliebige Anzahl Argumente angegeben werden dürfen, dazu zählt auch die Angabe keines Elements. Der Typ fällt dabei aber nicht unter den Tisch; er wird ebenfalls angegeben.

System.out.printf() nimmt eine beliebige Anzahl von Argumenten an

Eine Methode mit Varargs haben wir schon einige Male verwendet: printf(…). Die Deklaration ist wie folgt:

class java.io.PrintStream extends FilterOutputStream implements Appendable, Closeable

PrintStream printf(String format, Object… args)
Nimmt eine beliebige Liste von Argumenten an und formatiert sie nach dem gegebenen Formatierungsstring format.

Gültige Aufrufe von printf(…) sind demnach:

Aufruf

Variable Argumentliste

System.out.printf("%n")

ist leer

System.out.printf("%s", "Eins")

besteht aus nur einem Element: "Eins"

System.out.printf("%s,%s,%s", "1", "2", "3")

besteht aus drei Elementen: "1", "2", "3"

Maximum eines Feldes finden

Die Klasse java.lang.Math sieht eine statische max(…)-Methode mit zwei Argumenten vor, doch grundsätzlich könnte die Methode auch beliebig viele Argumente entgegennehmen und von diesen Elementen das Maximum bilden. Die Methode könnte so aussehen:

static int max( int… array ) {
}

max(…) behandelt den Parameter array wie ein Feld. Da wir Argumente vom Typ int fordern, ist array vom Typ int[] und kann so zum Beispiel mit dem erweiterten for durchlaufen werden:

for ( int e : array )

Werden variable Argumentlisten in der Signatur definiert, so dürfen sie nur den letzten Parameter bilden; andernfalls könnte der Compiler bei den Parametern nicht unbedingt zuordnen, was nun ein Vararg und was schon der nächste gefüllte Parameter ist:

public class MaxVarArgs {

  static int max( int… array ) {
    if ( array == null || array.length == 0 )
      throw new IllegalArgumentException( "Array null oder leer" );
    int currentMax = Integer.MIN_VALUE;
    for ( int e : array )
      if ( e > currentMax )
        currentMax = e;
    return currentMax;
  }
  public static void main( String[] args ) {
    System.out.println( max(1, 2, 9, 3) );     // 9
  }
}

Tipp: Vararg-Design. Muss eine Mindestanzahl von Argumenten garantiert werden – bei max(…) sollten das mindestens zwei sein – ist es besser, eine Deklaration wie folgt zu nutzen: max(int first, int second, int… rest).

Compiler baut Array auf

Der Nutzer kann jetzt die Methode aufrufen, ohne ein Feld für die Argumente explizit zu definieren. Er bekommt auch gar nicht mit, dass der Compiler im Hintergrund ein Feld mit vier Elementen angelegt hat. So übergibt der Compiler:

System.out.println( max( new int[] { 1, 2, 9, 3 } ) );

An der Schreibweise lässt sich gut ablesen, dass wir ein Feld auch von Hand übergeben können:

int[] ints = { 1, 2, 9, 3 };
System.out.println( max( ints ) );

Hinweis. Da Varargs als Felder umgesetzt werden, sind überladene Varianten wie max(int… array) und max(int[] array), also einmal mit einem Vararg und einmal mit einem Feld, nicht möglich. Besser ist es hier, immer eine Variante mit Varargs zu nehmen, da diese mächtiger ist. Einige Autoren schreiben auch die Einstiegsmethode main(String[] args) mit variablen Argumenten, also main(String… args). Das ist gültig, denn im Bytecode steht ja ein Array.

Arrays.asList() Beispiel und Hinweis

Beispiel: Gib das größte Element eines Feldes aus.

Integer[] ints = { 3, 9, -1, 0 };

System.out.println( Collections.max( Arrays.asList( ints ) ) );

Zum Ermitteln des Maximums bietet die Utility-Klasse Arrays keine Methode, daher bietet sich die max(…)-Methode von Collections an. Auch etwa zum Ersetzen von Feldelementen bietet Arrays nichts, aber Collections. Sortieren und Füllen kann Arrays aber schon, hier muss asList() nicht einspringen.

Hinweis: Wegen der Generics ist der Parameter-Typ von asList() ein Objekt-Feld, aber niemals ein primitives Feld. In unserem Beispiel von eben würde so etwas wie

int[] ints = { 3, 9, -1, 0 };

Arrays.asList( ints );

zwar kompilieren, aber die Rückgabe von Arrays.asList(ints) ist vom Typ List<int[]>, was bedeutet, die gesamte Liste besteht aus genau einem Element und dieses Element ist das primitive Feld. Zum Glück führt Collections.max(Arrays.asList(ints)) zu einem Compilerfehler, denn von einer List<int[]>, also eine Liste von Feldern, kann max(Collection<? extends T>) kein Maximum ermitteln.

How to put files in a ZIP file with NIO.2

URI p = Paths.get( "c:/Users/Christian/Dropbox/jokes.zip" ).toUri();
URI uri = URI.create( "jar:" + p );

Map<String, String> env = new HashMap<>();
env.put( "create", "true" );
try ( FileSystem zipfs = FileSystems.newFileSystem( uri, env ) ) {
  Files.write( zipfs.getPath( "/j1.txt" ), "The truth is out there. Anybody got the URL?".getBytes() );
  Files.write( zipfs.getPath( "/j2.txt" ), "The more I C, the less I see.".getBytes() );
}

Designfrage: Wie nutzt man GWT-RPC-Aufrufe lokal etwa bei Offline-Anwendungen?

Standardmäßig sieht es ja im “normalen” entfernen RPC-Aufruf so aus: Als erstes die Schnittstelle:

public interface ContactRpcService extends RemoteService {

  Contact getContactById( long id );

}

Dann die zugehörige Async-Schnittstelle:

public interface ContactRpcServiceAsync {

  void getContactById( long id, AsyncCallback<Contact> callback );

}

Der Client hat nun so was wie

ContactRpcServiceAsync contactService = GWT.create( ContactRpcService.class );

contactService.getContactById( contactId, new DefaultCallback<Contact>() {

  @Override protected void handleResponse( Contact response ) {

    ….

  }

} );

DefaultCallback ist eine meine abstrakte Klasse, die AsyncCallback implementiert, aber das ist jetzt nicht so wichtig.

Auch etwa anders mache ich noch, damit ich weniger schreiben muss; ich habe mir eine Klasse Rpc deklariert, mit Konstanten für alle GWT-creates():

public class Rpc {

  private Rpc() {}

  public final static ContactRpcServiceAsync  contactService = GWT.create( ContactRpcService.class );

  // … un ddie Anderen

}

Normalerweise sieht es also bei mir so aus:

Rpc.contactService.getContactById( contactId, new DefaultCallback<Contact>() {

  @Override protected void handleResponse( Contact response ) {

    …

  }

} );

Damit nun Rpc.contactService.getContactById() im Offline-Modus etwas anderes macht, kann man zum Beispiel folgendes tun: Rpc.contactService stammt nicht von GWT.create(), sondern ist ein Proxy. Falls nun der Remote-Fall gewünscht ist, delegiert man an an die echte Rpc-Implementierung, andernfalls an eine lokale Variante.

public class Rpc {

  private Rpc() {}

  public final static ContactRpcServiceAsync  contactService = new ContactRpcServiceAsync() {

    private final ContactRpcServiceAsync delegate = GWT.create( ContactRpcService.class );

    private final ContactRpcServiceAsync local = new ContactRpcServiceAsync() {
      @Override public void getContactById( long id, AsyncCallback<Contact> callback ) {

        // hier alles lokale machen, also etwas aus dem Cache holen. Wenn alles gut geht, und die Daten vorhanden sind, dann aufrufen
        callback.onSuccess( result );
      }

    };

    @Override public void getContactById( long id, AsyncCallback<Contact> callback ) {

      wenn der remote Fall

        delegate.getContactById( id, callback );

      andernfalls

        local.getContactById( id, callback );

    }

  };

}

JPA-Beispiel in wenigen Minuten mit OpenJPA

  1. Beziehe unter http://mvnrepository.com/artifact/org.apache.openjpa/openjpa-all das openjpa-all-2.2.0.jar (etwas mehr als 6 MiB) und setzte es in den Klassenpfad.
  2. Habe einen Datentreiber im Klassenpfad (bei mir den der HSQLDB).
  3. Lege im Projekt einen Ordner META-INF an, platziere dort eine Datei persistence.xml:
  4. <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_2_0.xsd"
      version="2.0">
      <persistence-unit name="traida" transaction-type="RESOURCE_LOCAL">
        <class>traida.shared.domain.Contact</class>
        <properties>
          <property name="openjpa.jdbc.DBDictionary" value="hsql"/>
          <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:file:hsqldbtest;user=sa" />
          <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" />
          <property name="openjpa.Log" value="DefaultLevel=ERROR, Tool=ERROR" />
          <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
        </properties>
      </persistence-unit>
    </persistence>

    Mehr zu den Properties unter http://openjpa.apache.org/builds/apache-openjpa-2.2.1-SNAPSHOT/docs/docbook/manual/main.html.

  5. Lege eine Klasse traida.shared.domain.Contact an:
  6. @Entity
    public class Contact {
      @Id
      @GeneratedValue( strategy = GenerationType.IDENTITY )
      public Long id;
      public String name;
      // Setter/Getter ausgelassen
    }

  7. Schreibe eine main(String[])-Methode mit:
  8. EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "traida" );
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    entityManager.getTransaction().begin();
    Contact c = new Contact();
    c.name = "Hallo Willi";
    entityManager.persist( c );
    entityManager.getTransaction().commit();
    System.out.println( entityManager.createQuery( "select c from Contact c" ).getResultList() );
    entityManager.close();

  9. Fertig, jetzt freuen.