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.