1

following Scenario:

JavaFxMainApp JavaFXUpdaterApp

Both are JavaFX applications with a GUI and a void main() method.

  1. The Updater has to be able to start the JavaFXMainApp by accessing the JavaFxMainApp.jar ONLY knowing about the main class -> it can only! call main().

  2. The JavaFxMainApp has to also be able to run on its own, by starting main().

I cannot start multiple VMS and the two apps have no means of communication.

Problem with this is:

Application.launch() can only be executed once per JVM.

The standard javaFx way to start the app:

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

Here it is impossible to fullwill Requirement 1). As both main() methods call launch().

A second approach i found is:

Updater + MainApp
 public static void main(String[] args) {
        Application app2 = JavaFxMainApp.class.newInstance(); 
        Stage anotherStage = new Stage();
        app2.start(anotherStage);
}

First off, i lose the possibility to pass args, but i can live with that as they would not be used anyways. Major Problem here is, that this code ONLY works, if the JVM already has a JavaFx Thread running, hence it requires that launch() has been called in the JVM at some point before. This is not the case as none of the two calls launch() anymore.

Hybrid approach:

Updater calls launch(), MainApp takes the second approach

Requirement 2) cannot be fulfilled, als starting MainApp without launcher Updater is impossible now.

Another idea i had where dirty "try launch() catch()-> try second approach() in both apps, but that couples both apps and make the setup less flexible.

Is there a way to accomplish this without having to override JavaFxs' LauncherImpl or Application classes to fit these needs?

4
  • Why can the updater only call main(...)? Also, can you clarify under what circumstances the updater "calls" the main app? Is the updater already running (as a GUI app)? The use cases are not very clear. Commented Sep 11, 2014 at 14:21
  • the updater can only call main because it has to be able to manage any java application without having to make adaptions. This way it can start any java-application that has a main() method. The updater searches for new versions and can start the MainApp in a thread. the reasons and specification is multiple boring pages long and not mine to publish i am afraid. Not my idea but i m going to have to live with the decisions made :/ Commented Sep 15, 2014 at 13:54
  • Those requirements are really at odds with the way JavaFX is designed. In Java 8, for example, a JavaFX Application doesn't even have a requirement for a main method at all - consequently in Java 8 there are exceptions to the rule that all Java applications have a main method. Commented Sep 15, 2014 at 15:47
  • Added a suggested hack though. Commented Sep 15, 2014 at 15:59

2 Answers 2

3

Can you do something like this:

public class MainApp extends Application {

    private Parent uiContent ;

    public static final double DEFAULT_WIDTH = 800 ;
    public static final double DEFAULT_HEIGHT = 600 ;

    public Parent getContent() {
        if (uiContent == null) {
            uiContent = initializeUI();
        }
        return uiContent ;
    }

    public Scene createScene() {
        return new Scene(getContent(), DEFAULT_WIDTH, DEFAULT_HEIGHT);
    }

    public void initializeAndShowStage(Stage stage) {
        stage.setScene(createScene());
        stage.show();
    }

    private Parent initializeUI() {
        // probably wise to check we are on the FX Application thread here...
        Pane root = ... ;
        // build ui....
        return root ;
    }

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

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

And

public class UpdaterApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        // whatever you need to launch the updater app here...

    }

    // invoke from the FX Application Thread to "start" main app:
    private void showMainApp(Stage stage) {
        MainApp app = new MainApp();
        app.initializeAndShowStage(stage);
    }

    private void showMainApp() {
        showMainApp(new Stage());
    }

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

This would be the far preferred approach. If you have requirements that force you to call main, then you could try something like this:

public class MainApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        // .... build UI etc
    }

    public static void main(String[] args) throws Exception {
        if (Platform.isFXApplicationThread()) {
            Stage someStage = new Stage();
            MainApp app = new MainApp();
            app.start(stage);
        } else {
            launch(args);
        }
    }
}

Then your updater app can just call MainApp().main(new String[0])); from the FX Application Thread.

This feels like a bit of a hack though.

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

2 Comments

MainApp app = new MainApp(); app.initializeAndShowStage(stage); this part contradicts the "only main can be called" part
the "hack" is what i came up with myself too (quite similar, i added a retry with start() if launch fails). I am aware that obviously it is not indented to do this kind of thing with fx and instead of hacking it was decided to just display the updater as swingUI, as is it only the progressbar showing how far the download has come. no clean but it works and is less of a hack. thanks anyway for coinfirming that this is not possible with "common" javafx methods
3

You want your MainApp to start of its own and you also want an UpdateApp to start the MainApp when required then you can follow my step. I have tried and this model is working.

This is the starting point. You need to call this Class to begin your application

public class StartAnApp {
    public static void main(String[] args){
        new Thread(new MainApp()).start(); // this will call your MainApp 
    }
}

This is the First Application which will start. As to start a JavaFX Application you need to have a main() methods. So ensure to provide a main method in this class.

public class MainApp extends Application implements Runnable{
    public MainApp(){}  // constructor

    @Override
    public void start(Stage stage){
        Text text = new Text("MainApp");
        Button startUpdate = new Button("Start Update");

        // When this button is pressed. It will launch UpdateApp Application
        startUpdate.setOnAction( e -> {
            Platform.runLater(new Runnable(){
                @Override
                public void run(){
                    new UpdateApp().start(new Stage());
                }
            });
        });

        Group root = new Group(text);
        Scene scene = new Scene(root,300,400);
        stage.setScene(scene);
        stage.setX(0);
        stage.setY(0);
        stage.show();
    }

    // This method will be used when you first start an Application for
    // which main method is required
    public static void main(String[] args){
        launch(args);
    }

    // This will be used when you call this Application from another JavaFX application
    // if you have closed this application before
    @Override
    public void run(){
        launch();
    }
}

This is your UpdateApp. This method does not have main() method.

public class UpdateApp extends Application implements Runnable{
    public UpdateApp(){}  // constructor

    @Override
    public void start(Stage stage){
        Text text = new Text("UpdateApp");
        Button startAnother = new Button("Start Another");

        // When this button is pressed. It will launch MainApp Application or you can add any other JavaApplication
        startAnother.setOnAction( e -> {
            Platform.runLater(new Runnable(){
                @Override
                public void run(){
                    new MainApp().start(new Stage());
                }
            });
        });

        Group root = new Group(text);
        Scene scene = new Scene(root,300,400);
        stage.setScene(scene);
        stage.setX(350);
        stage.setY(0);
        stage.show();
    }

    // This will be used when you call this Application from another JavaFX application
    @Override
    public void run(){
        launch();
    }
}

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.