JNotify: Was tut sich im Dateisystem?

Die kleine (native) unter Linux und Windows arbeitende Bibliothek JNotify meldet Änderungen an Dateien. Ein Beispiel:

String path = "c:/";

int mask = JNotify.FILE_CREATED | JNotify.FILE_DELETED | JNotify.FILE_MODIFIED | JNotify.FILE_RENAMED;


boolean watchSubtree = true;


int watchID = JNotify.addWatch(path, mask, watchSubtree, new JNotifyListener()
{
@Override
public void fileCreated( int arg0, String arg1, String arg2 )
{
System.out.println( "created" );
}


@Override
public void fileDeleted( int arg0, String arg1, String arg2 )
{
System.out.println( "deleted" );
}

@Override
public void fileModified( int arg0, String arg1, String arg2 )
{
System.out.println( "modified" );
}

@Override
public void fileRenamed( int arg0, String arg1, String arg2, String arg3 )
{
System.out.println( "renamed" );
}
} );

Und am Ende:

JNotify.removeWatch(watchID);

jodconverter für OpenOffice Dateiformatkonvertierungen

Der zweite Schritt meiner Rechnungserstellung ist die Konvertierung in PDF. Auch dazu gibt es eine prima Bibliothek: http://www.artofsolving.com/opensource/jodconverter. (Einziger Nachteil: viele Jars.) Damit kann man Word, PowerPoint, RTF und alles andere in ein bliebiges Zielformat bringen.

private static void ensureStartedOpenOfficeService()
{
try
{
new Socket( "127.0.0.1", 8100 );
}
catch ( Exception e )
{
String path = "C:/Programme/OpenOffice.org 2.3/program/";
ProcessBuilder processBuilder = new ProcessBuilder( path+"soffice", "-headless", "-accept=\"socket,host=127.0.0.1,port=8100;urp;\"", "-nofirststartwizard" );
try
{
processBuilder.start();
}
catch ( IOException ioe )
{
throw new RuntimeException( ioe );
}
}
}
public static void convert( String source, String destination )
{
ensureStartedOpenOfficeService();

OpenOfficeConnection connection = null;
try
{
connection = new SocketOpenOfficeConnection( 8100 );
connection.connect();
DocumentConverter converter = new OpenOfficeDocumentConverter( connection );
File inputFile = new File( source );
File outputFile = new File( destination );
converter.convert( inputFile, outputFile );
}
catch ( ConnectException e )
{
throw new RuntimeException( e );
}
finally
{
connection.disconnect();
}
}

Für meine Rechnungen also:

String destination    = "S:/Private/Traida/Bills/bill1";
String destinationOds = destination + ".ods";
String destinationPdf = destination + ".pdf";
convert( destinationOds, destinationPdf );

OpenOffice als Template-Engine: Dokument einlesen, verändern, schreiben

Da ich meine Rechnungen automatisch generiert und als PDF konvertiert haben möchte, habe nach einer Lösung gesucht, wie ich die Rechnungsdaten aus einer Datenquelle in der Template einsetzten kann, und am Ende eine PDF bekomme. Klar sind Report-Generatoren dafür auf der Welt, aber meine Vorlagen möchte ich nicht für Eclipse BIRT oder Jasper schreiben, sondern in Word bzw. OpenOffice. RTF ist relativ leicht zu schreiben und für ein Template meines Erachtens ganz gut. Meine Gedanken kreisten daher einige Zeit um RTF->PDF, doch da gibt es keine freie Lösung. Auch Wege wie RTF->FO->PDF sind möglich, aber dafür gibt es ebenfalls keine leichtgewichtigen freien Lösungen.

Schon für meinen PowerPoint->PDF-Konverter habe ich mit OpenOffice gearbeitet und das klappte ganz gut. Das habe ich für meine Templates nun wieder überlegt — ein recht harter Weg für simple RTFs zwar, aber es funktioniert. Doch anstatt RTF zu nutzen, wollte ich gleich das XML-Format verwenden. Dazu muss man wissen, dass OO ein Zip-Archiv für das OO-Dokument vorsieht und dort in einer XML-Datei den Content ablegt. Mit der großartigen Open-Source-Bibliothek https://truezip.dev.java.net/ ist der Zugriff auf Archive sehr einfach.

Mit TrueZIP ist eine einfache Lösung entstanden, ein OO-Dokument mit Template-Anweisungen wie ${address} zu lesen, Ersetzungen vorzunehmen, und alles wieder zu schreiben. Diesen Text hier zu schreiben hat länger gedauert, als die 90 Zeilen Quellcode. Also los:


import java.io.*;
import java.nio.channels.FileChannel;
import de.schlichtherle.io.ArchiveDetector;

public class OpenOfficeUtils
{
public static void main( String[] args )
{
String source = "S:/Private/Traida/Bills/template.ods";
String destination = "S:/Private/Traida/Bills/bill1.ods";

copyFile( source, destination );

String content = readOpenOfficeContent( source );

content = content.replace( "${addressline1}", "Christian Ullenboom" );

writeOpenOfficeContent( destination, content );
}

public static String readOpenOfficeContent( String filename )
{
Reader is = null;

try
{
de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
char[] fileContent = new char[ (int) file.length() ];
is = new de.schlichtherle.io.FileReader( file ); // TODO: <?xml version="1.0" encoding="UTF-8"?>
is.read( fileContent );

return new String(fileContent);
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { is.close(); } catch ( Exception e ) { }
}
}

public static void writeOpenOfficeContent( String filename, String content )
{
Writer os = null;

try
{
de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
os = new de.schlichtherle.io.FileWriter( file );
os.write( content );
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { os.close(); } catch ( Exception e ) { }
}
}

public static void copyFile( String in, String out )
{
FileChannel inChannel = null;
FileChannel outChannel = null;

try
{
inChannel = new FileInputStream( new File(in) ).getChannel();
outChannel = new FileOutputStream( new File(out) ).getChannel();
inChannel.transferTo( 0, inChannel.size(), outChannel );
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { inChannel.close(); } catch ( Exception e ) { }
try { outChannel.close(); } catch ( Exception e ) { }
}
}
}

Strg+F11: Noch so eine komische Eclipse-Änderung

Vor nicht allzulanger Zeit startete Eclipse mit Strg+F11 das letzte Programm. Dann änderte man dies und Eclipse versuchte automatisch das im Editor aktive Programm zu starten. Ich denke, dass diese Umstellung eher lästig ist und eines der ersten Sachen sein dürfte, die man abschaltet. Das geht so: In den Preferences unter Run/Debug > Launching den Schalter bei Always launch the … einstellen.

image

1 Stunde Suchen für eine doofe Eclipse-Einstellung

<Sarkasmus>Dankenswerterweise</Sarkasmus> hat Eclipse eine neue Einstellung bekommen, in dem gewissen Daten vom Kopieren in den Output-Folder ausgenommen werden können. Dass sich diese Einstellung unter Java > Compiler > Building befindet, fand ich erst nach langem Suchen hier: http://www.codecommit.com/blog/eclipse/wtps-crazy-and-undocumented-setting-change

wtp-screwup

Mein Problem: Bei der Entwicklung mit Wicket stehen die HTML-Seiten Seite an Seite mit den Java-Klassen. In den classes-Folder für die Web-Anwendung gingen aber nur die Klassen und nicht die HTML-Dateien. Da gestern auf einer anderen Eclipse-Version alles noch lief, war ich heute lange verzweifelt und kann glücklicherweise nach dieser Einstellung wieder an die Arbeit gehen.

SQL für Objekte: JoSQL (SQL for Java Objects)

JoSQL (SQL for Java Objects) unter http://josql.sourceforge.net/index.html ist eine Open-Source Biblitohek, um SQL-Anfragen an Objektgrafen zu stellen. Die Webseite gibt interessante Beispiele.

String query = "SELECT * FROM   java.io.File WHERE  name LIKE '%.mp3'";
Query q = new Query();
q.parse( query );
QueryResults results = q.execute( Arrays.asList( new File("C:/Data/Musik/").listFiles() ) );
System.out.println( results.getResults() );

Oder für Ausdrücke:

/*
* Use as a file filter.
*/
String exp = "lastModified BETWEEN toDate('10/May/2007') AND toDate('28/Jun/2007') " +
"AND " +
"length >= 10 * 1024" +
"AND " +
"path LIKE '%/subdir/%'";

ExpressionEvaluator ee = new ExpressionEvaluator (exp, File.class);

if (ee.isTrue (myfile))
{
// Process the file.
}

Buchkritik: Das Java Codebook

Marcus Wiederstein, Marco Skulschus, Mark Donnermeyer, Dirk Brodersen, Benjamin Rusch. Addison-Wesley. ISBN 3-8273-2059-3. 2003. 858 Seiten
Programmabschnitte, sog. Snippets, gibt es im Internet genug (wir wäre es alternativ mit dem Java-Almanac?) und wer eine Suchmaschine bedienen kann, wird immer, ja immer, die Bibliothek oder den Quellcode-Schnippsel finden, die er sucht. Um so erstaunlicher finde ich es, wenn Verlage Quellcode-Kochbücher veröffentlichen, ob sie nun Codebook oder Cookbook heißen. Ernsthaft: Wer eine Klasse zur Bruchrechnung sucht oder ein Beispielprogramm zum Aufbau eines Swing-Fensters, geht sicherlich nicht zum Regal, sucht im Index nach „Bruch“ und holt sich dann die Klasse von der CD. Daher die Frage – ist ein Buch wie „Das Java Codebook“ notwendig? Das Positive zuerst: Die Kapitel über reguläre Ausdrücke und Applets (Daten im Cookie speichern, Uploads, Applet-Konverter für Tags), die kleinen Beispielprogramme, in denen man etwa über ein BufferedImageOp.filter Bilder heller/dunkler macht, sowie Hinweise zur Transparenz und der Index finde ich prima. Eigentlich sind Bücher mit Quellcode gut, denn Software-Entwickler lesen im Allgemeinen zu wenig. Das Studium von Quellcode hilft jedem Entwickler, neue Idiome zu lernen und seine Fähigkeiten zu verbessern. Je nach Schwerpunkt des Buches lernt man weitere Möglichkeiten der Standardbibliothek kennen oder neue Open-Source-Bibliotheken. Soweit die Möglichkeiten guter Codebooks. Leider hat das Java Codebook dann doch die eine oder andere Schwachstelle, so dass man Software-Entwicklern vielleicht doch besser direkt zur Google-Suche raten sollte. (Immerhin gibt es das Buch als PDF.) Die Probleme im Detail: Die Klasse

public class CalcExample {
static final BigDecimal ZERO = new BigDecimal(0);
static final BigDecimal ONE = new BigDecimal(1);
static final BigDecimal FOUR = new BigDecimal(4);

deklariert drei Variablen (Sichtbarkeit könnte auch anders sein), doch vergisst, dass es ZERO, ONE, TEN schon in BigDecimal gibt. In anderen Beispielen gefällt mir die Schreibweise der Felder nicht. Einiges ist nach der Bauart String []array, also Typ, Leerzeichen, Variablenname aufgebaut. Das habe ich nach 11 Jahren Java hier zum ersten Mal gelesen. Zumal sich die Autoren hier uneins zu sein scheinen, denn sie verwenden auch String[]. Die fünf Autoren des Buchs folgen demnach keiner einheitlichen Coding-Konvention. Ein weiteres Thema betrifft die Methodennamen: Überwiegend werden englische Bezeichner (setNumber, less100, …) gebraucht, doch lassen sich innerhalb eines Beispiels Vermischungen mit deutschen Variablennamen entdecken, wie das Feld ZEHN. Einige Autoren mögen (immer noch) die Klassen Hashtable und Vector, andere nutzen doch besser List und Map – hat denn keiner der Autoren die anderen Kapitel gelesen und kommentiert? Bei den Datenstrukturen frage ich mich wirklich, was denn arrayList.add(new String(new java.util.Date().toString())); für eine Anweisung sein soll. Und warum wird ein Stack noch mal neu implementiert? OK, der Original-Stack erbt von Vector, was vom Design total verpfuscht wurde, aber dann könnte man doch immer noch den neuen Stack so implementieren, das er an den alten Stack delegiert, anstatt alle Operationen komplett mit einer ArrayList neu zu implementieren. (Und dann noch die fragwürdige Methode search() zu übernehmen.) Eine weitere Schwachstelle, die ich in der Insel weitestgehend versuche zu vermieden, sind Windows-spezifische Pfadangaben, wie etwa bei new FileOutputStream(„c:\\buecher.ser“). Bei IO-Beispielen fehlt oft das close() im finally. Vor allem bei HTTP-Servern ist das kritisch. Im JSP-Kapitel findet sich plötzlich ein JDBC-Close im finally; also hat es nicht jeder der Autoren vergessen. Frage: Was gibt dieses Programm bei Argumenten auf der Kommandozeile aus?

package javacodebook.io.stdout;
public class StdOut {
public static void main(String[] args) {
String text = "Hallo Welt";
//Wenn Text angegeben, dann diesen ausgeben
if(args.length < 1)
for(int i = 0; i < args.length; i++)
System.out.println(args[i]);
else
System.out.println(text);
}
}

Dass ist wohl eher eine Zertifizierungsfrage statt eines sinnvollen Codebeispiels, denn ist die Anzahl der Argumente kleiner 1, gibt es keine Argumente und die Schleife ist nutzlos. Genauso falsch ist der Satz „JDBC benutzt dabei die Interfaces Connection, Driver und DriverManager.“ Schade, denn DriverManager ist eine Klasse. Sollte es auch, denn aus dieser Zentrale erhält man durch die statische Methode getConnection() eine Verbindung. Im Anhang finden sich dann noch ein paar Dreher in der Groß-/Kleinschreibung. Z. B. beginnen einige Bezeichner in der Tabelle „Java Native Interface Typen“ mit Großbuchstaben. Besser wären „int“ statt „Int“ sowie „void“ statt „Void“ usw. Ähnliches gilt für JscrollPane, was zu JScrollPane werden sollte. Unter „Relationale Operatoren“ findet sich folgender Satz bei ==-Operator: „a == b ergibt true, wenn a gleich b ist. Handelt es sich bei a und b um Referenztypen, so ist der Rückgabewert dann true, wenn beide auf dasselbe Objekt zeigen.“ Wir sollten den Sonderfall a = b = NaN aber nicht Außeracht lassen, denn gerade dann ist a = b, aber das Ergebnis nicht true. In der Tabelle „Klassenobjekte für den primitiven Datentyp“ sollte Void besser nicht auftauchen, da es sich nicht um einen primitiven Datentyp handelt. Oder Void stehen lassen, aber die Überschrift anpassen. Einige Kritikpunkte betreffen die Auswahl der Klassen. Die Klasse Fraction sollte vielleicht besser einer bekannten Open-Source-Bibliothek entstammen und die Matrizen sowie der umfangreiche Quellcode nicht unbedingt abgebildet werden. Für CSV-Dateien wären populäre Standard-Klassen sinnvoller. Der Tipp 186 für Timer sollte diesen nicht selbst implementieren, sondern die zwei (!) Timer-Klassen der Java-Lib vorstellen. Da ein Codebook grundsätzlich eine unendliche Sache ist, könnten weitere Snippets eingebracht werden. Bei den Zufallszahlen wäre z. B. SecureRandom gut untergebracht. Das Beispiel FindInFile, was händisch nach einem String in einer Datei sucht, lässt sich viel effektiver mit NIO (mit Standard-Beispiel von Sun) lösen. Das NIO-Kapitel kommt mit nur zwei Beispielen daher (Datei kopieren und File-Lock). Das ist eine magere Ausbeute. Später kommt noch ein NIO-Server dazu. Der Verweis auf eine einfache NIO-Bibliothek (wie xSocket) wäre dann wünschenswert. Um ein Fenster mittig zu positionieren, muss man nicht rechnen, hier reicht eine Java-Funktion. Wenn es sich schon um ein Java 1.4 Buch handelt, dann sollte ImageIO Grafiken laden. Die Swing-Beispiele sind relativ unspektakulär. Hier wäre es wohl sinnvoller statt „wie erzeuge ich einen Button“ oder „wie etwas mehr mit den Modellen zu machen“, einen Dateibaum in ein JTree zu setzen. Und stilistisch wertvoll ist der Hinweis mit der JDBC-Verbindung in einem JSP-Tag sicher nicht. Und wo sind die DAOs? Bei JMS stimmt es zwar grundsätzlich, dass in einer Tabelle bei Programmiersprachenunabhängigkeit „Nein“ steht und bei HTTP „Ja“, doch allgemeingültig ist das nicht. Für ActiveMQ gibt es ein standardisiertes Protokollformat (OpenWire) und APIs für diverse andere Programmiersprachen. Das gleiche gilt auch für IBMs WebSphere MQ (früher MQSeries) – sicherlich eines der besten MOMs -, das heterogene Plattformen verbindet. Einen XML-Parser über die Anweisung org.apache.xerces.parsers.DOMParser parser = new org.apache.xerces.parsers.DOMParser(); statt über JAXP zu erfragen ist nicht schön sondern fast grob fahrlässig. Zudem haben auch die JAXP-Parser Features, obwohl die String-Kennungen dann immer ein wenig anders aussehen. Zu Tipp 196: Wann immer man sich mit getGraphics() einen Grafik-Kontext holt, sollte man ihn auch wieder freigeben. Dieser Hinweis fehlt leider in den Beispielen. Farbkonstanten sollten groß geschrieben sein, also Color.WHITE, statt Color.white, zumindest seitdem Sun diese Konvention in Java 1.4 eingeführt hat.

Erst wenn der letzte Programmierer eingesperrt und die letzte Idee patentiert ist, werdet ihr merken, dass Anwälte nicht programmieren können.

Shortest Java Quine with 106 chars

Because of a contest in a German Java forum I tried to formulate the shortest Java Quine:

enum _{_;{String _="enum _{_;{String _=%c%s%1$c;System.out.printf(_,34,_);}}";System.out.printf(_,34,_);}}

To compile and run under Windows (with Unix you need to redirect Exceptions to /dev/null and use diff):

>javac _.java 
>java _ 2> NUL > Quine.out
> fc Quine.out _.java
Vergleichen der Dateien Quine.out und _.JAVA
FC: Keine Unterschiede gefunden
>jrunscript -e "print(new java.io.File('_.java').length())"
106

I haven’t found a shorter solution on the net, so I suppose this is the shortest for now.

Inselupdate: JMX mit RMI-Adaptor

Für die Anbildung an den JMX-Server dient nicht nur die JConsole, sondern verschiedene andere Wege sind denkbar, etwa über HTTP oder über einen eigenen Client, der über RMI kommuniziert.

JMXConnectorServer für den Server

Zur Veröffentlichung der MBeans über RMI ist der JMXConnectorServer verantwortlich. Damit die MBeans an die RMI-Registry gebunden werden, wird eine besondere URL verwendet, die den Service genau beschreibt. Die JMXServiceURL hat den allgemeinen Aufbau:

service:jmx:<protocol>://[[[<host>]:<port>]/<path>]

Vor dem Start des Programms muss die RMI-Registry für Anmeldungen bereit sein; da unser Programm auf dem gleichen Rechner arbeitet, starten wir den RMI-Namensdienst selbst.

LocateRegistry.createRegistry( 1099 );
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
String url = „service:jmx:rmi:///jndi/rmi://localhost/maze“
JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(
new JMXServiceURL( url ), null, server );
connectorServer.start();

ObjectName name = new ObjectName( „com.tutego.insel.jmx:type=Maze“ );
MazeMBean maze = new Maze();
server.registerMBean( maze, name );

JOptionPane.showMessageDialog( null, „Ende“ );
System.exit( 0 );

JConsole mit Remote-Connection

Mit der JConsole könnten wir nun Verbindung aufnehmen, in dem unter Remote Connection die URL service:jmx:rmi:///jndi/rmi://localhost/maze Einsatz findet.

JMXConnector für den Client

Auf der Clientseite gibt es mit JMXConnector den Gegenspieler zum JMXConnectorServer. Das Objekt liefert uns mit getMBeanServerConnection() ein MBeanServerConnection-Objekt, was sozusagen die Repräsentation des MBean-Servers MBeanServer auf der anderen Seite entspricht (genauso genommen gilt für die Schnittstellen: MBeanServer extends MBeanServerConnection). Über getAttribute() lassen sich dann zum Beispiel Properties erfragen.

JMXServiceURL u = new JMXServiceURL( „service:jmx:rmi:///jndi/rmi://localhost/maze“ );
JMXConnector c = JMXConnectorFactory.connect( u );
MBeanServerConnection mbsc = c.getMBeanServerConnection();
ObjectName name = new ObjectName( „com.tutego.insel.jmx:type=Maze“ );
System.out.println( mbsc.getAttribute( name, „MaxNumberOfPlayers“ ) );

Schnittstelle und Klasse für die MBean

package com.tutego.insel.jmx;

public interface MazeMBean
{
void setMaxNumberOfPlayers( int numberOfPlayers );
int getMaxNumberOfPlayers();
void dragonAlarm();
}

package com.tutego.insel.jmx;

public class Maze implements MazeMBean
{
private int numberOfPersons;

public void setMaxNumberOfPlayers( int numberOfPersons )
{
this.numberOfPersons = numberOfPersons;
}

public int getMaxNumberOfPlayers()
{
return numberOfPersons;
}

public void dragonAlarm()
{
System.out.println( „Heul. Huhuhuhu.“ );
}
}

TeX-Formulate in Webseiten mit jsMath

http://www.math.union.edu/~dpvc/jsMath/ ist eine Layout-Engine, die TeX-Formulare in Webseiten rendert. Dabei werden nicht, wie bei anderen Ansätzen, Grafiken generiert, sondern die Ausdrücke mit CSS positioniert. Damit skaliert das Ganze schön. Das Ergebnis ist beeindruckend und die Engine wird auch von einigen Wiki-Systemen verwendet. Einige Beispiele: Examples of jsMath. Im Interactive jsMath lab kann man Formen eingeben und es kommt HTML raus, was man nur noch zusammen mit dem CSS auf die eigene Webseite setzen muss. Aus x_i^2 wird etwa:

<SPAN CLASS=“typeset“><nobr><span class=“scale“><span class=“icmmi10″>x</span><span style=“position: relative; top:0.372em;“><span class=“size2″><span class=“icmmi10″>i</span></span><span class=“spacer“ style=“margin-left:0.05em“></span></span><span style=“position: relative; margin-left:-0.260em; top:-0.362em;“><span class=“size2″><span class=“icmr10″>2</span></span><span class=“spacer“ style=“margin-left:0.05em“></span></span><span class=“blank“ style=“height:1.331em;vertical-align:0.908em“></span></span></nobr></SPAN>

Jetzt fehlen nur noch jsMath.js und, falls etwa Summen-Zeichen oder sonstiges Sonderzeichen verwendet werden, im fonts-Ordner die TeX-Fonts.

Danke für die Nutzung der Insel als Vorlage für Uni-Folien

Hier möchte ich mich bei Ihnen, Herrn Dr. Ralf Kunze, und Team der Universität Osnabrück herzlich bedanken. Es freut mich sehr, dass Sie für ihre Vorlesungsunterlagen http://www-lehre.inf.uos.de/~binf/2007/index.html „Objekt-orientierte Programmierung in Java“ im Sommersemester 2007 so viel Abschnitte der Insel gebrauchen konnten. Immer wieder beglückt es mich, wenn Referenten und Dozenten Abschnitte 1:1 übernehmen und damit die Qualität meines Buches „Java ist auch eine Insel“ auch für den wissenschaftlichen Alltag bestätigen. (Ich wusste auch nicht, dass Rechtschreibfehler im Buch ebenfalls zum Insel-Kult gehören, und sie deshalb auch immer mitkopiert werden müssen.) Dass Sie dabei die Insel erst an dritter Stelle Ihrer Literaturangaben platzieren, ist selbstverständlich zu verzeihen. Besonders gefallen hat mir auf Ihrer Uni-Seite die Aussage „Zum legalen online Lesen, herunterladen oder auch kaufen“. Das finde ich gut, denn „illegales Online-Lesen“ wäre ja quatsch. Natürlich habe weder ich noch mein Verlag Galileo-Computing etwas dagegen einzuwenden, wenn ohne Nachfrage die geglücktesten Abschnitte kopiert und ohne Kennung der Quellen übernommen werden – Seite für Seite als Zitat zu kennzeichnen ist natürlich lästig. Das ist Arbeiten im Sinne von Stanisław Jerzy Lec: „Von der Mehrzahl der Werke bleiben nur die Zitate übrig. Ist es dann nicht besser, von Anfang an nur die Zitate aufzuschreiben?“

Ich war so frei, einige Ihrer Folien auf meinen Blog aufzunehmen. Ich hoffe, Sie sehen von Klagen wegen Urheberrechtsverletzungen ab; immerhin kopiere ich Ihre Texte hier ohne Nachfrage. (Alles Aufzulisten wäre zu viel Arbeit.
Das sind nur einige Beispiele mit direkten Kopien ganzer Absätze
für je eine Folie.)

Insel Kopie
  • throws bei überschriebenen Methoden
  • Überschriebene Methoden in einer Unterklasse dürfen nicht mehr Ausnahmen auslösen wie schon beim throws-Teil der Oberklasse aufgeführt sind.
  • Da das gegen das das Substitutionsprinzip verstoßen würde, kann eine Methode der Unterklasse
    – nur dieselben Ausnahmen wie die Oberkasse auslösen
    – Ausnahmen spezialisieren oder
    – weglassen.
  • throws bei überschriebenen Methoden
  • Überschriebene Methoden in einer Unterklasse dürfen nicht mehr Ausnahmen in der throws-Klausel deklarieren als schon bei der throws-
    Klausel der Oberklasse aufgeführt sind
  • Das würde gegen das Substitutionsprinzip verstoßen. Eine Methode der Unterklasse kann:
    –dieselben Ausnahmen wie die Oberkasse auslösen
    –Ausnahmen spezialisieren
    –weglassen
  • Abschlussbehandlung mit finally
  • Nach einem (oder mehreren) catch kann optional ein finally-Block folgen.
  • Die Laufzeitumgebung führt die Anweisungen im finally-Block immer aus, egal, ob ein Fehler auftrat, oder die Anweisungen im try-catch-Block optimal durchliefen.
  • Das heißt, der Block wird auf jeden Fall ausgeführt, auch wenn im try-catch-Block ein return, break oder continue steht oder eine Anweisung eine neue Ausnahme auslöst.
  • Der Programmcode im finally-Block bekommt auch gar nicht mit, ob vorher eine Ausnahme auftrat oder alles glatt lief.
  • Sinnvoll sind Anweisungen im finally-Block immer dann, wenn Operationen immer ausgeführt werden sollen.
  • Eine typische Anwendung ist die Freigabe von Ressourcen oder das Schließen von Dateien.

  • Abschlussbehandlung mit finally
  • Nach einem (oder mehreren) catchkann optional ein finally-Blockfolgen
  • Die Laufzeitumgebung führt die Anweisungen im finally-Blockimmer aus, egal, ob ein Fehler auftrat, oder die Anweisungen im try/catch-Block optimal durchliefen.
  • Das heißt, der Block wird auf jeden Fall ausgeführt, auch wenn im try/catch-Blockein return, break oder continuesteht oder eine Anweisung eine neue Ausnahme auslöst
  • Der Programmcode im finally-Blockbekommt nicht mit, ob vorher eine Ausnahme auftrat
  • Sinnvoll sind Anweisungen im finally-Blockimmer dann, wenn Operationen immer ausgeführt werden sollen
  • Eine typische Anwendung ist die Freigabe von Ressourcen oder das Schließen von Dateien

  • Bei der Konvertierung eines größeren Ganzzahltyps in einen kleineren werden einfach die oberen Bits abgeschnitten.
  • Eine Anpassung des Vorzeichens findet nicht statt.
  • Die Darstellung in Bit zeigt das sehr anschaulich:

    int ii = 123456789; // 00000111010110111100110100010101

    int ij = –123456; // 11111111111111100001110111000000 short
    si = (short) ii; // 1100110100010101

    short sj = (short) ij; // 0001110111000000
    System.out.println( si ); // –13035

    System.out.println( sj ); // 7616

  • Bei der Konvertierung eines größeren Ganzzahltyps in einen kleineren werden einfach die oberen Bits abgeschnitten
  • Eine Anpassung des Vorzeichens findet nicht statt
  • Die Darstellung in Bit zeigt das sehr anschaulich:

    int m = 123456789; // 00000111010110111100110100010101

    shorts m = (short) m;// 1100110100010101
    System.out.println(sm); // -13035

    int n = -123456;// 11111111111111100001110111000000

    short sn= (short) n;// 0001110111000000

    System.out.println(sn); // 7616

  • Eine Datei oder ein Verzeichnis besitzt zahlreiche Eigenschaften, die sich mit Anfragemethoden auslesen lassen. In einigen wenigen Fällen lassen sich die Attribute auch ändern.
  • boolean canExecute(), canRead(), canWrite(). true, wenn die Ausführungsrechte/Leserechte/Schreibrechte gesetzt sind.
  • long length() Gibt die Länge der Datei in Byte zurück oder 0L, wenn die Datei nicht existiert oder es sich um ein Verzeichnis handelt.
  • long lastModified() Liefert den Zeitpunkt, zu dem die Datei zum letzten Mal geändert wurde. Die Zeit wird in Millisekunden ab dem 1. Januar 1970, 00:00:00 UTC, gemessen. Die Methode liefert 0, wenn die Datei nicht existiert oder ein Ein-/Ausgabefehler auftritt.
  • boolean setLastModified( long time ) Setzt die Zeit (wann die Datei zuletzt geändert wurde). Die Zeit ist wiederum in Millisekunden seit dem 1. Januar 1970 angegeben. Ist das Argument negativ, dann wird eine IllegalArgumentException ausgelöst.

  • Eine Datei oder ein Verzeichnis besitzt zahlreiche Eigenschaften, die sich mit Anfragemethoden auslesen und teilweise auch ändern lassen:
  • boolean canExecute() (Java6), canRead(), canWrite()liefern true, wenn die Ausführungsrechte/Leserechte/Schreibrechte gesetzt sind
  • long length() Gibt die Länge der Datei in Byte zurück oder 0L, wenn die Datei nicht existiert oder es sich um ein Verzeichnis handelt
  • long lastModified() Liefert den Zeitpunkt, zu dem die Datei zum letzten Mal geändert wurde. Die Zeit wird in Millisekunden ab dem 1. Januar 1970, 00:00:00 UTC, gemessen. Die Methode liefert 0, wenn die Datei nicht existiert oder ein Ein-/Ausgabefehler auftritt
  • boolean setLastModified(longtime)Setzt die Zeit (wann die Datei zuletzt geändert wurde). Die Zeit ist wiederum in Millisekunden seit dem 1. Januar 1970 angegeben. Ist das Argument negativ, dann wird eine IllegalArgumentException ausgelöst

  • Die Schnittstellen Closeable und Flushable
  • Closeable wird von allen lesenden und schreibenden Datenstrom-Klassen implementiert, die geschlossen werden können.
  • Das sind alle Reader/Writer- und InputStream/OutputStream-Klassen.
  • void close() throws IOException. Schließt den Datenstrom. Einen geschlossenen Strom noch einmal zu schließen hat keine Konsequenz.
  • Flushable findet sich nur bei schreibenden Klassen und ist insbesondere bei denen wichtig, die Daten puffern.
  • void flush() throws IOException Schreibt gepufferte Daten in den Strom.

  • Closeableund Flushable
  • Closeable wird von allen lesenden und schreibenden Datenstrom-Klassen implementiert, die geschlossen werden können
  • Das sind alle Reader/Writer-und InputStream/OutputStream-Klassen
  • void close() throws IOException schließt den Datenstrom. Einen geschlossenen Strom noch einmal zu schließen hat keine Konsequenz
  • Flushable findet wird nur bei schreibenden Klassen implementiert. Flushable ist insbesondere bei Klassen wichtig, die Daten puffern
  • void flush() throws IOException schreibt gepufferte Daten in den Strom

  • Ein SequenceInputStream-Filter hängt mehrere Eingabeströme zu einem großen Eingabestrom zusammen.
  • Nützlich ist dies, wenn wir aus Strömen lesen wollen und es uns egal ist, was für ein Strom es ist, wo er startet und wo er aufhört.
  • Der SequenceInputStream lässt sich erzeugen, indem im Konstruktor zwei InputStream-Objekte mitgegeben werden.
  • Soll aus zwei Dateien ein zusammengesetzter Datenstrom gebildet werden, benutzen wir folgende Programmzeilen:
    InputStream s1 = new FileInputStream( „teil1.txt“ );
    InputStream s2 = new FileInputStream( „teil2.txt“ );
    InputStream s = new SequenceInputStream( s1, s2 );
  • Ein Aufruf irgendeiner read()-Methode liest nun erst Daten aus s1.
  • Liefert s1 keine Daten mehr, kommen die Daten aus s2.
  • Liegen keine Daten mehr an s2, aber wieder an s1, ist es zu spät.

  • Ein SequenceInputStream-Filterhängt mehrere Eingabeströme zu einem großen Eingabestrom zusammen
  • Nützlich ist dies, wenn wir aus Strömen lesen wollen und es uns egal ist, was für ein Strom es ist, wo er startet und wo er aufhört.
  • Der SequenceInputStreamlässt sich erzeugen, indem im Konstruktor zwei InputStream-Objekte mitgegeben werden
  • Soll aus zwei Dateien ein zusammengesetzter Datenstrom gebildet
    werden, benutzen wir folgende Programmzeilen:
    InputStream s1 = newFileInputStream(„datei1.txt“);
    InputStream s2 = newFileInputStream(„datei2.txt“);
    InputStream s = newSequenceInputStream(s1, s2);
  • Ein Aufruf irgendeiner read()-Methodeliest nun erst Daten aus s1
  • Liefert s1keine Daten mehr, kommen die Daten aus s2
  • Kommen keine Daten mehr von s2, aber wieder von s1 können diese nicht mehr verarbeitet werden

  • Swing bietet viel mehr Komponenten als AWT. Das AWT bietet zum Beispiel keine Tabellen oder Bäume.
  • Schaltflächen und Labels nehmen Symbole auf, die sie beliebig um Text angeordnet darstellen.
  • Swing-Komponenten können transparent und beliebig geformt sein; eine Schaltfläche kann wie unter Mac OS X abgerundet sein.
  • Jede Swing-Komponente kann einen Rahmen bekommen.
  • AWT-Komponenten arbeiten nicht nach dem Model/View-Prinzip, nach dem die Daten getrennt von den Komponenten gehalten werden.

  • Swing bietet viel mehr Komponenten als AWT, so bietet AWT zum Beispiel keine Tabellen oder Bäume
  • Schaltflächen und Labels können Symbole aufnehmen, die sie beliebig um Text angeordnet darstellen
  • Swing-Komponenten können transparent und beliebig geformt sein. Eine Schaltfläche kann wie unter Mac OS X abgerundet sein.
  • Jede Swing-Komponente kann einen Rahmen bekommen
  • AWT-Komponenten arbeiten nicht nach dem Model/View-Prinzip, nach dem die Daten getrennt von den Komponenten gehalten werden

  • Jeder Swing-Komponente kann mit der Methode setBorder() ein Rahmen zugewiesen werden. Ein Rahmen ist eine Klasse, die die Schnittstelle Border implementiert. Swing stellt einige Standardrahmen zur Verfügung:
  • AbstractBorder. Abstrakte Klasse, die die Schnittstelle minimal implementiert
  • BevelBorder. (Eingelassener) 3D-Rahmen
  • CompoundBorder. Rahmen, der andere Rahmen aufnehmen kann
  • EmptyBorder. Rahmen, dem freier Platz zugewiesen werden kann
  • EtchedBorder. Noch deutlicher markierter Rahmen
  • LineBorder. Rahmen in einer einfachen Farbe in gewünschter Dicke
  • MatteBorder. Rahmen, der aus Kacheln von Icons besteht
  • SoftBevelBorder. 3D-Rahmen mit besonderen Ecken
  • TitledBorder. Rahmen mit einem String in einer gewünschten Ecke

  • Jeder Swing-Komponente kann mit der Methode setBorder()ein Rahmen zugewiesen werden. Ein Rahmen ist eine Klasse, die das Interface Borderimplementiert. Swing stellt einige Standardrahmen zur Verfügung:
  • AbstractBorder Abstrakte Klasse, die die Schnittstelle minimal implementiert.
  • BevelBorder (Eingelassener) 3D-Rahmen
  • CompoundBorder Rahmen, der andere Rahmen aufnehmen kann
  • EmptyBorder Rahmen, dem freier Platz zugewiesen werden kann
  • EtchedBorder Noch deutlicher markierter Rahmen
  • LineBorder Rahmen in einer einfachen Farbe in gewünschter Dicke
  • MatteBorder Rahmen, der aus Kacheln von Icons besteht
  • SoftBevelBorder3D-Rahmen mit besonderen Ecken
  • TitledBorder Rahmen mit einem String in einer gewünschten Ecke

Auch für zwei Beispiele durfte die Insel Vorlage sein. (Das ist rechtlich sicher, da die Insel im Vorwort erlaubt, dass alle Beispiele frei verwendet werden können.) Das eine ist JCheckBoxDemo, welches als CheckBoxDemo mit anderen Grafiken und Beschriftungen — aus der Auswahl "Ein Colt für alle Fälle" und "MacGyver" wird "Informatik A und Informatik B" — einer weiteren Zeile im Listener ein neues Leben führt.

Oder

package com.javatutor.insel.thread.group;

public class ShowThreadsInMain
{
public static void main( String[] args )
{
ThreadGroup top = Thread.currentThread().getThreadGroup();

while ( top.getParent() != null )
top = top.getParent();

showGroupInfo( top );
}

public static void showGroupInfo( ThreadGroup group )
{
Thread[] threads = new Thread[ group.activeCount() ];

group.enumerate( threads, false );
System.out.println( group );

for ( Thread t : threads )
if ( t != null )
System.out.printf( "%s -> %s is %sDaemon%n",
group.getName(), t, t.isDaemon() ? "" : "no " );

ThreadGroup[] activeGroup = new ThreadGroup[ group.activeGroupCount() ];
group.enumerate( activeGroup, false );
for ( ThreadGroup g : activeGroup )
showGroupInfo( g );
}
}

Und das Beispiel vom Doktor:

package threadgroup1;

/**
* Informationen ueber die laufenden Threads liefern.
*
* @author Ralf Kunze (rkunze@uos.de), Institut fuer Informatik, Universitaet
*         Osnabrueck
* @date 06.05.2007
*/
public class ThreadInfo {
public static void main(String[] args) {
ThreadGroup top = Thread.currentThread().getThreadGroup();

while (top.getParent() != null)
top = top.getParent();

showGroupInfo("    ",top);
}

public static void showGroupInfo(String indent, ThreadGroup group) {
Thread[] threads = new Thread[group.activeCount()];

group.enumerate(threads, false);
System.out.println(indent + group);

for (Thread t : threads)
if (t != null)
System.out.printf("%s%s -> %s is %sDaemon%n",
                                       indent ,group.getName(), t, t.isDaemon() ? "" : "no ");

ThreadGroup[] activeGroup = new ThreadGroup[group.activeGroupCount()];
group.enumerate(activeGroup, false);
for (ThreadGroup g : activeGroup)
showGroupInfo(indent+indent, g);
}
}

Das @author-Tag gefällt mir besonders. Die indent-Erweiterung ist natürlich anzuerkennen. Das habe ich gleich übernommen. Daher gefällt mir der wissenschaftliche Austausch so sehr. Leider gibt es das JavaDoc-Tag @date nicht (zumindest bis Java 6), aber man kann ja schon für die Zukunft programmieren.

Insgesamt finden sich sehr viele Abschnitte aus dem Kapitel IO, Thread, Reflection und Netzwerk in den Uni-Unterlagen, aber in der Reihenfolge, Satzbau und Formulieren sind auch in anderen Kapiteln deutliche Ähnlichkeiten zu erkennen. Danke für die Hommage.

Das ANTLR Eclipse-Plugin

Für den beliebten Parsergenerator ANTLR gibt es unter http://www.javadude.com/tools/antlr3-eclipse/ ein praktisches Plugin. Der Update-Manger von Eclipse wird auf die URL http://javadude.com/eclipse/update gelegt, installiert und neu gestartet. Anschließend geht man im Projekt auf das Kontextmenü und aktiviert Add/Remove ANTLR 3 Nature.

Als nächstes kann man eine ANTLR-Datei ablegen. Auf der Doku-Seite vom Plugin wird eine Grammatik (Datei Expr.g3) angegeben, die hier — leicht überarbeitet — angegeben werden soll:

grammar Expr;

@header {
package com.tutego.script.fp;

import java.util.HashMap;
}

@lexer::header {
package com.tutego.script.fp;
}

@members {
HashMap<String,Integer> memory = new HashMap<String,Integer>();
}

prog:
stat +
;

stat:
expr NEWLINE
{
System.out.println( $expr.value );
}

| ID '=' expr NEWLINE
{
memory.put( $ID.text, $expr.value );
}

| NEWLINE
;

expr returns [int value]:

e = multExpr { $value = $e.value; }
(
'+' e = multExpr { $value += $e.value; }
| '-' e = multExpr { $value -= $e.value; }
)*
;

multExpr returns [int value]:

e = atom { $value = $e.value; }
(
'*' e = atom { $value *= $e.value; }
)*
;

atom returns [int value]:

INT
{
$value = Integer.parseInt($INT.text);
}

| ID
{
Integer v = memory.get( $ID.text );
if ( v!=null ) $value = v.intValue();
else System.err.println( "Undefined variable " + $ID.text );
}

| '(' expr ')' {$value = $expr.value;}
;

ID:
('a'..'z' | 'A'..'Z') +
;

INT:
'0'..'9' +
;

NEWLINE:
'\r' ? '\n'
;

WS:
(' ' | '\t') +
{
skip();
}
;

Bearbeiten lässt sich die Grammatik in Eclipse nicht ordentlich, doch dafür dient ja die ANTLRWorks: The ANTLR GUI Development Environment.

Das Schöne beim Plugin: Es generiert automatisch im Hintergrund den Parser und Lexer. Das Testprogramm ist schnell geschrieben:

package com.tutego.script.fp; 

import org.antlr.runtime.*;

public class FpRunner
{
public static void main( String[] args ) throws Exception
{
ExprLexer lex = new ExprLexer( new ANTLRStringStream("age=34\nage*2\n12*111\n") );

ExprParser parser = new ExprParser( new CommonTokenStream( lex ) );

try
{
parser.prog();
}
catch ( RecognitionException e )
{
e.printStackTrace();
}
}
}

Die Ausgabe ist dann

68
1332

sun.misc.Unsafe zur Objekterzeugung ohne Standard-Konstruktor

Inselupdate: Die Laufzeitumgebung von Sun liefert noch über 3000 Klassendateien in den Paketen sun und sunw aus. Diese internen Klassen sind nicht offiziell dokumentiert[1], aber zum Teil sehr leistungsfähig und erlauben selbst direkten Speicherzugriff oder können Objekte ohne Standard-Konstruktor erzeugen:

com/tutego/insel/sun/UnsafeInstance.java, Ausschnitt

Field field = sun.misc.Unsafe.class.getDeclaredField( "theUnsafe" );

field.setAccessible( true );

sun.misc.Unsafe unsafe = (sun.misc.Unsafe) field.get( null );

File f = (File) unsafe.allocateInstance( File.class );

System.out.println( f.getPath() ); // null

File hat keinen Standard-Konstruktor, noch nicht einmal einen privaten. Diese Art der Objekterzeugung kann bei der Deserialisierung (siehe dazu Kapitel 13) hilfreich sein.


[1] Das Buch “Java Secrets“ von Elliotte Rusty Harold geht den Klassen nach, ist aber schon älter.

Inselupdate: Erkennungsstring (Action-Command) einer Schalftfläche ändern

Manche Ereignisbehandler sind für Schaltflächen so ähnlich, dass Entwickler nur einen Listener mit mehreren Schaltflächen verbinden möchten. Dann taucht nur das Problem auf, wie der Listener die Schaltflächen unterscheiden kann. Eine Idee wäre, die Beschriftung mit getText() auszulesen – das bringt allerdings das Problem mit sich, dass die Software stark landessprachlich ist, denn bei mehrsprachigen Anwendungen kann sich die Aufschrift ändern. Eine andere Lösung wäre mit getSource() zu arbeiten. Doch dann müsste im Listener die Komponenten für einen Vergleich verfügbar sein, was sie oft nicht ist.

Als Lösung bietet die AbstractButton-Klasse die Methode setActionCommand() an, mit der sich eine Kennung, der sogenannte Action-Command setzen lässt.

abstract class javax.swing.AbstractButton extends JComponent implements ItemSelectable, SwingConstants

  • void setActionCommand( String command )
    Setzt einen neuen Kommandostring, wenn das Ereignis ausgeführt wird.

Der Listener kann diesen Action-Command mit getActionCommand() aus dem ActionEvent auslesen.

class java.awt.event.ActionEvent extends AWTEvent

  • String getActionCommand()
    Liefert den String, der mit dieser Aktion verbunden ist.

Ohne explizites Setzen ist der Action-Command standardmäßig mit der Beschriftung der Schaltfläche initialisiert.

Inselupdate: Zugriff auf die gesamte Windows-Registry

Wird Java unter MS Windows ausgeführt, so ergibt sich hin und wieder die Aufgabe, Eigenschaften der Windows-Umgebung zu kontrollieren. Viele Eigenschaften des Windows-Betriebssystems sind in der Registry versteckt, und Java bietet als plattformunabhängige Sprache keine Möglichkeit, diese Eigenschaften in der Registry auszulesen oder zu verändern. (Die Schnittstelle java.rmi.registry.Registry ist eine Zentrale für entfernte Aufrufe und hat mit der Windows-Registry nichts zu tun. Auch das Paket java.util.prefs mit der Klasse Preferences erlaubt nur Modifikationen an einem ausgewählten Teil der Windows-Registry.)

Um von Java auf alle Teile der Windows-Registry zuzugreifen, gibt es mehrere Möglichkeiten, unter anderem:

  • Windows Registry API Native Interface (http://tutego.com/go/jnireg), die frei zu benutzen ist und keiner besonderen Lizenz unterliegt.
  • http://www.cogentlogic.com/jndi/ einen JNDI Service Provider for Windows Registries für teure 299 kanadische Dollar.
  • Preferences unter Windows realisiert: java.util.prefs.WindowsPreferences. Damit ist keine zusätzlich native Implementierung – und damit eine Windows DLL im Klassenpfad – nötig. Die Bibliothek https://sourceforge.net/projects/jregistrykey/ realisiert eine solche Lösung.
  • reg zum Setzen und Abfragen von von Schlüsselwerten.

Beispiel   Zeigen den Dateinamen für den Desktop-Hintergrund an:

$ reg query „HKEY_CURRENT_USER\Control Panel\Desktop“ /v Wallpaper

! REG.EXE VERSION 3.0

HKEY_CURRENT_USER\Control Panel\Desktop

Wallpaper REG_SZ C:\Dokumente und Einstellungen\tutego\Anwendungsdaten\Hintergrund.bmp

Unterschiedlicher Persistence Context und Speicherprobleme

Ein Persistence Context verwaltet Entities und kontrolliert ihren Lebenszyklus.

Man unterscheidet zwei Arten von Persistence Context:

Transaction-scoped Persistence Context

Extended Persistence Context

•Mit EntityManagerFactory.createEntityManager() bekommt man den Extended Persistence Context.

–createEntityManager() liefert den Application-Managed Entity Manager.

•Der Persistence Context beginnt, wenn der Application-Managed Entity Manager erzeugt wird und endet erst dann, wenn der Entity-Manager geschlossen wird.

•Werden Entities geladen, so bleiben sie so lange im Extended Persistence Context, bis der der Kontext geschlossen oder alle Entities ausdrücklich gelöscht werden.

–Entities bleiben also sehr lange „manged“ und nur dann „detached“, wenn der Entity-Manager geschlossen wird.

 

•Der Transaction Persistence Context ist der übliche Persistence Context eines „Container-Managed Entity Managers“, der also über den Container injiziert wird.

@PersistenceContext EntityManager em;

•Der EntityManager erzeugt pro Transaktionen einen neuen Persistence Context.

–Der Persistence Context endet bei einem Commit oder Rollback der Transaktion.

•Innerhalb der Transaktion werden die zum Beispiel über find() oder Query geladenen Entites zu managed Entities.

•Da nach der Transaktion der Persistence Context endet, werden die dort verwalteten Entities detached.

 

•Da der Transaction Persistence Context pro Transaktion aufgebaut wird, ist die Anzahl der verwalteten Objekte relativ klein.

•Der Extended Persistence Context ist oft deutlich länger und kann nach einer Zeit sehr viele Objekte aufnehmen.

–Damit kann das zu einem Speicherproblem werden.

 

•Um beim Extended Persistence Context kein Speicherproblem zu bekommen gibt es unterschiedliche Strategien:

–Von Zeit zu Zeit clear() vom EntityManager aufrufen. Das detached Objetkte.

–Auf das passende Lazy-Load achten.

–Weniger Objekte laden: Bei unachtsamem Design werden oft viel zu viele Objekte geladen.

•Besonders Gib-Mir-Alles-Ausdrücke wie „select o from Entity o“ sind gefährlich, da die Anzahl der Objekte groß sein kann, obwohl nur ein kleiner Teil der Objekte wirklich benötigt wird.

–Eine Paginierung kann hier sinnvoller sein um nur das zu laden, was zum Beispiel sichtbar ist.

 

•Einzelne Entities lassen sich nicht aus dem Entity-Manger entfernen (detachen).

–Bei Hibernate oder anderen Implementierungen funktioniert das, bei JPA standardmäßig nicht.

•Aber andersherum gibt es eine Lösung.

–Also alles freigeben, bis auf ausgewählte Objekte.

•Gibt es einen Verweis auf eine Entity-Objekt vom Persistence Context aus und von außen, und wird dann clear() den Persistence Context löschen, wird der Garbage-Collector das Objekt natürlich nicht freigeben.

–Das losgelöste Entity-Objekt kann später mit merge() wieder in den Persistence Context gesetzt werden.