X

Topics and trends related to the Java ecosystem with occasional random rants.

  • Sun |
    April 9, 2015

Update to JavaFX, Sockets and Threading: Lessons Learned

James Connors
Principal Solutions Consultant

Recently, a reader commented on a dated article of mine, circa 2010, entitled JavaFX, Sockets and Threading: Lessons Learned.  In it he correctly stated that, as the original content is based on the deprecated JavaFX 1.3 framework (aka JavaFX Script), he cannot test or utilize that code with the modern JavaFX API.  What follows is an update to that blog entry with code and references appropriate for the JavaFX 8 (Java SE 8 with JavaFX) platform.

Overview

For a more thorough understanding of the method behind this madness, please consult the original article.  Briefly stated, socket programming, especially in Java, often times lends itself to utilizing threads.  To facilitate using sockets, both from the "client" and "server" side, an abstract class called GenericSocket.java was and is provided and, baring a few minor changes, remains quite similar to the original version. Just as in the Original JavaFX Script framework, The JavaFX UI is still not thread-safe and its scene graph must only be accessed through the JavaFX application thread.   What has changed between old and new is the class and method name required to perform work on the JavaFX application thread.

Just like before, we've identified two methods associated with socket manipulations that need to perform work on the main thread.  These abstract method calls are incorporated into the GenericSocket.java source and are specified in the SocketListener.java interface file:

    public interface SocketListener {
        public void onMessage(String line);
        public void onClosedStatus(boolean isClosed);
    }

Within GenericSocket.java, you'll see references to these method calls as follows:

    /*
     * The onClosedStatus() method has to be implemented by
     * a sublclass.  If used in conjunction with JavaFX,
     * use Platform.runLater() to force this method to run
     * on the main thread.
     */
     onClosedStatus(true);

 and

    /*
     * The onMessage() method has to be implemented by
     * a sublclass.  If used in conjunction with JavaFX,
     * use Platform.runLater() to force this method to run
     * on the main thread.
     */
     onMessage(line);

As implied by these comments, JavaFX-specifc classes that extend the GenericSocket class and implement the SocketListener interface are required.  Correspondingly two helper classes have been created: FxSocketClient.java and FxSocketServer.java.  These extend the GenericSocket class and implement the SocketListener interface from the perspective of a JavaFX environment.  Both classes override the onMessage() method like this:

 

Likewise, the onClosedStatus() method is implemented as follows:

 

As these two code snippets exhibit, we enclose our execution requirements within a call to the Platform.runLater() method, ensuring that  it will be executed on the JavaFX application thread.  (For the Java 8 aficionados, you may notice that these two methods could be converted to lambda expressions. That exercise is left to the reader.)  What remains now is for any referencing JavaFX class to implement the SocketListener interface.

To demonstrate the usage of FxSocketClient and FxSocketServer classes within JavaFX 8, this article provides two NetBeans projects represented by the screenshots that follow.  By clicking on each image, you can start up the FxSocketClient and FxSocketServer applications via Java WebStart (assuming you have (1) the latest Java 8 installed and (2) you have a browser which allows Java applets to run.  The last browsers standing are Internet Explorer 11 and Safari).

 

The user interface for these programs was created with the assistance of a terrific tool called the JavaFX Scene Builder.  You can download the source code, including the UI (represented in FXML), here:

The javafx.concurrent Package

The intent of this article was to focus solely on the Platform.runLater() method, however, it is important to note that JavaFX 2.x also provides an additional means to create background tasks that safely interact with the JavaFX application thread.  Similar in capability to the venerable java.util.concurrent  package (and in fact extending some of those classes), the javafx.concurrent package also furnishes the ability to safely control the execution
and track the progress of the application code running in background tasks. For an overview of their use, check out the following article published as part of the Oracle documentation: Concurrency in Java FX.

 

 

Join the discussion

Comments ( 15 )
  • Wayne Richardson Monday, August 10, 2015

    Jim,

    This is a great tutorial. However, I am having problems with re-opening the socket server when it is shut down in auto connect mode. I think that the thread is not getting stopped properly.

    Here is what I do to reproduce the problem.

    Bring up the SocketClientFX application - Okay

    Bring up the SocketServerFX appliation - Okay

    On the SocketServer application, click on the button to Auto Connect - Okay

    On the SocketClient Application, click on the Connect button; Connection made - Okay

    On the SocketClient Application, write a message, and send it; Message sent - Okay

    On the SocketClient Application, click on Disconnect - Okay

    On the SocketServer application, exit the application - Okay

    Bring the SocketServer Application back up - Okay

    Click on the Connect or Auto-Connect button - Not Okay

    I think that the socket thread is still running from the previous running of the SocketServer application, and it won't connect.

    Do you know how to properly shut down the thread so that if it is in auto-connect, it won't leave the thread going?

    Thanks,

    Wayne


  • Jim Connors Tuesday, August 11, 2015

    Hi Wayne,

    Thanks so much for your comment, you are correct. There is a shutdown() method that can be called to close the SocketServer's socket but it looks like the application never gets signaled to do so because the SocketServer is in a separate thread.

    1. A quick and dirty solution would be to detect when the JavaFX application is terminated (when the user clicks on the red "x") and simply exit. To add slightly more elegance, a shutdownHook can be added whose method gets called at program exit. From within that method, we could call the SocketServer's shutdown() method if the socket is open.

    2. Another solution would be to include an "exit" button that would shut the program down more gracefully.

    I'll work on the quick and dirty fix

    -- Jim C


  • Jim Connors Wednesday, August 12, 2015

    New versions of SocketClientFX.zip and SocketServerFX.zip have been posted. Give 'em a try.

    https://blogs.oracle.com/jtc/resource/socket_FX8/SocketClientFX.zip

    https://blogs.oracle.com/jtc/resource/socket_FX8/SocketServerFX.zip

    -- Jim C


  • guest Monday, December 21, 2015

    Hi Jim C,

    Thanks for this useful tutorial.

    I've followed your project structure but I can't send messages at the server with multiple clients...

    Have you any ideas?

    Thanks a lot, Edoardo


  • Jim Connors Monday, December 21, 2015

    Hi Eduardo,

    The sample code shown here cannot accommodate multiple socket readers.

    It's been quite a while but in the blog entry, https://blogs.oracle.com/jtc/entry/source_code_for_javafx_scoreboard I made the source code available for a scoreboard application which uses, somewhere in its bowels, a server capable of handling multiple sockets. Check out the blog entry, and if you do download the source look for the MultipleSocketWriter.java and FxMultipleSocketWriter.java files for some hints.

    -- Jim C


  • Akki Friday, January 29, 2016

    Hey Jim,

    Thanks for this updated tutorial. Most of that was out of my league, but still I njoyed reading it. Although I'm having a project based on Swings which is on basis of P2P, so just thinking of migrating it to JavaFX. Although I'm reusing the code from swing model, is there anything prior to old codes, that I shud consider knowing very much that can help me in smoothing the porting process.!

    Thanks.


  • Jim Connors Friday, January 29, 2016

    Hello Akki,

    There's a fair amount of literature aimed at developers looking to migrate from Swing to JavaFX. You might want to start with the Oracle tutorial called:

    JavaFX for Swing Developers

    http://docs.oracle.com/javafx/2/swing/jfxpub-swing.htm

    Cheers,

    -- Jim C


  • guest Wednesday, May 18, 2016

    I just wanted to say thanks. I teach JavaFX at the High School level and my kids are really excited to be able to do networking using your example.


  • guest Thursday, April 13, 2017

    How would you use this to send binary data?


  • guest Thursday, April 13, 2017

    Just wanted to say thank you for the great tutorial, but I'm wondering how you would get this to work with binary data transfer? I have a client that needs to send binary data (hex) to a legacy java application that listens for client connections (handshake).


  • Jim Connors Friday, April 14, 2017

    You could use the binary-to-text encoding technique. When sending binary data, you encode (convert) it into printable characters, and on the remote side you decode it to its original form. One of the popular encoding standards in is Base64. The Java 8 encode/decode API is here:

    https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html

    https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html


  • Jan Ondrich Saturday, November 11, 2017
    Hi Jim,
    there more elegant solution is now available using ScheduledService:

    public class ServerSocketService extends ScheduledService {
    private static final int PORT_NUMBER = 12345;
    @Override
    protected Task createTask() {
    return new Task() {
    @Override
    protected String call() throws Exception {
    try (ServerSocket serverSocket = new ServerSocket(PORT_NUMBER);
    Socket clientSocket = serverSocket.accept();
    PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
    BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) {
    String inputLine = in.readLine(); //simplifying here, should be in loop until null
    out.println(inputLine); //repeat message to the client just to demonstrate sending reply
    return inputLine;
    }
    }
    };
    }
    }

    public class Main extends Application {
    .......
    @Override
    public void start(Stage primaryStage) throws Exception {
    ........
    ServerSocketService socketService = new ServerSocketService();
    socketService.setOnSucceeded(event -> navigate(socketService.getValue()));
    socketService.start();
    .........
    }
    }

    ScheduledService does callback in the Application thread upon receiving data and by default immediately restarts to listen again. String type is used just for easy demonstration and can be replaced by any serializable Object with appropriate modifications. ScheduledService can also recover from errors and it's easy to configure.

    Cheers!
    Jan
  • Mike Poirier Sunday, March 4, 2018
    Hi Jim and everyone in this community.

    Thank you Jim, for this JavaSocketFx app.
    It's cleaver. Love the non blocking IO, using Thread and runlater.
    I have modified the app to simplify it.
    I have done away with the complexity involve with the collections of ObservableList.
    I replaced both Listviews for TextAreas.
    Now you simply add text this way "TextAreaSend.appendText("led" + "n"); "
    and clear this way "TextAreaRcv.setText("");".

    Thanks you for sharing this cool work, it's very useful.
  • Roberto Xocop Thursday, April 19, 2018
    Hi,
    I downloaded your examples and I was trying to connect two Clients to the Socket Server, but I couldn't.
    How can I do this?

    thanks.
  • Jim Connors Thursday, April 19, 2018
    I've modified the blog entry such that now (assuming you have a browser which allows running Java WebStart applets) you can click on the two images to start the SocketClientFX and SocketServerFX applications respectively.
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha
Oracle

Integrated Cloud Applications & Platform Services