NPE on #Value and #Autowired - java

I have an javafx + spring app.
The application listening serial port, reading data and shows it to UI.
The problem caused by NPE with Controller class on outputLoggerFile and serialPort on same class.
This is my configuration file with PropertySource so my environment should know about these propoperties.
SpringConfig
#Configuration
#PropertySource({"classpath:com.properties", "classpath:application.properties"})
#ComponentScan
public class SpringConfig {
#Bean
public SerialPort serialPort(#Value("${serialPort.portName}") String portName){
return new SerialPort(portName);
}
#Bean
public AnnotationMBeanExporter annotationMBeanExporter(){
AnnotationMBeanExporter annotationMBeanExporter = new AnnotationMBeanExporter();
annotationMBeanExporter.addExcludedBean("dataSource");
return annotationMBeanExporter;
}
}
This class class setting my properties to the SerialPort object, injects EventListener class and openning connection. Works fine.
ComReader
#Scope("singletone")
#Component
public class ComReader {
#Autowired
private EventListener eventListener;
#Autowired
public SerialPort serialPort;
#Value("${serialPort.baudRate}")
private int baudRate;
#Value("${serialPort.dataBits}")
private int dataBits;
#Value("${serialPort.stopBits}")
private int stopBits;
#Value("${serialPort.parity}")
private int parity;
#PostConstruct
public void init(){
try {
System.out.println("Opening port: " + serialPort.getPortName());
serialPort.openPort();
serialPort.setParams(baudRate,dataBits,stopBits,parity);
serialPort.addEventListener(eventListener, 1);
} catch (SerialPortException e) {
e.printStackTrace();
}
}
}
The problem class everything work fine except any classes/fields which i want inject here.
Controller
#org.springframework.stereotype.Controller
public class Controller {
#Value("${logger.outputFilePath}")
private String outputLoggerFile;
private SerialPort serialPort;
#Autowired
public void setSerialPort(SerialPort serialPort) {
this.serialPort = serialPort;
}
private static ObservableList<CallDetailRecord> list = FXCollections.observableArrayList();
#FXML
void initialize(){
Timer scheduler = new Timer();
scheduler.schedule(new TimerTask() {
#Override
public void run() {
if (serialPort.isOpened()) circlePortStatus.setFill(Color.GREEN); //(NPE HERE)
else circlePortStatus.setFill(Color.RED);
}
}, 5_000, 60_000);
counterCol.setCellValueFactory(new PropertyValueFactory<>("id"));
startTimeCol.setCellValueFactory(new PropertyValueFactory<>("startTime"));
stopTimeCol.setCellValueFactory(new PropertyValueFactory<>("stopTime"));
numberACol.setCellValueFactory(new PropertyValueFactory<>("numberB"));
numberBCol.setCellValueFactory(new PropertyValueFactory<>("numberA"));
rescodeCol.setCellValueFactory(new PropertyValueFactory<>("resultCode"));
subACol.setCellValueFactory(new PropertyValueFactory<>("subscriberB"));
subBCol.setCellValueFactory(new PropertyValueFactory<>("subscriberA"));
table.setItems(list);
Label webLinkLabel = new Label("Веб ресурс");
AppStart appStart = new AppStart();
webLinkLabel.setOnMouseClicked(event -> appStart.getHostServices().showDocument(getURLPropertie()));
webLink.setGraphic(webLinkLabel);
Label logsLinkLabel = new Label("Логи");
logsLinkLabel.setOnMouseClicked(event -> appStart.getHostServices().showDocument(outputLoggerFile)); //(NPE HERE)
logsLink.setGraphic(logsLinkLabel);
}
public void addCdr(CallDetailRecord cdr){
list.add(cdr);
list.sort(Comparator.comparingInt(CallDetailRecord::getId).reversed());
}
private String getURLPropertie(){
try(InputStream is = new FileInputStream(Objects.requireNonNull(getClass().getClassLoader().getResource("application.properties")).getFile())){
Properties prop = new Properties();
prop.load(is);
return prop.getProperty("url.link");
} catch (IOException e) {
e.printStackTrace();
}
return "https://google.com";
}
}
This is the code that loads and displays the FXML:
this.primaryStage = primaryStage;
Platform.setImplicitExit(false);
Parent root = FXMLLoader.load(getClass().getResource("/primal.fxml"));
primaryStage.setTitle("NIIAR");
primaryStage.getIcons().add(new Image("/icon.png"));
primaryStage.setScene(new Scene(root, 1400, 900));
createTray();
primaryStage.show();
If im trying to debug in other class that using Controller its shows the variable outputLoggerFile contains my propertie. I dont have any idea why.
sources - https://github.com/mindgame73/CDRListener-FX

The default behavior of the FXMLLoader is to create a controller by instantiating the class specified in the fx:controller attribute of the FXML file (invoking its no-argument constructor); then it injects #FXML-annotated fields into the controller, and after it parses the FXML file, it calls the initialize() method (if there is one).
Since the controller is instantiated by directly invoking its constructor, the Spring application context knows nothing about it, and can't inject any #Autowired beans into it.
To fix this, you need to set a controllerFactory on the FXMLLoader, instructing it to "create" (really retrieve) the controller instance from the Spring ApplicationContext. The controller factory is basically just a function (a #FunctionalInterface) which takes a Class<?> and produces an object. Since this is exactly the signature of one of the ApplicationContext.getBean() methods, the code for this looks like:
FXMLLoader loader = new FXMLLoader(getClass().getResource("/primal.fxml"));
loader.setControllerFactory(context::getBean);
Parent root = loader.load();
where context is the Spring ApplicationContext (you may need to jump through some hoops to get a reference to this in the method where you load your FXML file; usually just creating a field for it and annotating the field Autowired works).
I would make a couple of tweaks to the configuration of the controller class, too. By default, Spring manages beans as singleton scope. This is definitely not what you want here: if you were to load the same FXML a second time, you would need a different controller instance (as you would have a different set of UI controls). So you definitely need to scope the controller as a prototype.
Secondly, the Spring #Controller stereotype is intended for controllers in the Spring MVC sense; so I don't think it's really what you want here (though I don't think it does any harm). I would annotate the controller class as
#Component
#Scope(BeanDefinition.PROTOTYPE_SCOPE)
public class Controller { /* ... */ }

Related

reference interface by creating object in java

Is it possible that one class access the method from another class by using the interface reference??
Suppose, i have a class that is loginUIController.java, from this class i want to access method of another class that is mainController.java. But I won't access the method directly. for accessing method from mainController.java i'll use the Interface of mainController.java which is InferfaceMainController.java.
The following classes are :-
Here is the loginUIController.java
public class LoginUIController
{
InterfaceMainController mainController = null;
private void handleButtonAction(ActionEvent event) throws IOException
{
String userid = txt_username.getText();
String password = txt_password.getText();
mainController.checkLogin(userid, password);
}
}
InterfaceMainController :-
public interface InterfaceMainController {
void checkLogin(String userid, String password);
void addContact(String name, String phone, String address, String email);
}
mainController :-
public class MainController implements InterfaceMainController{
#Override
public void checkLogin(String userid, String password){
Users u = new Users();
u.setUserid(userid);
u.setPassword(password);
DBLayer db1 = new DBLayer();
try
{
if(db1.userExists(u))
{
System.out.println("login Success.");
}
else
{
System.out.println("login Failed.");
}
} catch (Exception ex) {
e.printStackTrace();
}
}
}
Now, how to access checkLogin(String userid, String password);of mainController.java from the loginUIController.java by creating the interface object of InterfaceMainController.java?
From the point of view of LoginUIController, there's no such thing as MainController. LoginUIController only knows about InterfaceMainController. That's essentially what you defined in:
InterfaceMainController mainController = null;
To work with an instance of InterfaceMainController you first need an implementation of the interface, which in your case is MainController. Then, you create an instance of the implementation:
InterfaceMainController mainController = new MainController();
Finally, you need to provide the instance to whatever needs it, in your case LoginUIController. A common way to do this is to create a constructor like so:
public class LoginUIController
{
InterfaceMainController mainController = null;
public LoginUIController(InterfaceMainController mainController) {
this.mainController = mainController
}
...
}
The above is an example of the dependency injection Elliot referenced.
There's not really a difference in how you do it. It's really the handle you have.
InterfaceMainController foo = new MainController();
foo.checkLogin("me", "my password");
You can pass foo to any method that expects an InterfaceMainController. But the code otherwise looks the same. It's only the variable type that changes.
From the code snippets, it appears you are missing one object which implements the interface InterfaceMainController (in your example this would be an instance of MainController). I can think of two options to remedy this:
Pass the object to the constructor of LoginUIController.
Use Inversion of Control (IOC) / Dependency Injection.
Option #1 is ok so far as it goes, but in such an approach you would be responsible for object lifecycle and ownership. Option #2 is not a Java language feature, but rather something provided by Frameworks, eg Spring.
Yes it is possible to pass MainController obj to InterfaceMainController reference variable.
Without Spring
InterfaceMainController mainController = new MainController();
Using Spring annotation
If you are using spring then you can use annotations to auto wire the MainController object with #component and #Autowired something like this
#Component
public class MainController implements InterfaceMainController {
........
}
and auto wire mainController with annotation #Autowired like this
#Autowired
InterfaceMainController mainController

How do I add injection of custom fields to the default ControllerFactory of FXMLLoader?

I want to set some non-UI fields in the controller before the initialize method of the controller gets called automatically upon creation. As I understand it, the way to do it is to provide custom ControllerFactory, since initialize() gets called after ControllerFactory returns the created object. I wanted to use the following code as per this answer:
FXMLLoader loader = new FXMLLoader(mainFXML); // some .fxml file to load
loader.setControllerFactory(param -> {
Object controller = null;
try {
controller = ReflectUtil.newInstance(param); // this is default behaviour
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
if (controller instanceof Swappable) {
((Swappable) controller).setSwapper(swapper); // this is what I want to add
}
return controller;
});
However, the ReflectUtil class (which is used in default setControllerFactory method) is part of com.sun.reflect.misc package, which I am not able to use, since compiling fails with error: package sun.reflect.misc does not exist.
As I understand it, I can't use sun packages, since this is not public API. So the question is: what do I do? I can't find any other examples of this, only the ones with ReflectUtil and, well, I want my ControllerFactory to comply with default workflow of JavaFX with #FXML annotations and all that, is this possible with some other DI framework like Jodd Petite, for example? Is there some other way to set the field? (other than to synchronize on it and wait in initialize() until the setter method gets called from other thread).
Full code on github for context.
If you want to create an instance via reflection then you need to use Class.getConstructor(Class...)1 followed by Constructor.newInstance(Object...).
FXMLLoader loader = new FXMLLoader(/* some location */);
loader.setControllerFactory(param -> {
Object controller;
try {
controller = param.getConstructor().newInstance();
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
if (controller instanceof Swappable) {
((Swappable) controller).setSwapper(swapper);
}
return controller;
}
This code requires that your controller class has a public, no-argument constructor. If you want to inject your dependencies through the constructor you could do something like:
FXMLLoader loader = new FXMLLoader(/* some location */);
loader.setControllerFactory(param -> {
Object controller;
try {
if (Swappable.class.isAssignableFrom(param)) {
controller = param.getConstructor(Swapper.class).newInstance(swapper);
} else {
controller = param.getConstructor().newInstance();
}
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
return controller;
}
This code assumes that all subclasses of Swappable have a public, single-argument constructor that takes a Swapper.
If you want to get a non-public constructor you'll need to use Constructor.getDeclaredConstructor(Class...). Then you'd need to call setAccessible(true) on the Constructor before invoking it.
Couple things to remember if using Jigsaw modules (Java 9+) and this controller factory code is not in the same module as the controller class. Let's say the controller factory code is in module foo and the controller class is in module bar:
If using a public controller with a public constructor then bar must exports the controller class' package to at least foo
If using a non-public controller and/or constructor then the same thing must happen but with opens instead of exports
Otherwise an exception will be thrown.
1. If using a no-argument (not necessarily public) constructor you can bypass getConstructor and call Class.newInstance() directly. However, please note that this method has issues and has be deprecated since Java 9.
Personally, using reflection for my own code is a sign of bad design.
Here's a suggestion that uses FXML mechanisms to inject a user instance of an object. For this purpose, an object is created that describes the context in which the application works. Object user entities are registered in this object. This imposes some constraint on users not to implement a direct interface but to inherit an abstract class that will implement the logic of registering the instance in the context.
public interface Swapper {
}
public abstract class AbstractSwapper implements Swapper {
public AbstractSwapper() {
ApplicationContext.getInstance().setSwapper(this);
}
}
public class ApplicationContext {
private static ApplicationContext instance;
private Swapper swapper;
private ApplicationContext() {
}
public synchronized static ApplicationContext getInstance() {
if(instance == null) {
instance = new ApplicationContext();
}
return instance;
}
public synchronized static Swapper swapperFactory() {
Swapper swapper = getInstance().getSwapper();
if(swapper == null) {
swapper = new AbstractSwapper() {
};
getInstance().setSwapper(swapper);
}
return swapper;
}
public Swapper getSwapper() {
return swapper;
}
public void setSwapper(Swapper swapper) {
this.swapper = swapper;
}
}
In this case, the FXML file can be used fx:factory to use the swapper instance registered in ApplicationContext. Thus, FXMLLoader will inject the instance directly into the controller.
<GridPane fx:controller="sample.Controller" xmlns:fx="http://javafx.com/fxml" >
<fx:define>
<ApplicationContext fx:factory="swapperFactory" fx:id="swapper"/>
</fx:define>
</GridPane>
and sample.Controller
public class Controller {
#FXML
private Swapper swapper;
}
Another solution is for the controller to initialize the fields using the ApplicationContext directly. So the swapper field does not bind to the FXML file.
public class Controller {
private Swapper swapper;
#FXML
private void initialize() {
swapper = ApplicationContext.swapperFactory();
}
}
In both versions, the user simply has to create an instance of AbstractSwapper before using FXMLLoader.
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
AbstractSwapper s = new AbstractSwapper() {
};
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Also, there is an option to use FXMLLoader to inject the object. In this case it goes through fx:reference or through fx:copy (if you have copy constructor)

Does JavaFX have a function similar to onInit? [duplicate]

My Application class looks like this:
public class Test extends Application {
private static Logger logger = LogManager.getRootLogger();
#Override
public void start(Stage primaryStage) throws Exception {
String resourcePath = "/resources/fxml/MainView.fxml";
URL location = getClass().getResource(resourcePath);
FXMLLoader fxmlLoader = new FXMLLoader(location);
Scene scene = new Scene(fxmlLoader.load(), 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The FXMLLoader creates an instance of the corresponding controller (given in the FXML file via fx:controller) by invoking first the default constructor and then the initialize method:
public class MainViewController {
public MainViewController() {
System.out.println("first");
}
#FXML
public void initialize() {
System.out.println("second");
}
}
The output is:
first
second
So, why does the initialize method exist? What is the difference between using a constructor or the initialize method to initialize the controller required things?
Thanks for your suggestions!
In a few words: The constructor is called first, then any #FXML annotated fields are populated, then initialize() is called.
This means the constructor does not have access to #FXML fields referring to components defined in the .fxml file, while initialize() does have access to them.
Quoting from the Introduction to FXML:
[...] the controller can define an initialize() method, which will be called once on an implementing controller when the contents of its associated document have been completely loaded [...] This allows the implementing class to perform any necessary post-processing on the content.
The initialize method is called after all #FXML annotated members have been injected. Suppose you have a table view you want to populate with data:
class MyController {
#FXML
TableView<MyModel> tableView;
public MyController() {
tableView.getItems().addAll(getDataFromSource()); // results in NullPointerException, as tableView is null at this point.
}
#FXML
public void initialize() {
tableView.getItems().addAll(getDataFromSource()); // Perfectly Ok here, as FXMLLoader already populated all #FXML annotated members.
}
}
In Addition to the above answers, there probably should be noted that there is a legacy way to implement the initialization. There is an interface called Initializable from the fxml library.
import javafx.fxml.Initializable;
class MyController implements Initializable {
#FXML private TableView<MyModel> tableView;
#Override
public void initialize(URL location, ResourceBundle resources) {
tableView.getItems().addAll(getDataFromSource());
}
}
Parameters:
location - The location used to resolve relative paths for the root object, or null if the location is not known.
resources - The resources used to localize the root object, or null if the root object was not localized.
And the note of the docs why the simple way of using #FXML public void initialize() works:
NOTE This interface has been superseded by automatic injection of location and resources properties into the controller. FXMLLoader will now automatically call any suitably annotated no-arg initialize() method defined by the controller. It is recommended that the injection approach be used whenever possible.

javaFX Load Exception and javaFX Null Pointer Exception [duplicate]

My Application class looks like this:
public class Test extends Application {
private static Logger logger = LogManager.getRootLogger();
#Override
public void start(Stage primaryStage) throws Exception {
String resourcePath = "/resources/fxml/MainView.fxml";
URL location = getClass().getResource(resourcePath);
FXMLLoader fxmlLoader = new FXMLLoader(location);
Scene scene = new Scene(fxmlLoader.load(), 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The FXMLLoader creates an instance of the corresponding controller (given in the FXML file via fx:controller) by invoking first the default constructor and then the initialize method:
public class MainViewController {
public MainViewController() {
System.out.println("first");
}
#FXML
public void initialize() {
System.out.println("second");
}
}
The output is:
first
second
So, why does the initialize method exist? What is the difference between using a constructor or the initialize method to initialize the controller required things?
Thanks for your suggestions!
In a few words: The constructor is called first, then any #FXML annotated fields are populated, then initialize() is called.
This means the constructor does not have access to #FXML fields referring to components defined in the .fxml file, while initialize() does have access to them.
Quoting from the Introduction to FXML:
[...] the controller can define an initialize() method, which will be called once on an implementing controller when the contents of its associated document have been completely loaded [...] This allows the implementing class to perform any necessary post-processing on the content.
The initialize method is called after all #FXML annotated members have been injected. Suppose you have a table view you want to populate with data:
class MyController {
#FXML
TableView<MyModel> tableView;
public MyController() {
tableView.getItems().addAll(getDataFromSource()); // results in NullPointerException, as tableView is null at this point.
}
#FXML
public void initialize() {
tableView.getItems().addAll(getDataFromSource()); // Perfectly Ok here, as FXMLLoader already populated all #FXML annotated members.
}
}
In Addition to the above answers, there probably should be noted that there is a legacy way to implement the initialization. There is an interface called Initializable from the fxml library.
import javafx.fxml.Initializable;
class MyController implements Initializable {
#FXML private TableView<MyModel> tableView;
#Override
public void initialize(URL location, ResourceBundle resources) {
tableView.getItems().addAll(getDataFromSource());
}
}
Parameters:
location - The location used to resolve relative paths for the root object, or null if the location is not known.
resources - The resources used to localize the root object, or null if the root object was not localized.
And the note of the docs why the simple way of using #FXML public void initialize() works:
NOTE This interface has been superseded by automatic injection of location and resources properties into the controller. FXMLLoader will now automatically call any suitably annotated no-arg initialize() method defined by the controller. It is recommended that the injection approach be used whenever possible.

How can i use Guice in JavaFX controllers?

I have a JavaFX application where I would like to introduce Guice because my Code
is full of factorys right now, only for the purpose of testing.
I have one use case where i have a controller class of a certain view.
This controller class has a viewmodel and I pass the model to the viewmodel via
the constructor of the controller class.
In the controller class I have a contactservice object that provides the edit/save/delete operations.
As of now I have an interface of that object and provide an implementation and a Mock. This object can be retrieved by a Factory.getInstance() method.
What I want to do is something like this:
public class NewContactController implements Initializable {
// ------------------------------------------------------------------------
// to inject by guice
// ------------------------------------------------------------------------
private ContactService contactService;
#Inject
public void setContactService(ContactService srv) {
this.contactService = srv;
}
// edit window
public NewContactController(Contact c) {
this.viewModel = new NewContactViewModel(c);
}
// new window
public NewContactController() {
this.viewModel = new NewContactViewModel();
}
#FXML
void onSave(ActionEvent event) {
//do work like edit a contcat,
contactService.editContact(viewModel.getModelToSave());
}
#Override
public void initialize(URL location, ResourceBundle resources) {
// bind to viewmodel---------------------------------------------------
}
}
How can I achive this? Is it a good a idea to do something like that?
While I was searching for a solution I found fx-guice and similar frameworks but how can i combine these two?
Specially how can I let this fields be injected AND instanciate the controller myself or at least give it some constructor args?
I don't use Guice, but the simplest approach would appear to be just to use a controller factory on the FXMLLoader. You can create a controller factory that instructs the FXMLLoader to use Guice to initialize your controllers:
final Injector injector = Guice.createInjector(...);
FXMLLoader loader = new FXMLLoader(getClass().getResource(...));
loader.setControllerFactory(new Callback<Class<?>, Object>() {
#Override
public Object call(Class<?> type) {
return injector.getInstance(type);
}
});
// In Java 8, replace the above with
// loader.setControllerFactory(injector::getInstance);
Parent root = loader.<Parent>load();
There's a good DI framework for javaFX called afterburner.fx. Check it out, I think it's the tool you're looking for.
Assuming you (could) instantiate the controller by hand/guice and not from the FXML, you could use https://github.com/google/guice/wiki/AssistedInject if you need to pass any non DIable parameter to the constructor. You would then set the controller manually to the FXMLLoader with .setController(this) and load the FXML file in the constructor of the controller.
Not sure if there are any drawbacks, but this kind of system seems to work for me :)
To use JavaFx with Guice :
Extend javafx.application.Application & call launch method on that class from the main method. This is the application’s entry point.
Instantiate dependency injection container of your choice. E.g. Google Guice or Weld.
In application’s start method, instantiate FXMLLoader and set it’s controller factory to obtain controllers from the container. Ideally obtain the FXMLLoader from the container itself, using a provider. Then give it an .fxml file resource. This will create content of the newly created window.
Give the Parent object instantiated in previous step to Stage object (usually called primaryStage) supplies as an argument to the start(Stage primaryStage) method.
Display the primaryStage by calling it’s show() method.
Code Example MyApp.java
public class MyApp extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
Injector injector = Guice.createInjector(new GuiceModule());
//The FXMLLoader is instantiated the way Google Guice offers - the FXMLLoader instance
// is built in a separated Provider<FXMLLoader> called FXMLLoaderProvider.
FXMLLoader fxmlLoader = injector.getInstance(FXMLLoader.class);
try (InputStream fxmlInputStream = ClassLoader.getSystemResourceAsStream("fxml\\login.fxml")) {
Parent parent = fxmlLoader.load(fxmlInputStream);
primaryStage.setScene(new Scene(parent));
primaryStage.setTitle("Access mini Stock App v1.1");
primaryStage.show();
primaryStage.setOnCloseRequest(e -> {
System.exit(0);
});
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
launch();
}
}
Then FXMLLoaderProvider.java
public class FXMLLoaderProvider implements Provider<FXMLLoader> {
#Inject Injector injector;
#Override
public FXMLLoader get() {
FXMLLoader loader = new FXMLLoader();
loader.setControllerFactory(p -> {
return injector.getInstance(p);
});
return loader;
}
}
Thanks to mr. Pavel Pscheidl who provided us with this smart code at Integrating JavaFX 8 with Guice

Categories

Resources