Archiv für den Monat: November 2010

Eclipse-Maven-WTP-Deployment

Es ist von großem Vorteil, wenn klar wird, dass die m2eclipse-Entwickler die WTP-Integration nicht als ihre Hauptaufgabe betrachten. Deswegen ist die Unterstützung des WTP-Deploy-Prozesses bei der Maven-Integration im Eclipse auch oft brüchig.

Aber es gibt eine Abhilfe, welche sich in optionalen Paketen unter einer eigenen Update-URL verbirgt:

http://m2eclipse.sonatype.org/sites/m2e-extras

Wenn man die dortige WTP-Unterstützung ausgewählt und installiert hat, dann entfallen meistens die manuellen Anpassungen der org.eclipse.wst.common.component, um die richtigen Artefakte, respektive Workspace-Abhängigkeiten einzutragen.

JNDI vs. LDAP-Binding

Wenn man z. B. einen Rollbacktest baut und dieser programmatisch JNDI-Ressourcen bindet, dann funktioniert eventuell ein ebenfalls gebundener LDAP nicht.


final SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
final Session mailSession = javax.mail.Session.getDefaultInstance(mailProperties);
builder.bind("java:comp/env/mailSession", mailSession);

Wenn man nun in seinem LdapAccess sich einen Context geben lässt, dann ist dies der zum SimpleNamingContextBuilder Gehörende und nicht wie angedacht ein LDAP-Directory-Context.

env.put(Context.INITIAL_CONTEXT_FACTORY, LdapCtxFactory.class.getCanonicalName());
anonCtx = new InitialDirContext(env);

Ohne das programmatische JNDI-Binding funktioniert die Sache wie gedacht und man erhält den LDAP-Context.

Java += mit implizitem Cast

Ein Freund von der Universität schickte mir folgenden Schnipsel, der eine überraschende Ausgabe erzeugt.


long g = 287654322;
g += 0f;
System.err.println(g);

Dem Problem kam ich nicht sofort auf die Spur. Aber ein Blick in die Spec verrät es uns:

E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.(Java Spec)

Also ist unsere Lösung das implizite Casten von float nach long.

final float a = (g + 1f);
g = (long)a; // ((long)2.87654336E8)

Dabei ist es natürlich egal, ob man 0f, 1f oder sonstige kleine Zahl wie 42f verwendet. Das Ergebnis ist dasselbe.

Auf alle Fälle ist mir wie an vielen anderen Stellen klar, dass man „Verkürzungen“ oder andere coole aber zulässige Schreibweisen einfach lassen sollte.
Keep it small and simple heißt hier, dass die „Verkürzung“ eine Verkomplizierung ist, deren Tragweite man manchmal nicht absieht.

Pitfall getDate()

Lange kein JDBC mehr gemacht und prompt in die Date-Time-Timestamp-Falle getappt.

1. Date vs. Timestamp

Haben wir in der Datenbank z. B. PostgreSQL ein Date, dann ist es auch nur ein Datum ohne Zeit. In Oracle hingegen sagt DATE, dass wir auch eine Zeit anbei haben.
Besitzt nun die Datenbank-Spalte einen Zeitanteil und man benutzt java.sql.ResultSet.getDate(String), dann erhält man ein java.sql.Date nur mit einem Datum. Ruft man java.sql.ResultSet.getTimestamp(String) auf, dann erhält man ein java.sql.Timestamp, der ein java.util.Date ist und klaglos dem Konstruktor in meinem Row-Mapper übergeben wird.

Dieser Timestamp besitzt nun einen Zeitanteil wie gewünscht, bring aber einen Gotcha mit. Das Gemeine an der Sache ist, dass dieser Timestamp nicht kommutativ bzgl. der equals() Methode ist.


public boolean Timestamp.equals(java.lang.Object ts) {
if (ts instanceof Timestamp) {
return this.equals((Timestamp)ts);
} else {
return false;
}
}

Siehe hierzu auch Martin Odersky: How to Write an Equality Method in Java bzw. Joshua Bloch: Effective Java Programming.

Mittlerweile benutze ich, wenn es geht, ein Long in der Datenbank, bzw. eine String-Repräsentanz wie z. B. „yyyyDDMMHH24MI“. Letzeres ist zwar langsamer, aber vernünftig zu lesen, wenn man wie ich gerne in die DB selbst schaut.

Das kann man auch sehr schön als org.hibernate.usertype.UserType modellieren CalendarUserType -> long.

Aber es gibt durchaus auch andere Weisen, damit umzugehen Convert a java.sql.Timestamp Object to a java.util.Date Object?. 😀

2. java.sql.ResultSet.getDate(…, Calendar)

Gibt man bei obiger Methode keinen Calendar an, dann wird der der VM benutzt. Das zeitigt zum Teil üble Fehler, besonders wenn der Hosting-Dienstleister virtualisert oder ein neues Blech mit geänderter Timezone usw. hinstellt. Man sollte hier „immer“ den richtigen Calendar explizit übergeben. In der Praxis habe ich das bisher sehr selten gesehen und wundere mich, warum das alles überhaupt funktioniert.

Mosquita Stufe 2

Es geht schleppend voran und ich komme kaum mal zum Basteln vom meinem Mosquita mit DF6HF, der mich sehr unterstützt. Aber immerhin geht es vorwärts und die Aufbau-Anleitung von QRPproject ist auch richtig gut: in kleinen Schritten mit genauer Testbeschreibung, detaillierten Hinweisen. Es ist genau das Richtige für Einsteiger, bzw. Wiedereinsteiger wie mich.

Vielen Dank auch für die super Unterstützung durch OV F12.

Das CW-Üben hab ich im Augenblick leider auch erst einmal auf Eis gelegt.

Ernsthaftes Problem: Maven2 und transitive Repositories

Plötzlich erhält man vom seinem Bamboo-Build mittels Maven2 folgende Meldung:
Unable to get dependency information: Unable to read local copy of metadata

Beim Blick in die Datei stellt sich heraus, dass es sich bei den maven-metadata-jaspersoft.xml um eine HTML-Datei handelt.
Diese beinhaltet den Hinweis, dass eine bestimmte URL nicht gefunden wird. Wir dachten, dass wir mit Nexus und unseren POMs auf der sicheren Seite wären, aber weit gefehlt. Dies kann übrigens auch passieren, wenn man zu Hause mit seinem neuen DSL spielt und der Provider einem statt 404 eine HTML-Seite durchreicht. So landen dann auch schon einmal HTML-Seiten als JAR-Dateien im Maven2-Repository.

Im Moment existiert noch keine befriedigende Lösung, soweit ich das gesehen habe.

Selbst wenn man sein Nexus-Repository-Proxy abgeschottet hat, muss man natürlich darauf achten, dass die POMs abgeschottet sind.

  • alle repository-Einträge zeigen nur auf gekapselte Nexus-Repositories
  • „central“ ist gesperrt


<repository>
<id>central</id>
<name>Maven Repository Switchboard</name>
<layout>default</layout>
<url>http://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>

Das Gleiche gilt natürlich auch für die Plug-In-Repositories.

Aber dies nützt einem alles nichts bei dem Problem der transitiven-Repositories.


[INFO] artifact commons-collections:commons-collections: checking for updates from jaspersoft
[DEBUG] Checking for pre-existing User-Agent configuration.
[DEBUG] Adding User-Agent configuration.
[DEBUG] Connecting to repository: 'jaspersoft' with url: 'http://www.jasperforge.org/maven2'.

Besonders unangenehm ist uns das bei jaspersoft (http://www.jasperforge.org/maven2) aufgefallen.
(Siehe: jasperforge.org espforum, http://stackoverflow.com)

Currently, there's no way to exclude more than one transitive dependency at a time, but there is a feature request for this on the Maven JIRA site:

http://jira.codehaus.org/browse/MNG-2315

Da mogelt sich doch tatsächlich ein weiteres Repository in die Abarbeitungskette. Und das ist eine fatale Sicherheitslücke. Es bleibt einem nichts anderes übrig, als auf mögliche Transitivitäten zu achten mit dem Scannen seines JAR-Dependency-Baumes z. B. unter WEB-INF/lib.

Auf alle Fälle zeigt dieser Vorfall, dass eine solch komplexe und transitive Struktur, wie die Maven-Abhhängigkeiten, gepflegt und überwacht sein will. Wäre uns dieser Crash beim Deployment zum Kunden passiert …

Java goes business

Es ist ja schön, wenn sich alles professionalisiert und man für Geld besseren Service erhält. Aber die aktuellen Entwicklungen bzgl. Oracle verunsichern schon einige Entwickler, gerade wenn sie im Enterprise-Bereich entwickeln wie ich. Wenn sich der Verkaufspreis der Applikation signifikant wegen eines Bezahl-JVM erhöht, kann man sich in der Masse schon Problemen gegenüber sehen.

Andererseits sorgt natürlich die Reputation Oracles bei Großkunden für ein höheres Ansehen und bessere Argumentationsmöglichkeiten.

Golem.de Oracle Spezial