Search code examples
javafxfilechooser

JavaFX FileChooser throws NullPointerException when called


I am having a problem opening a FileChooser in JavaFX as it throws an error when i call showOpenDialog(...) method, i searched for similar threads like the link i posted below but couldn't make it work.

The error is thrown when i call the addHtml() method on EmailSenderController to add the html file text to the html editor.

Similar thread but with the exception that i didn't initialize the @FXML injected controls like he did : JavaFX FileChooser Throws Error (probably easy fix, but still confused)

Main class

package emailsender;

import configuration.ProjectConfiguration;
import emailsender.controllers.EmailSenderController;
import emailsender.services.FileReaderService;
import emailsender.services.ViewLoaderService;
import java.io.IOException;
import javafx.application.Application;
import javafx.stage.Stage;

/**
 *
 * @author DJava
 */
public class EmailSender extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException, Exception 
    {

        //Main configuration
        ProjectConfiguration configuration = new 
        ProjectConfiguration();
        configuration.setStage(primaryStage);
        configuration.setViewsPath("emailsender/views/");

        //Services
        ViewLoaderService viewloaderService = new 
        ViewLoaderService(configuration);
        FileReaderService fileReaderService = new 
        FileReaderService(configuration);

        //Controllers
        EmailSenderController sender = new 
        EmailSenderController(viewloaderService, fileReaderService);
        sender.start(configuration.getStage());
        }

        public static void main(String[] args) {
           launch(args);
        }

    }

View Loader Service

    package emailsender.services;

import configuration.ProjectConfiguration;
import java.io.IOException;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;

/**
 *
 * @DJava
 */
public class ViewLoaderService {

    ProjectConfiguration configuration;

    public ViewLoaderService(ProjectConfiguration configuration) {

        this.configuration = configuration;
    }

    public Scene loadViewOnScene(String view) throws IOException {

        Parent root = FXMLLoader.load(getClass().getClassLoader().getResource(this.configuration.getViewsPath() + view + ".fxml"));
        return new Scene(root);
    }

}

Controller

package emailsender.controllers;

import emailsender.services.FileReaderService;
import javafx.application.Application;
import javafx.stage.Stage;
import emailsender.services.ViewLoaderService;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.scene.web.HTMLEditor;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;

/**
 *
 * @author DJava
 */
public class EmailSenderController extends Application {

    private ViewLoaderService viewLoaderService;
    private FileReaderService fileReaderService;
    private WebEngine webEngine;

    //Header
    //EmaiLSender menu
    @FXML
    public MenuItem newListMenuItem;
    @FXML
    public MenuItem preferencesMenuItem;
    @FXML
    public MenuItem closeMenuItem;

    //Help menu
    @FXML
    public MenuItem aboutMenuItem;

    //Email tab
    @FXML
    public TextField subjectTextField;
    @FXML
    public ComboBox destinationListComboBox;
    @FXML
    public Button addHtmlButton;
    @FXML
    public HTMLEditor messageEditor;
    @FXML
    public ProgressIndicator sendTaskProgressIndicator;

    //Preview tab
    @FXML
    public WebView previewWebView;

    //Footer
    @FXML
    public Button sendButton;

    public EmailSenderController() {
    }

    public EmailSenderController(ViewLoaderService viewLoaderService, 
    FileReaderService fileReaderService) {

        this.viewLoaderService = viewLoaderService;
        this.fileReaderService = fileReaderService;
    }

    @FXML
    public void addHtml() {

    this.messageEditor.setHtmlText
    (this.fileReaderService.getFileText()); // Here is the problem ..........
    }

    @FXML
    public void previewEmail() {

        this.webEngine = this.previewWebView.getEngine();
        this.webEngine.loadContent(this.messageEditor.getHtmlText());
    }

    @FXML
    public void sendEmail() {

        this.sendTaskProgressIndicator.setProgress(0.3D);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        primaryStage.setTitle("EmailSender");
        primaryStage.setScene(viewLoaderService.loadViewOnScene("EmailSender"));
        primaryStage.setResizable(false);
        primaryStage.show();
    }

}

File Reader Service

package emailsender.services;

import configuration.ProjectConfiguration;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import javafx.stage.FileChooser;

/**
 *
 * @author DJava
 */
public class FileReaderService {

    private final ProjectConfiguration configuration;
    private final FileChooser fileChooser;

    public FileReaderService(ProjectConfiguration configuration) {

        this.configuration = configuration;
        this.fileChooser = new FileChooser();
    }

    public String getFileText() {

        String text = "";

        try {

            BufferedReader reader = new BufferedReader(new FileReader(this.fileChooser.showOpenDialog(this.configuration.getStage())));
            String line;
            while ((line = reader.readLine()) != null) {

                text += line + "\n";
            }

        } catch (IOException event) {
            event.printStackTrace();
        }
        System.out.println(text);
        return text;
    }

}

Error

    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.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.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)
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:1771)
    ... 49 more
Caused by: java.lang.NullPointerException
    at emailsender.controllers.EmailSenderController.addHtml(EmailSenderController.java:77)
    ... 59 more

Solution

  • The problem is the controller that executes the addHtml method and the one you initially create is not the same. When you load the EmailSender, the FXMLLoader uses the empty constructor to create a new controller. Hence the constructor which sets the attributes is not called and you end up with null attributes.

    To get the correct controller, use the loader's getController method and set the attributes with appropriate set methods:

    FXMLLoader loader = new FXMLLoader("fxml file") ;
    loader.load();
    EmailSenderController controller = (EmailSenderController)  loader .getController() ;
    controller.setFileReadService(frs) ; // frs created elsewhere
    

    On another note, your class architecture is a bit weird. You should only have one Application class. A controller should never be an application class. Its job is to control a view, not to run an entire window. As a suggestion, change your configuration to load the views and controllers as shown above. The controller attributes must be set right after loading the views.

    Then in your EmailSender start method, simply load the view you want to be shown first before the end of the method.

    EDIT

    The reason your ViewLoaderService currently works is the way you set up your program, and illustrates why a controller shouldn't be an application. You create an instance of the controller with attributes. Then you instruct the controller to load the view (coincidentally the very view it represents). On the load of the view a new instance of the controller is created with its empty constructor (hence the null values). This new controller then acts as the controller for the displayed view, not the one you created at the start of the application.