-3

I get this error message, when i try calling a method "undo" in the object invoker of class Invoker:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at view.MainWindow.lambda$2(MainWindow.java:112)
    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.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.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.control.MenuItem.fire(MenuItem.java:462)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1405)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(ContextMenuContent.java:1358)
    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.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:394)
    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$353(GlassViewEventHandler.java:432)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
    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$147(WinApplication.java:177)
    at java.lang.Thread.run(Unknown Source)

I'm guessing the problem has to do with the javafx thread? I have tried using Platform.runLater, but the problem persisted.

I'm launching a JavaFX window through this:

public static void main(String[] args) {
    Board board = new Board();
    Invoker invoker = new Invoker();
    TileBoardController tbc = new TileBoardController();
    MainWindow mainWindow = new MainWindow();
    com.sun.javafx.application.PlatformImpl.startup(()->{});
    mainWindow.launch(board, tbc.CreateTiles(board), invoker);
    com.sun.javafx.application.PlatformImpl.exit();
}

And the event that crashes the program lies in a menu bar in that "MainWindow" class:

menuOptions.setOnAction(e -> invoker.undo());

Strange thing is, the invoker class works, and is successfully used in another part of the same "MainWindow" object:

tileBoardStatic[rank][file].setOnAction( e -> this.onTileClick(finalRank, finalFile));

private void onTileClick(int rank, int file) {

        invoker.storeAndExecute(new MovePiece(board, activeTile.getSquare(), tileBoardStatic[rank][file].getSquare()));

}

I have tried invoker.undo() in that same method, and that works. Anyone know what the problem is here?

Here is all the code in MainWindow:

public class MainWindow extends Application{

    public static final int TILE_SIZE = 100;
    public static final int WIDTH = 8;
    public static final int HEIGHT = 8;

    private Invoker invoker;
    private static Group tileGroup = null;
    private static Tile[][] tileBoardStatic = null;
    private Tile activeTile;
    private Board board;


    public void launch(Board board, Tile[][] tileBoard, Invoker invoker) {
        this.board = board;
        this.tileBoardStatic = tileBoard;
        this.invoker = invoker;
        tileGroup = new Group();
        for (int rank=0; rank<tileBoard.length; rank++) {
            for(int file=0; file<tileBoard[rank].length; file++) {
                final int finalRank = rank;
                final int finalFile = file;
                tileBoardStatic[rank][file].setOnAction( e -> this.onTileClick(finalRank, finalFile));
                tileGroup.getChildren().add(tileBoard[rank][file]);
            }
        }
        Application.launch(MainWindow.class);
    }


    //Handles click event for Tiles
    private void onTileClick(int rank, int file) {


        if (activeTile == null) {
            if (tileBoardStatic[rank][file].getSquare().getPiece() == null) 
                System.out.println("No piece in tile");
            else {
                if(tileBoardStatic[rank][file].getSquare().getPiece().isWhite() != board.getTurnIsWhite()) {
                    System.out.println("Not your turn");
                } else {
                    activeTile = tileBoardStatic[rank][file];
                    activeTile.getStyleClass().add("tile-clicked");
                }
            }
        } 
        else {
            activeTile.getStyleClass().removeAll("tile-clicked");
            //Creates and sends command to invoker
            invoker.storeAndExecute(new MovePiece(board, activeTile.getSquare(), tileBoardStatic[rank][file].getSquare()));

            //Repaints squares
            activeTile.paintSquare();
            tileBoardStatic[rank][file].paintSquare();

            activeTile = null;
        }



    }


    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = new BorderPane();
        primaryStage.getIcons().add(new Image(MainWindow.class.getResourceAsStream("resources/chess.png")));
        root.getChildren().addAll(tileGroup);
        root.setPrefSize(WIDTH * TILE_SIZE, HEIGHT * TILE_SIZE);

        Scene scene = new Scene(root);
        primaryStage.setTitle("MainWindow");
        primaryStage.setScene(scene);
        scene.getStylesheets().add(MainWindow.class.getResource("resources/stylesheet.css").toExternalForm());



        //Menu bar
        MenuBar menuBar = new MenuBar();
        Menu gameMenu = new Menu("Game");
        menuBar.getMenus().add(gameMenu);
        MenuItem menuExit = new MenuItem("Exit game");
        menuExit.setOnAction(e -> exit());
        gameMenu.getItems().add(menuExit);
        Menu menuOptions = new Menu("Edit");
        MenuItem menuUndo = new MenuItem("Undo");
        menuOptions.setOnAction(e -> invoker.undo());
        menuOptions.getItems().add(menuUndo);
        menuBar.getMenus().add(menuOptions);



        root.setTop(menuBar);
        primaryStage.show();

    }
    private void exit() {
        Platform.exit();
        System.exit(0);
    }

}
Slaw
  • 37,820
  • 8
  • 53
  • 80
Sletten
  • 9
  • 5
  • Possible duplicate of [What is a NullPointerException, and how do I fix it?](https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it) – Robert Columbia Mar 18 '19 at 16:35

2 Answers2

1

You're launching your application incorrectly. Your main method initializes some objects, including an instance of MainWindow. You then call a method on said instance of MainWindow which sets some fields then calls Application.launch. This is where the problem manifests: The call to Application.launch results in a new, separate instance of MainWindow to be created. It is this instance that is used by the JavaFX runtime.

Since the start(Stage) method is invoked on an instance other than the one you called launch(Board,Tile[][],Invoker) on, none of the instance fields are set properly; this ultimately leads to your NullPointerException. Your solution to make invoker static merely covers up a symptom of the larger problem—initializing objects outside the typical life-cycle.

Try changing your main method to the following:

public static void main(String[] args) {
    Application.launch(MainWindow.class, args);
}

Note: If the main method is inside the Application subclss, you can use launch(args) instead.

Then move all the initializing logic to ether Application#init() or Application#start(Stage) (or at least call the appropriate method(s) from inside one of them). Any initializing that requires the JavaFX Application Thread should be done in the start method; the init method is called on the JavaFX-Launcher thread.

Also, using Application.launch will implicitly startup the JavaFX runtime. There's no reason for you to manually call PlatformImpl.startup. You should also avoid using internal code (e.g. com.sun.*) unless there's no other option. If you find yourself needing to use internal code, take a step back and check if you're perhaps doing something wrong.

Slaw
  • 37,820
  • 8
  • 53
  • 80
0

Fixed! All i had to do was to set my invoker object to static, of course. Stupid miss.

private static Invoker invoker;
Sletten
  • 9
  • 5
  • A comment more or less for others who might read this thread: I would go with the answer below from Slaw, this is the correct way to initialize a JavaFX app. – Robert Rohm Mar 22 '19 at 07:07