14

How to call the launch() more than once in java i am given an exception as "ERROR IN MAIN:java.lang.IllegalStateException: Application launch must not be called more than once"

I have create rest cleint in my java application when request comes it call javafx and opening webview after completing webview operarion am closing javafx windows using Platform.exit() method. when second request comes am getting this error how to reslove this error.

JavaFx Application Code:

public class AppWebview extends Application  {

    public static Stage stage;

    @Override
    public void start(Stage _stage) throws Exception {

        stage = _stage;
        StackPane root = new StackPane();

        WebView view = new WebView();

        WebEngine engine = view.getEngine();
        engine.load(PaymentServerRestAPI.BROWSER_URL);
        root.getChildren().add(view);
        engine.setJavaScriptEnabled(true);
        Scene scene = new Scene(root, 800, 600);
        stage.setScene(scene);

        engine.setOnResized(new EventHandler<WebEvent<Rectangle2D>>() {
            public void handle(WebEvent<Rectangle2D> ev) {
                Rectangle2D r = ev.getData();
                stage.setWidth(r.getWidth());
                stage.setHeight(r.getHeight());
            }
        });

        JSObject window = (JSObject) engine.executeScript("window");
        window.setMember("app", new BrowserApp());

        stage.show();

    }

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

RestClient Method: Calling to JavaFX application

// method 1 to lanch javafx
javafx.application.Application.launch(AppWebview.class);

// method 2 to lanch javafx
String[] arguments = new String[] {"123"};
AppWebview .main(arguments);
venu
  • 2,971
  • 6
  • 40
  • 59

3 Answers3

27

You can't call launch() on a JavaFX application more than once, it's not allowed.

From the javadoc:

It must not be called more than once or an exception will be thrown.

Suggestion for showing a window periodically

  1. Just call Application.launch() once.
  2. Keep the JavaFX runtime running in the background using Platform.setImplicitExit(false), so that JavaFX does not shutdown automatically when you hide the last application window.
  3. The next time you need another window, wrap the window show() call in Platform.runLater(), so that the call gets executed on the JavaFX application thread.

For a short summary implementation of this approach:

If you are mixing Swing you can use a JFXPanel instead of an Application, but the usage pattern will be similar to that outlined above.

Wumpus Sample

This example is bit more complicated than it needs to be because it also involves timer tasks. However it does provide a complete stand-alone example, which might help sometimes.

import javafx.animation.PauseTransition;
import javafx.application.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.*;

// hunt the Wumpus....
public class Wumpus extends Application {
    private static final Insets SAFETY_ZONE = new Insets(10);
    private Label cowerInFear = new Label();
    private Stage mainStage;

    @Override
    public void start(final Stage stage) {
        // wumpus rulez
        mainStage = stage;
        mainStage.setAlwaysOnTop(true);

        // the wumpus doesn't leave when the last stage is hidden.
        Platform.setImplicitExit(false);

        // the savage Wumpus will attack
        // in the background when we least expect
        // (at regular intervals ;-).
        Timer timer = new Timer();
        timer.schedule(new WumpusAttack(), 0, 5_000);

        // every time we cower in fear
        // from the last savage attack
        // the wumpus will hide two seconds later.
        cowerInFear.setPadding(SAFETY_ZONE);
        cowerInFear.textProperty().addListener((observable, oldValue, newValue) -> {
            PauseTransition pause = new PauseTransition(
                    Duration.seconds(2)
            );
            pause.setOnFinished(event -> stage.hide());
            pause.play();
        });

        // when we just can't take it  anymore,
        // a simple click will quiet the Wumpus,
        // but you have to be quick...
        cowerInFear.setOnMouseClicked(event -> {
            timer.cancel();
            Platform.exit();
        });

        stage.setScene(new Scene(cowerInFear));
    }

    // it's so scary...
    public class WumpusAttack extends TimerTask {
        private String[] attacks = {
                "hugs you",
                "reads you a bedtime story",
                "sings you a lullaby",
                "puts you to sleep"
        };

        // the restaurant at the end of the universe.
        private Random random = new Random(42);

        @Override
        public void run() {
            // use runlater when we mess with the scene graph,
            // so we don't cross the streams, as that would be bad.
            Platform.runLater(() -> {
                cowerInFear.setText("The Wumpus " + nextAttack() + "!");
                mainStage.sizeToScene();
                mainStage.show();
            });
        }

        private String nextAttack() {
            return attacks[random.nextInt(attacks.length)];
        }
    }

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

Update, Jan 2020

Java 9 added a new feature called Platform.startup(), which you can use to trigger startup of the JavaFX runtime without defining a class derived from Application and calling launch() on it. Platform.startup() has similar restrictions to the launch() method (you cannot call Platform.startup() more than once), so the elements of how it can be applied is similar to the launch() discussion and Wumpus example in this answer.

For a demonstration on how Platform.startup() can be used, see Fabian's answer to How to achieve JavaFX and non-JavaFX interaction?

jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • @jewelsea can u give sample code this, for request process comleted i want close window. i have intregrated this to my spring mvc project. please can u give me sample code. – venu Jun 23 '14 at 09:30
  • @jewelsea please can u send me sample code for this problem.thanks – venu Jun 23 '14 at 13:58
  • @jewelsea thank you evrey much for posting answer. but this is not valid answer for me.. i have implemented spring mvc application and hosted in my server. when server start i want to start java fx application also. and one of my spring module will call java fx application passing url, then java fx i need show that web page in webview and after processing that page i need to send back webpage output to spring mvc mobule until getting output form webpage spring will waiting then ,i need to do javafx functinality using only one thread . – venu Jun 25 '14 at 16:05
  • my spring application want full access contol of javafx application at any pointer of time one agian thanks – venu Jun 25 '14 at 16:07
  • The Wumpus example is both helpful and hilarious. Thanks! – Charles H Oct 13 '16 at 20:02
6

I use something like this, similar to other answers.

private static volatile boolean javaFxLaunched = false;

public static void myLaunch(Class<? extends Application> applicationClass) {
    if (!javaFxLaunched) { // First time
        Platform.setImplicitExit(false);
        new Thread(()->Application.launch(applicationClass)).start();
        javaFxLaunched = true;
    } else { // Next times
        Platform.runLater(()->{
            try {
                Application application = applicationClass.newInstance();
                Stage primaryStage = new Stage();
                application.start(primaryStage);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}
sergioFC
  • 5,926
  • 3
  • 40
  • 54
  • Thank you so much Sergio. I will have a look at it. May i know how did you pass arguments every time you call myLaunch method ? – Nithin May 13 '20 at 10:12
  • I don't need to pass arguments. You can use `Application.launch(applicationClass, args)` instead of `Application.launch(applicationClass)`, but I think you cannot pass arguments in Platfor.runLater – sergioFC May 13 '20 at 11:10
  • Thank you for the update ! Do we have to call Platform.exit() explicitly in the Application to stop it ? – Nithin May 13 '20 at 11:31
  • 2
    Thank you indeed! This example is straightforward and minimalistic. – Alex S. Nov 03 '20 at 08:28
5

try this, I tried this and found successful

@Override
public void start() {
    super.start();
    try {
                    // Because we need to init the JavaFX toolkit - which usually Application.launch does
                    // I'm not sure if this way of launching has any effect on anything
        new JFXPanel();

        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                // Your class that extends Application
                new ArtisanArmourerInterface().start(new Stage());
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Irshad Babar
  • 1,311
  • 19
  • 26
  • Thank you. In my scenario I solved it using `Platform.setImplicitExit(false)` + `Application.launch(ApplicationX.class)` to launch the first Application + your `Platform.runLater` for applications that I use later. This way I didn't need to use the `new JFXPanel();` – sergioFC Jul 17 '19 at 08:18
  • Sorry, but in which class does this code go? In `AppWebview`? – Guillaume F. Sep 07 '19 at 13:26
  • Source: https://www.powerbot.org/community/topic/1044933-launching-jfx-application-more-than-once/ – lepe Oct 09 '19 at 05:07
  • @sergioFC Hi, What if you have to pass input parameters every time in this case ? Any Idea ? – Nithin May 11 '20 at 08:32
  • 1
    @Nithin do you mean parameters to `Application.launch(myClass)`? Maybe you could use some static method previously like `MyClass.staticMethodToInit(value1, value2)`. – sergioFC May 12 '20 at 09:09
  • @sergioFC Yes, i have to pass arguments while launching. I have posted another question over here. I am try to call it like this. Can you please advise ?https://stackoverflow.com/questions/61320938/how-can-i-get-the-data-which-has-been-generated-using-load-html-feature-of-javaf – Nithin May 12 '20 at 18:00
  • 1
    @Nithin I am not sure I understand what does this have to do with your problem. You can still pass arguments to `launch` method as you do. – sergioFC May 12 '20 at 18:33
  • @sergioFC Thanks for the update. Actually, I am using JavaFx WebView as a headless browser to render the html. So, I need to be able to launch the JavaFx application multiple times by passing dynamic arguments. When i saw your comment "Platform.setImplicitExit(false) + Application.launch(ApplicationX.class) to launch the first Application + your Platform.runLater" saying you could launch it multiple times , i wanted to know more details about it. – Nithin May 12 '20 at 19:02
  • @Nithin I've just posted an answer with something similar to what I use to launch Application multiple times. I think it isn't useful for your case, as I cannot pass arguments in `Platform.runLatter` – sergioFC May 13 '20 at 09:53