Hibernate Envers Timestamp

Ein Envers Timestamp sieht in der Datenbank bsplw so aus: 1441090155727 (und ist ein Bigint). Das wäre der 30.04.47636, 06:02:07, und ich bezweifel, dass der Datenbankeintrag aus der Zukunft kommt. Die Lösung ist so naheliegend wie einfach: Die letzten drei Stellen stehen für die Millisekunden, der “korrekte” Timestamp lautet 1441090155, und das ist der 01.09.2015, 08:49:15.

Play Dependencies für JBoss ActiveMQ mit Zookeeper

Es gibt bereits ein gutes Tutorial, wie man eine MQ via Zookeeper anspricht, hier aber noch mal explizit für Play (1 und 2).

Schritt 1: Libraries hinzufügen

Play 1

Die Excludes waren für mein Setup nötig, das müssen sie aber nicht generell sein:

Damit das funktioniert, müssen die folgenden Repositories in den Ivy-Setting definiert werden:

Eine Anmerkung dazu: Das “Early Access” Repo ist meiner Einschätzung nach eigentlich nicht nötig; alle letztlich verwendeten Dependencies kommen aus dem “Releases” Repo. Aber: Offenbar gibt es irgendwelche transitiven Dependencies, die dort zu finden sind. Sie werden “später” durch andere transitive Dependecies “überschrieben”, aber sie müssen trotzdem aufgelöst werden können! Andernfalls funktioniert zwar auch alles, aber es gibt unschöne Warnings. Und ich habe einige Zeit darauf verwendet, das “Early Access” Repo durch force und exclude wegzukonfigurieren – ein Fass ohne Boden 🙂

Play 2

Schritt 2: Konfiguration

Schritt 3: Im Code

HTH!

Java: Object Type Mappings in Elasticsearch

Verschachtelte Strukturen in Elasticsearch zu mappen, ist strukturell gesehen eigentlich gut dokumentiert, siehe Object Type (Achtung: Nicht zu verwechseln mit Nested Type!). In Java kann dabei trotzdem einiges schiefgehen – zumindest in v0.90.x, über die aktuellen Versionen kann ich nichts sagen.

Zuerst ein Klassiker, wenn man sich die zahlreichen Tipps dazu im Internet ansieht:

Merge failed with failures {[mapper [field.innerField] has different index values, mapper [field.innerField] has different tokenize values, mapper [field.innerField] has different index_analyzer]}

Das liegt gerne mal daran, dass der Index vor dem Mapping aufgebaut wurde. Was aber nicht bedeutet, dass man das selber explizit so macht! Es kann ganz simpel daran liegen, dass die früher erzeugten (und jetzt veralteten) Dateien auf der Platte nicht mehr passen. Alle Dateien löschen und neu erzeugen lassen, dann kann der Fehler schon verschwunden sein m(

Dann, auch sehr verwirrend: Der XContentBuilder hat eine string()-Funktion (nicht zu verwechseln mit toString()). Die hilft beim Debuggen insofern, als dass man das erzeugte Mapping/JSON ausgeben kann. Allerdings “tut” string() intern irgendwas, keine Ahnung, den Stream schließen oder so. Mit einem string() im Code bekommt man eine NullPointerException in UTF8JsonGenerator.writeRaw(UTF8JsonGenerator.java:670). Tollerweise (trollerweise?) liegt die Klasse zwar unter org.elasticsearch.common.jackson.core.json.UTF8JsonGenerator, die Sourcen sind aber nicht Teil der ES-Sourcen… und der UTF8JsonGenerator hat sehr viele writeRaw()-Methoden.

Bis man darauf kommt, dass es ausschließlich an string() liegt, kann schon mal einige Zeit vergehen: Mit einem string() im Code kann man zwar das erzeugte JSON ausgeben, dieses ist aber völlig irrelevant. Es kann sogar ungültig sein, etwa durch ein überzähliges endObject() – durch string() sieht man statt des eigentlichen

 JsonGenerationException: Current context not an object but ROOT

nur den NullPointer von oben.

Trotzdem ist string() nötig, denn drölf verschachtelte endObject() und field() werden schnell unübersichtlich. Meine Empfehlung: Das JSON unter Einsatz von string() gemäß der Doku aufbauen, string() dann auskommentieren, und erst dann die auftretenden Fehlermeldungen lesen.

Java: Fields (inkl. Typ, Getter und Setter) dynamisch auslesen

zusammengestoppelt auf stackoverflow

Java: “T implements Interface”

Folgendes geht NICHT:

Aber das hier geht:

That would require an object of type T to both extend B and implement SomeInterface.

Abgefahren. Habe ich bis heute noch nie gesehen, kommt aber gerade ganz praktisch 🙂

Jackson: Map JSON Array

(von)

MS SQL: Alle Tabellen leeren

Ich möchte das nicht jedes mal googlen:

In dem Zusammenhang: Flyway initOnMigrate in Play Applikationen:

Gson: JSON-Serializer für bestimmte Typen überschreiben

Play nutzt (zumindest in Version 1.3?) Gson für JSON-Serialisierung. Gson macht aus Float.NaN die Zahl NaN, was für JSON nicht erlaubt ist:

play.exceptions.JavaExecutionException: NaN is not a valid double value as per JSON specification. To override this behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.

Schöner wäre es, daraus den String “NaN” zu machen.

Ansatz 1, wie empfohlen:

Gleicher Fehler. Deshalb Ansatz 2:

mit:

und DoubleSerializer analog.

Öfter mal was Neues: Neuer Job

(Zu Folge 4)

Zum ersten mal überhaupt seit Bestehen dieses Blogs (immerhin Mitte 2008!) habe ich den Job gewechselt: Seit 1.11. arbeite ich nicht mehr bei Jung von Matt/next, sondern bei mindsmash.

Bei der next hatte sich mein Job über die Jahre stark gewandelt: Eingestellt als Web Developer war ich fast von Beginn an der Flash Developer, der ich mich dann auch bald nennen durfte. Darin brachte ich es bis zum Senior; aber als Flash dann starb, war ich tatsächlich gerade mit einem komplexen Java-Projekt beschäftigt. Java-Projekte sind im Werbeagenturumfeld eher selten, das zweite Standbein wurde deshalb PHP, und ich zum Application Developer. Java blieb trotzdem das Wunschziel, und bei mindsmash bin ich dann auch “Java Web Developer”.

Was das Ganze mit diesem Blog zu tun hat: Es dürften vermehrt Java-Posts folgen* – embrace yourself!

*ein paar kamen ja schon.

PS, die Aussicht aus’m Büro nach hinten raus:

Morgens auf dem Raucherbalkon - schade, dass ich nicht rauche. #newoffice

PPS: Darum geht’s:

Spring Boot Security: 403 Forbidden

Es gibt durchaus gute Tutorials und Examples für Spring Boot Security. Nur benutzen die alle Thymeleaf als Templating Engine. Und Thymeleaf hat eine Besonderheit: Wenn man im Form “th:action” statt “action” verwendet, dann wird automatisch ein hidden Input Field mit dem CSRF-Token injected!

Wenn man stattdessen bsplw. Velocity verwendet, und das Template 1:1 übersetzt, merkt man nicht, dass etwas fehlt m( Der Server gibt “403 Forbidden” ohne weitere Fehlermeldung zurück (Tipp zum Debuggen: Der Request wird irgendwann auf die Error-Route umgebogen, aber der Fehler (hier noch inklusive Fehlermeldung) steht im Response-Object).

Offenbar muss man den Token dann manuell dem Template übergeben (Quelle, weicht aber leicht ab):

Gegenprobe:

Update

Übrigens: Hier wird beschrieben, wie man in Thymeleaf den Token in jeden Ajax-Request bekommt. Leider funktioniert das so nicht, denn um den Platzhalter zu ersetzen, muss man th:content verwenden (Quelle indirekt), sonst wird der Platzhalter nicht ausgewertet m(

Dann: Siehe hier für das Absetzen der Requests (in jQuery oder Angular)