Skip to content

Instantly share code, notes, and snippets.

@james-d
Last active November 15, 2015 11:59
Show Gist options
  • Save james-d/95457d1ac1a90e063337 to your computer and use it in GitHub Desktop.
Save james-d/95457d1ac1a90e063337 to your computer and use it in GitHub Desktop.
Demo of a JavaFX application which is updated by data streaming in. Created as a solution to https://community.oracle.com/message/12694944#12694944. There are two components: the main application, which consists of a server waiting for messages of the form "speed=<value>", an FXML file, FXML controller, and a main class that creates the server, …
package streamingdatafx;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
public class DataStreamParser {
public static final int DEFAULT_PORT = 1028;
private final int port;
private final ExecutorService exec;
private final Logger logger ;
private final DoubleProperty speed = new SimpleDoubleProperty(this,
"speed", 0);
public final DoubleProperty speedProperty() {
return this.speed;
}
public final double getSpeed() {
return this.speedProperty().get();
}
public final void setSpeed(final double speed) {
this.speedProperty().set(speed);
}
public DataStreamParser(int port) throws IOException {
this.port = port;
this.exec = Executors.newCachedThreadPool(runnable -> {
// run thread as daemon:
Thread thread = new Thread(runnable);
thread.setDaemon(true);
return thread;
});
this.logger = Logger.getLogger("DataStreamParser");
try {
startListening();
} catch (IOException exc) {
exc.printStackTrace();
throw exc;
}
}
public DataStreamParser() throws IOException {
this(DEFAULT_PORT);
}
public void startListening() throws IOException {
Callable<Void> connectionListener = () -> {
try (ServerSocket serverSocket = new ServerSocket(port)) {
logger.info(
"Server listening on " + serverSocket.getInetAddress()
+ ":" + serverSocket.getLocalPort());
while (true) {
logger.info( "Waiting for connection:");
Socket socket = serverSocket.accept();
logger.info( "Connection accepted from " + socket.getInetAddress());
handleConnection(socket);
}
} catch (Exception exc) {
logger.log(Level.SEVERE, "Exception in connection handler", exc);
}
return null;
};
exec.submit(connectionListener);
}
public void shutdown() {
exec.shutdownNow();
}
private void handleConnection(Socket socket) {
Callable<Void> connectionHandler = () -> {
try (BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()))) {
String line;
while ((line = in.readLine()) != null) {
logger.info("Received: " + line);
processLine(line);
}
System.out.println("Connection closed from "+socket.getInetAddress());
}
return null;
};
exec.submit(connectionHandler);
}
private void processLine(String line) {
String[] tokens = line.split("=");
if ("speed".equals(tokens[0]) && tokens.length == 2) {
try {
speed.set(Double.parseDouble(tokens[1]));
} catch (NumberFormatException exc) {
logger.log(Level.WARNING, "Non-numeric speed supplied", exc);
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Label?>
<?import javafx.geometry.Insets?>
<HBox xmlns:fx="http://javafx.com/fxml/1" spacing="5"
alignment="CENTER" fx:controller="streamingdatafx.DisplayController">
<padding>
<Insets top="10" left="10" right="10" bottom="10"/>
</padding>
<Label text="Speed:"/>
<Label fx:id="speedLabel"/>
</HBox>
package streamingdatafx;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class DisplayController {
@FXML
private Label speedLabel ;
public void setSpeed(double speed) {
speedLabel.setText(String.format("%.1f", speed));
}
}
package streamingdatafx;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.fxml.FXMLLoader;
public class Main extends Application {
private DataStreamParser dataStream ;
@Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Display.fxml"));
HBox root = loader.load();
DisplayController controller = loader.getController();
dataStream = new DataStreamParser();
dataStream.speedProperty().addListener((obs, oldSpeed, newSpeed) -> {
// update controller on FX Application Thread:
Platform.runLater(() -> controller.setSpeed(newSpeed.doubleValue()));
});
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
@Override
public void stop() {
if (dataStream != null) {
dataStream.shutdown();
}
}
public static void main(String[] args) {
launch(args);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<HBox xmlns:fx="http://javafx.com/fxml/1" alignment="CENTER" fx:controller="test.TestController">
<TextField editable="false" fx:id="speedTextField" />
<Button text="^" onAction="#increaseSpeed"/>
<Button text="v" onAction="#decreaseSpeed" fx:id="decreaseSpeedButton"/>
</HBox>
package test;
import java.net.InetAddress;
import streamingdatafx.DataStreamParser;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Test extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Test.fxml"));
Parent root = loader.load();
TestController controller = loader.getController();
InetAddress localhost = InetAddress.getLocalHost();
int port = DataStreamParser.DEFAULT_PORT;
System.out.println("Connecting to: "+localhost+":"+port);
controller.connect(localhost, port);
Scene scene = new Scene(root, 350, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
package test;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
public class TestController {
@FXML
private Button decreaseSpeedButton ;
@FXML
private TextField speedTextField ;
private Socket socket ;
private PrintWriter out ;
private IntegerProperty speed = new SimpleIntegerProperty(55);
public void initialize() {
speedTextField.textProperty().bind(speed.asString());
speed.addListener((obs, oldSpeed, newSpeed) -> {
String message = "speed="+speed.get()+"\n";
if (out != null) {
System.out.println(message);
out.println(message);
out.flush();
}
});
}
@FXML
private void increaseSpeed() {
speed.set(speed.get()+1);
}
@FXML
private void decreaseSpeed() {
speed.set(Math.max(0, speed.get()-1));
}
public void connect(InetAddress address, int port) throws IOException {
socket = new Socket(address, port);
out = new PrintWriter(socket.getOutputStream());
System.out.println("Connected");
}
public void shutdown() throws IOException {
if (out != null) {
out.close();
}
if (socket != null && socket.isConnected()) {
socket.close();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment