Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
270 views
in Technique[技术] by (71.8m points)

java - Returning values from thread

I am running selenium tests inside a for loop which takes time.

I needed to indicate the progress of those tests using javafx progressbar.

So I replaced the code inside for loop with a task.

Following is my code

The runTests() method returns a String which is displayed as an Alert

I cannot return a String from inside Task<Void> as the return type is Void

The test code is inside runTest(data) method which returns true or false

@FXML
private void handleRunTestButton(ActionEvent aEvent) throws IOException {
    String testResultMessage = runTests();
    if (!testResultMessage.equals("Testing Complete!")) {
        Alert alert = DialogUtils.getAlert("Info", "Information", testResultMessage, "info");
        alert.showAndWait();

    } else {
        Alert alert = DialogUtils.getAlert("Error", "Error(s)", testResultMessage, "error");
        alert.showAndWait();
    }

}

private String runTests() {
    /* xlsx read */
    FileInputStream fis = null;
    File testDataFile = null;
    try {
        fis = new FileInputStream(selectedTestDataFile);
    } catch (FileNotFoundException e) {
        return "File Input Stream Error: File Not Found";
    }

    // Finds the workbook instance for XLSX file
    XSSFWorkbook myWorkBook = null;
    try {
        myWorkBook = new XSSFWorkbook(fis);
    } catch (IOException e) {
        return "XSSFWorkbook I/O Error";
    }

    // Return first sheet from the XLSX workbook
    XSSFSheet mySheet = myWorkBook.getSheetAt(0);

    int totalWids = mySheet.getLastRowNum();

    final Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            for (int rowIndex = 1; rowIndex <= totalWids; rowIndex++) {
                updateProgress(rowIndex, totalWids);
                Row row = mySheet.getRow(rowIndex);
                if (row != null) {
                    String data = "";

                    Cell cellData = row.getCell(2);

                    if (cellData != null) {
                        data = cellWid.getStringCellValue();
                        boolean testresult = runTest(data);
                        System.out.println(rowIndex + ". data = " + data + ", testresult = " + testresult);
                    }
                }
            }
            return null;
        }
    };

    progressBar.progressProperty().bind(task.progressProperty());
    progressIndicator.progressProperty().bind(task.progressProperty());

    final Thread thread = new Thread(task, "task-thread");
    thread.setDaemon(true);
    thread.start();

    /* xlsx read */
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(selectedTestDataFile);
    } catch (FileNotFoundException e) {
        try {
            myWorkBook.close();
        } catch (IOException e1) {
            return "Error: Please Close Workbook";
        }
        return "Error: File Not Found";
    }
    try {
        myWorkBook.write(fos);
    } catch (IOException e) {
        try {
            myWorkBook.close();
        } catch (IOException e1) {
            return "Error: Please Close Workbook";
        }
        return "Error: Workbook Write";
    }
    try {
        fos.close();
    } catch (IOException e) {
        try {
            myWorkBook.close();
        } catch (IOException e1) {
            return "Error: Please Close Workbook";
        }
        return "Error: File Output Stream";
    }
    try {
        myWorkBook.close();
    } catch (IOException e) {
        return "Error: Please Close Workbook";
    }
    try {
        fis.close();
    } catch (IOException e) {

        return "Error: Input file format";
    }
    return "Testing Complete!";
}

However, now it returns Testing Complete! while the tests are still running.

I am new to multithreading. Please suggest me how to structure the code.

How can I make the runTests() method return a String value from inside

final Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            for () {

            }
            return null;
        }
    };

Before this, when I didn't use Task my code showed the alert properly however the progress bar did not update despite of setting the progress from within the for loop.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Your code, in general, seems pretty solid, but there are several problems. The task you created does the trick, and the progress bar will work, but it uses a thread so returning that the tests are complete without confirming the progress of the thread is wrong. Because the tests are in a thread and the method returns a value without being dependent on it, the value is returned before the tests are done. When calling thread.start() the thread starts execution seperatly from your current thread, meaning that your code continues to execute as usual even if the thread was not done.

You have 2 possible options: keep the thread, or don't. If you don't keep the thread, that means that the tests are executed in the method which causes the javaFX event that called it to wait for the tests to finish. This is a bad idea because now the javaFX thread is stuck and the window can't handle any other events (basically, iresponsive).

A good option is to keep the thread, only that at the end of the thread you could show a dialog indicating whether the tests were complete or not. To do that you can use Platform.runLater(runnable) and pass it a Runnable object which shows the dialog:

Platform.runLater(()->{
   //show dialog
});

It is required because you can't show a dialog while not in the javaFX thread. This allows you to run something in the javaFX thread.

Another issue is the fact that you're accessing the files outside of your thread. Meaning that at the same time the thread runs your test, you attempt to access the files and write to them. Instead of doing that, you should either write to the file in the thread or before it is started.

To summerize it all, you should use your thread to execute the tests and show the dialogs which indicate whether or not the tests were completed. Writing to your test file should not be done while the thread is still executing tests, but rather after the thread was finished, so you can do it at the end of the task.

public void runTests(){
    if(testsRunning) return;

    testsRunning = true;

    final Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            FileInputStream fis = null;
            File testDataFile = null;
            try {
                fis = new FileInputStream(selectedTestDataFile);
            } catch (FileNotFoundException e) {
                displayResponse("File Input Stream Error: File Not Found");
            }

            // Finds the workbook instance for XLSX file
            XSSFWorkbook myWorkBook = null;
            try {
                myWorkBook = new XSSFWorkbook(fis);
            } catch (IOException e) {
                displayResponse("XSSFWorkbook I/O Error");
            }

            // displayResponse(first sheet from the XLSX workbook
            XSSFSheet mySheet = myWorkBook.getSheetAt(0);

            int totalWids = mySheet.getLastRowNum();

            for (int rowIndex = 1; rowIndex <= totalWids; rowIndex++) {
                updateProgress(rowIndex, totalWids);
                Row row = mySheet.getRow(rowIndex);
                if (row != null) {
                    String data = "");

                    Cell cellData = row.getCell(2);

                    if (cellData != null) {
                        data = cellWid.getStringCellValue();
                        boolean testresult = runTest(data);
                        System.out.println(rowIndex + ". data = " + data + ", testresult = " + testresult);
                    }
                }
            }

            /* xlsx read */
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(selectedTestDataFile);
            } catch (FileNotFoundException e) {
                try {
                    myWorkBook.close();
                } catch (IOException e1) {
                    displayResponse("Error: Please Close Workbook");
                }
                displayResponse("Error: File Not Found");
            }
            try {
                myWorkBook.write(fos);
            } catch (IOException e) {
                try {
                    myWorkBook.close();
                } catch (IOException e1) {
                    displayResponse("Error: Please Close Workbook");
                }
                displayResponse("Error: Workbook Write");
            }
            try {
                fos.close();
            } catch (IOException e) {
                try {
                    myWorkBook.close();
                } catch (IOException e1) {
                    displayResponse("Error: Please Close Workbook");
                }
                displayResponse("Error: File Output Stream");
            }
            try {
                myWorkBook.close();
            } catch (IOException e) {
                displayResponse("Error: Please Close Workbook");
            }
            try {
                fis.close();
            } catch (IOException e) {
                displayResponse("Error: Input file format");
            }

            displayResponse("Testing Complete!");

            return null;
        }
        private void displayResponse(String testResultMessage){
            Platform.runLater(()->{
                if (testResultMessage.equals("Testing Complete!")) {
                    Alert alert = DialogUtils.getAlert("Info", "Information", testResultMessage, "info");
                    alert.showAndWait();

                } else {
                    Alert alert = DialogUtils.getAlert("Error", "Error(s)", testResultMessage, "error");
                    alert.showAndWait();
                }
                testsRunning = false;
            });
        }
    };

    progressBar.progressProperty().bind(task.progressProperty());
    progressIndicator.progressProperty().bind(task.progressProperty());

    final Thread thread = new Thread(task, "task-thread");
    thread.setDaemon(true);
    thread.start();
}

So this code now does everything test related in the thread and doesn't interrupt your window from handling events. There is one problem from this: someone might press the runTests button again, while the tests are running. One option is to use a boolean indicating whether the tests are already active and check its value when runTests is called which I added and is called testsRunning. displayResponse is called when the tests where finished (completed or not) and it displayes the response dialog.

Hope I helped, and sorry for the long answer.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...