1

As an experiment I'm trying out some tests in integrating JavaFX with JavaServer Faces 2.2. I'm using Eclipse Mars.2 Release 4.5.2 with Java 1.8, which includes JavaFX 8, and the Glassfish 4.1.1 server on a Windows 10 computer. The idea is to see if it's possible to get a JSF application running on a server and invoke some JavaFX GUIs, with the eventual aim of being able to display 3D graphics on the client.

However, I've run into some problems, so show here a very simple example. The xhtml code is just:

<?xml version="1.0" encoding="UTF-8"?>   
<!DOCTYPE html>   
<html lang="en"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html">
<h:head>
  <title>Test of JavaFX with JSF</title>
</h:head>
<h:body>
  <h1>Test of JavaFX with JSF</h1>
  <h:form id="myForm" prependId="false">
    <h:commandButton value="Launch JavaFX Application" actionListener="#{myBean.launchApp}" />
    <br />
    <h:commandButton value="Stop JavaFX Application" actionListener="#{myBean.stopApp}" />
  </h:form>
</h:body>
</html>

which simply displays two buttons in the browser's window on localhost:8080/Test, one to launch the JavaFX application, and one which will eventually be used to close the application.

The code for the bean is:

package shape3dfxjsf;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@SessionScoped
@ManagedBean(name="myBean")
public class MyBean implements Serializable {
    private static final long serialVersionUID = 1L;
    private static boolean launched = false;

    public static void launchApp() {
        if (launched) return;
        launched = true;
        TestJavaFX.main(null);
    }

    public static void stopApp() {
        launched = false;
    }
}

and the code for the JavaFX application with all the graphics stripped out and just the screen is:

package shape3dfxjsf;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TestJavaFX extends Application {

    @Override
    public void start(Stage primaryStage) {
        try {
          BorderPane root = new BorderPane();
          Scene scene = new Scene(root,400,400);
          primaryStage.setScene(scene);
          primaryStage.show();
        } catch(Exception e) {
          e.printStackTrace();
        }
    }

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

The main() method in the last listing for the class TestJavaFX is called by the class MyBean when JSF is used on the server, and it can also be called directly when run as a Java application, which is a useful test.

When Glassfish is started up and a browser window is opened, I click on the "Launch JavaFX Application" button and the launchApp() method in MyBean is invoked. As the launched flag is initially false, the main() method in TestJavaFX is called, and indeed a JavaFX window appears on my screen exactly as if I had invoked the main() method directly from TestJavaFX, so this works correctly.

As the launched flag is now true, clicking again on the launch button has no effect (the button can be disabled in a later version of the code). After closing the JavaFX window, the launched flag is still true, so the the "Stop JavaFX Application" button has to be clicked to make it false again to re-enable the "Launch JavaFX Application". This minor issue of manually setting the flag back to false can easily be automated.

However, the main problem is that I cannot restart the JavaFX application, even though it has apparently closed. If I click the "Launch JavaFX Application" after it is re-enabled and after the JavaFX application is closed, I keep getting the error:

java.lang.IllegalStateException: Application launch must not be called more than once

I've read up on this and looked at other postings. The issue is that as far as I know JavaFX has closed down, so I don't understand why I'm getting this error. Trying Platform.exit() in the JavaTestFX class doesn't work, and invoking System.exit(0) just kills everything.

How do I restart JavaFX when calling it from a JSF bean without closing everything and starting again? There must be someone who has tried this out, and I would most appreciate some ideas on fixing this problem.

RamenChef
  • 5,557
  • 11
  • 31
  • 43
csharp
  • 464
  • 2
  • 10
  • 19
  • 4
    I'm confused by this set up. The code in the JSF bean is executing on the server, not on the client, so your JavaFX GUI is running server-side. The [Javadocs](http://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html#launch-java.lang.String...-) are pretty clear that you cannot call `launch()` more than once (in the lifecycle of the JVM). So to make this work, you should have an application class with an empty `start()` method which is invoked from an application context listener, and your JSF bean should just create and show a stage. – James_D Dec 15 '16 at 11:49
  • 1
    Of course, this would still run a JavaFX GUI on the *server*, and it's completely unclear what the point of that would be. – James_D Dec 15 '16 at 11:49
  • I assumed that after the JavaFX application has been closed, you can launch it again. If that is not the case, how do you "unlaunch" a JavaFX application so that it can be launched afresh? Also, how do you set up an application class with an empty start() method? Although the JavaFX application runs on the server, its window is displayed on the client's screen separate to the browser's window, and may be a useful alternative to using Web Start. – csharp Dec 15 '16 at 15:53
  • 1
    No, you can't launch it again. The `Application` class represents the entire application, it starts once and once only. When you exit the `Application`, the application exits. That's it: the lifecycle of the JavaFX application is bound to the lifecycle of the JVM executing it. – James_D Dec 15 '16 at 15:57
  • 2
    "how do you set up an application class with an empty start() method?". Just as it says, define the `Application` subclass and make it's `start()` method a no-op. Call `launch()`. I posted a slightly more complex example as an answer to [this question](http://stackoverflow.com/questions/32739199/javafx-software-design), which may help (though I still have no idea how you expect this to work). – James_D Dec 15 '16 at 16:00
  • 1
    "Although the JavaFX application runs on the server, its window is displayed on the client's screen". Hmm. I don't think that can possibly be true. How would that work? How do you know the client even has Java installed? Have you actually tried this with a second computer? – James_D Dec 15 '16 at 16:01
  • Well it is true! The JavaFX window is opened on my screen exactly as if I had launched it directly. Somehow the server knows where to display the output window. Of course as I'm running it on localhost, the server may have found out where to show the stage and is using the same VM. At the moment I don't have access to a suitable remote computer where I could deploy the application to see what happens, and have no way of testing this on two separate client computers. – csharp Dec 15 '16 at 16:16
  • 1
    Unless you are running the server on one machine, and the client on another, you cannot possibly know that the JavaFX UI is running on the client (hint: it isn't: it is running inside the same JVM that is running Glassfish). – James_D Dec 15 '16 at 16:20
  • 2
    Here's maybe a way I can convince you. Shut down all Java applications except Glassfish. If you generate a list of processes you will see only one Java process. Open a web browser, hit your application with it, and launch the JavaFX window. Check the process list again: there is still only one Java process running - therefore the JavaFX application is *running inside the same JVM as the server*. It cannot be running on the client. – James_D Dec 15 '16 at 16:23
  • 1
    Or here's another idea: run glassfish on an address on your local network. So the machine will have an IP address that looks something like 10.0.0.1 and Glassfish is likely using port 8080. Then take any other device on the same network (even a phone or tablet), open a web browser, and load up the web page (so enter, say, `http://10.0.0.1:8080` in the browser address bar). Press your button that opens the JavaFX application. You will see the window open on the browser (you were not expecting it to open on the phone, right?). – James_D Dec 15 '16 at 16:51
  • I've just tried this. Java Update Scheduler is always running. When I start up Eclipse, Java (TM) Platform SE binary starts up, and when I start up Glassfish a 2nd instance of Java (TM) starts up. When I open a browser, navigate to the application and click the launch button and JavaFX starts up, no new instance appears in the list of processes, but one of the two instances shows more CPU usage. – csharp Dec 15 '16 at 16:54
  • 1
    Right, so there is no client JVM at all. Your Java Update Scheduler and Eclipse instance are running in their own JVMs (you could easily close these for testing: they are irrelevant to what you are doing). The Glassfish application server is obviously running in its own JVM. The JSF managed bean is an object sitting in the heap in the JVM in which Glassfish runs. When you invoke a method on that object that opens a JavaFX stage, that stage is running in the same JVM as the managed bean, i.e. it is running on the server. – James_D Dec 15 '16 at 16:59
  • 2
    So basically, what you are trying to do here makes *absolutely no sense whatsoever* (unless I suppose you are intending to use Glassfish, running on localhost, as a wrapper for a JavaFX application, which also makes no sense at all). Either you have a fundamental misunderstanding about what a web application is, or a fundamental misunderstanding about what JavaFX is, or both. – James_D Dec 15 '16 at 17:01
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/130719/discussion-between-csharp-and-james-d). – csharp Dec 15 '16 at 17:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/130846/discussion-between-csharp-and-james-d). – csharp Dec 17 '16 at 18:44

0 Answers0