javajavafxjlinkjnativehook

java.lang.UnsatisfiedLinkError: URI scheme is not "file" with javafx:jlink


Hello i am using jnativehook library with javafx running with mvn javafx:run works fine but with mvn javafx:jlink fails to start with a launcher, here is javafx plugin xml,

<plugin>
   <groupId>org.openjfx</groupId>
   <artifactId>javafx-maven-plugin</artifactId>
   <version>0.0.6</version>
   <executions>
      <execution>
         <id>default-cli</id>
         <configuration>
            <launcher>launcher</launcher>
            <mainClass>facsimile/com.github.srilakshmikanthanp.facsimile.Launcher</mainClass>
         </configuration>
      </execution>
   </executions>
</plugin>

Here is the stack trace,

java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1071)
Caused by: java.lang.RuntimeException: Exception in Application start method
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
        at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.UnsatisfiedLinkError: URI scheme is not "file"
        at com.github.kwhat.jnativehook@2.2-SNAPSHOT/com.github.kwhat.jnativehook.GlobalScreen.<clinit>(GlobalScreen.java:91)        
        at facsimile@1.0.0/com.github.srilakshmikanthanp.facsimile.Launcher.start(Launcher.java:34)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
        at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
        ... 1 more
Exception running application com.github.srilakshmikanthanp.facsimile.Launcher

The github respository is here at line, How resolve this error ?


Solution

  • Caution

    JavaFX has keyboard handling in-built.

    Absolutely do not do this unless you need some capability not built into JavaFX.

    That said . . . if you really need to do this . . .

    I got this to work via the following steps:

    1. Created a new JavaFX project via the Idea New JavaFX project wizard.

    2. Added a dependency to JNativeHook 2.2-SNAPSHOT.

      • Snapshot was required because 2.2.1 release module-info would not work for me.

        <dependency>
            <groupId>com.github.kwhat</groupId>
            <artifactId>jnativehook</artifactId>
            <version>2.2-SNAPSHOT</version>
        </dependency>
        
      • Needed to enable the sonatype snapshot repository to access it.

        <repositories>
           <repository>
              <id>oss.sonatype.org-snapshot</id>
              <url>https://oss.sonatype.org/content/repositories/snapshots</url>
              <releases>
                  <enabled>false</enabled>
              </releases>
              <snapshots>
                  <enabled>true</enabled>
              </snapshots>
           </repository>
        </repositories>
        
    3. Added an import to the app:

      import com.github.kwhat.jnativehook.GlobalScreen;
      
    4. Added the following line to the start method of the app:

      GlobalScreen.registerNativeHook();
      
    5. Added the following line to the stop method of the app:

      GlobalScreen.unregisterNativeHook();
      
    6. Added the following line to module-info.java

      requires com.github.kwhat.jnativehook;
      
    7. Linked the app:

      • In the maven window right click on Plugins | javafx | javafx:jlink and choose "Run Maven Build".
    8. Extracted the native lib from the jar.

      • Right click on jnativehook-<longversioninfo>.jar.

      • Choose Copy Path/Reference... | Absolute path

      • In a command line window jar xvf <copied path>

      • Find the appropriate native lib for the platform.

        find . | grep dylib   
        
      • For me it is an intel mac, so the file was:

        ./com/github/kwhat/jnativehook/lib/darwin/x86_64/libJNativeHook.dylib
        
      • The name was confirmed by the output of this line added to the start method:

        System.out.println("libname: " + System.mapLibraryName("JNativeHook"));           
        
    9. Copy the native lib libJNativeHook.dylib to the lib directory created for the app created by jlink, this was:

      <projectname>/target/app/lib
      
    10. Attempt to run the app:

      cd <projectname>/target/app/bin
      ./app
      
    11. App run will fail with the following error (means that the native lib has loaded and there is another error):

      Caused by: com.github.kwhat.jnativehook.NativeHookException: Failed to enable access for assistive devices.
      
    12. Follow the instructions here to enable assistive devices for the Terminal app:

    13. Run the app again:

      ./app
      

    Now the app runs fine . . .

    Perhaps there is an easier way to do this . . . I don't know.

    If you want, you can create an issue asking the jnativehook developers to make this simpler. If you do that, definitely link back to this question for context.

    Somehow (I don't know how) the JavaFX libraries are able to be picked up when a jlink is done without having to copy the native libraries to the jlink output lib directory manually. Perhaps whatever is done for JavaFX could be done for jnativehook to make it easier to use with jlink/jpackage.