Interesting code: org.codehaus.plexus.util.FileUtils.forceDelete(File)

Interesting code: Today we have a look at org.codehaus.plexus.util.FileUtils.forceDelete(File).

FileUtils.foreceDelete is a very common helper function, found in nearly all util libs around the world. Why is this one from the plexus-utils project so interesting to have a detailed look into it? Short answer: it turns nearly all my Eclipse full builds into a CPU consuming long running task.

As mentioned forceDelete is very common and if you have look into the Appache commons-io project you will found the nearly same function — but a little bit different.

forceDelete in the commons-io

This code is from Appache commons-io:2.5 (newest version):

[code lang=java]
public static void forceDelete(File file) throws IOException {
if (file.isDirectory()) {
deleteDirectory(file);
} else {
boolean filePresent = file.exists();
if (!file.delete()) {
if (!filePresent){
throw new FileNotFoundException("File does not exist: " + file);
}
String message =
"Unable to delete file: " + file;
throw new IOException(message);
}
}
}
[/code]

forceDelete in the plexus-utils

An the next code is from the plexus-utils:3.0.24 project (newest version).

[code lang=java]
public static void forceDelete( final File file ) throws IOException {
if ( file.isDirectory() ) {
deleteDirectory( file );
} else {
/* NOTE: Always try to delete the file even if it appears to be non-existent.
* This will ensure that a symlink whose target does not exist is deleted, too. */
boolean filePresent = file.getCanonicalFile().exists();
if ( !deleteFile( file ) && filePresent ) {
final String message = "File " + file + " unable to be deleted.";
throw new IOException( message );
}
}
}
[/code]

On the first view both do the same:
* first check if the given file is a directory,
* second check if the file exists
* third delete the file
* last throw an exception, if the file could not be deleted.

But the plexus version version does not call the file.delete method, it calls a private delete(File) method. So we have a look at this method:

[code lang=java]
private static boolean deleteFile( File file ) throws IOException {
if ( file.isDirectory() ) {
throw new IOException( "File " + file + " isn't a file." );
}
if ( !file.delete() ) {
if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) {
file = file.getCanonicalFile();
System.gc();
}
try {
Thread.sleep( 10 );
return file.delete();
} catch ( InterruptedException ignore ) {
return file.delete();
}
}
return true;
}
[/code]

This method does a lot of magic in case the file could not be deleted:
* first it starts a Garbage-Collection on a Windows system
* second it sleeps for 10 millis
* last: it just tries the delete a second time

Currently I have no idea, why a Garbage-Collection run and to wait 10 millis will change the result for the file.delete on any Windows-Systems. In case the file was opened by an other application we can nothing do in our application to solve the situation. If the file was opened in our own application, I suggest to just close the file.

In the past I came around a lot of situations, where we could not delete a file in our application and in all those situations it was always the same reason: some one did not close the stream and it was always a problem on Windows-Systems — we choose to fix the code to close the stream and get every file file.delete() deleted with any other magic 🙂 .

I assume the version of commons-io will just du the right things and suggest to remove all the additional stuff from the `plexis-utils‘ version.

forceDelete in the yuicompressor-maven-plugin

If you call this method for one or two files you will not be aware of the System.gc() call and of the 10 millis sleeping — but if your application used already 2 GB memory and you call it 1.000 or more times your application drastically slows down and the garbage collecter will consume 100% CPU.

This happened in the yuicompressor-maven-plugin, if you have a lot of *.css files and do your development with Eclipse on a Windows-System (Team members on Linux system did not have any problems.) — I assume it will slow down every IDE which uses this plugin.

Now we will have a look at the yuicompressor-maven-plugin code:

[code lang=java]
@Override
protected void processFile(SourceFile src) throws Exception {

InputStreamReader in = null;
OutputStreamWriter out = null;
File outFileTmp = new File(outFile.getAbsolutePath() + ".tmp");
FileUtils.forceDelete(outFileTmp);
try {
in = new InputStreamReader(new FileInputStream(inFile), encoding);

[/code]

The {{forceDelete}} is used to just make sure to have no old tmp files laying around. I suggest to just use a ‚better‘ forceDelete implementation or to put a file.exists before the forceDelete to workaround the „Windows-Problems“ of the original FileUtils.foreceDelete.

In my environment I just disable the yuicompressor-maven-plugin as quick fix and second I opend a bug on github and looking forward what happend. Stay tuned.

(1) https://github.com/codehaus-plexus/plexus-utils/issues/21

Interesting code: Today File.toURI vs. File.getAbsoluteFile

Interesting findings while profiling Eclipse

While profiling Eclipse on a Windows-System I came a round the File.toURI. Every profiler session this method shows up under the top 10 hotspot method, while I never found that method on a MAC system — profiling the same application with the same workspace.

  1. I checked how many times this method was called while compiling my workspace and it was round about 100,000 times.
  2. I dig into the code: toURI was called to calculate a string which is used in a cache (as far as I understand the code)

Benchmark File.toURI vs. File.getAbsoluteFile

With a simple test cases we can check the runtime for both variants on files and directories. For 100.000 calls I could not find a difference for getAbsoluteFile on both systems. For toURI it looks different: the Windows was 10 times slower.

toURI produced an absolute path to a file with a schema prefix — in general all files in a workspace are on the same filesystem with the same schema prefix, therefore I suggest: Just replace toURI().toString() to getAbsoluteFile().toString() and the cache will still work as expected, but it will be much faster on Windows systems.

Conclusion

Or in other words: keep things simple and use only the information you really need: if you do not need the schema information of an URI, do not use a URI in favor of getAbsoluteFile().

Second: Java programs run on any platform and performance issues should be tracked down on that system they are reported for — ok, if you hit a n^2 algorithms this will likely operating system in dependent :).

Remarks

  • The Windows system was a desktop machine with Windows 10 and SSD.
  • The MAC was a MacBook with 10.12.3 and SSD.
  • I did not publish run times, because the will differe on different system. A factor of 10 for the same base function on different systems with different underlaying file system is really interesting.
  • I checked also the toUri() and toAbsolutePath from the Path objects and get nearly the same results — no better performance on Windows.

Interesting code: Wy JDBC database driver do not register correctly

Interesting code: Wy database driver do not register correctly

Today I came around the static method java.sql.DriverManager.loadInitialDrivers which is called in the static initializer of DriverManager. It handles the automatic JDBC driver registration, which depends highly on the java.util.ServiceLoader class.
In short the DriverManger searches the classpath for META-INF/services/java.sql.Driverresources to force the load of the classes defined in the found resources. This is done by the following code found in loadInitialDrivers

[code lang=java]
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
[/code]

In the past this works in nearly every environment with all kind of JARs on the classpath. But today it fails
with a simple exception:

SQLException: No suitable driver found for …

It took me some time to find out why:
On the classpath a JAR shows up, which introduced a META-INF/services/java.sql.Driver containing a class, which static initializer throws a what every exception. The problem is: every exception which is thrown in the while will leave the loop — pending resource will not be evaluated and all pending JDBC driver will not get registered.

I found the following comment before this loop:

Load these drivers, so that they can be instantiated.
It may be the case that the driver class may not be there
i.e. there may be a packaged driver with the service class
as implementation of java.sql.Driver but the actual class
may be missing. In that case a java.util.ServiceConfigurationError
will be thrown at runtime by the VM trying to locate
and load the service.

Adding a try catch block to catch those runtime errors
if driver not available in classpath but it’s
packaged as service and that service is there in classpath.

Nice try, because in those cases no other driver will be registered, because the first which fails will exit the loop.

@JDK-Developer: Can we just put the try...catch into the loop
@JDBC-Driver-Developer: Please do not throw any exception during your static initializer.

JDK: 1.8.0_101 Mac

Plugin für XJC, Teil 2

Plugin für vereinfachte getter/setter in generierten JAXB Klassen

Problembeschreibung

Bei der Benutzung von generierten JAXB-Klassen kann es in einigen Situationen zu recht umständlichen getter/setter Konstruktionen kommen. Als Beispiel soll der folgende Auszug aus einer XSD dienen:

[code language=“xml“]
<xsd:complexType name="Bestellung">
<xsd:sequence>
<xsd:element name="BestellNummer" type="BestellNummer" />
<xsd:element name="ArtikelNummern">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ArtikelNummer" type="xsd:string" minOccurs="1" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ArtikelNummer">
<xsd:attribute name="value" type="xsd:string" />
</xsd:complexType>
[/code]

Das setzen der Artikelnummer gestaltete sich dann wie folgt:

[code language=“java“]
BestellNummer nummer = new BestellNummer();
nummer.setValue("foobar");
bestellung.setBestellNummer(nummer);
[/code]

und das Auslesen ist auch nicht einfacher:

[code language=“java“]
String bestNummer = null;
BestellNummer nummer = bestellung.getBestellNummer();
if (nummer != null) {
bestNummer = nummer.getValue();
}
[/code]

Schöner wäre es, wenn man den Wert direkt auslesen bzw. setzen kann:

[code language=“java“]
bestellung.setBestellNummer_("foobar");
bestNummer = bestellung.getBestellNummer_();
[/code]

Dafür müssen in der entsprechenden JAXB-Klasse folgenden Methoden vorhanden sein:

[code language=“java“]
public String getBestellNummer_() {
if (this.bestellNummer == null) {
return null;
} else {
return this.bestellNummer.getValue();
}
}
public void setBestellNummer_(String value) {
if (value == null) {
this.bestellNummer = null;
} else {
this.bestellNummer = new BestellNummer();
this.bestellNummer.setValue(value);
}
}
[/code]

Damit man diese Methoden nicht immer bei einer Schema-Generierung verloren gehen, werden sie von einem eigenen Plugin einfach mit generiert. Nach schöner wäre es, wenn wir die Erzeugung des Attributes plus Getter/Setter Methoden anpassen. Das XJC bietet dafür in Ansätzen einen Mechanismus, der aber ist leider nicht vernünftigt umgesetzt, denn die wichtigen Funktionen sind entweder private (z.b. das Erzeugen des richtigen getter Namens) oder aber wichtige Klassen haben nur package visibility und erzeugen schon im Konstruktor Code, sodass man nicht wirklich etwas anpassen kann. Bleibt nur noch die zusätzliche Erzeugung von gettern/settern (damit wir keinen Namenskonflikt bekommen, bekommen die Methoden ein „_“ ans Ende). Soweit die Vorrede.

Algorithmus

Für alle Attribute einer Klasse, die

  • als Typ eine von uns generierte Klasse haben (Target-Type)
  • deren Target-Typ genau ein Attribut (Target-Attribut) besitzt

generieren wir:

  • eine optimierte setter-Methode, wenn wir eine setter-Methode in der Target-Type-Klasse zum passenden Target-Attribut finden
  • eine optiierte getter-Methode, wenn wir eine getter-Methode in der Target-Type-Klasse zum passenden Target-Attribut finden

Helferlein

Als erstes nehmen wir das Plugin-Gerüst aus dem letzten Blog. Dann brauchen wir noch ein paar Helferlein, um unseren Algorithmus besser programmieren zu können.

Die folgende Methode berechnet das Prefix für die Getter-Methoden. Der Quelltext wurde inspiriert von der XJC Implementierung, sodass beide das gleiche Verhalten zeigen (schön wäre eine einheitliche Hilfsmethode gewesen, die konnte ich aber leider nicht finden).

[code language=“java“]
protected String getterPrefix(JType type) {
boolean useIs = false;
if (options.enableIntrospection) {
useIs = type.isPrimitive() == true && codeModel.BOOLEAN.equals(type.boxify().getPrimitiveType());
} else {
useIs = codeModel.BOOLEAN.equals(type.boxify().getPrimitiveType());
}
return (useIs == true ? "is" : "get");
}
[/code]

Wir wollen für alle Klassen, die genau ein Attribut haben „vereinfachte“ getter/setter erstellen, sodass wir für eine generierte Klasse prüfen müssen, ob diese genau ein Feld besitzt. im XJC werden generierte Klassen durch JDefinedClass dargestellt. Damit ist es einfach die einzige definierte Klassen-Variable herauszubekommen. Sollte die Klasse mehrere Variablen beinhalten geben wir null zurück.

[code language=“java“]
protected JFieldVar getSingleField(JDefinedClass cls) {
final Map<String, JFieldVar> map = cls.fields();
if (map.size() != 1) {
return null;
} else {
return map.values().iterator().next();
}
}
[/code]

Als nächstes benötigen wir zu einem Attribute einer Klasse, den jeweiligen Typ des Attributes. In XJC werden die Attribute einer Klasse als FieldOutline dargestellt. Zu einem Field müssen wir nun den entsprechenden Java-Typ heraus finden. Dabei interessieren wir uns nur für von XJC erzeugte Klassen. Für alle anderen Klassen wollen wir den Zugriff nicht vereinfachen. Finden wir keine Klasse, geben wir wieder null zurück.

[code language=“java“]
protected JDefinedClass mapFieldToDefinedClass(FieldOutline field) {
if (field.getRawType() instanceof JDefinedClass) {
return (JDefinedClass) field.getRawType();
}
return null;
}
[/code]

Zu einem FieldOutline benötigen wir die zugehörige Definition in der erzeugten Java Klasse. Diese suchen wir über den Namen des Felds. Sollten wir keine zugehörige Field-Definition finden, oder der Type stimmt nicht überein, geben wir null zurück.

[code language=“java“]
protected JFieldVar mapToFieldVar(FieldOutline field) {
Map<String, JFieldVar> map = field.parent().implClass.fields();
JFieldVar fieldVar = map.get(field.getPropertyInfo().getName(false));
if (fieldVar.type().equals(field.getRawType()) == true) {
return fieldVar;
} else {
return null;
}
}
[/code]

Als letztes benötigen wir noch eine Methode mit der wir die getter bzw. setter zu einem Feld innerhalb einer Java-Klasse finden können. Zum Suchen benötigen wir die Klasse, in der wir suchen wollen, als JDefinedClass, ein Prefix mit der die zu suchende Methode anfangen soll, der Rückgabe-Type als JType und die Signatur als JType[]. Wir geben nur dann einen JMethod zurück, wenn wir genau eine passende Methode finden. Ansonsten geben wir null zurück.

[code language=“java“]
protected JMethod searchMethod(JDefinedClass cls, String prefix, JType returnType, JType[] signatur) {
JMethod found = null;
for (JMethod m : cls.methods()) {
if (m.name().startsWith(prefix) == true // assume only the starting name
&& m.type().equals(returnType) == true //
&& m.hasSignature(signatur) == true) {
if (found != null) {
return null; // if we can not find a unique getter for the field, should never happen
} else {
found = m;
}
}
} // for
return found;
}
[/code]

Herzstück

Jetzt haben wir alle Bausteine zusammen mit der wir unsere optimierten getter/setter-Methoden erzeugen können. Wir werden jetzt die run Methode erweitern, sodass wir versuchen für alle uns bekannten Felder die optimierten Methoden zu erzeugen. Dafür rufen wir die process-Methode auf allen Feldern aller Klassen auf.

[code language=“java“]
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException {
System.out.println("run " + getOptionName() + " …");
this.codeModel = outline.getCodeModel();
this.options = opt;
for (ClassOutline cls : outline.getClasses()) {
for (FieldOutline field : cls.getDeclaredFields()) {
process( field);
}
}
System.out.println("run " + getOptionName() + " … done");
return true;
}
[/code]

Die process-Methode fürht jetzt folgende Schritte durch:
1) wir suchen eine Feld-Definition für das übergeben Feld, sollten wir keine finden können wir keine Methoden erzeugen

[code language=“java“]
JFieldVar $property = mapToFieldVar(field);
if ($property == null) {
return; // stop if we could not find a local field for given field outline
}
[/code]

2) dann ermitteln wir den Typen des gefunden Felds. Wir können nur weiter arbeiten, wenn der Type eine von uns erzeugte Klasse ist.

[code language=“java“]
JDefinedClass jTargetCls = mapFieldToDefinedClass(field);
if (jTargetCls == null) {
return; // stop if target class is not a generated one
}
[/code]

3) dann überprüfen wir, ob der gefunden Typ genau eine lokale Variable enthält. Sollte das nicht der Fall sein, können keine optimierten getter/setter erzeugt werden:

[code language=“java“]
final JFieldVar jFieldVar = getSingleField(jTargetCls);
if (jFieldVar == null) {
return; // no optimized getter if we do not have a unique target field
}
[/code]

4) damit eine optimierte getter-Methode erzeugt werden kann, müssen wir die entsprechende getter Methode auf dem Typen des lokalen Properties finden. Find wir eine, werden wir eine optimierte getter-Methode erzeugen

[code language=“java“]
JMethod targetGetter = searchMethod(jTargetCls, getterPrefix(jFieldVar.type()), jFieldVar.type(), null);
if (targetGetter != null) {
// if( this.property == null ) {
// return null;
// } else {
// return this.property.getV();
// }
JType type = targetGetter.type();
JMethod getter = field.parent().implClass.method(JMod.PUBLIC, type, getterPrefix(jTargetCls)
+ field.getPropertyInfo().getName(true) + "_");

JConditional $if2 = getter.body()._if(JExpr._this().ref($property).eq(JExpr._null()));
$if2._then() //
._return(JExpr._null());
$if2._else() //
._return(JExpr._this().ref($property).invoke(targetGetter));
} // endif getter
[/code]

5) für die optimierte setter-MEthode machen wir das gleiche

[code language=“java“]
JMethod targetSetter = searchMethod(jTargetCls, "set", codeModel.VOID, new JType[] { jFieldVar.type() });
if (targetSetter != null) {
// if( value == null ) {
// this.propert = null;
// } else {
// this.property = new …()
// this.property.set….(value)
// }
JType type = targetSetter.params().get(0).type();
JMethod setter = field.parent().implClass.method(JMod.PUBLIC, Void.TYPE, "set"
+ field.getPropertyInfo().getName(true) + "_");

JVar $newValue = setter.param(type, "value");
JConditional $if = setter.body()._if($newValue.eq(JExpr._null()));
$if._then() //
.assign(JExpr._this().ref($property), JExpr._null());
$if._else() //
.assign(JExpr._this().ref($property), JExpr._new(field.getRawType())) //
.invoke(JExpr._this().ref($property), targetSetter).arg($newValue);
} // endif setter
[/code]

Die so generierten setter/getter erlauben einen direkten Zugriff auf das Klassen-Feld der Ziel-Klasse. Auch werden beim Setzen die entsprechenden „Zwischen-Klassen“ erzeugt bzw. gelöscht. Beim Zugrif auf Listen wird nur eine optimierte getter-Methode erzeugt.

Als kleinen Nebeneffekt lernt man mit diesem Beispiel noch das Erzeugen von Java-Quelltext mit dem internen Java CodeModel, welches in der JAXB-Distribution sehr gut per JavaDoc dokumentiert ist.

Weitere Referenzen:

Plugin für XJC, Teil 1

Plugin schreiben und starten:

Als erstes benötigen wir ein Plugin, dass bei der Code-Generierung des XJC-Tool aufgerufen wird. Das Plugin muss von der Klasse com.sun.tools.internal.xjc.Plugin erben. Unsere erste Version sieht wie folgt aus:

Unser erstes Plugin muss die folgenden Methoden implementieren:

  • getOptionName: diese Methode muss einen eindeutigen Namen für das Plugin zurück geben. Mittels diesem Namen kann das Plugin über die Komandozeile des XJC enabled bzw. disabled werden.
  • getUsage: Mit diese Methode gibt einen Hilfetext zurück, die beim Aufruf des XJC mit dem Parameter -help ausgegeben werden. Es werden keine weiteren Parameter unterstützt, sodass wir uns damit begnügen dem Benutzer zu sagen, wie er das Plugin enablen kann.
  • run: diese Methode wird während der Code-Generierung ausgeführt und ermöglicht das Verändern des erzeugten Code.

[code language=“java“]
package de.kue.sandbox.xjc;

import com.sun.tools.internal.xjc.Options;
import com.sun.tools.internal.xjc.Plugin;
import com.sun.tools.internal.xjc.outline.Outline;

public class PluginImpl extends Plugin {

@Override
public String getOptionName() {
return "Xcodeplugin";
}

@Override
public String getUsage() {
return "\t-Xcodeplugin\tenable";
}

@Override
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException {
System.out.println("run " + getOptionName() + " …");
System.out.println("run " + getOptionName() + " … done");
return true;
}
}
[/code]
Als nächstes müssen wir dem XJC-Tool das neue Plugin „unterjubeln“. Das XJC-Tool nutzt den ServiceLocator vom Java um neue Plugins zu finden. Aus diesem Grund muss das neue Plugin entsprechend angemeldet werden. Dafür wird im JAR die folgende Datei mit dem entsprechenden Pfad angelegt.

META-INF/services/com.sun.tools.internal.xjc.Plugin

Die Datei enthält pro Zeile den vollqualifizierten Pfad der neuen Plugins. In unserem Fall ist das der Name de.kue.sandbox.xjc.PluginImpl .
Als nächstes muss das JAR, welches das Plugin und die Service-Datei enthält, zum Classpath des XJC hinzugefügt werden.
Bei einem xjc -help sollte jetzt der oben angegebene Hilfetext zu lesen sein und bei der Code-Generierung sollte die Zeilen

run Xcodeplugin…
run Xcodeplugin… done

auftauchen, wenn man xjc -Xcodeplugin ... nutzt. Soweit so gut, der Teufel steckt im Detail.

Bei der Benutzung des JDK’s:

  • Das JDK hat eine eigene JAXB-Implementierung, die andere Packages verwendet als Standalone-Version. Bei der Entwicklung muss man das entsprechende tools.jar in den Classpath aufnehmen. Dort findet man dann die benötigten Klassen in einem Package mit dem Namen com.sun.tools.internal.xjc.*.
  • Das Plugin sollte mit dem gleichen JDK compiliert werden zu dem auch das XJC gehört. Damit ist man auf der sichereren Seite.
  • Auf einem Apple mit dem JDK 7 klappt das wie folgt: xjc -cp codeplugin.jar -help

Bei der Benutzung des Standalone JAXB Distribution:

  • Ich habe es nach zahllosen Versuchen und extrem debugging im XJC-Tool aufgegeben. Aus welchen Gründen auch immer werden die Klassen der JAXB Distribution mit einem anderen Classloader geladen als meine Plugin-Klasse und damit kann der Java ServiceLocator kein Plugin finden.
  • Christian Ullenboom konnte Licht ins Dunkel bringen: das Problem haben schon x andere gehabt: http://java.net/projects/jaxb/lists/users/archive/2005-06/message/6 und da die Antwort http://java.net/projects/jaxb/lists/users/archive/2005-06/message/12 . Danke für die spontane Klärung.

Bei der Benutzung von Maven/Ant:

  • Hier muss ich auf die entsprechend Doku der jeweiligen Plugins/Tasks verweisen. Dort wird beschrieben, wie man das erzeugte JAR in den Classpath des XJC-Tools bekommt.
  • Hier gilt aber auch, aufpassen bei den Namen der Packages und dem erzeugten Byte-Code.
  • Und soweit ich das sehen kann, gibt es auch hier Classloader-Problem.

Mit den Hilfen sollte es jetzt einfacher sein, das Plugin zum laufen zu bekommen. Ich habe es nur mit dem internen JDK 7 JAXB zum Laufen bekommen, aus diesem Grund nutze ich die „internal“ Packages.

Weitere Referenzen:

Muss man bei Oracle 11g Date immer TRUNC() nutzen — es kommt drauf an

Problem: ein select * from SUCHEN where A_DATE <= (select OTHER_DATE from OTHER_TABLE ) gibt keine Datensätze zurück, obwohl es in der Tabelle SUCHEN Datensätze mit dem gleichen Datum, wie in OTHER_TABLE gibt.

Die Aussage der Kollegen zu dem Thema: „Wenn man in Oracle ein DATE benutzt werden auch Stunden, Minuten und Sekunden abgespeichert und deshalb MUSS man immer TRUNC() benutzen, wenn man mit dem Type Date hantiert. Das gilt auch wenn das Date per setDate über JDBC gesetzt wird“.

Die Aussage hat mich sehr verwirrt, den bis dato hatte ich noch nie ein Problem mit dem Date-Type in Oracle und auch nicht mit entsprechenden Abfragen — also muss ich mal wieder recherchieren, was hier passiert ist.

Als erstes habe ich mich beim Hersteller mit der entsprechenden Dokumentation schlau gelesen:
Database SQL Language Reference > 11g2 > Basic Elements of Oracle SQL > Oracle Build-in Data Types > Date

 „Oracle stores the following information: year, month, day, hour, minute, and second.“

Hopsala, das muss man wirklich wissen. Die Wette hätte ich verloren.

Aber wie kann es dann doch funktionieren? Ganz einfach, man muss wissen, was man tut:

JDBC: Bei einem Update/Insert über JDBC muss man sich keine Gedanken machen, denn setDate schneidet die Zeit ab. Eine entsprechende REferenze war schon etwas schwieriger zu finden: Database JDBC Developer’s Guide > 11g2 > JDBC Reference Information > Embedded JDBC Escape Syntax. Dort gibt es eine Note, das es bei der Benutzung von Date Problem mit dem Index geben kann und man eine entsprechende Syntax benutzen muss. Als Begründung steht dort:

„This is because if you use the setDate method, then the time component of the Oracle DATE data will be lost and if you use the setTimestamp method, then the index on the DATE column will not be used.“

Man muss also „nur“ aufpassen, wenn man ein Date per setDate in einem PreparedStatement nutzt und Oracle dann einen Index nutzen soll — soweit so gut.

Plain SQL: Hier muss man aufpassen, wenn man Daten-Typen mischt. Ein leidlich bekanntes Thema, dass bei den heutigen zu meist automatischen Typ-Konvertierungen in Java immer seltener beachtet wird — kein Entwickler will sich mit diesen explizieten Umwandlungen herumschlagen. Beim Date ist das aber extrem wichtig, denn ein Date speichert (wie oben gelernt auch eine Zeit mit ab). Bei einer Zuweisung eines Werts vom Typ Timestamp wird die Zeitkomponente nicht abgeschnitten, sondert wandert mit in den Wert vom Datentyp Date. Bei einem einfach select kann man diesen Unterschied nicht mehr sehen, denn es wird immer nut Jahr-Monat-Tag ausgegeben — die Zeit fällt unter den Tisch.
Interessant wird es erst, wenn man zwei Werte vom Datentyp Date mit einander vergleicht: solange beide Werte korrekt initialisiert wurden, also per JDBC oder im SQL mit einem trunc() ist alles okay. Wird aber ein Wert ohne trunc() also z.b. mit ein einem einfach sysdate initialisiert, dann sind zwei scheinbare Werte vom Type Date nicht mehr gleich.
Also: bei der Zuweisung darauf achten, dass man einer Date Variable immer nur ein Datum mit gibt und keine Zeit (JDBC macht das implizit, bei SQL muss man ggf. Trunc() nutzen)

Kommen wir zurück zum Ausgangs-Select. In der Tabelle SUCHEN wurden in einigen Zeilen die Spalte A_DATE mit der Funktion sysdate zugewiesen, ohne dass der Zeit-Anteil auf 0 gesetzt wurde. Damit wurden genau diese Datensätze nicht mehr gefunden für ein gleiches Datum. Unterumständen hätte auch schon ein einfaches kleiner anstatt des kleiner-gleichs das PRoblem gelöst. Nur muss man dann auf das zweite Datum immer einen Tag drauf rechnen — das scheuen sich viele Programmiere vor.

Ergo: Die Aussage, bei Date muss man immer TRUNC() benutzen ist mal wieder nur die halbe Wahrheit. Viel schlimmer an der Aussage ist, dass man bei einem falschen Einsatz von trunc(), d.h. wenn man die Werte eine Spalte einer Tabelle trunc(), Oracle in das PRoblem läuft, dass es keine Index mehr nutzen kann. (Oracle SQL Tuning with function-based indexes)

OSGi: Configuration Admin und Declarative Services

Zwei sehr interessante Gebiete der OSGi-Umgebung:

  • 104 Configuration Admin Specification: Sie beschreibt, wie man die Konfiguration seiner Anwendung auf die Platte speichern kann
  • 112 Declarative Service Specification: Sie beschriebt, wie man relativ elegant per XML seine Services definieren kann (wobei elegant immer subjektiv ist).

Die beiden Spezifikationen zusammen ergeben eine elegante Möglichkeit Services in Abhängigkeit von der gespeicherten Konfiguration a) zu starten und b) zu konfigurieren.

Das Interface

Als Beispiel soll innerhalb der Konfiguration eine Service für eine Begrüßung implementiert werden. Der Service soll das folgende Interface implementieren:
[code language=“java“ wraplines=“false“]
public interface IGreetingService {

String getLanguage();

String sayHello(String name);
}
[/code]

Die Implementierung (Teil 1)

Innerhalb der Konfiguration soll die Sprache und ein Pattern für die Begrüßung hinterlegt werden. So können wir für jede „neue“ Sprache einfach eine neue Konfiguration anlegen. Die Implementierung kann dann so aussehen:
[code language=“java“ wraplines=“false“]
public class GreetingService implements IGreetingService {

private String pattern;
private String language;

@Override
public String sayHello(String name) {
return MessageFormat.format(pattern, name);
}

@Override
public String getLanguage() {
return language;
}
}
[/code]
Die beiden Variablen pattern und language sollen in der Konfiguration gespeichert werden – wie die aus der Konfiguration in den Service wandern erkläre ich später. Als nächste brauchen wir noch eine XML-Datei, mit der wir den Service definieren:
Die wichtigsten Unterschiede zu einem nicht konfigurierbaren Service liegen in der Kombination der Attribute des
scr:component Elements:

  • enabled=“true“: Die Komponenten muss immer enabled sein, weil sie aus der Konfiguration „bestückt“ werden soll.
  • immediate=“false“: Die Komponente soll nur dann „erzeugt“ werden, wenn wir wirklich ein brauchen, d.h. wenn min. eine entsprechende Konfiguration vorliegt.
  • modified=“modified“: Damit die Komponente benachrichtigt wird, wenn wir die Konfiguration ändern, müssen wir dem Framework die entsprechende Methode mitteilen.
  • configuration-policy=“required“: Es soll für genau eine Konfiguration eine Instanze der Komponente angelegt werden. In diesem Fall stellt die Komponente den IGreetingService zur Verfügung, was bedeutet, das jede Instanz der Komponente gleichzeitig auch als OSGi Service angemeldet wird.
  • factory: Dieses Element wird in unserem Fall nicht genutzt!

Bevor wird das kleine Beispiel starten können müssen wir die Komponente noch in der MANIFEST.MF unter dem Attribute Service-Component eintragen. Weiterhin sollte man sicher stellen, dass die beiden Bundles (Configuration Admin Specification und Declarative Service Specification) für die beiden Services beim Starten mit eingebunden werden. In Equinox sind dies die Bundles org.eclipse.equinox.cm und org.eclipse.equinox.ds. (in anderen Implementierungen werden die Namen ggf. abweichen).

Nach dem ersten Starten macht sich Ernüchterung breit, denn es passiert nichts spektakuläres. Innerhalb der Equinox Konsole zeigen die Befehle ss zwar das unser Bundles korrekt geladen ist. Aber wir können mit services keinen IGreetingService’s finden. Der Befehl list zeigt und aber schon mal etwas:

1 Unsatisfied GreetingService GreetingService-Bundle

Ggf. haben wir beim Starten auch eine Warnung gesehen:

!MESSAGE The ‚GreetingService‘ component’s configuration could not be satisfied because it is required to be initialized by a ConfigurationAdmin service Configuration object, but one was not found.

Diese Informationen bestätigen, was wir schon wissen und was wir als nächste beheben wollen: Es gibt natürlich noch keine Konfiguration für unseren Service. Aus diesem Grund bekommen wir die Warnung und aus dem gleichen Grund ist die Komponente noch Unsatisfied.

Konfiguration anlegen

Als nächste müssen wir eine (oder mehrere) Konfiguration(en) für unseren GreetingService. In OSGi wird dies mit dem ConfigurationAdmin-Service durchgeführt:
[code language=“java“ wraplines=“false“]
Configuration config = configurationAdmin.createFactoryConfiguration("GreetingService");
Properties props = new Properties();
props.put("language", language);
props.put("pattern", pattern);
config.update(props);
[/code]
Die Daten für den Service übergeben wir wie üblich über eine Map mit den beiden Schlüssel language und pattern. An dieser Stelle sind zwei Dinge wichtig:

  • wir müssen über den Service eine FactoryConfiguration erzeugen und dem entsprechend die Methode createFactoryConfiguration nutzen. Auf keinen Fall dürfen wir zum Erzeugen getConfiguration benutzen!
  • Als factoryPid müssen wir den Namen (Attribute name) der Komponente aus der XML-Datei benutzen.

Daten aus der Konfiguration in den Service leiten

Als nächstes müssen wir noch die Daten aus der Konfiguration den den Service bekommen. An dieser Stelle müssen wir die aus der Declarative Service Specification schon bekannten Methoden bedienen:
[code language=“java“ wraplines=“false“]
protected void modified(ComponentContext cc) {
language = (String) cc.getProperties().get("language");
pattern = (String) cc.getProperties().get("pattern");
}

protected void activate(final ComponentContext cc) {
language = (String) cc.getProperties().get("language");
pattern = (String) cc.getProperties().get("pattern");
}

protected void deactivate(final ComponentContext cc) {
// nothing to do
}
[/code]
In den Methoden modified und activate lesen wir die Daten der Konfiguration aus und setzen die entsprechenden Variablen des Service.

  • deactivate: Diese Methode wir immer aufgerufen, wenn eine Konfiguration für diesen Service gelöscht wird. In der Regel sollen hier Ressourcen freigegeben werden
  • activate: Diese Methode wir immer aufgerufen, wenn der Service das erste mal erzeugt wird.
  • modified: Diese Methode wir immer aufgerufen, wenn sich die Konfiguration zu diesem Service ändert.

Konfiguration ändern

Das Ändern einer Konfiguration funktioniert analog zur Änderung von „normalen“ Konfigurationen.
[code language=“java“ wraplines=“false“]
String filter = "(&amp;(service.factoryPid=GreetingService)(language=" + language + "))";
Configuration[] cList = configurationAdmin.listConfigurations(filter);
for (Configuration c : cList) {
Dictionary props = c.getProperties();
props.put("pattern", pattern);
c.update(props);
}
[/code]
In diesem Fall können wir über den Filter nach der entsprechenden Sprache suchen und die Konfiguration aktualisieren. In diesem Fall wird die modified-Methode des entsprechenden Service gerufen.

Konfiguration löschen

Auch das löschen funktioniert wie bei „normalen“ Konfigurationen.
[code language=“java“ wraplines=“false“]
String filter = "(&amp;(service.factoryPid=GreetingService)(language=" + language + "))";
Configuration[] cList = configurationAdmin.listConfigurations(filter);
for (Configuration c : cList) {
c.delete(m);
}
[/code]
In diesem Fall wird die deactivate-Methode des entsprechenden Service gerufen.

Zusammenfassung

Mit den hier gezeigten Beispiel kann man jetzt über den ConfigurationAdmin-Service entsprechend viele Konfigurationen erzeugen. Die OSGi-Umgebung erzeugt für jede Konfiguration einen entsprechenden Service und ruft, je nach Kontext, activate, modified oder deactivate auf. Weiterhin übernimmt OSGi das Speichern der Konfiguration und das laden der selbigen beim Starten.

Der schwierige Punkt bei dem Zusammensetzen der Configuration Admin Specification mit der Declarative Service Specification waren:

  • Ein Bug in der Equinox-Implementierung, wenn man dynamische Bundles nutzt und die Reihenfolge der Aktivierung eine ganz spezielle ist — der Bug war extrem schwer zu finden und wurde von den Equinox-Leuten im 3.6 Release behoben.
  • Eine korrekte XML für die Komponente zu erstellen, weil die Doku an dieser Stelle etwas undeutlich ist.

Java Stream Handling

Letzte Woche war es wieder so weit: Die Stream-Falle hat zugeschlagen.

Was ist passiert?

Der Betreiber einer Server-Anwendung beschwert sich, dass sein Anwendung nicht läuft. Das Log-File zeigt eine Menge von IOException, frei nach dem Motto: To many open files.

Bei einer Quelltextgröße von über 1000 Klassen wird es schwer das vergessene close zu finden. Also werfen wir den Yourkit an und siehe da ein kurzer Blick auf die Probs und schon haben wir unsere Kandidaten.

  • in einem Fall wurde der Stream nicht geschlossen
  • in einem Fall wurde der Stream zwar geschlossen, nur leider nicht in einem finally — dummerweise hat die Anwendung genau in diesem Fall eine Exception geworfen (Pferde suchen sich auch immer Apotheken zum …)
  • ein XMLStreamReader wurde mit einem File gefüttert — ganz übler Fall.

Stream-Spielregeln

Grundsätzlich sollten die folgenden Regeln beim Umgang mit Streams befolgt werden:

  • ein Stream wird immer von dem geschlossen, der ihn geöffnet hat
  • ein Stream wird immer in einem finally-Block geschlossen
  • ein Stream wird sofort nach dem er nicht mehr benötigt wird geschlossen
  • alle Team-Mitglieder verarbeiten Streams in der gleichen Art und Weise

Wenn diese Regeln vom Team gemeinsam befolgt werden und jeder auch mal rechts und links schaut, dann kann in Sachen Streams nicht mehr viel schief gehen.

Für häufig benutzte Stream-Operation können entsprechend Hilfsklassen erstellt werden. Für diese trivialen Methoden muss man nicht die komplette commons-io Bibliothek einbinden (die dann noch die commons-logging mit bringt). Insbesondere dann nicht, wenn man im Team beschlossen hat, Exceptions bei einem close wenigstens zu loggen.

XMLStreamReader-Problem

[code language=“java“ wraplines=“false“]
File file = …;
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
StreamSource source = new StreamSource(file);
try {
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(source);
try {
// … do some work on xmlStreamReader
} finally {
xmlStreamReader.close();
}
} catch (XMLStreamException e) {
throw new RuntimeException("Error while reading file " + file.getAbsolutePath() + " Error message: " + e.getMessage(), e);
}
[/code]

Ganz unschöne Sache. Der Programmierer sieht keine Streams, denn die StreamSource erzeugt den Stream für den Programmier, also kann man keinen Stream schließen, ganz einfache Schlussfolgerung. Und genau hier ist der Hund begraben. Intern wird ein Stream von der StreamSource geöffnet, dies geschieht im inneren der StreamSource. Leider bietet die StreamSource keine close-Methode, sodass der XMLStreamReader den geöffneten Stream nicht wieder schließen kann. Von außen kann man den Stream auf Grund der fehlenden close-Methode auch nicht schließen — ein Teufelskreis.

Ganz böse Falle! Besonders weil dieser Konstruktor sich quer durch die ganze XML-Api zieht:

  • SchemaFactory.newSchema(File) (der Stream für das Schema-File wird nie geschlossen)
  • JAXB.unmarshal(File, Class) (jede mit dieser Methode eingelesen XML-Datei wird nicht geschlossen)

Beide Methoden sind so schön einfach aufzurufen, denn man muss sich nicht um den Stream, das close und das try…catch…finally kümmern. Ein Irrglaube. Der geneigte Leser kann sich ja zum Spaß mal die close-Methode vom XMLStreamReaderImpl anschauen, er wird erstaunt sein.

Gebräuchliche Variante (bis Java7)

[code language=“java“ wraplines=“false“]
File file = …
InputStream in = null;
try {
in = new FileInputStream(file);
// … do some work on in
// … do some other work
} catch (IOException e) {
throw new RuntimeException("Error while reading file " + file.getAbsolutePath() + " Error message: " + e.getMessage(), e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
// ignore
}
}
[/code]

  • Im finally ist eine Null-Check nötig, weil new FileInputStream eine FileNotFoundException werfen kann.
  • In einigen Fällen wird der Stream erst am Ende einer Methode geschlossen lange, nachdem das letzte Byte gelesen wurde.
  • Der finally-Block wird in vielen Fällen durch ein IOUtils.close(in) ersetzt — das spart Platz.
  • Der Nebeneffekt bei dieser Lösung ist, durch den Catch-Block für das close werden potentielle Probleme beim Schließen des Streams nicht bemerkt. Das kann insb. bei Server-Anwendungen zu Probleme führen, wenn File-Handles z.B. nicht korrekt wieder freigegeben werden können.

Üblicher Variante (ab Java7)

[code language=“java“ wraplines=“false“]
try (InputStream in = new FileInputStream(file)) {
// … do some work on in
} catch (IOException e) {
throw new RuntimeException("Error while processing file " + file.getAbsolutePath() + " Error message: " + e.getMessage(), e);
}
[/code]

  • Ein saubere elegante Lösung mit dem neuen Java-Sprach-Feature.
  • Diese Lösung funktioniert leider nicht XMLStreamReader oder XMLEventReader

Zusammenfassung

Egal welche von den Varianten genutzt wird, Streams müssen in Java immer explizit geschlossen werden! Dabei ist es egal ob Produktions-, Prototyp- oder
Testcode geschrieben wird. Welcher Variante man sich bedient ist letztendlich zweitrangig.