Witam
Używam w swojej aplikacji swing app framework i bardzo mi się spodobała prostota implementacji zadań wykonywanych w tle i łatwość monitorowania takich tasków owocującego np. zmianą progressbara. Problem pojawił się gdy chciałem aby task po określonym czasie przerywał się jeżeli w tym własnie czasie nie zdązżył się normalnie zkończyć (taki timeout).
Moje próba rozwiązania problemu niby działa:
package example;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
*
* @author lucjan.lipka
*/
public class TestTask extends org.jdesktop.application.Task<Object, Void> {
private int TIME_OUT = 10;
private ScheduledExecutorService canceller = Executors.newScheduledThreadPool(1);
private ExecutorService taskExec = Executors.newSingleThreadExecutor();
public TestTask(org.jdesktop.application.Application app) {
super(app);
}
@Override
protected Object doInBackground() {
final Future<String> future = taskExec.submit(new InnerEvenIterator(this));
canceller.schedule(new Runnable() {
@Override
public void run() {
System.out.println("Przed anulowaniem taska");
try {
future.cancel(true);
} catch (Exception e) {
System.out.println("Cancel error: " + e.getMessage());
}
System.out.println("Po anulowaniem taska");
}
}, TIME_OUT, TimeUnit.SECONDS);
try {
String wynik = future.get();
System.out.println("Zdazylem: " + wynik);
} catch (Exception e) {
System.out.println("Future get error: " + e.getMessage());
}
return null;
}
@Override
protected void succeeded(Object result) {
System.out.println("Tak czy siak londuje tuta i jest super");
}
private class InnerEvenIterator implements Callable<String> {
TestTask currentTask;
public InnerEvenIterator(TestTask currentTask) {
this.currentTask = currentTask;
}
@Override
public String call() {
try {
currentTask.setProgress(1, 0, 3);
Thread.sleep(5000);
currentTask.setProgress(2, 0, 3);
Thread.sleep(5000);
currentTask.setProgress(3, 0, 3);
return "Zdazylem przed czasem";
} catch (Exception e) {
System.out.println("Chyba mi przerwano?: " + e.getMessage());
return "Nie zdazylem przed czasem";
}
}
}
}
Widok zawierajacy metodę z adnotacją @Action
package example;
import org.jdesktop.application.FrameView;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.Action;
import org.jdesktop.application.Task;
public class ExampleView extends FrameView {
private TestTask testTask;
public ExampleView(SingleFrameApplication app) {
super(app);
obtainTestTask().execute();
}
@Action
public Task obtainTestTask() {
testTask = new TestTask(getApplication());
return testTask;
}
}
I klasa z metodą main to testu:
package example;
import org.jdesktop.application.Application;
import org.jdesktop.application.SingleFrameApplication;
public class Main extends SingleFrameApplication {
@Override
protected void startup() {
show(new ExampleView(this));
}
@Override
protected void configureWindow(java.awt.Window root) {
}
public static void main(String[] args) {
launch(Main.class, args);
}
}
Jak timeout jest większy niż suma sleepów to wychodzi ok a jak nie to przerywa - to działa dobrze (nie da się prosciej?).
Problem jest ze jak mam dziesiątki tasków to do każdego taska musze dodawać kolejne dwa egzekutory i robić tą klasę wewnętrzną i przepisywać kod z doingBackgroung do metody call. Czy jest jakieś prostrze rozwiazanie? Nie dałoby się zastosować tutaj jakieś fabtyki przerabiajacej już istniejaće taski do takiej postaci z mojego kodu?
PS: Takie spostrzeżenie: Jeżeli task wywołam np. kliknięciem buttona który ma
baton.setAction(actionMap.get("TestTask"));
to taskMonitor dziła ok i moje setprogresy i setmesege działa dobrze.
Natomiast jezeli w wywołam wprost:"
obtainTestTask().execute();
to task zachowuje się normalnie ale taskmonitor już nie przechwytuje zmian z postępie itp.