title: interacting with JavaFX network service in a GUI
body: <p>I am transitioning from writing a java Swing application to JavaFX to write a modern java based GUI application. </p>
  
  <p>I would like to know the best approach to create a network based reusable threading service.  The way I coded up the network service was to use a controller class (generated from the FXML via the Net-beans GUI).  I put the threading logic here via a private Service member named '<code>transmitter</code>' and I wired up the start/stop logic via the Start/Stop button's event callback.<br>
  The network based thread is implemented as a javafx Service - I did this since I would like to restart the service/thread whenever the destination address changes.  This seems to be the recommended approach in place of a stand alone Task.</p>
  
  <p>The network service is very simple right now, all it does is use some GUI widgets to configure a packet to transmit to a host/port once a second.  I need to restart the service only if the host/port widget changes, however if the network service is running, I would like to modify the packet without interrupting/restarting the DatagramSocket. The place where I have questions and require some guidance are:</p>
  
  <ol>
  <li>What is the recommended approach to threading a network thread in an
  FXML based application?  An example would be greatly appreciated.</li>
  <li>How do I safely communicate changes from GUI widgets (via their
  action performed callbacks) to the running service class?</li>
  </ol>
  
  <p>Shown below are the most relevant parts of my controller class:</p>
  
  <pre><code>/**
   * FXML Controller class
   *
   * @author johnc
   */
  public class OpMessageServerController implements Initializable {
      @FXML
      private Text mCurrentDateTimeText;
      @FXML
      private Label mApplicationStatus;
      @FXML
      private ComboBox&lt;DiscreteStatus&gt; mPofDS;
      @FXML
      private ComboBox&lt;PhaseOfFlightFMS&gt; mPofFMS;
      @FXML
      private ComboBox&lt;DiscreteStatus&gt; mTailNumberDS;
      @FXML
      private ComboBox&lt;DiscreteStatus&gt; mConfigTableDS;
      @FXML
      private ComboBox&lt;DiscreteStatus&gt; mDateTimeDS;
      @FXML
      private TextField mEpicPN;
      @FXML
      private TextField mConfigTablePNHash;
      @FXML
      private TextField mTailNumber;
      @FXML
      private ComboBox&lt;DiscreteStatus&gt; mTopLevelPNDS;
      @FXML
      private Button mStartStopButton;
      @FXML
      private ComboBox&lt;String&gt; mDLMUHostSpec;
      @FXML
      private CheckBox connectionStatusC1;
      @FXML
      private CheckBox wsuConnectionStatus;
      @FXML
      private CheckBox connectionStatusC4;
      @FXML
      private CheckBox connectionStatusC3;
      @FXML
      private CheckBox connectionStatusC2;
      @FXML
      private CheckBox dlmuwConnectionStatus;
  
      private Service&lt;Void&gt; transmitter;
  
  
  
      /**
       * Initializes the controller class.
       * @param url
       * @param rb
       */
      @Override
      public void initialize(URL url, ResourceBundle rb) {        
          mPofDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
          mPofDS.getSelectionModel().selectFirst();
          mPofFMS.setItems(FXCollections.observableArrayList(PhaseOfFlightFMS.values()));
          mPofFMS.getSelectionModel().selectFirst();
          mTailNumberDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
          mTailNumberDS.getSelectionModel().selectFirst();
          mConfigTableDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
          mConfigTableDS.getSelectionModel().selectFirst();
          mDateTimeDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
          mDateTimeDS.getSelectionModel().selectFirst();
          mTopLevelPNDS.setItems(FXCollections.observableArrayList(DiscreteStatus.values()));
          mTopLevelPNDS.getSelectionModel().selectFirst();
  //      mDLMUHostSpec.setItems(FXCollections.observableArrayList(
  //          FXCollections.observableArrayList("localhost:1234", "192.168.200.2:1234")));
  
          // add event handler here to update the current date/time label
          // this should also update the transmit datastructure
          final Timeline timeline = new Timeline(new KeyFrame(
              Duration.seconds(1), (ActionEvent event) -&gt; {
              LocalDateTime currentDateTime = LocalDateTime.now();
              mCurrentDateTimeText.setText(currentDateTime.format(
                  DateTimeFormatter.ofPattern("kk:mm:ss uuuu")));  
          }));  
  
          timeline.setCycleCount(Animation.INDEFINITE);  
          timeline.play();
  
          // create a service.
          transmitter = new Service() {
              @Override 
              protected Task createTask() {
                  return new Task&lt;Void&gt;() {
                      @Override 
                      protected Void call() throws InterruptedException {
                          updateMessage("Running...");
                          updateProgress(0, 10);
                          DatagramSocket sock = null;
                          while (!isCancelled()) {
                              try {
                                  if (sock == null) {
                                      DatagramSocket sock = new DatagramSocket();
                                  }
                              } catch (SocketException ex) {
                                  Logger.getLogger(OpMessageServerController.class.getName()).log(Level.SEVERE, null, ex);
                              }
                              //Block the thread for a short time, but be sure
                              //to check the InterruptedException for cancellation
                              OpSupportMessage opSupportMessage = new OpSupportMessage(
                                  DiscreteStatus.NormalOperation, 
                                  PhaseOfFlightFMS.Cruise, 
                                  DiscreteStatus.NormalOperation, 
                                  "TAILNUM",
                                  DiscreteStatus.NormalOperation);
                              ByteArrayOutputStream bos = new ByteArrayOutputStream();
                              String[] specParts = mDLMUHostSpec.getValue().split(":");
                              if (specParts.length == 2) {
                                  try {
                                      opSupportMessage.write(bos);
                                      byte[] buff = bos.toByteArray();
                                      DatagramPacket packet = new DatagramPacket(
                                          buff, buff.length, InetAddress.getByName(
                                          specParts[0]), Integer.parseInt(specParts[1]));
                                      mSocket.send(packet);
                                      Thread.sleep(1000);
                                  } catch (IOException ex) {
                                  } catch (InterruptedException interrupted) {
                                      if (isCancelled()) {
                                          updateMessage("Cancelled");
                                          break;
                                      }
                                  }
                              }
                          }                        
                          updateMessage("Cancelled");
                          return null;
                      }
  
                      @Override
                      protected void succeeded() {
                          System.out.println("Scanning completed.");
                      }
  
                      @Override 
                      protected void failed() {
                          System.out.println("Scanning failed.");
                      }
  
                      @Override 
                      protected void running() {
                          System.out.println("Scanning started.");
                      }
  
                      @Override 
                      protected void cancelled() {
                           System.out.println("Scanning cancelled.");
                      }                    
  
                      private void DatagramSocket() {
                          throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                      }
                  };
              }
          };
  
          mApplicationStatus.textProperty().bind(transmitter.messageProperty());
  
      };
  
  @FXML
  private void startStopButtonAction(ActionEvent event) {
      if (!transmitter.isRunning()) {
          transmitter.reset();
          transmitter.start();
      }                
  }
  
   …
  
  }
  </code></pre>