Sophie Brown
Sophie Brown

Reputation: 11

javafx: Using a Task to update the UI then halting execution

I'm developing an application to detect phishing websites, in javafx. I have a "predict" button that does all the necessary processing within its EventHandler. I also want to be able to update a ProgressBar with information about how far it's gotten during processing. I'm using a Task for this, calling updateProgress and updateValue with the final result.

However, in the event of an exception, I want to update the UI then immediately terminate execution of the EventHandler, using updateProgress with some error value. However, updateProgress does not update the UI immediately. Is there something that exists that can not only update the UI from inside an EventHandler, like Task, but also give me control over exactly when the UI updates?

For reference, here's my complete event handler code:

 predict.setOnAction(new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent event)
        {
            Task<Result> task = new Task<Result>()
            {
                @Override
                protected Result call() throws Exception
                {
                    String url = urlText.getText();
                    ArffData arffData = new ArffData();
                    try
                    {
                        updateProgress(1, 10);
                        URL uri = new URL(url);

                        String domain = uri.getHost();
                        arffData.setUrlSimilarity(DataGatherer.readLevenshtein(domain));
                        updateProgress(2, 10);
                        boolean redirection = DataGatherer.getRedirectionStatus(url);

                        arffData.setRedirection(redirection);
                        updateProgress(3, 10);
                        Response response = Jsoup.connect(url).execute();
                        arffData.setSpellingErrors(DataGatherer.getSpellingErrors(response).size());
                    }
                    catch (IOException e1)
                    {
                        updateProgress(-1, 10); //Should update UI before terminating
                        return null;
                    }



                    Classifier rf;
                    Instances instances;
                    try
                    {
                        updateProgress(4, 10);
                        rf = (Classifier) SerializationHelper.read("RF100.model");
                        instances = new DataSource("phishingData.arff").getDataSet();
                    }
                    catch (Exception e1)
                    {
                        updateProgress(-1, 10);
                        return null;
                    }
                    if (instances.classIndex() == -1)
                        instances.setClassIndex(instances.numAttributes() - 1);
                    String offers = offerText.getValue();
                    String lf = lfText.getValue();
                    updateProgress(5, 10);
                    Instance inst = InstanceSetup.setUpInstance(arffData, offers, lf, instances);

                    try
                    {
                        updateProgress(6, 10);
                        double clsLabel = rf.classifyInstance(inst);
                        instances.add(inst); 
                        rf.buildClassifier(instances);
                        SerializationHelper.write("RF100.model", rf);
                        Evaluation eval = new Evaluation(instances);
                        eval.crossValidateModel(rf, instances, 10, new Random(1));
                        boolean phishing = clsLabel ==0 ?true: false;
                        Result result = new Result(phishing, eval.pctCorrect());
                        updateProgress(10, 10);
                        if(clsLabel == 0)
                        {
                            predictionLabel.setText("the given website IS a phishing website.");
                        }
                        else
                        {
                            predictionLabel.setText("the given website IS NOT a phishing website.");
                        }
                        updateValue(result);
                        accuracyLabel.setText("PhishGuard is " + String.format("%.4f%%", eval.pctCorrect()) + 
                                " confident in this prediction.");
                        return result;

                    }
                    catch (Exception e)
                    {
                        updateProgress(-1, 10);
                        return null;
                    }

                }                   
            };

            task.progressProperty().addListener(new ChangeListener<Number>()
            {

                @Override
                public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue)
                {
                    switch(newValue.intValue())
                    {
                        case 1:
                        {
                            pBar.setProgress(10);
                            progressLabel.setText(progressLabels[1]);
                        }
                        case 2:
                        {
                            pBar.setProgress(30);
                            progressLabel.setText(progressLabels[2]);
                        }
                        case 3:
                        {
                            pBar.setProgress(50);
                            progressLabel.setText(progressLabels[3]);
                        }
                        case 4:
                        {
                            pBar.setProgress(70);
                            progressLabel.setText(progressLabels[4]);
                        }
                        case 5:
                        {
                            pBar.setProgress(80);
                            progressLabel.setText(progressLabels[5]);                               
                        }
                        case 6:
                        {
                            pBar.setProgress(90);
                            progressLabel.setText(progressLabels[6]);
                        }
                        case 10:
                        {
                            pBar.setProgress(100);
                            progressLabel.setText(progressLabels[0]);
                        }
                        case -1:
                        {
                            predictionLabel.setText("a prediction could not be made.");
                            accuracyLabel.setText("");
                            pBar.setProgress(0);
                            progressLabel.setText(progressLabels[0]);
                        }

                    }


                }

            });

            task.valueProperty().addListener(new ChangeListener<Result>(){

                @Override
                public void changed(ObservableValue<? extends Result> observable, Result oldValue, Result newValue)
                {
                    // TODO Auto-generated method stub
                    boolean phishing = newValue.isPhishing();
                    if(phishing)
                    {
                        predictionLabel.setText("the given website IS a phishing website.");
                    }
                    else
                    {
                        predictionLabel.setText("the given website IS NOT a phishing website.");
                    }

                    accuracyLabel.setText("PhishGuard is " + String.format("%.4f%%", newValue.getAccuracy()) + 
                            " confident in this prediction.");

                }

            });
            new Thread(task).start();
        }});

Upvotes: 0

Views: 602

Answers (1)

James_D
James_D

Reputation: 209553

The progressProperty() both of a Task and of a ProgressBar are either INDETERMINATE or are set to a value between 0 and 1 (inclusive). For the progress bar:

A positive value between 0 and 1 indicates the percentage of progress where 0 is 0% and 1 is 100%. Any value greater than 1 is interpreted as 100%.

So when you observe the progress value of the task, and then get the intValue(), there are only three possibilities: -1 (when the progress is indeterminate), 1 (when the task is complete), or 0 (all other values). When you register the listener with the task's progressProperty, it is already in an INDETERMINATE state, so the first change you see has an intValue() of 0 (matching none of your cases), and all subsequent changes see the same value until the task is complete, when you see 1. At that point you set the progress of the progress bar to 10, which, as above, is interpreted as 100%.

Probably what you really want to do is just bind the progress property of the progress bar to the progress property of the task (so they just increase together).

To update text periodically, you can use the task's messageProperty.

So I would remove the listener on the task's progress property entirely, and replace it with

pBar.progressProperty().bind(task.progressProperty());
pBar.textProperty().bind(task.messageProperty());

Call updateMessage() as needed in the task with each message you need (code later).

For this:

However, in the event of an exception, I want to update the UI then immediately terminate execution of the EventHandler

I assume you really mean "terminate execution of the Task", since the event handler has long since completed. As noted in the comments, Tasks handle exceptions by default. So you can just let the exception propagate from the call() method, and do the UI updates in an onFailed handler.

The result looks something like this:

predict.setOnAction(new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent event)
    {
        Task<Result> task = new Task<Result>()
        {
            @Override
            protected Result call() throws Exception
            {
                String url = urlText.getText();
                ArffData arffData = new ArffData();

                updateProgress(1, 10);
                updateMessage(progressLabels[1]);
                URL uri = new URL(url);

                String domain = uri.getHost();
                arffData.setUrlSimilarity(DataGatherer.readLevenshtein(domain));
                updateProgress(2, 10);
                updateMessage(progressLabels[2]);

                boolean redirection = DataGatherer.getRedirectionStatus(url);

                arffData.setRedirection(redirection);
                updateProgress(3, 10);
                updateMessage(progressLabels[3]);
                Response response = Jsoup.connect(url).execute();
                arffData.setSpellingErrors(DataGatherer.getSpellingErrors(response).size());






                Classifier rf;
                Instances instances;

                updateProgress(4, 10);
                updateMessage(progressLabels[4]);
                rf = (Classifier) SerializationHelper.read("RF100.model");
                instances = new DataSource("phishingData.arff").getDataSet();
                if (instances.classIndex() == -1)
                    instances.setClassIndex(instances.numAttributes() - 1);
                String offers = offerText.getValue();
                String lf = lfText.getValue();
                updateProgress(5, 10);
                updateMessage(progressLabels[5]);

                Instance inst = InstanceSetup.setUpInstance(arffData, offers, lf, instances);

                updateProgress(6, 10);
                updateMessage(progressLabels[6]);

                double clsLabel = rf.classifyInstance(inst);
                instances.add(inst); 
                rf.buildClassifier(instances);
                SerializationHelper.write("RF100.model", rf);
                Evaluation eval = new Evaluation(instances);
                eval.crossValidateModel(rf, instances, 10, new Random(1));
                boolean phishing = clsLabel ==0 ?true: false;
                Result result = new Result(phishing, eval.pctCorrect());
                updateProgress(10, 10);
                updateMessage(progressLabels[0]);

                if(clsLabel == 0)
                {
                    predictionLabel.setText("the given website IS a phishing website.");
                }
                else
                {
                    predictionLabel.setText("the given website IS NOT a phishing website.");
                }
                updateValue(result);
                accuracyLabel.setText("PhishGuard is " + String.format("%.4f%%", eval.pctCorrect()) + 
                        " confident in this prediction.");
                return result;


            }                   
        };

        pBar.progressProperty().bind(task.progressProperty());
        pLabel.textProperty().bind(task.messageProperty());


        task.valueProperty().addListener(new ChangeListener<Result>(){

            @Override
            public void changed(ObservableValue<? extends Result> observable, Result oldValue, Result newValue)
            {
                // TODO Auto-generated method stub
                boolean phishing = newValue.isPhishing();
                if(phishing)
                {
                    predictionLabel.setText("the given website IS a phishing website.");
                }
                else
                {
                    predictionLabel.setText("the given website IS NOT a phishing website.");
                }

                accuracyLabel.setText("PhishGuard is " + String.format("%.4f%%", newValue.getAccuracy()) + 
                        " confident in this prediction.");

            }

        });

        task.setOnFailed(e -> {
            predictionLabel.setText("a prediction could not be made.");
            accuracyLabel.setText("");
            pBar.progressProperty().unbind();
            pBar.setProgress(0);
            progressLabel.setText(progressLabels[0]);
        });

        new Thread(task).start();
    }});

Upvotes: 1

Related Questions