Cześć,
Mam problem z kolejkowaniem zadań w JavieFX. Chciałbym, aby moja aplikacja była w pełni (no może prawie w pełni) zautomatyzowana i wymagała minimum interakcji z użytkownikiem. Posiada ona 4 - 5 "stepów" które mają się wykonać po sobie kolejno, ale dopiero jeżeli wykona się poprzedni z nich. Stwierdziłem, że dobrym pomysłem będzie podzielenie tego na osobne wątki i uruchamianie ich kolejno po sobie - dzięki wątkom mam łatwą możliwość restartu "stepu", jeżeli któryś z nich zakończy się niepowodzeniem (np. brak połączenia z internetem i brak możliwości pobrania pliku z serwera). Myślałem że będę tym mógł zarządzać za pomocą zwykłego switcha w prawie nieskończonej pętli - w zależności od stanu zakończenia wątku (succeeded, failed) mam możliwość przejścia dalej lub restartu danego stepu (inkrementując INTa do switcha).
Niestety, tu się zaczynają schody... nie wziąłem pod uwagę, że unlimited-switch będzie mi zawieszał appkę, nawet jeżeli jest robiony w osobnym Tasku :/ Poniżej przedstawiam sposób, w jaki chciałem to zrobić:
MainApp.java
@Override
public void start(Stage primaryStage) throws Exception {
locale = new Locale(params.getLanguage());
this.primaryStage = primaryStage;
this.primaryStage.setTitle("MyApp");
try {
FXMLLoader loader = new FXMLLoader(MyApp.class.getResource("/fxml/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
log.debug("Root frame loaded properly");
} catch (IOException e) {
log.error("Failed to load Root Frame properly! " + e);
}
showMainOverview();
Task task = new Task() {
@Override
protected Object call() throws Exception {
do {
switch (params.getProgressStep()) {
// connection with server and download document
case 0: {
try {
connController = new ConnController();
connController.initialize();
} catch (Exception exception) {
log.error(exception.getMessage(), exception);
}
break;
}
case 1: {
showSelectTerminalStage();
params.setProgressStep(55);
break;
}
case 55: {
break;
}
}
} while (params.getProgressStep() != 99);
return null;
}
};
Platform.runLater(task);
}
Oraz ConnController:
private ConnHandlerThread connHandler;
{...}
public void initialize() {
connHandler = new ConnHandlerThread();
connHandler.start();
}
{...}
protected class ConnHandlerThread extends Service<Void> {
// CountDownLatch
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
Connect();
CallAndDownload();
return null;
}
};
}
@Override
protected void succeeded() {
super.succeeded();
params.setProgressStep(1);
log.info("Thread " + this.getClass().getName() + " done.");
log.debug("Next progress Step: " + params.getProgressStep());
}
@Override
protected void failed() {
super.failed();
log.info("Thread " + this.getClass().getName() + " failed.");
}
}
}
Każdy "step" jest zrobiony w bardzo podobny sposób, powycinałem jedynie kod żeby nie był jakiś super długi (całą logikę mam już zaimplementowaną, pozostało jedynie "zakolejkować stepy") - a została tylko idea problemu.
Może nie jest to najbardziej elegancki sposób, ale jedyny jaki mi przyszedł do głowy :/ Niestety nie działa i nie wiem jak sobie mogę z tym poradzić. Wiem, że powinienem zrobić to na lockach - ale nie mam jeszcze dużego doświadczenia w pisaniu aplikacji wielowątkowych - dodatkowo działających w sposób zautomatyzowany. Próbuję to zrobić na CountLatchDown, jednak bez skutku i kończą mi się pomysły... Uruchamianie kolejnych stepów w succeeded() również wolałbym ominąć.
Czy moglibyście mi doradzić, jak mogę obsłużyć "kolejkowanie" zadań? najlepiej, gdyby działało to właśnie w switchu, ale teraz bardzo mocno wątpię czy można to zrobić w ten sposób...
Proszę również o wyrozumiałość - mało dotychczas pisałem appek okienkowych (głównie konsolowe) i dopiero się uczę :)
Pozdrawiam,
Michał :)
karolinaa