From 2b1841e17533543d1cb15220ca86a2a37c853daf Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Sat, 15 Aug 2026 10:51:11 +0200 Subject: Die Web-App rechnet im Browser statt auf dem Server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bisher lief die Weboberfläche auf einem JDK-HttpServer: Uploads landeten als temporäre Dateien auf dem Server, die Synopse entstand dort. Das kostete Betrieb (systemd, Reverse Proxy, Rate-Limiting, Timeouts) und verlangte ein Datenschutzversprechen, das nur zusicherbar, nicht nachprüfbar war — gerade Entwurfstexte verließen den Rechner der Nutzer:innen. Neu übersetzt `./mvnw -Pwasm package` dieselbe Pipeline mit GraalVM Web Image (`native-image --tool:svm-wasm`) nach WebAssembly, PDFBox eingeschlossen. Ausgeliefert werden nur noch statische Dateien; gerechnet wird im Browser. Die erzeugte Synopse ist byteweise identisch mit der der Befehlszeile (SHA-256 verglichen für IfSG 48/27/21 und BayJG 151/3/54). Die Befehlszeile bleibt unberührt: `./mvnw package` erzeugt unverändert das JAR, alle Optionen und Meldungstexte sind gleich, das Wasm-Profil ist rein additiv und verlangt Oracle GraalVM 25.1+ (die CE hat kein Web Image). Portabilitätsschnitt (nützt beiden Fassungen): * `Quelle` (Name + Bytes) ersetzt `Path` in der Pipeline; nur die Befehlszeile kennt noch ein Dateisystem. Der Name trägt genau den bisherigen `getFileName()`-Text, damit Warnungen und Quellenzeile wortgleich bleiben. * `DateiTyp` erkennt PDF/XML/Klartext an den Signaturbytes. Tika entfällt — eine schwergewichtige Abhängigkeit samt ServiceLoader- und XML-Konfiguration weniger, was der Wasm-Übersetzung unmittelbar zugutekommt. Vier Eigenheiten von Web Image, die der Quelltext jeweils an Ort und Stelle vermerkt: * `java.util.zip.Inflater` ist nicht angebunden (GR-65205), ohne Inflate ist kein PDF lesbar. `InflaterErsatz` substituiert ihn durch jzlib. * Typisierte JS-Felder lassen sich nicht nach `byte[]` umsetzen („byteArrayHub is not defined“); der Dateiinhalt wandert als Base64. * JULs Standardformatter ruft `StackWalker`, den es dort nicht gibt. * Im Worker fehlt `document.currentScript`, worauf die Laufzeit das Wasm-Modul neben `worker.js` sucht; die VM wird deshalb mit ausdrücklichem Pfad ein zweites Mal gestartet. Die Reachability-Metadaten stammen aus einem Lauf des Tracing-Agents über die Pipeline; die PDFBox- und FontBox-Ressourcen sind als Globs ergänzt, sonst scheitern PDFs an „Could not find referenced cmap stream Identity-H“. Entfallen: WebMain, UploadHandler, StaticHandler, Multipart und die systemd-Unit. Die nginx-Vorlage liefert jetzt statische Dateien aus, und die Datenschutzseite sagt, was nun stimmt: Die Dateien verlassen den Rechner nicht. Co-Authored-By: Claude Opus 5 Change-Id: I38faf2ac0f764d601f080d4276babe4747773683 --- .../eu.mulk/aendggner/reachability-metadata.json | 361 +++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 src/main/resources/META-INF/native-image/eu.mulk/aendggner/reachability-metadata.json (limited to 'src/main/resources/META-INF/native-image/eu.mulk') diff --git a/src/main/resources/META-INF/native-image/eu.mulk/aendggner/reachability-metadata.json b/src/main/resources/META-INF/native-image/eu.mulk/aendggner/reachability-metadata.json new file mode 100644 index 0000000..4f85735 --- /dev/null +++ b/src/main/resources/META-INF/native-image/eu.mulk/aendggner/reachability-metadata.json @@ -0,0 +1,361 @@ +{ + "reflection": [ + { + "type": "ch.qos.logback.classic.Logger" + }, + { + "type": "com.sun.crypto.provider.AESCipher$General", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "java.awt.image.BufferedImage", + "jniAccessible": true, + "fields": [ + { + "name": "colorModel" + }, + { + "name": "imageType" + }, + { + "name": "raster" + } + ], + "methods": [ + { + "name": "getRGB", + "parameterTypes": [ + "int", + "int", + "int", + "int", + "int[]", + "int", + "int" + ] + }, + { + "name": "setRGB", + "parameterTypes": [ + "int", + "int", + "int", + "int", + "int[]", + "int", + "int" + ] + } + ] + }, + { + "type": "java.awt.image.ColorModel", + "jniAccessible": true, + "fields": [ + { + "name": "colorSpace" + }, + { + "name": "colorSpaceType" + }, + { + "name": "isAlphaPremultiplied" + }, + { + "name": "is_sRGB" + }, + { + "name": "nBits" + }, + { + "name": "numComponents" + }, + { + "name": "supportsAlpha" + }, + { + "name": "transparency" + } + ], + "methods": [ + { + "name": "getRGBdefault", + "parameterTypes": [] + } + ] + }, + { + "type": "java.awt.image.Raster", + "jniAccessible": true, + "fields": [ + { + "name": "dataBuffer" + }, + { + "name": "height" + }, + { + "name": "minX" + }, + { + "name": "minY" + }, + { + "name": "numBands" + }, + { + "name": "numDataElements" + }, + { + "name": "sampleModel" + }, + { + "name": "sampleModelTranslateX" + }, + { + "name": "sampleModelTranslateY" + }, + { + "name": "width" + } + ] + }, + { + "type": "java.awt.image.SampleModel", + "jniAccessible": true, + "fields": [ + { + "name": "height" + }, + { + "name": "width" + } + ], + "methods": [ + { + "name": "getPixels", + "parameterTypes": [ + "int", + "int", + "int", + "int", + "int[]", + "java.awt.image.DataBuffer" + ] + }, + { + "name": "setPixels", + "parameterTypes": [ + "int", + "int", + "int", + "int", + "int[]", + "java.awt.image.DataBuffer" + ] + } + ] + }, + { + "type": "java.awt.image.SinglePixelPackedSampleModel", + "jniAccessible": true, + "fields": [ + { + "name": "bitMasks" + }, + { + "name": "bitOffsets" + }, + { + "name": "bitSizes" + }, + { + "name": "maxBitSize" + } + ] + }, + { + "type": "java.lang.Boolean", + "jniAccessible": true, + "methods": [ + { + "name": "getBoolean", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "type": "java.lang.System", + "jniAccessible": true, + "methods": [ + { + "name": "load", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "type": "org.apache.commons.logging.LogFactory" + }, + { + "type": "org.apache.commons.logging.impl.SLF4JLogFactory" + }, + { + "type": "org.apache.commons.logging.impl.WeakHashtable", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "org.apache.log4j.LogManager" + }, + { + "type": "org.apache.logging.log4j.Logger" + }, + { + "type": "org.apache.pdfbox.pdmodel.encryption.StandardSecurityHandler", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "org.jboss.logmanager.LogManager" + }, + { + "type": "org.slf4j.jul.JULServiceProvider" + }, + { + "type": "sun.awt.image.IntegerComponentRaster", + "jniAccessible": true, + "fields": [ + { + "name": "data" + }, + { + "name": "dataOffsets" + }, + { + "name": "pixelStride" + }, + { + "name": "scanlineStride" + }, + { + "name": "type" + } + ] + }, + { + "type": "sun.misc.Unsafe", + "fields": [ + { + "name": "theUnsafe" + } + ], + "methods": [ + { + "name": "invokeCleaner", + "parameterTypes": [ + "java.nio.ByteBuffer" + ] + } + ] + }, + { + "type": "sun.security.provider.MD5", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "sun.security.provider.NativePRNG", + "methods": [ + { + "name": "", + "parameterTypes": [ + "java.security.SecureRandomParameters" + ] + } + ] + }, + { + "type": "sun.security.provider.SHA2$SHA256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "sun.text.resources.cldr.FormatData" + }, + { + "type": "sun.util.resources.cldr.CalendarData" + } + ], + "resources": [ + { + "glob": "META-INF/services/java.net.spi.URLStreamHandlerProvider" + }, + { + "glob": "META-INF/services/java.time.zone.ZoneRulesProvider" + }, + { + "glob": "META-INF/services/javax.xml.parsers.DocumentBuilderFactory" + }, + { + "glob": "META-INF/services/org.apache.commons.logging.LogFactory" + }, + { + "glob": "META-INF/services/org.jboss.logging.LoggerProvider" + }, + { + "glob": "META-INF/services/org.slf4j.spi.SLF4JServiceProvider" + }, + { + "glob": "commons-logging.properties" + }, + { + "module": "java.base", + "glob": "jdk/internal/icu/impl/data/icudt76b/nfc.nrm" + }, + { + "module": "java.base", + "glob": "jdk/internal/icu/impl/data/icudt76b/ubidi.icu" + }, + { + "glob": "org/apache/fontbox/**" + }, + { + "glob": "org/apache/pdfbox/resources/**" + } + ] +} \ No newline at end of file -- cgit v1.2.1