Last active
November 15, 2015 11:59
-
-
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, …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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