0

Im trying to host javafx application within a simple springboot application, using web and thymleaf.

It serves up static html to local host, and i can get this content both from the browser and trough the webview of a independant javafx, using webengine.load("localhost:port")

The problem happens when i try to instantiate a javafx application/bean in spring boot. I successfully instantiate the client, get the fxml and controller running fine, but the webengine.load method return an empty window. I tried loading content served up from separate running applications, and it works, so the problem is trying to load the content served up by the springbootapplication with a javafx client instantiated by the same springbootapplication.

The JAVAfx application is instantiated as a bean like so,

@Component
public class Cool extends Application {


    public void start(Stage stage) throws IOException {

        Parent parent = FXMLLoader.load(getClass().getResource("/rendum/webView.fxml"));
        Scene scene = new Scene(parent, 300, 300);
        stage.setScene(scene);
        stage.show();

    }

    @Bean
    public void go() {
        launch();
    }
}

the webcontent is attempted loaded like this

public class Controller {
    @FXML
    WebView webView;


    public void initialize() {
        System.out.println("Controller up");
        WebEngine webEngine = webView.getEngine();

        webEngine.load("http://localhost:8080/");
        JSObject window = (JSObject) webEngine.executeScript("window");

        window.setMember("java", new JSApi());

        webEngine.setOnAlert((e)-> {
            System.out.println(e.getData());
        });
    }
}

So just to confirm, i've tried changing the url to other running application and it works, and i can atm use this same exact controller setup in a different javafx application and get the content displayed.

Im using jdk 8. Does anyone know why the content can not get loaded? Entrypoint

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

Controller instantiation. It serves html with som js at "/" (entrypoint)

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class ViewController {

    @GetMapping("/")
    public String index() {
        return "index";
    }
}

Project structure.

So it instantiete the controller that serves up html and js to localhost. This is unavailable for the webengine in the javafxbean, but it is available for applications outside this springboot application

Treeview

6
  • "The JavaFX application is instantiated as a bean". Well, maybe, but a completely different instance is created if/when you call go() (which you haven't shown). This doesn't make a whole lot of sense. Where are you starting Spring? The Application class (and the start() method) is the entry point for the entire application. So if this is all you have, there is no Spring application context etc. Commented Feb 18, 2021 at 22:01
  • The spring application is a very basic from a spring boot initializer. it has the main method that Uses The Springboot.run, and a Controller component to render thymleaf templates. The sprinboot application simply instantiate a Controller that returns thymeleaf templates and it works fine. I onyl showed the javafx part, cause there is simply just basic boilerplate to get a simple html page served up to local host, and it works fine. Commented Feb 18, 2021 at 22:25
  • No, post a minimal reproducible example (so we can reproduce the whole thing). Are you saying you are trying to run both the client and the server in the same application? That just seems wrong. But either way, the structure is completely unclear here. Commented Feb 18, 2021 at 22:28
  • I added the rest of the code and the projectstucture. And yes, that is what i am saying. I want springboot to serve up a simple weclient, but at the same time serve up a simple gui that views that webclient in a webview Commented Feb 18, 2021 at 22:49
  • Yeah, this just doesn't seem to me to be the intended use for a lot of what you're using. For example, how are you ensuring the web server has started before the web engine tries to load the web page? Creating a new instance of a Spring bean outside of the spring application content (which you do via launch()) is not really a supported use-case either. Commented Feb 18, 2021 at 23:27

1 Answer 1

0

Turns out there is a way to achive this. Instead of running it like a normal spring-boot application, you actually want to launch the JavaFx application from your Springboot entrypoint. Inside your Javafx application you going to need a ConfigurableApplicationContext, that will be used to launch an event that will tell your applicaton that your primarystage is ready. This event can be listened to by initalizing a bean of the spring-boot ApplicationListener, and you will create your scene from its onApplicationEvent method.

You can use the spring initializer and add dependancies as usual. A normal spring-boot application launches like this

public class SpringApplication{

    public static void main(String[] args) {
        SpringApplication.run(SpringApplication.class, args);

    }

}

Instead, use the Application class from java fx to start the application

    public class SpringApplication{

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

    }

}    

Configure the JavaFXApplication by using the SpringApplicationBuilder to create a Configurable application context in the init method. Launch an instance of ApplicationEvent from it in the start method and pass it the stage. ApplicationEvent is abstract, so you need to extend a custom class from it.

The JavaFXApplication will look like this

public class JavaFxApplication extends Application {
    ConfigurableApplicationContext applicationContext;

    @Override
    public void start(Stage stage) throws Exception {
        applicationContext.publishEvent(new StageReadyEvent(stage));
    }

    public void init() {
        applicationContext = new SpringApplicationBuilder(SpringApplication.class).run();
    }

    public void stop() {

        applicationContext.close();
    }


    public static class StageReadyEvent extends ApplicationEvent {
        public StageReadyEvent(Stage stage) {
            super(stage);
        }
    }
}

Finally, instantiate a Bean that will listen to that event by implementing the ApplicationListener

@Component
public class StageReadyListener implements ApplicationListener<JavaFxApplication.StageReadyEvent> {

    @Override
    public void onApplicationEvent(JavaFxApplication.StageReadyEvent stageReadyEvent) {
        Stage stage = (Stage)stageReadyEvent.getSource();
        stage.setScene(new Scene(new Label("Hello"), 300, 300));
        stage.show();
    }
}

This way it is now possible to use spring dependancy injection in the javafx application aswell.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.