This also doesn't seem to affect apps running with the zulufx distributions (the JRE with JavaFX bundled).
E.g. this build of JavaFX ensemble https://www.jdeploy.com/~jdeploy-demo-javafx-ensemble It uses Java 19, and OpenJFX 20, and it doesn't seem to create an .openjfx directory anywhere that I can find. Therefore, you can deploy your app using jDeploy and it won't have this issue. (Disclosure, I'm the creator of jDeploy). Similarly, if you bundle your app with a ZuluFX distribution, and strip out your maven jars (which is what jDeploy does), you won't have this issue. Steve On Sat, Feb 8, 2025 at 9:35 AM Cormac Redmond <credm...@certak.com> wrote: > Hi, > > Thanks for the reply. Yes -- my project uses JFX JARs rather than jmods > (as many do). And the clashing application in question, must be the same. > But this is a real problem that occurred with a real user, and I only have > a handful of users. > > Oracle signing the JFX DLLs, while an improvement, would still leave the > following problems wide open: > > - There's no way to stop a developer (or some build tool) from > re-signing or removing signature from signed DLLs, in which case this > problem can still re-occur > - In the event of a genuine difference of a DLLs (under the same cache > folder version), if the DLL cannot be deleted (to be replaced), because a > running application application is using it -- a completely feasibly > scenario -- then we have one application breaking another. > > Also, the developers shipping apps are in full control of the JFX JARs, > their DLLs, and the *reported" javafx.version and javafx.runtime.version. > E.g., I could have JFX 21.0.5 in a JFX 23.0.2 cache folder if I wanted > simply by changing javafx.runtime.version (or javafx.cachedir). > > It's far too brittle. > > > > Regards, > > *Cormac Redmond* > Software Engineer, Certak Ltd. > > e: credm...@certak.com | m: +353 (0) 86 268 2152 | w: www.certak.com > > > > > On Sat, 8 Feb 2025 at 12:49, Christopher Schnick <crschn...@xpipe.io> > wrote: > >> I think that went a bit under the radar as this only occurs when using >> the maven dependencies. The SDK and jmod downloads do not have that issue >> as they don't copy DLLs into any temp directory. Only the maven jars have >> to do this. >> >> About signing any JavaFX DLLs, that would indeed be a good addition >> considering all other JDK DLLs are signed. >> >> Best >> Christopher Schnick >> On 08/02/2025 13:31, Cormac Redmond wrote: >> >> Hi, >> >> I am surprised nobody else sees this bug as a higher-priority >> conversation point. >> >> It's troubling to see how running one self-contained application can >> break another self-contained application (because of a cache that most JFX >> devs wouldn't even know exist). >> >> If one well-behaved JFX application cannot delete/replace a file JFX >> cache on start-up, because another well-behaved JFX application is using >> that cached file (it will be if built on the same JFX version) -- then the >> application will not run, at all. >> >> And as I explained earlier, this is a likely occurring scenario in the >> wild -- the only reason this bug isn't more prevalent /reported / >> noticeable, is that it's not too likely for a user to have two JFX >> applications using the same JavaFX version, on their machine. But that's >> down to pure coincidence / chance. I wouldn't say the same for Electron or >> Flutter, etc. If they had this bug, it would be noticed and it would be >> news. >> >> Also, the creators of these applications would have no idea that their >> application is not starting, nor would the users know why. >> >> By the way, the problem is not just about *signed* DLLs + checksums, >> obviously. It would occur if the DLLs are different for any other >> reason too, obviously, and the authors of NativeLibLoader thought this >> possibility is high enough to do the checksum + delete + replace check. The >> application shouldn't silently fail because of a cache management bug. >> >> >> >> >> Regards, >> >> *Cormac Redmond* >> Software Engineer, Certak Ltd. >> >> e: credm...@certak.com | m: +353 (0) 86 268 2152 | w: www.certak.com >> >> >> >> On Thu, 6 Feb 2025 at 19:56, Cormac Redmond <credm...@certak.com> wrote: >> >>> Hi, >>> >>> I have found a "serious" bug, where two completely independent JFX >>> applications, both with their own embedded runtime (built with jlink & >>> jpackage) & using the same JavaFX version, are unable to run >>> simultaneously, because of the JFX cache -- at least on Windows. >>> >>> When trying to run any application, NativeLibLoader does a checksum on >>> DLLs in the cache (e.g.: >>> C:\Users\xyz\.openjfx\cache\23.0.2+3\amd64\prism_d3d.dll); >>> and tries to delete any files that exist where the checksums do not match >>> (in order to replace them): >>> >>> if (!Arrays.equals(isHash, fileHash)) { >>>> Files.delete(f.toPath()); >>>> } >>> >>> >>> But a second application *fails* to start, as it is unable to delete >>> these files because they're in use by the first application (see stacktrace >>> below). >>> >>> But why are the checksums different, shouldn't they be the same? They >>> are different because the DLLs are signed by the builder of the >>> applications -- different authors and different timestamps. So the DLL >>> checksums will be different despite the DLLs being the same, in terms of >>> the JFX version. >>> >>> Why are these JFX DLLs signed by the authors? Because for some reason, >>> they come *unsigned*, and all DLLs when packaged *should* be signed, >>> including any embedded in JARs, to avoid alarming Windows Defender warnings >>> and mistrust, etc. There's no point spending a small fortune on Windows >>> code-signing certs and shipping with any unsigned third-party DLLs >>> (including any embedded in JARs). You might sign your own binaries and any >>> unsigned third-party binaries. Similarly for MacOS, everything, including >>> embedded native libs in JARs, etc., needs to be signed for gatekeeper & >>> notarization to allow it. >>> >>> So it would be better if JFX DLLs came signed, to avoid forcing >>> developers to do it (which would also avoid this checksum mishap). Or, if >>> developers need to sign Oracle's DLLs, then this checksum approach isn't >>> suitable. Also, there should really be a proper fallback in place -- a >>> cache bug should not have such a catastrophic outcome. >>> >>> FYI: JLink also removes signatures from a bunch of JDK DLLs when >>> assembling the runtime, but leaves a bunch of Windows DLLs >>> untouched. Personally, I'd prefer all DLLs to come signed, and let the >>> developers re-sign if they want. Removing the signatures is adding >>> unnecessary hurdles for folks, for no benefit I can see. >>> >>> Anyway, example stacktrace: >>> >>> Loading D3D native library ... >>>> WARNING: java.lang.UnsatisfiedLinkError: Can't load library: C:\Program >>>> Files\KafkIO\bin\prism_d3d.dll >>>> Loading library prism_d3d from resource failed: >>>> java.nio.file.AccessDeniedException: C:\Users\xyz >>>> \.openjfx\cache\23.0.2+3\amd64\prism_d3d.dll >>>> java.nio.file.AccessDeniedException: C:\Users\xyz >>>> \.openjfx\cache\23.0.2+3\amd64\prism_d3d.dll >>>> at >>>> java.base/sun.nio.fs.WindowsException.translateToIOException(Unknown >>>> Source) >>>> at >>>> java.base/sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source) >>>> at >>>> java.base/sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source) >>>> at >>>> java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(Unknown Source) >>>> at >>>> java.base/sun.nio.fs.AbstractFileSystemProvider.delete(Unknown Source) >>>> at java.base/java.nio.file.Files.delete(Unknown Source) >>>> at >>>> com.sun.glass.utils.NativeLibLoader.cacheLibrary(NativeLibLoader.java:300) >>>> at >>>> com.sun.glass.utils.NativeLibLoader.installLibraryFromResource(NativeLibLoader.java:218) >>>> at >>>> com.sun.glass.utils.NativeLibLoader.loadLibraryFromResource(NativeLibLoader.java:200) >>>> at >>>> com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:142) >>>> at >>>> com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:58) >>>> at >>>> com.sun.prism.d3d.D3DPipeline.lambda$static$0(D3DPipeline.java:54) >>>> at >>>> java.base/java.security.AccessController.doPrivileged(Unknown Source) >>>> at com.sun.prism.d3d.D3DPipeline.<clinit>(D3DPipeline.java:50) >>>> at java.base/java.lang.Class.forName0(Native Method) >>>> at java.base/java.lang.Class.forName(Unknown Source) >>>> at java.base/java.lang.Class.forName(Unknown Source) >>>> at >>>> com.sun.prism.GraphicsPipeline.createPipeline(GraphicsPipeline.java:218) >>>> at >>>> com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:92) >>>> at >>>> com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125) >>>> at java.base/java.lang.Thread.run(Unknown Source) >>>> GraphicsPipeline.createPipeline failed for com.sun.prism.d3d.D3DPipeline >>>> java.lang.UnsatisfiedLinkError: no prism_d3d in java.library.path: >>>> C:\Program >>>> Files\KafkIO;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\Program >>>> Files\Oculus\Support\oculus-runtime;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;c:\dev\apps\apache-maven-3.9.9\bin;C:\dev\apps\cygwin64\bin;C:\Program >>>> Files >>>> (x86)\Windows Kits\10\Windows Performance Toolkit\;C:\Program >>>> Files\dotnet\;C:\Program Files\Git\cmd;C:\Program Files\7-Zip;C:\Program >>>> Files\SafeNet\Authentication\SAC\x64;C:\Program >>>> Files\SafeNet\Authentication\SAC\x32;C:\Program >>>> Files\Docker\Docker\resources\bin;%JAVA_HOME%\bin;;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Users\ >>>> xyz\scoop\apps\zulu-jdk\current\bin;C:\Users\xyz >>>> \scoop\apps\zulu22-jdk\current\bin;C:\Users\xyz >>>> \scoop\apps\zulu21-jdk\current\bin;C:\Users\xyz \scoop\shims;C:\Users\ >>>> xyz \AppData\Local\Microsoft\WindowsApps;C:\dev\scripts;C:\Program >>>> Files\JetBrains\IntelliJ IDEA 2024.2\bin;C:\Program Files\KafkIO\app;. >>>> at java.base/java.lang.ClassLoader.loadLibrary(Unknown Source) >>>> at java.base/java.lang.Runtime.loadLibrary0(Unknown Source) >>>> at java.base/java.lang.System.loadLibrary(Unknown Source) >>>> at >>>> com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:170) >>>> at >>>> com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:58) >>>> at >>>> com.sun.prism.d3d.D3DPipeline.lambda$static$0(D3DPipeline.java:54) >>>> at >>>> java.base/java.security.AccessController.doPrivileged(Unknown Source) >>>> at com.sun.prism.d3d.D3DPipeline.<clinit>(D3DPipeline.java:50) >>>> at java.base/java.lang.Class.forName0(Native Method) >>>> at java.base/java.lang.Class.forName(Unknown Source) >>>> at java.base/java.lang.Class.forName(Unknown Source) >>>> at >>>> com.sun.prism.GraphicsPipeline.createPipeline(GraphicsPipeline.java:218) >>>> at >>>> com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:92) >>>> at >>>> com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125) >>>> at java.base/java.lang.Thread.run(Unknown Source) >>> >>> >>> >>> Kind Regards, >>> Cormac >>> >>> >>> -- Steve Hannah Web Lite Solutions Corp.