Reputation: 11
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
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, Task
s 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