Java: application.properties ohne Spring

Wer, bsplw. aus Analogiegründen, seine Konfiguration in eine application.properties aus lagern will, selbst wenn dieses projekt kein Spring-Projekt ist, der kann Apache Commons Configuration verwenden (via, Achtung, erfordert commons-beanutils):

mit:

 

JNA: Mehrdimensionale Arrays

Anders als in C liegen Javas mehrdimensionale Arrays nicht in einem Stück im Speicher. C kennt (intern) gar keine mehrdimensionalen Arrays, bzw. unterscheiden sich diese (intern) nicht von eindimensionalen. Und so muss man das dann auch beim Zugriff aus Java heraus nachbauen:

Spring Boot: @ConditionalOnMissingProperty

Spring Boot bietet ein Conditional namens ConditionalOnProperty, die die Component nur dann registriert, wenn eine gewisse System-Property entsprechend (siehe Logik unter dem Link) gesetzt ist – und ggf. auch, wenn das Property fehlt und matchIfMissing auf true gesetzt ist.

Das “auch” störte mich in einem Fall, weil ich zwar gerne gewisse Komponenten per System-Property einkonfigurieren können möchte. Aber wenn ich diese nicht habe, brauche ich Fallback-Komponenten, und dafür eine Annotation, die nur triggert, wenn das Property fehlt.

Ansage ist hier:

Since detecting missing properties can be problematic, and we’ve not got a direct need for this ourselves, I think your own custom condition is the best approach.

, was ich hiermit getan habe. Jeder Kommentar erwünscht (gerne auf gist, aber wer keinen Account hat, kann auch hier).

Pre-shared key Authentifizierung mit Spring Boot

Pre-Shared Keys sind mit Spring Boot eine Geschichte voller Missverständnisse. Hier jeweils meine:

HTTP/HTTPS

In der Theorie scheint die Verwendung von https “überflüssig”, denn wenn ich eh nur vorher als “vertrauenswürdig” zertifizierte Clients zulasse, warum dann noch die Verbindung absichern. Naheliegende Antwort: Austausch von Identitäten über http ist nie gut – ⚠

Also https in den application.properties (/application.yml) der Server-Anwendung einschalten:

Zertifikatserstellung

Die eigentliche Idee ist jetzt: Jeder Client muss ein “trusted” Zertifikat vorweisen. Diese Zertifikate werden in einem “Trust-Store” abgelegt, den der Server bekommt. Der Client bekommt sein Zertifikat ebenfalls, dort in einem “Key-Store”, um sich auszuweisen. Andersrum analog: Das Serverzertifikat bekommt der Client in einem Trust-Store, um den https-Server zu verifizieren, der Server legt das in seinem Key-Store ab.

Insgesamt sieht die Erstellung so aus:

Wichtig dabei: Die verwendeten Passwörter (klar), aber auch der verwendete Name, beides brauchen wir unten.

PS, siehe unten und hier

Zertifikate einbinden

Die Zertifikate kann man dem Server jetzt über einen TomcatConnector bekannt machen, muss man in Spring Boot aber nicht (⚠; Danke, Nils). Stattdessen genügt Folgendes, ebenfalls in den application.properties:

Hier verweise ich auf Key-Stores in der .jar, was seit Tomcat 7.0.66+ funktioniert. ⚠

In der Client-Anwendung sieht das etwas anders aus, zuerst die application.properties:

Verwendung dann beispielhaft in einem RestTemplate (org.apache.http.* gibt’s hier)

ACHTUNG: Der NoopHostnameVerifier war hier nötig, da ich das Zertifikat nicht mit expliziten Hostnames erstellt habe, in Production würde man das wohl tun. ⚠

Userverwaltung

Natürlich kann man mehrere Client-Zertifikate verwenden, und natürlich kann man jedem Client separate Rechte zuweisen. In der Server-Anwendung:

(Quelle und Stack Overflow)

btw: Spring Security bringt einen default User mit (“user”, Passwort wird auf der Konsole beim Hochfahren ausgegeben). Deaktivierung in den application.properties:

hth

JNAerator und time_t

Nachtrag zu neulich: JNAerator macht aus einem time_t in etwa Folgendes (schon an Java Naming Conventions angepasst):

Damit kann man fast nichts anfangen; insbesondere bekommt man den Eigentlichen Timestamp nicht da raus. Das “Pointer” ist außerdem irreführend, denn C liefert an der Stelle keinen Pointer, sondern die Sekunden seit Epoch. Funktionieren tut:

Quelle, hth

UPDATE: long durch int ersetzt, das ist wichtig.

JNAerator?

Das Anstrengende Fehleranfällige an JNI ist das Mapping von (komplexen) Datentypen. Nun gibt es aber auch noch JNA,

a community-developed library that provides Java programs easy access to native shared libraries without using the Java Native Interface.

(Wikipedia), und dafür insbesondere den “JNAerator“, der komplexe Datentypen “automatisch” mappt. Die Idee liegt nahe, statt JNI(-Strukturen) JNA(-Strukturen) zu nutzen. Im Folgenden ein Versuch.

Die Installation ist so weit klar:

Der Aufruf allerdings schon nicht mehr so sehr (“0.13” und den Pfad zum lokalen Maven-Repo ggf. anpassen):

Wichtig ist nämlich -runtime JNA, mit den default Einstellungen handelt man sich Abhängigkeiten zu org.bridj (laut Doku “faster runtime that supports C++”) und damit die Notwendigkeit ein, die dll darüber zu laden. Und das will man zumindest dann nicht, wenn man plain JNI möchte.

Das JNAerator Studio (hier) scheint das übrigens per default anders zu machen, aber das nur am Rande. Trotzdem für die Akten: Den Pfad zur dll würde man BridJ so bekannt machen:

Aber zurück zu den so kompilierten Klassen. -mode Directory erzeugt sie als .java-Dateien, nicht als .jar. Das ist hilfreich, denn man muss sie editieren, siehe unten. Sie enthalten (übrigens neben Kommentaren mit dem vollen Pfad der Quelldatei, im Beispiel also /some/path/to/some/HeaderFile.h, evt. will man den nicht öffentlich machen) dann alle structs in ihrer Java Version. Das eigentliche Interface, das die Methoden der Header-Datei bereitstellt, muss man trotzdem selber schreiben und wie folgt laden:

In MyInterface deklariert man die Methoden, auf die man in der dll zugreifen möchte. Primitive Übergabe-/Rückgabeparameter sind dabei ziemlich selbsterklärend (int=int, bool=boolean, etc.), Strings sind char-Arrays, Pointer bringt JNA mit (bsplw. IntByReference, IMHO ein Vorteil gegenüber JNI), structs hat man sich ja eben in Java-Klassen übersetzt. Also alles gut? Tatsächlich gibt es Probleme mit den erzeugten Klassen:

Einige sind offensichtlich, bsplw. überschreiben sie

mit

, was natürlich nicht kompiliert. Globales Suchen-und-Ersetzen, fertig.

Anderes ist schon komplexer: Structures (also die Java-Klasse jetzt) haben ein Alignment, im Konstruktor zu übergeben. Wenn ich das richtig verstehe, korrespondiert das mit der entsprechenden Compiler-Option in VS und sollte automatisch erkannt werden? Oder es hängt an der Plattform? Wie auch immer: Wenn man eine struct hat, die, sagen wir, 22 Byte belegt, dann ist es nicht hilfreich, wenn der default von 4 Byte großen Blöcken ausgeht. Entweder werden dann nämlich zwei Byte abgeschnitten, oder zwei unnötige Bytes angehängt, was zu Verschiebungen in der Folgestruktur führt!

Beispiel: In Arrays von structs beginnt der zweite Eintrag dann zwei Bytes zu spät (es fehlen zwei Bytes zu Beginn) oder zu früh (der Eintrag beginnt mit den letzten zwei Bytes aus dem ersten Eintrag). Nicht unbedingt einfach zu debuggen m( Hilfreich ist es, das System-Property jna.dump_memory auf true zu setzen, und die Ausgabe in einen Hex-Editor zu kopieren. Man sieht die beschriebenen Byte-Offsets dann wenigstens (Danke, Nils!).

Ich habe dazu eine eigene Zwischenschicht eingezogen:

Zusammenfassung

Ja, JNAerator erzeugt einem Java-Klassen aus Headerdateien. Und das ist bei sehr großen Headern auch hilfreich. Aber es ist auch sehr fehleranfällig – und teilweise sogar fehlerhaft. Es kann nur eine Basis für eigenen Code sein.

Update

Nachtrag zu time_t

Java: C/C++ über JNI einbinden

Neulich musste ich eine DLL aus Java heraus ansprechen. Zur Wahl stehen JNI und JNA, wobei JNA “nur” ein Wrapper für JNI ist – und bei mir nicht funktioniert hat. In der Theorie ist JNI aber auch nicht schwer, allerdings legt Visual Studio einem mir Steine in den Weg. Ein Grund mehr, bei Java zu bleiben.

Der Java-Teil ist dann auch denkbar einfach:

Diese DllBridge wird dann in eine Headerdatei übersetzt:

Und diese Header-Datei dann in “das” C/C++-Projekt kopiert. An “das” Projekt kommt man wie folgt:

  • Visual Studio installieren; ich habe Visual Studio Community 2017 mit den Standardkomponenten genommen
  • Neues C++/Win32-Projekt anlegen, im Dialog unter “Anwendungseinstellungen” den Anwendungstyp “DLL” wählen
  • Ich habe das Projekt testweise MyDll genannt, die entstehende .dll heißt später genauso

Nachdem man die DllBridge.h in das Projekt kopiert und über Rechtsklick > Include In Project eingebunden hat, kann man ihre Methoden implementieren. O.g. Code erzeugt genau eine Methode Java_DllBridge_hello, siehe DllBridge.h. Deren Implementierung in der .c-Datei:

Alleine die Importe und die korrekte Version von MessageBox herauszufinden, hat sicher eine Stunde gedauert 😡 Oh, Stichwort “Importe”: jni.h wird vom  JDK mitgebracht, die entsprechenden Ordner muss man in VS bekannt machen. Das funktioniert nicht über einen Symlink, sondern über die Projekteinstellungen, indem man unter Configuration Properties > C/C++ > General > Additional Include Directories diese drei Verzeichnisse hinzufügt (Rekursion wäre ja auch zu einfach.):

  • C:\Program Files (x86)\Java\jdk1.8.0_121\include
  • C:\Program Files (x86)\Java\jdk1.8.0_121\include\win32
  • C:\Program Files (x86)\Java\jdk1.8.0_121\include\win32\bridge
    PS Ja: Das ist ein 32-Bit-Java, weil ich letztlich eine 32Bit dll ansprechen will.

Das Ganze sollte jetzt bauen (Build > Build solution, oder STRG+Shift+B) und eine <Projektname>.dll erzeugen, siehe Konsolenausgabe. Der Name der .dll muss dem im initialen Java-Code entsprechen.

Ein Aufruf von

öffnet dann die MessageBox:

Sowie die erwartete Ausgabe auf der Konsole. Für reine C-Anbindung könnte man hier aufhören.

Interessant wird es nun, wenn man langlebigere C++-Objekte von Java aus referenzieren möchte. Idee:

  • Eine Klasse auf Java-Seite, die die dll lädt, sowie die nativen Methoden bereitstellt, und eine Klasse auf C++-Seite, die diese implementiert
  • Die C++-Klasse wird initialisiert, ihren Pointer gibt man in Form einer long an Java zurück (bzw. jlong, vgl. hier), vermutlich ist das die Speicheradresse
  • Über diese long kann Java dann das Objekt referenzieren
  • Braucht man das C++-Objekt nicht mehr, wird es deleted.

(via, längeres Beispiel, Danke Nils!)

Hier sieht das so aus:

Bzw. in C++:

Aufruf in Java dann:

Last but not least die andere Richtung, also C++ nach Java. Die Methode

lässt sich von C++ aus aufrufen über

Weiterführendes dazu hier.

hth

PostgreSQL: Entity ID GenerationType migrieren

Angenommen, man möchte die ID einer Entity von

auf

migrieren, und dazu explizit eine Sequence in der Datenbank anlegen. Ein Migrationsskript könnte dann so aussehen:

Aaaber: IDs können negativ sein:

org.postgresql.util.PSQLException: ERROR: setval: value -91 is out of bounds for sequence “foo_bar_id_sequence” (1..9223372036854775807)

😄

Naheligende Lösung wäre, das SELECT in

zu ändern. setval geht allerdings per default aber beim nächsten Wert los, im Fall “1” (wenn gar keine oder nur negative IDs vorhanden sind) also bei 2. Das ist nicht direkt schlimm, aber uncool. Eine (umständliche!) Lösung ist eine Fallunterscheidung – und falls man die Migration viele viele male durchführen muss, verpackt man die Fallunterscheidung in einer FUNCTION:

Aufruf dann per:

Deutlich einfacher ist allerdings:

FALSE sorgt dafür, dass nicht der nächste Wert genommen wird, sondern genau dieser – und “+ 1” vermeidet einen Wert von 0, der ebenfalls out of bounds wäre.

Danke an Nils für’s vereinfachen! 🙂 Die umständliche Version bleibt trotzdem online als Template für PG Functions.