0

I am simply trying to draw a line from another class in my JavaFX project (as a start for some homework). But I am running into this InvocationTargetException along with a NullPointerException exception.

"C:\Program Files\Java\jdk1.8.0_144\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.2.4\lib\idea_rt.jar=58260:C:\Program Files\JetBrains\IntelliJ IDEA 2017.2.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;C:\Users\caspe\Desktop\myFinalMiniProject\out\production\myFinalMiniProject" sample.Main
drawing maze..
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1774)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Node.fireEvent(Node.java:8413)
    at javafx.scene.control.Button.fire(Button.java:185)
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:381)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:417)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1769)
    ... 48 more
Caused by: java.lang.NullPointerException
    at sample.KrushkalA.drawMaze(KrushkalA.java:23)
    at sample.Controller.doKruskal(Controller.java:13)
    ... 58 more

I have tried this answer from James_D, but it still won't run after trying his solution.

Main:

public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 350, 350));
        primaryStage.show();
    }

FXML:

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

<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.Pane?>

<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="350.0"
      prefWidth="350.0" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
    <children>
        <Button fx:id="buttonKruskal" layoutX="14.0" layoutY="2.0" mnemonicParsing="false" onAction="#doKruskal"
                prefHeight="20.0" prefWidth="100.0" text="Kruskal"/>
        <Button fx:id="buttonRecursiveD" layoutX="236.0" layoutY="2.0" mnemonicParsing="false" onAction="#doRecursiveD"
                prefHeight="20.0" prefWidth="100.0" text="RecursiveD"/>
        <Button fx:id="buttonRecursiveB" layoutX="125.0" layoutY="2.0" mnemonicParsing="false" onAction="#doRecursiveB"
                prefHeight="20.0" prefWidth="100.0" text="RecursiveB"/>
        <Canvas height="300.0" layoutX="25.0" layoutY="36.0" width="300.0"/>
    </children>
</Pane>

Controller:

public void doKruskal(ActionEvent event) {
    System.out.println("drawing maze..");
    KrushkalA kruAlg = new KrushkalA();

    kruAlg.drawMaze();

    // kruAlg.solveMaze();

}

KrushkalA class:

    @FXML
    Canvas canvas;
    GraphicsContext gc;


    public void drawMaze(){
        gc = canvas.getGraphicsContext2D();
        gc.strokeLine(0,0,300,50);
    }

Perhaps this is what I'm trying to do; to run a method in the KrushkalA class from the controller, which draws a line or just does-something within the canvas when pressing a button.

Is that the wrong way to do it? I can run the code that causes the exception in my Controller class, but not from any other class? Example code will be much appreciated.

Casper Hansen
  • 443
  • 3
  • 10
  • 1
    `@FXML`-annotated fields are only initialized in the controller. `kruAlg` is not the controller, so `canvas` is never initialized. Not really clear what you intend, or why you are delegating controller-type methods to other objects. Why don't you just put the `drawMaze()` method in the controller, which is where it naturally belongs? – James_D Apr 05 '18 at 17:47
  • It's also unclear what the linked question has to do with this question. The linked question doesn't even have a null pointer exception. – James_D Apr 05 '18 at 17:48
  • Okay, so the core of my assignement is this: implement at least one of the algorithms (Kruskal, Recursive backtracking, Recursive Division) into a JavaFX program. That is why I want to take it outside the controller, which I thought were possible, but maybe not? – Casper Hansen Apr 05 '18 at 21:57
  • Sorry about the other post, I just searched for many posts, but couldn't find a solution, hence why I am posting this. Maybe I misunderstood how the controller is supposed to be used. I could, of course, run all three algorithms in the controller, but I just thought it would be nice with different classes. – Casper Hansen Apr 05 '18 at 21:59
  • The implementation of an algorithm shouldn't really have anything to do with the UI though (ie the implementation of an algorithm should be independent from the visualization of it). So either have your `drawMaze()` method return some object that describes the result of performing the algorithm (I am not familiar with these algorithms), or perhaps have callbacks from the algorithm that you can hook into the controller. – James_D Apr 06 '18 at 00:08
  • If you really want `drawMaze()` to draw on the canvas, inject the canvas into the controller (in the same way as your other FXML elements) and then just pass the canvas to the `drawMaze()` method. – James_D Apr 06 '18 at 00:09

1 Answers1

1

The reason your code doesn't work is that you are expecting the canvas to be injected from the FXML into your KrushkalA instance. This simply isn't how this works: @FXML-injection is done by the FXMLLoader when the FXML file is loaded, and any @FXML-annotated fields in the controller instance are initialized with corresponding elements in the FXML file with matching fx:id. Even if you add a fx:id attribute to the canvas defined in the FXML, there's no way that the FXMLLoader can possibly know about the KrushkalA object (which isn't even created at that point, though it wouldn't help if it were).

The whole premise here seems flawed anyway. It's generally a bad idea to allow references to your UI elements to escape from the controller. Suppose at some point in the future you decide you don't want to draw this with a Canvas any more (e.g. you might decide to add Lines, or some other node, to a Pane). In this case, you really should only need to modify the FXML and controller class; if you have allowed your KrushkalA class access to the canvas, then you would need to change that too, and you'd need to look at whether that class passed the canvas reference to anyone else. Maintaining this gets much more difficult if you break encapsulation in this way.

If your KrushkalA class is really just implementing the algorithm, it shouldn't have anything to do with the UI. I think here you are creating a maze (or, more abstractly, a spanning tree for a graph), so your drawMaze() method should just compute the maze and return the result. At it's core, a maze is just a collection of boundaries (or edges) between cells (nodes), so I think I would define a Boundary class with some simple representation of which boundary it is (maybe int x, int y, and boolean horizontal fields, or something else convenient); then you would have:

public class KrushkalA {

    public List<Boundary> drawMaze() {
        List<Boundary> maze = new ArrayList<>();
        // implementation of algorithm...
        return maze ;
    }
}

Then back in your controller, you would do:

public class MazeController {

    @FXML
    private Canvas canvas ;

    @FXML
    private void doKrushkal(ActionEvent e) {
        KrushkalA kruAlg = new KrushkalA();
        List<Boundary> maze = kruAlg.drawMaze();
        // now render maze on canvas, using data in the List<Boundary> from above
        renderMaze(maze);
    }

    private void renderMaze(List<Boundary> maze) {
        // clear canvas, iterate through boundaries in maze,
        // and draw lines in appropriate place, etc.
    }
}

And, of course, add fx:id="canvas" to the <Canvas> element in the FXML.

This approach properly manages separation of concerns and abides by the single responsibility principle: the controller only has responsibility for updating the UI, and doesn't concern itself with how the algorithm is implemented, and the KrushkalA class only has responsibility for implementing the algorithm, and doesn't care how the UI is rendered (or even if there is a UI...).


If you really wanted the algorithm class to render the maze on the canvas, which I strongly do not recommend, then of course you could just pass the canvas to it:

    @FXML
    private void doKrushkal(ActionEvent e) {
        KrushkalA kruAlg = new KrushkalA();
        kruAlg.drawMaze(canvas);
    }

and

public class KrushkalA {

    public void drawMaze(Canvas canvas) {
        // compute maze AND render it on canvas...
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Thank you for your help, it is greatly appreciated. Maybe my understanding of JavaFX is not quite there yet, but your explanation will helped me a lot. I will go the route which you do recommend! – Casper Hansen Apr 07 '18 at 06:02