javajavafxjarfxyz3d

FileSystemNotFoundException while running JAR (Fxyz3d library)


I built a quite fat JavaFX application (the JAR is about 128 MB) and I got no problem in running in through IntelliJ. But when I run it from the terminal my 3D model loaders (Fxyz3d library) launch this exception.

Exception in thread "JavaFX Application Thread" java.nio.file.FileSystemNotFoundException
    at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:172)
    at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:158)
    at java.base/java.nio.file.Path.of(Path.java:208)
    at java.base/java.nio.file.Paths.get(Paths.java:98)
    at org.fxyz3d.importers.obj.ObjImporter.read(ObjImporter.java:115)
    at org.fxyz3d.importers.obj.ObjImporter.loadAsPoly(ObjImporter.java:102)
    at org.fxyz3d.importers.Importer3D.loadIncludingAnimation(Importer3D.java:160)
    at org.fxyz3d.importers.Importer3D.loadAsPoly(Importer3D.java:80)
    at it.polimi.ingsw.PSP50.View.GUI.GuiView.lambda$startingGame$1(GuiView.java:201)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96) 

This is thrown only for the 3D-object Loader from the Fxyz3d library, not for my other normal FXML loaders. I'm using the same way to get the files from my src/main/resources folder, that is getClass().getResource. So is this really a path issue? or is it a library issue? In IntelliJ there's no problem at all instead, it all works fine. Here it's the part of the code that doesn't work:

Model3D boardModel = Importer3D.loadAsPoly(getClass().getResource("/boardcliff2.obj"));
 

If anyone has encountered something like this before and knows what it going on, help would be really appreciated


Solution

  • José Pereda opened an issue for this which has since been fixed. As of now (14 Aug 2020), the latest version of FXyz is 0.5.2 which does not include the fix for this issue. You can continue to use the workaround shown in this answer, build the library yourself from the most recent commit, or wait for the library's next release.


    This appears to be an issue with the implementation. It attempts to convert the URL into a Path but the necessary FileSystem does not exist1. The best workaround probably is to extract the resource into a temporary file and then import the object from said file. That way the URL will have a file: protocol and the conversion to Path will work (the default FileSystem always exists). Here's an example of how you could extract the resource:

    // Note: 'Path' is 'java.nio.file.Path', not 'javafx.scene.shape.Path'
    public static Path copyToTempFile(URL url, String suffix) throws IOException {
      // 'suffix' will default to ".tmp" if null
      Path tempFile = Files.createTempFile(null, suffix);
      try (InputStream in = url.openStream();
           OutputStream out = Files.newOutputStream(tempFile)) {
        in.transferTo(out); // 'transferTo' method added in Java 9
      }
      return tempFile;
    }
    

    Then you can use the resulting Path to import the 3D object:

    Path tempFile = copyToTempFile(getClass().getResource("/boardcliff2.obj"), ".obj");
    Model3D boardModel = Importer3D.loadAsPoly(tempFile.toUri().toURL());
    

    If desired, you can delete the temporary file when finished with it.


    1. When embedded in a JAR file a resource's URL has the jar: protocol. This means a FileSystem from the ZIP FileSystemProvider, open to the specific ZIP/JAR file, must exist in order for the conversion to Path to work. This issue would not occur if the implementation simply used URL#openStream(), which accesses the JAR entry via a different mechanism.