Bug: Impossible to load 2+ independent applications if both are using the same JFX version (Windows, JFX 23.0.2+3)
Johan Vos
johan.vos at gluonhq.com
Sun Feb 9 10:56:25 UTC 2025
Hi Cormac,
I understand the problem, and I agree it can be really annoying. It either
needs better communication, or a fix. The maven artifacts are something we
added with Gluon (hence not Oracle). As you said, windows signing
certificates come with a price (and building hassle).
The reason we started uploading those artifacts is mainly because
developers are very used to the maven artifact approach during development.
I believe this lowered the bar for JavaFX development after the JDK didn't
ship with the JavaFX code anymore. A developer who just wants to explore
using JavaFX is less likely to download an additional SDK or jmods. Simply
adding a few lines in a pom.xml is something most developers are familiar
with.
For shipping applications, I strongly discourage this, and highly recommend
the SDK/jmods approach. I can see now though that offering the "maven
artifacts for developers" created higher expectations than intended.
The problem you described is more or less documented in
https://bugs.openjdk.org/browse/JDK-8316276 . There have been discussions
(e.g. on the jigsaw list) in the past about this, as other projects (e.g.
dl4j) have similar issues with jars containing native code. There is no
standard approach in Java to deal with this, which is why the
NativeLibLoader contains a bunch of logic that allows to invoke the code in
the native libs from the classes in the same jar. That logic has been
modified over the years, to account for specific issues (e.g. [1] and [2]),
but it's not perfect.
I'm not against signing those libraries (it is a net loss of money and time
though), but as you noticed yourself, this won't fix all problems. We
should probably make it more clear that the maven artifacts should not be
used in production systems, and are for development only -- for developers
by developers.
- Johan
[1] https://bugs.openjdk.org/browse/JDK-8317308
[2] https://bugs.openjdk.org/browse/JDK-8307536
On Sat, Feb 8, 2025 at 10:01 PM Cormac Redmond <credmond at certak.com> wrote:
> Hi Steve,
>
> Thanks. I can also workaround it (with very limited changes) in another
> way by overriding javafx.runtime.version with something specific to my
> app, and its version (which will ultimately dictate the cache directory
> name) -- as the chances of having a clash then, are astronomically low.
>
> However, none of these things are solutions, but workarounds -- my stance
> is that it should not be up to the developer to firstly know this problem
> even exists (most won't), and then to workaround it by avoiding using
> certain JARs. It's not like we're building apps in un-documented or unusual
> ways, quite the opposite.
>
>
> Kind Regards,
> Cormac
>
> On Sat, 8 Feb 2025 at 18:16, Steve Hannah <steve at weblite.ca> wrote:
>
>> 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 <credmond at 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: credmond at certak.com | m: +353 (0) 86 268 2152 | w: www.certak.com
>>>
>>>
>>>
>>>
>>> On Sat, 8 Feb 2025 at 12:49, Christopher Schnick <crschnick at 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: credmond at certak.com | m: +353 (0) 86 268 2152 | w: www.certak.com
>>>>
>>>>
>>>>
>>>> On Thu, 6 Feb 2025 at 19:56, Cormac Redmond <credmond at 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.
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20250209/11483515/attachment-0001.htm>
More information about the openjfx-dev
mailing list