scalajarfxmlscalafxonejar

Issues with reading xml file after creating jar


We are building an application using ScalaFX. When I run the project in IntelliJIDEA, everything works fine. However, when I create jar file and try to execute it, I am getting errors in reading some xml file. I tried various solutions posted in SO, but with no use.

package com.app.adt

import scalafx.application.JFXApp
import scalafx.Includes._
import scalafx.scene.Scene
import scala.reflect.runtime.universe.typeOf
import scalafxml.core.{FXMLView, DependenciesByType}

object App extends JFXApp {

  val root = FXMLView(getClass.getResource("/com/app/adt/Home.fxml"),
    new DependenciesByType(Map(
      typeOf[TestDependency] -> new TestDependency("ADT"))))

  stage = new JFXApp.PrimaryStage() {
    title = "ADT"
    scene = new Scene(root)
  }
}

The xml file(Home.fxml) is placed in com/app/adt package. I am creating the jar file using sbt-one-jar.

I have tried different combinations of path, but alwasys gives the same error.

Error Stack:

Caused by: javafx.fxml.LoadException:
file:/adt-app_2.11-1.3-SNAPSHOT-one-jar.jar!/main/adt-app_2.11-1.3-S
NAPSHOT.jar!/com/app/adt/Home.fxml

        at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2611)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2589)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2435)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2403)
        at scalafxml.core.FXMLView$.apply(FXMLView.scala:17)

Jar Structure:

adt-app_2.11-1.3-SNAPSHOT-one-jar.jar
   |
   main
       |
       adt-app_2.11-1.3-SNAPSHOT.jar
            |
             com\app\adt
                       |
                       App.scala
                       Home.fxml

Also, I have tried with sbt-assembly instead of sbt-one-jar. But , still getting the same error. :(

Tried with below answers in SO:

Q1

Q2


Solution

  • The real problem is rather tricky. Firstly, one needs to realize that JAR is an archive (e.g. similar to ZIP) and archives are regular files. Thus the archive itself is located somewhere in the file system, hence, it is accessible via URL.

    On the contrary, the "subfiles" (entries) are just data-block within the archive. Neither the operating system nor the JVM knows that this particular file is an archive therefore they treat is as a regular file.

    If you're interested in deeper archive handling, try to figure out how ZipFile works. JAR is basically ZIP so you're able to apply this class to it.

    Java provides Class.getResourceAsStream methods that enables the programmer to read files as streams. This solution is obviously useless in this particular example since the ScalaFX method expects the File instead.

    So basically you have three options

    1. Use the stream API in order to duplicate the XML into temporary file, than pass this file to the method.
    2. Deploy your resources separately in a way they remain regular files.
    3. Re-implement JavaFX in order to accept streams (this should probably happen anyway)