0

I'm trying to to use spring xml configuration (only for autowiring some objects - business logic classes and jdbcTemplate) with javafx but whenever I'm using @Autowired the object is getting null.

Here's my code of Main class :

@Component
public class App extends Application {

    public static ApplicationContext appContext;
    public static Stage window;

    static {
        appContext = new ClassPathXmlApplicationContext("spring//beans.xml");
    }

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        System.out.println("test1");
        window = primaryStage;
        Parent root = FXMLLoader.load(getClass().getClassLoader().getResource("scenes/Login.fxml"));
        Scene scene = new Scene(root);
        primaryStage.setTitle(Titles.APPLICATION_TITLE);
        primaryStage.setScene(scene);
        primaryStage.show();
        window.setOnCloseRequest(e -> closeProgram(e));
        System.out.println("test2");
    }

    private void closeProgram(WindowEvent windowEvent) {
        try {
            windowEvent.consume();
            CommonUtility.openNewWindow("ConfirmExit", Titles.APPLICATION_TITLE);
        } catch (IOException e) {
            e.printStackTrace();
        }
}
}

Spring configuration file, beans.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
    <context:annotation-config />
    <context:component-scan base-package="in.csitec.sp" />

    <import resource="classpath:database-config1.xml" />

    <bean id="taskExecutor"
        class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="corePoolSize" value="5" />
        <property name="maxPoolSize" value="20" />
        <property name="queueCapacity" value="25" />
        <property name="WaitForTasksToCompleteOnShutdown" value="true" />
    </bean>


</beans>

Login Controller :

@Component
public class LoginController implements Initializable {


    @FXML
    private Label statusLabel;

    @FXML
    private TextField usernameTextField, passwordTextField;

    @FXML
    private Button loginButton;

    @FXML
    private ProgressBar progressBar;

    private Task<Object> loginTask;

    public static String loggedInUser;

    @Autowired
    LoginManager loginManager;

    @Override
    public void initialize(URL location, ResourceBundle resources) {


    }

    public void onLoginButtonClick(ActionEvent actionEvent){
        if (usernameTextField.getText().isEmpty()) {
            statusLabel.setText(Messages.EMPTY_USERNAME);
            NotificationManager.showNotification(Titles.APPLICATION_TITLE, Messages.EMPTY_USERNAME);
            return;
        }
        if (passwordTextField.getText().isEmpty()) {
            statusLabel.setText(Messages.EMPTY_PASSWORD);
            NotificationManager.showNotification(Titles.APPLICATION_TITLE, Messages.EMPTY_PASSWORD);
            return;
        }
        loginButton.setDisable(true);

        progressBar.setProgress(0);

        loginTask = createLoginTask();

        progressBar.progressProperty().unbind();
        progressBar.progressProperty().bind(loginTask.progressProperty());


        loginTask.messageProperty().addListener(new ChangeListener<String>() {

            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {

                if(LoginManager.statusCode.equalsIgnoreCase("1")){
                    System.out.println("Invalid username or password.");
                    progressBar.progressProperty().unbind();
                    loginButton.setDisable(false);
                    return;
                }
                System.out.println("Logged in successfully");
                progressBar.progressProperty().unbind();
                loginButton.setDisable(false);
            }
        });
        new Thread(loginTask).start();
    }

    private Task<Object> createLoginTask() {

        return new Task<Object>() {

            @Override
            protected Object call() throws Exception {
                try{
                    Map<String, String> requestMap = new HashMap<>();
                    requestMap.put(Constants.USERNAME, usernameTextField.getText());
                    requestMap.put(Constants.PASSWORD, passwordTextField.getText());
                    loginManager.loginManager(requestMap);
                    updateProgress(100, 100);
                    updateMessage("Got Response");
                }catch(Exception e){
                    System.out.println("checkin");
                    e.printStackTrace();                
                }

                return true;
            }
        };
    }

    public void onFooterHyperLinkClick(ActionEvent actionEvent) throws IOException, URISyntaxException {
        Desktop.getDesktop().browse(new URI(Hyperlinks.CSITEC_WEBSITE));
    }

}

Here in LoginController I have Autowired LoginManager's object but it is getting assigned null every time therefore whenever I'm calling loginManager.loginManager(requestMap), I have no clue why it is happening because I'm sure my beans.xml file is loading and in that file I have written configuration for component scanning from base package and I have also written @Component in my LoginManager class file.

Here's the code for LoginManager as well :

@Component
public class LoginManager {

    @Autowired
    CommonDAO commonDAO;

    @Autowired
    JdbcTemplate jdbcTemplate1;

    public static String statusCode;
    public static String errorMessage;


    public void loginManager(Map<String, String> requestMap){
        try{

            String userName = requestMap.get(Constants.USERNAME);
            String passwordSHA = requestMap.get(Constants.PASSWORD);
            final byte[] authBytes = passwordSHA.getBytes(StandardCharsets.UTF_8);
            final String encodedPassword = Base64.getEncoder().encodeToString(authBytes);

            Object[] params = { userName, encodedPassword };
            String sqlQuery = ResourceFileReader.getSQLQuery(LookupSQLQueries.AUTHENTICATE_USER_QUERY);

            boolean result = commonDAO.validate(sqlQuery, params, jdbcTemplate1);

            if (!result) {
                errorMessage = Messages.INVALID_USERNAME_OR_PASSWORD;
                statusCode = "1";
                return;
            }

            statusCode = "0";

        }catch(Exception e){
            errorMessage = Messages.SOMETHING_WENT_WRONG;
            statusCode = "1";
        }
    }


}

Please help me out and guide me to make it work.

Siddharth Sachdeva
  • 1,812
  • 1
  • 20
  • 29
  • Did you put spring annotation on CommonDAO? you can inject only those beans which are defined as spring beans. – Amit Bhati Jan 18 '17 at 09:31
  • Why you injecting jdbcTemplate in your controller? don't you think, DAO layer should have this dependency not controller layer. – Amit Bhati Jan 18 '17 at 09:32
  • Regarding the null ... please read through this post http://stackoverflow.com/questions/19414734/understanding-spring-autowired-usage – J_D Jan 18 '17 at 09:35
  • @AmitBhati I have written @ Component in that CommonDAO as well, here the main thing is LoginManager is not getting autowired I have commented @ Autowired CommonDAO commonDAO and @ Autowired JdbcTemplate jdbcTemplate1 but still LoginManager is not getting autowired. – Siddharth Sachdeva Jan 18 '17 at 09:43
  • @J_D Can you specify please, that would be really helpful, as far I can see I don't have to declare several id=".." class=".." – Siddharth Sachdeva Jan 18 '17 at 09:45
  • @SiddharthSachdeva - Do the beans deploy correctly on startup? Which application server is been used? – J_D Jan 18 '17 at 09:59
  • @J_D beans.xml is loading as I can see it while debugging, moreover I'm not using any server it is a desktop based (javafx) application. – Siddharth Sachdeva Jan 18 '17 at 10:05
  • @SiddharthSachdeva - Sorry, I'm in over my head - missed the javafx bit. In general, if an autowired bean is null, it means that the bean was not initialized correctly in the application. Have seen some examples on Google regarding using spring and javafx together. Please share when you do find the solution. – J_D Jan 18 '17 at 10:16
  • 1
    The structure of your application is just not going to work. The `Application` subclass in a JavaFX application is the entry point for the application: think of the `start()` method (or the `init()` method) in `Application` as the *replacement for `main()`* in a traditional Java application. The JavaFX framework creates an instance of your `Application` class on startup and invokes `init()`, then `start()`. So there's no way to let spring manage the `Application` instance: by the time you create the application context, the `Application` instance has already been created. – James_D Jan 18 '17 at 13:38
  • Here's another way to see the problem. If you look at your code, you create an `ApplicationContext` and *never use it*. At no point do you ever retrieve a bean from it. So surely it is no surprise that nothing is injected. – James_D Jan 18 '17 at 13:44

1 Answers1

2

I'm not expert on JavaFX, but it seems your object is managed by JavaXF rather than Spring, hence no autowiring is happening.

You need to tell JavaXF how to deal with Spring. See JavaFX and Spring - beans doesn't Autowire:

public class SpringFxmlLoader {

    private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringApplicationConfig.class);

    public Object load(String url, String resources) {
        FXMLLoader loader = new FXMLLoader();
        loader.setControllerFactory(clazz -> applicationContext.getBean(clazz));
        loader.setLocation(getClass().getResource(url));
        loader.setResources(ResourceBundle.getBundle(resources));
        try {
            return loader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

The important line being loader.setControllerFactory(clazz -> applicationContext.getBean(clazz));

Tutorials

Community
  • 1
  • 1
alexbt
  • 16,415
  • 6
  • 78
  • 87
  • I have seen this example, your theory is may be right but I can't find any justification for it. Is there any way to achieve it with spring xml configuration (not java configuration) – Siddharth Sachdeva Jan 18 '17 at 13:11
  • @SiddharthSachdeva Exactly the same thing will work with XML config: this has nothing to do with how you configure Spring . Just initialize `applicationContext` using a `ClassPathXmlApplicationContext` instead. – James_D Jan 18 '17 at 13:40