JDK-8186429 : SceneBuilder can't open FXML files with event handler attributes but no controller specified
  • Type: Bug
  • Component: javafx
  • Sub-Component: fxml
  • Affected Version: 9
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: x86
  • Submitted: 2017-08-17
  • Updated: 2018-09-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Tested on:
Darwin MacBook-Pro 16.7.0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 x86_64

Fails on Windows/Linux as well.

A DESCRIPTION OF THE PROBLEM :
Using SceneBuilder to open FXML files that include event handler attributes but no controller specified, fails under Java 9.

The onAction attribute is used to specify a controller method handler within the FXML file, and when loading the file, FXMLLoader.processEventHandlerAttributes() is called to resolve the method event handlers.

SceneBuilder performs a static load, but the staticLoad field is always false. So FXMLLoader.getControllerMethodHandle() is called, and when the FXML file doesn't specify a controller FXMLLoader.constructLoadException() is called an a LoadException is thrown., 

This worked fine under Java 8, as there was a FXMLLoader.impl_setStaticLoad() method, that allowed SceneBuilder to set the staticLoad field to true and skip the process in case of event handler attributes.

This issue https://bugs.openjdk.java.net/browse/JDK-8159005 was solved concluding that there was no need for a public setStaticLoad method, and precisely this is a use case that proves that such method is required, at least by SceneBuilder.


REGRESSION.  Last worked in version 8u144

ADDITIONAL REGRESSION INFORMATION: 
The test works with the latest SceneBuilder version for Java 8:

http://hg.openjdk.java.net/openjfx/8u-dev/rt/file/9072b92a4353/apps/scenebuilder

java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Fork SceneBuilder from the OpenJFX repo 
http://hg.openjdk.java.net/openjfx/9-dev/rt/file/c734b008e3e8/apps/scenebuilder

2. Build and run SceneBuilder with Java 9

3. Open the attached simple FXML file, that includes an AnchorPane and a  Button with an event handler method. A dialog shows that the file can't be opened due to java.io.IOException: javafx.fxml.LoadException: No controller specified.




EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The expected result is SceneBuilder opening the FXML file and loading it in the editor
ACTUAL -
There is a dialog saying that "Could not open 'testStaticLoad.fxml'".

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.io.IOException: javafx.fxml.LoadException: No controller specified.
/private/tmp/testStaticLoad.fxml:8

	at com.oracle.javafx.scenebuilder.kit.fxom.FXOMLoader.load(FXOMLoader.java:91)
	at com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument.<init>(FXOMDocument.java:82)
	at com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument.<init>(FXOMDocument.java:97)
	at com.oracle.javafx.scenebuilder.kit.editor.EditorController.updateFxomDocument(EditorController.java:2384)
	at com.oracle.javafx.scenebuilder.kit.editor.EditorController.setFxmlTextAndLocation(EditorController.java:664)
	at com.oracle.javafx.scenebuilder.app.DocumentWindowController.loadFromFile(DocumentWindowController.java:381)
	at com.oracle.javafx.scenebuilder.app.SceneBuilderApp.performOpenFiles(SceneBuilderApp.java:549)
	at com.oracle.javafx.scenebuilder.app.SceneBuilderApp.performOpenFile(SceneBuilderApp.java:495)
	at com.oracle.javafx.scenebuilder.app.SceneBuilderApp.performControlAction(SceneBuilderApp.java:192)
	at com.oracle.javafx.scenebuilder.app.menubar.MenuBarController$ApplicationControlActionController.perform(MenuBarController.java:1670)
	at com.oracle.javafx.scenebuilder.app.menubar.MenuBarController.handleOnActionMenu(MenuBarController.java:1114)
	at com.oracle.javafx.scenebuilder.app.menubar.MenuBarController.lambda$new$3(MenuBarController.java:1108)
	at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
	at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
	at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
	at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
	at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
	at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
	at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
	at javafx.controls/javafx.scene.control.MenuItem.fire(MenuItem.java:465)
	at javafx.controls/com.sun.javafx.scene.control.GlobalMenuAdapter.lambda$bindMenuItemProperties$2(GlobalMenuAdapter.java:153)
	at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
	at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
	at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
	at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
	at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
	at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
	at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
	at javafx.controls/javafx.scene.control.MenuItem.fire(MenuItem.java:465)
	at javafx.graphics/com.sun.javafx.tk.quantum.GlassSystemMenu$1.action(GlassSystemMenu.java:234)
Caused by: javafx.fxml.LoadException: No controller specified.
/private/tmp/testStaticLoad.fxml:8

	at javafx.fxml/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2621)
	at javafx.fxml/javafx.fxml.FXMLLoader.access$100(FXMLLoader.java:105)
	at javafx.fxml/javafx.fxml.FXMLLoader$Element.getControllerMethodHandle(FXMLLoader.java:565)
	at javafx.fxml/javafx.fxml.FXMLLoader$Element.processEventHandlerAttributes(FXMLLoader.java:607)
	at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:778)
	at javafx.fxml/javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2838)
	at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2557)
	at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:2450)
	at com.oracle.javafx.scenebuilder.kit.fxom.FXOMLoader.load(FXOMLoader.java:88)
	... 31 more

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
testStaticLoad.fxml



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

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
   <children>
      <Button layoutX="200.0" layoutY="200.0" onAction="#onActionHandler" text="Button" />
   </children>
</AnchorPane>
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The workaround is using reflection to access the protected method FXMLLoader.setStaticLoad, and call it from SceneBuilder (kit  Deprecation.setStaticLoad()).


Comments
One option is to make this API public. Webrev is here: http://cr.openjdk.java.net/~jgiles/8186429/
23-08-2017