0

I am wrtiting a simple GUI port scanner in JavaFX. It is using multiple threads to discover online hosts in the local network. I have hostDiscovery button that I want to disable before host discovery process is started. When I execute my code, the host discovery process will finish and only after it is finished my button will become disabled.

Part of my FXML file

<Button fx:id="hostDiscovery" layoutX="470.0" layoutY="370.0" mnemonicParsing="false" prefHeight="60.0" prefWidth="200.0" text="Start Host Discovery" AnchorPane.bottomAnchor="30.0" AnchorPane.rightAnchor="30.0" />

Debug code in Controller class

public class FXMLController implements Initializable
{
    @FXML
    private Button hostDiscovery;

    @Override // This method is called by the FXMLLoader when initialization is complete
    public void initialize(URL fxmlFileLocation, ResourceBundle resources)
    {
        assert hostDiscovery != null : "fx:id=\"hostDiscovery\" was not injected: check your FXML file 'PortScanner.fxml'.";

        // initialize your logic here: all @FXML variables will have been injected

        hostDiscovery.setOnAction(event -> {
            System.out.println("HD Test Start");
            hostDiscovery.setDisable(true); //Here I want to disable the button

            final int hostDiscoveryTimeout = 1000;
            final int threads = 25;
            final ExecutorService exService = Executors.newFixedThreadPool(threads);
            final List<Future<HostDiscoveryResult>> futures = new ArrayList<>();

            String ipAddress = "192.168.0.";

            for (int i = 1; i < 255; i++)
            {
                StringBuilder currentIPAddress = new StringBuilder().append(ipAddress).append(i);
                HostDiscovery dHost = new HostDiscovery(currentIPAddress.toString(), hostDiscoveryTimeout);
                futures.add(dHost.multithreadedHostDicovery(exService));
            }

            try
            {
                exService.shutdown();
                exService.awaitTermination(hostDiscoveryTimeout, TimeUnit.MILLISECONDS);
            } catch (Exception e)
            {
                System.out.println(e);
            }

            for (final Future<HostDiscoveryResult> i : futures)
            {
                try
                {
                    if (i.get().isOnline())
                    {
                        System.out.print(i.get().getIpAddress() + " ");
                        System.out.println(i.get().getHostName());
                    }
                } catch (Exception e)
                {
                    System.out.println(e);
                }
            }
            System.out.println("HD Test Stop");
        });
    }
}

OK, after reading this thread that James_D posted Using threads to make database requests I managed to modify my code to run as expected. Here is the modified version

hostDiscovery.setOnAction(event -> {
        Task<Void> hostDiscoveryTask = new Task<Void>()
        {
            @Override
            protected Void call() throws Exception
            {
                System.out.println("HD Test Start");

                final int hostDiscoveryTimeout = 1000;
                final int threads = 25;
                final ExecutorService exService = Executors.newFixedThreadPool(threads);
                final List<Future<HostDiscoveryResult>> futures = new ArrayList<>();

                String ipAddress = "192.168.0.";

                for (int i = 1; i < 255; i++)
                {
                    StringBuilder currentIPAddress = new StringBuilder().append(ipAddress).append(i);
                    HostDiscovery dHost = new HostDiscovery(currentIPAddress.toString(), hostDiscoveryTimeout);
                    futures.add(dHost.multithreadedHostDicovery(exService));
                }

                try
                {
                    exService.shutdown();
                    exService.awaitTermination(hostDiscoveryTimeout, TimeUnit.MILLISECONDS);
                } catch (Exception e)
                {
                    System.out.println(e);
                }

                for (final Future<HostDiscoveryResult> i : futures)
                {
                    try
                    {
                        if (i.get().isOnline())
                        {
                            System.out.print(i.get().getIpAddress() + " ");
                            System.out.println(i.get().getHostName());
                        }
                    } catch (Exception e)
                    {
                        System.out.println(e);
                    }
                }
                System.out.println("HD Test Stop");
                Platform.runLater(() -> hostDiscovery.setDisable(false));
                Platform.runLater(() -> hostDiscovery.setText("Restart Host Discovery"));
                return null;
            }
        };
        Thread thr = new Thread(hostDiscoveryTask);
        hostDiscovery.setDisable(true);
        hostDiscovery.setText("WORKING");
        thr.setDaemon(true);
        thr.start();
    });
}

}

jpiechowka
  • 75
  • 1
  • 12
  • 1
    You're performing blocking operations on the FX Application Thread, so you prevent the UI from updating. You need to do those on a background thread. See, e.g. the answer to http://stackoverflow.com/questions/30249493/using-threads-to-make-database-requests – James_D Apr 26 '16 at 22:08
  • Yes, this is what I needed. Thank you. – jpiechowka Apr 26 '16 at 22:16
  • You might also be able to do it a bit more elegantly. If you wrap each individual `Future` in a `Task` and submit the `Task` (not the `Future`) to the executor, you can register an on succeeded handler with each `Task`, which allows you to easily update the UI as each completes. Then there's no real need to wait for them all to finish. – James_D Apr 26 '16 at 22:19

0 Answers0