Archiv der Kategorie: Java

Maven-PlugIn und transitive Workspace-Abhängigkeiten

Die Fehler-Situation war eine Exception im XStream/CGLIB. Sowohl der POM-Editor, die Eclipse „Maven-Dependencies“ und auch sonst zeigten XStream 1.3 an, wie man es erwarten konnte.
Im POM für Projekt A ist XStream 1.3 als Abhängigkeit eingetragen. A hängt noch von B ab, in welchem XStream 1.2.2 eingetragen ist. Der Dependency-Eintrag im POM von A enthält einen entsprechenden Exclude auf XStream aus B. Soweit so gut.

Wenn man nun aber in Eclipse und dem „Maven Integration for Eclipse“-PlugIn die „Workspace Resolution“ eingeschaltet hat, dann kann es passieren, das über diesen Bypass ein zweites XStream in den Classpath wandert. Das kostet dann Zeit, weil es auf dem einen Eclipse-Helios-Workspace geht und auf dem anderen nicht. Das macht sich beim Kunden besonders schlecht, wenn man für das Deployment einen solchen Workspace benutzt (wie bei mir geschehen) und Stunden benötigt, auf das Problem zu kommen.

Ich tendiere immer mehr dazu, dass man mit Maven einen festen überprüfbaren Abhängigkeitsbaum initial erzeugt und diesen dann fest in das Projekt einbindet. Sollten neue Abhängigkeiten nötig werden, so geht man wieder diesen Weg: get dependencies, test it and freeze.

Properties und Encoding

Manchmal sollte man die Warnings lesen, die diverse Tools auswerfen.

Es ist ein guter Hinweis von Maven2, dass man ggf. folgendes einfügen sollte:


<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

Ansonsten kann es sein, dass die Quellcode-Dateien stillschweigend umgesetzt werden.
In diesem Zusammenhang sind wir auf einen Mechanismus gestoßen, der komischerweise bis jetzt immer so weit funktioniert hat.

Die Properties-Dateien besitzen gesonderte Einstellungen im Eclipse und sie sind voreingestellt auf ISO-8859-1.
Der Grund ist das Default-Einlese-Verhalten von Ressource-Bundles.
Alle Zeichen, die nicht ISO-8859-1 entsprechen, müssen als Unicode-Escape-Sequencen geschrieben werden.
Wenn man nun UTF-8 kodierte Properties-Dateien benutzt, dann können die Zeichen beim Default-Einlesen zerstört werden.

Siehe hierzu z. B.: globalizer.wordpress.com

Aber unter Java 1.6 gibt es eine neue Möglichkeit, dieses Default-Einlesen mit einem eigenen Reader zu umgehen.
Nur leider wird beim Verwenden von verschiedensten Frameworks diese neue Möglichkeit sicher nicht konsistent umgesetzt.


Update: With Java 6 you can use PropertyResourceBundle constructed with a Reader, which can use UTF-8 directly.

Es kann passieren, dass beim Ressouce-Kopieren durch Maven die Dateien umkodiert werden und man lustige Ausgaben erhält, je nach Zielplattform und deren Encoding.

Missing artifact com.sun:tools:jar:1.5.0:system

Stellt man ein Maven-Projekt im Helios-Eclipse von Java 1.5 auf Java 1.6 um, dann kann es passieren, dass folgende Meldung ein weiteres Arbeiten stoppt:

Missing artifact com.sun:tools:jar:1.5.0:system

Die Lösung ist, erstens das JDK1.6 im Eclipse einstellen 🙂

Verschwindet das Problem nicht, dann einfach im „Java Build Path > Libraries“ die „Maven Dependencies löschen.
Danach mit „Maven > Update Project Configuration“ diese Abhängigkeit neu erstellen.

Hibernate neue Sequence einfügen

Wenn man im Projekt „nur“ Integer als Primary Key zur Verfügung hat und ein Refactoring ist nicht abzuschätzen, so kann man für besonders zahlreiche Entities eigene Sequencen anlegen.


@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tsgen")
@SequenceGenerator(name = "tsgen", sequenceName = "sq_t_timeseries_data", allocationSize = 1, initialValue = 7000000)
@Column(insertable = false, updatable = false)

Nicht vergessen den aktuellen respektive initialen Wert zu setzen. Bei PostgreSQL war es notwendig, nach dem Schema-Update von Hibernate, den „CurrentValue“ manuell noch einmal anzupassen.

Ebenfalls empfehlenswert ist allocationSize = 1 zu setzen, da sonst in 50ger Schritten die Schlüssel bezogen werden.

Calendar.isLenient()

Es gibt leider bei Date und Calendar in Java eine Menge Ungemach. Hierzu gehört auch das Aufweichen der Validität von Datumsangaben Leniency genannt.
In all cases, arguments given to methods for these purposes need not fall within the indicated ranges; for example, a date may be specified as January 32 and is interpreted as meaning February 1. java.util.Calendar

Aber hier haben die Designer in Java zwei Stolpersteine eingebaut.

1. isLenient() ist standardmäßig auf true(!) gesetzt. Das gibt bei fehlerhafter oder fehlender Validierung durch die höheren Layer lustige Effekte.

Gut, denkt man sich, dann mache ich einen Wrapper und kann hierbei noch diverse andere Klippen gleich mit umschiffen. Dann kommt aber manchmal ein Stolperstein 2 zum Tragen.

2. Und dieses ist, dass java.util.Calendar.isLenient() auch wirklich manchmal eingeschaltet werden muss. Weil es sonst (zumindest unter einer Java 1.5-Version, die wir just auch produktiv eingesetzt haben) eine interne Exception gibt, wenn nach mehreren SetXXX-Aufrufen der interne Calendar-Status bei periodEnd.getTime(); nicht mehr konsistent ist. Unter Java 1.6 tritt dies nicht mehr auf.

Lift auf neues Hibernate 3.6.0.Final

Für ein Lift reichte im Wesentlichen folgender POM-Eintrag im Parent-POM

<!-- Core and annotations -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.0.Final</version>
</dependency>
<!-- Cache provider second level cache, we use NoCache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.2.0</version>
</dependency>
<!-- need to run with hibernate -->
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.8.0.GA</version>
</dependency>

Die Basis-Annotations sind schon im Core-Paket enthalten. Dies macht es einfacher.
Allerdings muss man die entsprechenden Abhängigkeiten aus dem JBOSS-Repository holen: https://repository.jboss.org/nexus/content/groups/public/

Für ein Logging musste ebenfalls angepasst werden auf eine neue API


<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jcl</artifactId>
<version>1.6.1</version>
</dependency>

Um für z. B. Tests das Caching abzuschalten, wird Folgendes eingetragen:


<prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.use_query_cache]">false</prop>

Siehe hierzu auch z. B.: stackoverflow

Calendar.getTime()/Date immer UTC

Was man sich vielleicht immer mal wieder klar machen sollte, wenn man nicht in die Date-Timestamp-Calendar-Hölle kommen will:
Calendar.getTime() ist immer UTC und hat nichts mit irgendeiner Timezone zu tun.

Although the Date class is intended to reflect coordinated universal time (UTC), it may not do so exactly, depending on the host environment of the Java Virtual Machine. java.util.Date

Hier gibt es ein paar der gesammtelten Irrtümer über die Datums und Zeitangaben, die ich immer noch in Legacy- und auch aktuellem Code finde (torsten-horn.de). Z. B. fängt bei meinem aktuellen Projekt der Tag – als Gas-Tag – entweder um 08:00 Uhr bzw. 06:00 Uhr an. Analog beginnt das Gas-Jahr am 1. Oktober des jeweiligen Kalender-Jahres. Von Lieferjahren und den darin vorkommenden Quartalen wollen wir gar nicht reden.

Oracle: Timezones and the Java Runtime Environment

Warum ist Number nicht Comparable

Das ist eine Frage, die gerade im Team aufgetaucht ist bei der Berechnung für die Darstellung von Charts.

Die schnelle Antwort hierzu ist, dass von Number als Haupt-Typ sehr unterschiedliche Kind-Typen abgeleitet werden. Man wollte das Grundverhalten dieser Typen nicht vorbestimmen. Weiterhin sind diese Kind-Typen untereinander nicht „generisch“ vergleichbar.

Im Zweifelsfall ist man dazu gezwungen seinen eigenen Comparator zu schreiben. Dieser kann z. B. davon abhängig sein, welchen Rundungsfehler man annimmt. So kann man Gleichheit in bestimmten Intervallen definieren, zwei Zahlen a und b sind gleich, wenn (b-d) <= a <= (b+d); d>=0 ist. Oder man normalisiert auf den größten Zahlentyp und vergleicht dort.

Auf alle Fälle stößt man hier an die Grenzen der Umsetzung auf Maschinenarchitektur. Siehe z. B. strictfp.

(Siehe stackoverflow)

Outboxing mit NPE

Ein Beispiel, welches mir gerade passiert ist:

long along = aObject.getAintegerObject();

Wenn aObject.getAintegerObject() null zurück liefert, dann knallt es mit einer NPE.
Es wird automatisch versucht, ein Integer in ein long zu überführen.

Es gibt Features, die besser abzustellen wären. Man stelle sich Matrixmanipulationen vor, wie z. B. Multiplikation, bei denen elementweise In- und Outboxing stattfindet, ohne, dass man eine Warnung vom Compiler erhält.

Wenn man mit Eclipse arbeitet, dann kann man hier den gewünschten Level Warn oder Error einschalten:

Preferences->Java->Compiler->Errors/Warnings->Potential Programming Problems->Boxing and unboxing conversions

Siehe auch stackoverflow.com