1

I want to create a Singleton main class in JavaFX but I am having difficulties because the main class must extend Application, so the constructor can't be private.

I want the main class to be a Singleton because I want to access a couple of instance methods from any class.

In my current code I am using the main class as a fake Singleton because I do not provide any guarantee that the class will not be instantiated again in some part of the code.

I would like to know if there is a decent way to create a Singleton when you can't use private constructors.

Here is the code of my main class:

public final class MainClass extends Application {
    private static MainClass instance;
    private Stage primaryStage, optionsStage;

    @Override
    public void start(final Stage primaryStage) {
        instance = this;
        try {

            // Main scene.
            {
                Parent page = (Parent) FXMLLoader.load(
                        MainWindowController.class.getResource("main.fxml"));
                Scene mainScene = new Scene(page);
                primaryStage.setScene(mainScene);
                primaryStage.show();
            }

            // Options scene.
            {
                optionsStage = new Stage();
                optionsStage.setTitle("Options");
                optionsStage.setResizable(false);
                optionsStage.initModality(Modality.APPLICATION_MODAL);
                optionsStage.initOwner(primaryStage);

                Parent page = (Parent) FXMLLoader.load(
                        OptionsWindowController.class.getResource("options.fxml"));
                Scene scene = new Scene(page);
                optionsStage.setScene(scene);
            }

        } catch (Exception ex) {
            Constants.LOGGER.log(Level.SEVERE, ex.toString());
        }
    }

    /**
     * Returns the instance of this class.
     * @return
     */
    public static MainClass getInstance() {
        return instance;
    }

    public Stage getPrimaryStage() {
        return primaryStage;
    }

    /**
     * Returns the options stage.
     * @return
     */
    public Stage getOptionsStage() {
        return optionsStage;
    }

    /**
     * The main() method is ignored in correctly deployed JavaFX
     * application. main() serves only as fallback in case the
     * application can not be launched through deployment artifacts,
     * e.g., in IDEs with limited FX support. NetBeans ignores main().
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}
ceklock
  • 6,143
  • 10
  • 56
  • 78
  • @AndrewThompson I would like to redesign it, but I don't have a manual about best ways to design this kind of application. May you point a good link? Actually the main problem now is how to get the windows that I need. Maybe there is a better way to manage the windows. – ceklock Dec 19 '12 at 05:59
  • @StefanLindenberg: maybe not... But I would like to know how to create a singleton from this kind of class. Maybe it is not really possible. – ceklock Dec 19 '12 at 06:02
  • 1
    *"how to get the windows that I need."* See [The Use of Multiple JFrames, Good/Bad Practice?](http://stackoverflow.com/a/9554657/418556) – Andrew Thompson Dec 19 '12 at 17:03
  • 1
    Thanks @AndrewThompson. But I am not using multiple "JFrames". I have only one top window, the rest are dialogs. This is exactly what I am doing: "Establish a single main JFrame, then have JDialog instances appear for the rest of the free-floating elements, using the frame as the parent for the dialogs." – ceklock Dec 19 '12 at 20:27
  • 1
    My bad, sometimes I throw the suggestion out there even when it is not entirely clear if the right approach might already be applied (as is here). The term 'windows' is what made it less clear for me - since they can be anything from a `JWindow` to a modal `JDialog`. Thanks for clarifying. -- Best of luck with the technical problem. – Andrew Thompson Dec 20 '12 at 02:01
  • @AndrewThompson In JavaFX the creation of dialogs is a little confusing because you use only Stage class to create decorated windows. To create an equivalent of JFrame you create a not modal Stage. To create an equivalent of JDialog you create a modal Stage. Good luck for you too, and may the Force be with you. :-) – ceklock Dec 20 '12 at 04:14
  • 1
    No problem. My text description was not so good about my issue. Changed it now to be more clear. Anyway thanks for your comments, the link you provided includes some good tips about how to develop applications without the use of dialogs. I will try that in the future. – ceklock Dec 20 '12 at 05:16
  • A JavaFX application launched using [Java Web Start](http://stackoverflow.com/tags/java-web-start/info) has access to the [`SingleInstanceService`](http://docs.oracle.com/javase/7/docs/jre/api/javaws/jnlp/javax/jnlp/SingleInstanceService.html) of the [JNLP](http://stackoverflow.com/tags/jnlp/info) API. Here is a (Swing) based [demo. of the single instance service](http://pscode.org/jws/api.html#sis). – Andrew Thompson Dec 20 '12 at 05:35

1 Answers1

4

I'd think that adding a public constructor like the following and removing the instance = this; assignment in your start method would do the trick, although I am not well-versed in JavaFX. I don't think it would ever try to instantiate your main class more than once, but if the API makes no such guarantee, you might find yourself in trouble later on down the road.

In any case... you'd find out pretty darn quickly if you did something like the following:

public MainClass(){
    super();
    synchronized(MainClass.class){
        if(instance != null) throw new UnsupportedOperationException(
                getClass()+" is singleton but constructor called more than once");
        instance = this;
    }
}

NetBeans' compiler (if you're using it) will whine about a "leaking this", but this will guarantee that you don't run the constructor to completion more than once.

CodeBlind
  • 4,519
  • 1
  • 24
  • 36
  • Does it need to be synchronized? – ceklock Dec 19 '12 at 20:26
  • 1
    Only if it's possible that more than one Thread might call this constructor. Imagine that two threads called `new MainClass()` at the same time, and the `synchronized` block wasn't there. It's possible that the first thread could perform the null check and then get put to sleep because of context switching. The second thread could potentially enter and leave the null check prior to the first thread getting context-switched back in, so you'd have two instances of `MainClass` floating around. The `synchronized` block makes those two statements atomic, thus eliminating the race condition. – CodeBlind Dec 20 '12 at 00:38
  • Thanks for your answer. As the constructor must be public I think this is the only way to go. – ceklock Dec 20 '12 at 05:10