Hot questions for Using Applets in javafx

Question:

I'd like to embed one ore more JavaFX applications on a webpage. How do you do that?

There are several bits and pieces on the Oracle website, but there's no complete example.

There's the Deployment in the Browser tutorial, the Packaging Basics tutorial, etc. There's mentioning of Ant tasks and what not.

So there were still a lot of questions after I read them. Like do I need Ant? Do I need to create an applet? etc

All I'd like to see is a minimal and complete "Hello World" example in order to see how it works. Even here on StackOverflow are only bits and pieces to answers of the same question, so that don't really help.

I had this question up yesterday, but deleted it and thought I'd try myself. Turned out that it is only easy when you know the pitfalls. So since this was already asked here I thought I'd share my minimal and complete example in the answer.

Using the JavaFX samples it took only a few minutes to create the code for this html page:


Answer:

Create a JavaFX application project, e. g. "FxInHtml".

Create your JavaFX application, e. g.:

package application;

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Group root = new Group();

            Label label = new Label( "Hello World!");
            root.getChildren().add( label);

            Scene scene = new Scene(root,200,200);

            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

in FxInHtml/src/application

Now you can use the javafx packager tool to do the rest. You can find it in the bin folder of your JDK installation. Note that in Java 7 it's called javafxpackager and in Java 8 it's called javapackager. Let's assume we use Java 8 and your development environment compiled the class files into the bin folder.

Open a command shell and navigate to the project folder FxInHtml.

Create a package using

javapackager -createjar -outdir compiled -outfile myapp -appclass application.Main -srcdir bin -v

Now you have an executable myapp.jar file in the compiled folder.

Create the jnlp and html files using

javapackager -deploy -outdir deployed -outfile outfile -width 400 -height 400 -name AppName -appclass application.Main -v -srcdir compiled

The important thing to note is that "srcdir" is never any dir with java classes and it's different per javapackager directive. The output directory of the one javapackager call is the source dir of the other.

Now that you invoked the command, you got a new folder "deployed" which contains all the files you need: myapp.jar, outfile.html, outfile.jnlp.

If you open the outfile.html in a browser, you can already see the embedded JavaFX application. Most probably you'll have to change the security settings, e. g. allow "file:/" applications to be executed. But bear in mind that you remove "file:/" again after development, it is a security risk. Or you could sign the jar files which you'll have to do in the end anyway. You can use javapacker for the signing as well.

That's it for the minimal and complete example.


But let's look into the generated files. The generated outfile.html looks like this:

<html><head>
  <SCRIPT src="http://java.com/js/dtjava.js"></SCRIPT>
<script>
    function launchApplication(jnlpfile) {
        dtjava.launch(            {
                url : 'outfile.jnlp'
            },
            {
                javafx : '8.0+'
            },
            {}
        );
        return false;
    }
</script>

<script>
    function javafxEmbed() {
        dtjava.embed(
            {
                url : 'outfile.jnlp',
                placeholder : 'javafx-app-placeholder',
                width : 200,
                height : 200
            },
            {
                javafx : '8.0+'
            },
            {}
        );
    }
    <!-- Embed FX application into web page once page is loaded -->
    dtjava.addOnloadCallback(javafxEmbed);
</script>

</head><body>
<h2>Test page for <b>AppName</b></h2>
  <b>Webstart:</b> <a href='outfile.jnlp' onclick="return launchApplication('outfile.jnlp');">click to launch this app as webstart</a><br><hr><br>

  <!-- Applet will be inserted here -->
  <div id='javafx-app-placeholder'></div>
</body></html>

In order to embed various JavaFX applications, you need to modify/duplicate this part:

dtjava.embed(
    {
        url : 'outfile.jnlp',
        placeholder : 'javafx-app-placeholder',
        width : 200,
        height : 200
    },
    {
        javafx : '8.0+'
    },
    {}
);

and reference the application in your html using the placeholder using this div tag

<div id='javafx-app-placeholder'></div>

eg if you have an additional barchart.jnlp, you add it like this (I removed the webstart part since we want our app to be embedded):

    <html><head>
      <SCRIPT src="http://java.com/js/dtjava.js"></SCRIPT>
    <script>
        function javafxEmbed() {
            dtjava.embed(
                {
                    url : 'outfile.jnlp',
                    placeholder : 'javafx-app-placeholder',
                    width : 200,
                    height : 200
                },
                {
                    javafx : '8.0+'
                },
                {}
            );

            dtjava.embed(
                {
                    url : 'barchart.jnlp',
                    placeholder : 'barchart-placeholder',
                    width : 400,
                    height : 400
                },
                {
                    javafx : '8.0+'
                },
                {}
            );
        }
        <!-- Embed FX application into web page once page is loaded -->
        dtjava.addOnloadCallback(javafxEmbed);
    </script>

    </head><body>
      <h2>Test page for <b>AppName</b></h2>
      <!-- Applet will be inserted here -->
      <div id='javafx-app-placeholder'></div>

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tem
      <div id='barchart-placeholder'></div>
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequa ...
    </body></html>

Question:

I created the standard NetBeans JavaFX application and I tried to run it in a browser but I had no luck. I have followed every instruction out there with no luck. I am not doing anything special, standard JavaFX and built it with NetBeans IDE.

Here is the error message I am getting:

CacheEntry[file:/Users/mo/NetBeansProjects/WebAppTest/dist/WebAppTest.jar]: updateAvailable=false,lastModified=Fri Mar 04 07:03:31 EST 2016,length=19089
    java.security.AccessControlException: access denied ("java.util.PropertyPermission" "user.dir" "read")
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
        at java.security.AccessController.checkPermission(AccessController.java:884)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at sun.plugin2.applet.FXAppletSecurityManager.checkPermission(Unknown Source)
        at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1294)
        at java.lang.System.getProperty(System.java:717)
        at java.io.UnixFileSystem.resolve(UnixFileSystem.java:133)
        at java.io.File.getAbsolutePath(File.java:556)
        at webapptest.WebAppTest.start(WebAppTest.java:24)
        at com.sun.javafx.applet.FXApplet2$2.run(FXApplet2.java:134)
        at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "user.dir" "read")
        at com.sun.javafx.applet.FXApplet2$2.run(FXApplet2.java:150)
        at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    Caused by: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "user.dir" "read")
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
        at java.security.AccessController.checkPermission(AccessController.java:884)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at sun.plugin2.applet.FXAppletSecurityManager.checkPermission(Unknown Source)
        at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1294)
        at java.lang.System.getProperty(System.java:717)
        at java.io.UnixFileSystem.resolve(UnixFileSystem.java:133)
        at java.io.File.getAbsolutePath(File.java:556)
        at webapptest.WebAppTest.start(WebAppTest.java:24)
        at com.sun.javafx.applet.FXApplet2$2.run(FXApplet2.java:134)
        ... 4 more

JavaFX application:

FXML File

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="webapptest.FXMLDocumentController">
    <children>
        <Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
        <Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
    </children>
</AnchorPane>

Controller:

public class FXMLDocumentController implements Initializable {

    @FXML
    private Label label;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
        label.setText("Hello World!");
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

}

main method

public class WebAppTest extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        File f = new File("FXMLDocument.fxml");
        String filePath = f.getAbsolutePath();
        Parent root = FXMLLoader.load(getClass().getResource(filePath));
        System.out.println(filePath);

        Scene scene = new Scene(root);

        stage.setScene(scene);
        stage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

Due to the Certificate signing issues: I manually trusted the dist\ directory:

Regarding the FXML File, If I didn't do getAbsolutePath(); and I get the resources using the line code below. I get a different error message:

Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

if I didn't use getAbsolutePath() I get the following error:

updateAvailable=true,lastModified=Fri Mar 04 09:12:56 EST 2016,length=19089
java.lang.NullPointerException: Location is required.
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3207)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
    at webapptest.WebAppTest.start(WebAppTest.java:26)
    at com.sun.javafx.applet.FXApplet2$2.run(FXApplet2.java:134)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.NullPointerException: Location is required.
    at com.sun.javafx.applet.FXApplet2$2.run(FXApplet2.java:150)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
Caused by: java.lang.NullPointerException: Location is required.
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3207)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
    at webapptest.WebAppTest.start(WebAppTest.java:26)
    at com.sun.javafx.applet.FXApplet2$2.run(FXApplet2.java:134)
    ... 4 more

Here is the content of the Jar file: 


Answer:

Since you updated your answer, it's now clear what you actually want to do. Instead of converting a classpath resource (as retrieved with getClass().getResource()) to a file, you should just use the resource directly:

Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

You do not want to deal with the File class at all, since the resource is coming from inside of a JAR file. Ditch all of that code.

Also, make sure you have the FXMLDocument.fxml placed inside of the webapptest package. If you have it placed at the root of the classpath, use .getResource("/FXMLDocument.fxml") instead.

To include what has been mentioned in comments: You might need to clean and rebuild your project in order for the resources to show up properly.

Question:

I am trying to create an applet that will replace a confusing CLI with a nice JavaFX GUI. I don't have extensive experience with GUI design and most of what we learned in the classroom environment was Swing/AWT. Having toyed a little bit with JavaFX, I feel that it has already shown to be far better, however there aren't many good tutorials/articles out there about creating JavaFX programs intended to run as applets. Right now I can't even seem to convert the following simple login UI from Swing to JavaFX.

import java.applet.Applet;
import java.awt.*;
import javax.swing.*;


public class ACLEditor extends Applet {

    public void init() {

        /* Basic Layout */
        setLayout(new GridLayout(5, 2));

        /* Components */
        JLabel username_l = new JLabel("Username:");
        JTextField username = new JTextField("", 10);
        JLabel password_l = new JLabel("Password:");
        JPasswordField password = new JPasswordField("", 10);
        JLabel hostname_l = new JLabel("Hostname:");
        JTextField hostname = new JTextField("0.0.0.0", 10);

        JButton connect_btn = new JButton("Connect");

        /* Place all Controls on the Layout */
        add(username_l);
        add(username);
        add(password_l);
        add(password);
        add(hostname_l);
        add(hostname);
        add(connect_btn);

    }


}

Any help on this would be appreciated. Note: I do understand how to create a JavaFX UI for a standalone application, I just can't seem to grasp how to make it into an applet


Answer:

As of JavaFX 8, you can deploy a JavaFX app in some desktop browsers using the JavaFX packaging tools and deployment toolkit. There are caveats regarding this approach. A brief description of some of these and summary of how to deploy is in: JavaFX - can it really be deployed in a browser?.

You don't need a JFXPanel to achieve this and I wouldn't recommend one unless your app also used Swing controls.

Question:

We are having a web application which is developed in Oracle ADF.

1) From the doc page Download and Open Folder button , We are using the applet to download files to a location in client machine and opens the folder

2) From the doc page Download and Open Files button , We are using the applet to download files to a location in client machine and open those files

3) From the doc page Open Folder Button, Which will open the corresponding doc folder if exist.

The above 3 points already exist and its working perfectly.

Now since plugin support will be removed from browsers. We are looking for an alternative solution.

Any suggestions would help us.. Please advise

Regards Arun


Answer:

To replace the java aplet, you can use a local service that implements a restful interface.

Launch local service with a simple GET request to jnlp file. For example, http://example.com/localservice.jnlp This will download, install, and run the local service (the requirements for it are as applet - it must be signed). Once the local service has been successfully launched, the web application can submit and read data from it, just as it would with any web server. However, it should be kept in mind that browsers do not allow cross-queries. Therefore, either a CORS header from the local service should be returned or the address that it listens to (127.0.0.1) should be part of the application domain (this is done by subdomain with A record for 127.0.0.1 for example localhost.example.com)

Here's an example conversion (pretty rough) This is the local service that listens on port 8888 for incoming HTTP queries and returns a JSON response.

public class LocalService {

    public LocalService() {
    }

    public static void main(String[] args) throws IOException  {
        HttpServer server =  HttpServer.create(new InetSocketAddress("localhost", 8888), 0);
        server.setExecutor(Executors.newSingleThreadExecutor());
        server.createContext("/", httpExchange -> {

            StringBuilder builder = new StringBuilder();

            int read;
            byte buffer[] = new byte[1024];

            InputStream is = httpExchange.getRequestBody();
            while ((read = is.read(buffer)) != -1) {
                builder.append(new String(buffer, 0, read));
            }

            is.close();

            String response = String.format("{\"request\": \"%s\"}", builder.toString());

            Headers headers = httpExchange.getResponseHeaders();
            headers.add("Content-Type", "application/json");
            headers.add("Access-Control-Allow-Origin", "*");
            httpExchange.sendResponseHeaders(200, response.length());

            OutputStream os = httpExchange.getResponseBody();
            os.write(response.getBytes());
            os.flush();
            os.close();
        });

        server.start();
    }
}

This is the JNLP file used to start the service.

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
    <information>
        <title>Local Service</title>
    </information>

    <resources>
        <!-- Application Resources -->
        <jar href="http://example.com/localservice.jar" main="true" />
    </resources>

    <security>
        <all-permissions/>   
    </security>

    <application-desc name="Local Service" main-class="LocalService">

    </application-desc>
    <update check="background"/>
</jnlp>

And this is an HTML page that starts automatically or manually the service and sends and receives data from it

<!DOCTYPE html>
<html>
    <head>
        <title>Local Service</title>
        <script
            src="https://code.jquery.com/jquery-3.3.1.min.js"
            integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
            crossorigin="anonymous"></script>

        <script>
            $(function() {
                $('#send').click(function() {
                    $.ajax({
                        method: 'POST', 
                        url: 'http://localhost:8888', // or localhost.example.com:8888
                        data: $('#data').val(),

                        success: function(data, textStatus, jqXHR) {
                            alert(data.request);
                        },

                        error: function(jqXHR, textStatus, errorThrown) {
                            alert(errorThrown);
                        }
                    });
                });
            });
        </script>
    </head>
    <body>
        <h1>Local Service</h1>
        <!-- auto load local service -->
        <iframe style="display: none;" src="localservice.jnlp"></iframe>
        <a href="localservice.jnlp">Run Local Service Manually</a>

        <div style="margin-top: 50px;">
            <input id="data"/>
            <button id="send">Send</button>
        </div>
    </body>
</html>

Question:

Relevant code:

@Override
public void start(Stage primaryStage) {
    primaryStage.setTitle("MousAbility");
    primaryStage.show();
}

So "MousAbility" needs to be displayed on the centre of the title-bar (instead of on the left side like it is on the image).


Answer:

I don't think this is possible. You can undecorate the stage so the default controls are removed. Then you can add your custom controls to the top positions: Read here for more information