0

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
DJava
  • 11
  • 5
  • Can you test that the `fileReaderService` attribute is not null in the `addHtml` method? I suspect the way the controller is constructed may be the culprit. A simple `if(fileReaderService==null){System.out.println("fileReaderService is null");} ` will do – TM00 Mar 05 '18 at 20:23
  • @TM00 Indeed it is null, but i need to call it as that method opens the filechooser, kind of weird the fileReaderService is null. – DJava Mar 05 '18 at 20:27
  • `FXMLLoader` uses the constructor that does not take parameters to create the controller instance if the `fx:controller` attribute is present. Probably this is what happens and `fileReaderService` is null... Take a look at this question for ways to properly pass parameters: https://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml – fabian Mar 05 '18 at 20:32
  • Then it's not the FileChooser that is the issue. Your issue lies with the way you create your controller. Have you used the getController method of the FXMLLoader when you load the fxml file to set the attribute? If not, please add the code showing how you load your views. – TM00 Mar 05 '18 at 20:36
  • @fabian but the other ViewLoaderService is working properly, what you say means that both classes passed to the controller should not work but one of them does. – DJava Mar 05 '18 at 20:37
  • @TM00 Updated question with ViewLoaderSerivce code, anyways that class is getting the job done in opening the views. – DJava Mar 05 '18 at 20:40
  • You create one instance of `EmailSenderController` in the `EmailSender.start` method and `FXMLLoader` creates another one. The `ViewLoaderService` code you just posted supports that assumption. – fabian Mar 05 '18 at 20:42
  • @fabian but FXMLLoader just loads the fxml files and returns a scene with loadViewOnScene, there is only one instance of EmailSenderController which is in EmailSender start method. – DJava Mar 05 '18 at 20:49
  • Not true, but if you don't trust my word, use a debugger and set break points inside the consturctors or simply add `System.out.println("something");` to both constructors... Or simply comment out the constructor that does not take parameters and wait for `FXMLLoader` to throw an exception. – fabian Mar 05 '18 at 21:00

1 Answers1

0

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.

TM00
  • 1,330
  • 1
  • 14
  • 26
  • So at the end: i created an instance in EmailSender and FXMLLoader created another one, i tried to pass 2 references to a constructor that is never called so i need to pass them to the controller FXMLLoader created using loader.getController() to get the reference and then use setters and getters to pass the services. anyways i will change the architecture based on what you said. – DJava Mar 05 '18 at 21:17
  • Correct :) if your are happy with the answer you should consider accepting it. – TM00 Mar 05 '18 at 21:27