Timeout na swingowym tasku

0

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.

0

Task zachowuje się normalnie ponieważ SwingWorker z którego dziedziczy Task implementuje interfejs Runnable. Stąd wszelkie egzekutory będą go poprawnie wykonywać. Jednak nie będą korzystały z właściwości synchronizacji i aktualizacji, które daje SwingWorker, a tym samym i Task.

1 użytkowników online, w tym zalogowanych: 0, gości: 1