scalascalafx

ScalaFX @sfxml Annotation Problems


I am writing a Scalafx application using FXML for the views with the scalafxml library. I am having a ton of trouble getting the classes to compile when using the @sfxml annotation on controller classes. When using them, I keep getting NoSuchMethodExceptions on my constructors as if I have no default constructor but this is definitely not the case. I have been studying this so much that I can only conclude that the problem must be with the library itself. I have followed any information on it as best I know how.

The controller is for creating a form for the user. Since the "IllegalArgumentException" will be thrown if I try to set the root node's scene twice, I use the root node's id in my constructor for purposes of avoiding the exception.

Any help is much appreciated. Thanks.

My code:

@sfxml
class ToolbarController(private val rootLayout: BorderPane) {

  val parent = calcParent

  import javafx.application.Platform
  def newForm = {
    new JFXPanel()
    Platform.runLater(new Runnable() {
      override def run() {

        val dialogStage = new Stage {
            scene = new Scene(parent) {
              stylesheets = List(getClass.getResource("/scala/css/form.css").toExternalForm)
            }
        }
        dialogStage.show()
        }
    })
  }


  def calcParent: Parent = {
    val resource = getClass.getResource("scala/form.fxml")
    if(rootLayout.scene == null)
      FXMLView(resource, NoDependencyResolver)
    else rootLayout.getParent
  }
}

The FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane id="rootLayout"maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="FormController">
   <center>...

And my stack trace:

Exception in Application start method
Workaround until RT-13281 is implemented: keep toolkit alive
[error] (run-main-0) java.lang.RuntimeException: Exception in Application start method
java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152(LauncherImpl.java:182)
    at com.sun.javafx.application.LauncherImpl$$Lambda$11/1268584418.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javafx.fxml.LoadException: 
/Users/patrickslagle/scala/MyApps/PattyCakes/target/scala-2.11/classes/scala/calendar.fxml:7

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2605)
    at javafx.fxml.FXMLLoader.access$700(FXMLLoader.java:104)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:928)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:967)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:216)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:740)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2711)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2531)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3218)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3179)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3152)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3128)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3108)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3101)
    at Calendar$.delayedEndpoint$Calendar$1(Calendar.scala:15)
    at Calendar$delayedInit$body.apply(Calendar.scala:9)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scalafx.application.JFXApp$$anonfun$init$1.apply(JFXApp.scala:297)
    at scalafx.application.JFXApp$$anonfun$init$1.apply(JFXApp.scala:297)
    at scala.collection.immutable.List.foreach(List.scala:383)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
    at scala.collection.mutable.ListBuffer.foreach(ListBuffer.scala:45)
    at scalafx.application.JFXApp$class.init(JFXApp.scala:297)
    at Calendar$.init(Calendar.scala:9)
    at scalafx.application.AppHelper.start(AppHelper.scala:33)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
    at com.sun.javafx.application.LauncherImpl$$Lambda$62/2116169040.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl$$Lambda$58/1458556428.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
    at com.sun.javafx.application.PlatformImpl$$Lambda$60/778909025.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
    at com.sun.javafx.application.PlatformImpl$$Lambda$59/1397409682.run(Unknown Source)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
Caused by: java.lang.InstantiationException: controller.ToolbarController
    at java.lang.Class.newInstance(Class.java:427)
    at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:51)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:923)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:967)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:216)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:740)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2711)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2531)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3218)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3179)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3152)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3128)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3108)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3101)
    at Calendar$.delayedEndpoint$Calendar$1(Calendar.scala:15)
    at Calendar$delayedInit$body.apply(Calendar.scala:9)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scalafx.application.JFXApp$$anonfun$init$1.apply(JFXApp.scala:297)
    at scalafx.application.JFXApp$$anonfun$init$1.apply(JFXApp.scala:297)
    at scala.collection.immutable.List.foreach(List.scala:383)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
    at scala.collection.mutable.ListBuffer.foreach(ListBuffer.scala:45)
    at scalafx.application.JFXApp$class.init(JFXApp.scala:297)
    at Calendar$.init(Calendar.scala:9)
    at scalafx.application.AppHelper.start(AppHelper.scala:33)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
    at com.sun.javafx.application.LauncherImpl$$Lambda$62/2116169040.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl$$Lambda$58/1458556428.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
    at com.sun.javafx.application.PlatformImpl$$Lambda$60/778909025.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
    at com.sun.javafx.application.PlatformImpl$$Lambda$59/1397409682.run(Unknown Source)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
Caused by: java.lang.NoSuchMethodException: controller.ToolbarController.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.newInstance(Class.java:412)
    at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:51)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:923)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:967)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:216)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:740)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2711)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2531)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3218)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3179)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3152)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3128)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3108)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3101)
    at Calendar$.delayedEndpoint$Calendar$1(Calendar.scala:15)
    at Calendar$delayedInit$body.apply(Calendar.scala:9)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scalafx.application.JFXApp$$anonfun$init$1.apply(JFXApp.scala:297)
    at scalafx.application.JFXApp$$anonfun$init$1.apply(JFXApp.scala:297)
    at scala.collection.immutable.List.foreach(List.scala:383)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
    at scala.collection.mutable.ListBuffer.foreach(ListBuffer.scala:45)
    at scalafx.application.JFXApp$class.init(JFXApp.scala:297)
    at Calendar$.init(Calendar.scala:9)
    at scalafx.application.AppHelper.start(AppHelper.scala:33)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
    at com.sun.javafx.application.LauncherImpl$$Lambda$62/2116169040.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl$$Lambda$58/1458556428.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
    at com.sun.javafx.application.PlatformImpl$$Lambda$60/778909025.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
    at com.sun.javafx.application.PlatformImpl$$Lambda$59/1397409682.run(Unknown Source)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
[trace] Stack trace suppressed: run last pattycakes/compile:run for the full output.
java.lang.RuntimeException: Nonzero exit code: 1
    at scala.sys.package$.error(package.scala:27)
[trace] Stack trace suppressed: run last pattycakes/compile:run for the full output.
[error] (pattycakes/compile:run) Nonzero exit code: 1
[error] Total time: 46 s, completed May 26, 2016 4:18:42 PM

Solution

  • You should not instantiate a class annotated with @fxml directly. This class is modified by the SFXML compiler macro. It is not shown in the code you posted but somewhere you must instantiate ToolbarController to call newForm. Posting Minimal, Complete, and Verifiable example is very important.

    Attempting to directly instantiate ToolbarController is the most likely cause of the NoSuchMethodExceptions as ToolbarController constructor is modified by the macro. You need to move methods newForm and calcParent outside of ToolbarController. Only instantiate ToolbarController using FXMLView(resource, ...). You also need to add a reference to ToolbarControllerin the FXML file in the top pane:

    <BorderPane ... fx:controller="mypackage.ToolbarController"> ...
    

    With that the FXMLView can instantiate ToolbarController using only reference to the FXML file. For instance, the instantiation could look like this:

    import java.io.IOException
    import scalafx.Includes._
    import scalafx.application.JFXApp
    import scalafx.application.JFXApp.PrimaryStage
    import scalafx.scene.Scene
    import scalafxml.core.{NoDependencyResolver, FXMLView}
    
    object MyFXMLDemo extends JFXApp {
    
      val resource = getClass.getResource("/scala/css/form.css")
      if (resource == null) {
        throw new IOException("Cannot load resource: /scala/css/form.css")
      }
    
      val root = FXMLView(resource, NoDependencyResolver)
    
      stage = new PrimaryStage() {
        scene = new Scene(root)
      }
    
    }
    

    Please take look carefully at the example at https://github.com/vigoo/scalafxml-unit-converter-example.