BackgroundTasks JavaDocs and documentation. #PL-1930

This commit is contained in:
Konstantin Krivopustov 2013-03-12 11:57:15 +00:00
parent 5852963a75
commit c0d339e923
14 changed files with 289 additions and 200 deletions

View File

@ -3794,7 +3794,7 @@ create index IDX_SALES_DOC_CARD on SALES_DOC (CARD_ID)^</programlisting></para>
<title>Типы экранов</title>
<section id="frame">
<title>Фрейм</title>
<para>Фреймы представляют собой части экранов, которые можно использовать для декомпозиции и многократного использования.</para>
<para>Фреймы представляют собой части экранов, которые применяются для декомпозиции и многократного использования.</para>
<para>Для подключения фрейма в XML экрана используется элемент <link linkend="gui_IFrame">
<sgmltag>iframe</sgmltag>
</link> c указанием либо пути к файлу XML фрейма, либо идентификатора фрейма, если он зарегистрирован в <link linkend="screens.xml">
@ -3820,7 +3820,7 @@ create index IDX_SALES_DOC_CARD on SALES_DOC (CARD_ID)^</programlisting></para>
</section>
<section id="screen_simple">
<title>Простой экран</title>
<para>Простой экран предназначен для отображения и редактирования произвольной информации, в том числе отдельных экемпляров и списков сущностей. Данный тип экрана имеет только базовую функциональность, позволяющую отобразить его в главном окне системы, закрыть, а также работать с <link linkend="datasources">источниками данных</link>.</para>
<para>Простой экран предназначен для отображения и редактирования произвольной информации, в том числе отдельных экземпляров и списков сущностей. Данный тип экрана имеет только базовую функциональность, позволяющую отобразить его в главном окне системы, закрыть, а также работать с <link linkend="datasources">источниками данных</link>.</para>
<para>Идентификатор экрана в файле <link linkend="screens.xml">
<filename>screens.xml</filename>
</link> может быть произвольного вида.</para>
@ -9448,116 +9448,123 @@ where
</section>
<section id="background_tasks">
<title>Фоновые задачи</title>
<para><emphasis role="bold">Предназначение</emphasis></para>
<para>Фоновые задачи используются на клиентском уровне для выполнения длительных процессов без заморозки пользовательского интерфейса. </para>
<para>В платформе реализован объект <code>BackgroundWorker</code>, который предоставляется окружением и управляет фоновыми задачами</para>
<programlisting>BackgroundWorker backgroundWorker = AppConfig.getBackgroundWorker();</programlisting>
<para><emphasis role="bold">Использование</emphasis></para>
<orderedlist>
<listitem>
<para>Задача описывается как наследник абстрактного класса <code>BackgroundTask</code>. Для нее необходимо задать окно, которому принадлежит задача, и описать главный метод <methodname>run()</methodname>.</para>
</listitem>
<listitem>
<para>Создается объект управления задачей <code>BackgroundTaskHandler</code>. Для этого задачу необходимо передать классу <code>BackgroundWorker</code>.</para>
</listitem>
<listitem>
<para>Выполняется запуск задачи</para>
<programlisting>// Задача с ограничением 10 секунд и с текущим окном в качестве родителя
final BackgroundTask&lt;Integer, Void&gt; progressIndicator = new BackgroundTask&lt;Integer, Void&gt;(10, this) {
<section>
<title>Назначение</title>
<para>Фоновые задачи используются на клиентском уровне для асинхронного выполнения длительных операций без заморозки пользовательского интерфейса. </para>
<para>В платформе реализован бин <code>BackgroundWorker</code>, который управляет фоновыми задачами. Ссылка на него может быть получена инжекцией в <link linkend="screen_controller">контроллер экрана</link>, либо статическим методом класса <code>AppBeans</code>.</para>
</section>
<section><title>Использование</title><orderedlist>
<listitem>
<para>Задача описывается как наследник абстрактного класса <code>BackgroundTask</code>. В конструктор задачи необходимо передать ссылку на контроллер экрана, с которым будет связана задача, и значение таймаута ее выполнения.</para>
<para>Если экран указан, то при его закрытии пользователем активная задача будет прервана. Кроме того, задача будет автоматически прервана по истечении указанного таймаута.</para>
<para>Собственно действия, выполняемые задачей, реализуются в методе <code>run()</code>.</para>
</listitem>
<listitem>
<para>Создается объект управления задачей <code>BackgroundTaskHandler</code>. Для этого экземпляр задачи необходимо передать методу <code>handle()</code> бина <code>BackgroundWorker</code>.</para>
</listitem>
<listitem>
<para>Выполняется запуск задачи.</para>
</listitem>
</orderedlist>Пример:<programlisting>// Задача с ограничением 10 секунд и с текущим экраном в качестве владельца
BackgroundTask&lt;Integer, Void&gt; task = new BackgroundTask&lt;Integer, Void&gt;(10, this) {
@Override
public Void run(TaskLifeCycle&lt;T&gt; taskLifeCycle) {
// 1 2 3 4 5 :-)
public Void run(TaskLifeCycle&lt;Integer&gt; taskLifeCycle) throws Exception {
for (int i = 0; i &lt; 5; i++) {
Long res;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ignored) {
return null;
}
// Оглашаем прогресс
taskLifeCycle.publish(i);
TimeUnit.SECONDS.sleep(1);
}
return null;
}
@Override
public void canceled() {
// отменено
// Действия в UI потоке при прерывании задачи
}
@Override
public void done(Void result) {
// завершено
// Действия в UI потоке при завершении задачи
}
@Override
public void progress(List&lt;Integer&gt; changes) {
// индикация прогресса
}
@Override
public Map&lt;String, Object&gt; getParams() {
// передаём параметры
return Collections.emptyMap();
// Действия в UI потоке для индикации прогресса
}
};
// Получение управляющего объекта и запуск
BackgroundTaskHandler taskHandler = backgroundWorker.handle(progressIndicator);
taskHandler.execute();</programlisting>
</listitem>
</orderedlist>
<para><emphasis role="bold">Объект задачи</emphasis></para>
<para><code>BackgroundTask&lt;T, V&gt;</code> параметризованный класс:</para>
<itemizedlist>
<listitem>
<para><code>T</code> тип объектов, показывающих прогресс задачи. Они передаются в метод <methodname>progress()</methodname> при вызове <methodname>publish()</methodname> в рабочем потоке</para>
</listitem>
<listitem>
<para><code>V</code> тип результата задачи, его можно получить после выполнения задачи или вызвать синхронно <methodname>getResult()</methodname> для ожидания.</para>
</listitem>
</itemizedlist>
<para>Метод <methodname>canceled()</methodname> вызывается только в случае управляемой отмены задачи (то есть при вызове <methodname>cancel()</methodname> у <code>TaskHandler</code>).</para>
<para>Если у задачи истек таймаут, или было закрыто окно, в котором она исполнялась, то задача будет завершена без уведомлений.</para>
<warning>
<para>Следует помнить, что в Java невозможно прервать поток, если он не использует операций, выбрасывающих <errorname>InterrruptedException</errorname>. Никогда не перехватывайте это исключение или все исключения с целью тихо завершить операцию. Хорошим тоном является проверка флага <code>isInterrupted()</code> у объекта <code>TaskLifeCycle</code> в различных циклических операциях, для того чтобы вовремя отменить выполнение при прерывании задачи.</para>
</warning>
<para>Объекты <code>BackgroundTask</code> не имеют состояния. Если придерживаться этого подхода и не заводить полей для хранения промежуточных данных, то можно использовать множество параллельно работающих задач, используя всего один объект задачи.</para>
<para>Объект <code>BackgroundHandler</code> можно запускать всего один раз; если требуется частый перезапуск задач, то используйте <code>BackgroundTaskWrapper</code></para>
<para><emphasis role="bold">Отображение фоновых действий для пользователя</emphasis></para>
<para>Иногда необходимо показывать пользователю окно с прогрессом и кнопкой <guibutton>Отмена</guibutton>. Для этого есть <code>BackgroundWorkWindow&lt;T,V&gt;</code> с набором статических методов.
В окне можно отображать статус задачи и разрешать/запрещать отмену фонового процесса.</para>
<para><emphasis role="bold">Отслеживание исполнения задач</emphasis></para>
<para>Если Вы хотите использовать параметры из UI компонентов, то необходимо переопределить метод <methodname>Map&lt;String, Object&gt; getParams()</methodname> . Он выполняется один раз при запуске задачи в потоке UI. В методе <methodname>run</methodname> они доступны в объекте <code>TaskLifeCycle</code>, аксессор <methodname>getParams()</methodname>.</para>
<para>При возникновении исключительных ситуаций вызывается метод <methodname>handleException</methodname>, в котором можно отобразить ошибку на UI.</para>
<para>Для отмены и удаления зависших задач предусмотрены следующие меры:</para>
<orderedlist>
<listitem>
<para><code>WatchDog</code> поток, постоянно проверяющий задачи на истечение таймаута. Зависшие задачи прерываются и удаляются из обработки</para>
</listitem>
<listitem>
<para>При закрытии родительского окна задачи она прерывается</para>
</listitem>
<listitem>
<para>По истечению сессии пользователя все его задачи прерываются.
Для этого в <filename>web.xml</filename> указать:</para>
<programlisting>&lt;listener&gt;
&lt;listener-class&gt;com.haulmont.cuba.web.gui.utils.BackgroundWorkerListener&lt;/listener-class&gt;
&lt;/listener&gt;</programlisting>
</listitem>
</orderedlist>
<para><emphasis role="bold">Объявление WatchDog</emphasis></para>
<para>В <filename>app-web-spring.xml</filename> и <filename>app-desktop-spring.xml</filename> добавить объявление задачи по расписанию:</para>
<programlisting>&lt;bean id=&quot;backgroundWorkerScheduler&quot; class=&quot;org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler&quot;&gt;
BackgroundTaskHandler taskHandler = backgroundWorker.handle(task);
taskHandler.execute();</programlisting><para>Подробная информация о назначении методов приведена в JavaDocs классов <code>BackgroundTask</code>, <code>TaskLifeCycle</code>, <code>BackgroundTaskHandler</code>. </para><para>Ниже приведены моменты, на которые следует обратить внимание:</para><itemizedlist>
<listitem>
<para><code>BackgroundTask&lt;T, V&gt;</code> параметризованный класс:</para>
<itemizedlist>
<listitem>
<para><code>T</code> тип объектов, показывающих прогресс задачи. Объекты этого типа передаются в метод <code>progress()</code> задачи при вызове <code>TaskLifeCycle.publish()</code> в рабочем потоке.</para>
</listitem>
<listitem>
<para><code>V</code> тип результата задачи, он передается в метод <code>done()</code>. Его также можно получить вызовом метода <code>BackgroundTaskHandler.getResult()</code>, что приведет к ожиданию завершения задачи.</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>Метод <code>canceled()</code> вызывается только в случае управляемой отмены задачи, то есть при вызове <code>cancel()</code> у <code>TaskHandler</code>.</para>
</listitem>
<listitem>
<para>Если у задачи истек таймаут, или было закрыто окно, в котором она исполнялась, то задача будет завершена без уведомлений. В блоке Web Client завершение по таймауту производится с задержкой, задаваемой свойством приложения <property>
<link linkend="cuba.backgroundWorker.maxClientLatencySeconds">cuba.backgroundWorker.maxClientLatencySeconds</link>
</property>.</para>
</listitem>
<listitem>
<para>Метод <code>run()</code> задачи должен поддерживать возможность прерывания извне. Для этого в долгих процессах желательно периодически проверять флаг <code>TaskLifeCycle.isInterrupted()</code>, и соответственно завершать выполнение. Кроме того, нельзя тихо проглатывать исключение <code>InterruptedException</code> (или вообще все исключения). Вместо этого нужно либо вообще не перехватывать его, либо выполнять корректный выход из метода.</para>
</listitem>
<listitem>
<para>Объекты <code>BackgroundTask</code> не имеют состояния. Если при реализации конкретного класса задачи не заводить полей для хранения промежуточных данных, то можно запускать несколько параллельно работающих процессов, используя единственный экземпляр задачи.</para>
</listitem>
<listitem>
<para>Объект <code>BackgroundHandler</code> можно запускать (т.е. вызывать его метод <code>execute()</code>) всего один раз. Если требуется частый перезапуск задачи, то используйте класс <code>BackgroundTaskWrapper</code>.</para>
</listitem>
<listitem>
<para>Для показа пользователю модального окна с прогрессом и кнопкой <guibutton>Отмена</guibutton> используйте классы <code>BackgroundWorkWindow</code> или <code>BackgroundWorkProgressWindow</code> с набором статических методов.
Для окна можно задать режим отображения прогресса и разрешить или запретить отмену фоновой задачи.</para>
</listitem>
<listitem>
<para>Если внутри потока задачи необходимо использовать некоторые значения визуальных компонентов, то нужно реализовать их получение в методе <code>getParams()</code>, который выполняется в потоке UI один раз при запуске задачи. В методе <methodname>run()</methodname> эти параметры будут доступны через метод <code>getParams()</code> объекта <code>TaskLifeCycle</code>.</para>
</listitem>
<listitem>
<para>При возникновении исключительных ситуаций в потоке UI вызывается метод <code>BackgroundTask.handleException()</code>, в котором можно отобразить ошибку.</para>
</listitem>
</itemizedlist></section>
<section>
<title>Настройка окружения</title>
<para>Для корректной работы фоновых задач в проекте приложения необходимо произвести следующие настройки:</para>
<itemizedlist>
<listitem>
<para>Прерывание задач по таймауту реализуется бином <code>WatchDog</code>. Для его периодического вызова в файлы <filename>
<link linkend="spring.xml">spring.xml</link>
</filename> блоков Web Client и Desktop Client необходимо добавить следующее объявление:<programlisting>&lt;bean id=&quot;backgroundWorkerScheduler&quot; class=&quot;org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler&quot;&gt;
&lt;property name=&quot;daemon&quot; value=&quot;true&quot;/&gt;
&lt;property name=&quot;poolSize&quot; value=&quot;1&quot;/&gt;
&lt;/bean&gt;
&lt;task:scheduled-tasks scheduler=&quot;backgroundWorkerScheduler&quot;&gt;
&lt;task:scheduled ref=&quot;cuba_BackgroundWorker_WatchDog&quot; method=&quot;cleanupTasks&quot; fixed-delay=&quot;2000&quot;/&gt;
&lt;/task:scheduled-tasks&gt; </programlisting>
<para><emphasis role="bold">Настройки</emphasis></para>
<para>Для Web слоя в WebConfig настраивается частота проверки изменений на стороне клиента (браузера): <parameter>cuba.backgroundWorker.uiCheckInterval</parameter> (По умолчанию 2000 мс)</para>
&lt;/task:scheduled-tasks&gt; </programlisting></para>
</listitem>
<listitem>
<para>В блоке Web Client необходимо прерывать все задачи пользователя при истечении его HTTP сессии. Для этого нужно в файл <filename>web.xml</filename> модуля web добавить следующий элемент:<programlisting>&lt;listener&gt;
&lt;listener-class&gt;com.haulmont.cuba.web.gui.utils.BackgroundWorkerListener&lt;/listener-class&gt;
&lt;/listener&gt;</programlisting></para>
</listitem>
<listitem>
<para>В блоке Web Client опрос состояния задачи инициируется клиентским кодом, выполняющимся в веб-браузере. Периодичность опроса задается свойством приложения <property>
<link linkend="cuba.backgroundWorker.uiCheckInterval">cuba.backgroundWorker.uiCheckInterval</link>
</property>, по умолчанию - 2 сек.</para>
<para>Кроме того, на выполнение фоновых задач в блоке Web Client влияют свойства приложения <property>
<link linkend="cuba.backgroundWorker.maxActiveTasksCount">cuba.backgroundWorker.maxActiveTasksCount</link>
</property> и <property>
<link linkend="cuba.backgroundWorker.maxClientLatencySeconds">cuba.backgroundWorker.maxClientLatencySeconds</link>
</property>.</para>
</listitem>
</itemizedlist>
</section>
</section>
<section>
<title>Таймеры</title>

View File

@ -732,6 +732,33 @@ menu-config.sales$Customer.lookup=Customers</programlisting></para>
<para>Используется во всех стандартных <link linkend="app_tiers">блоках</link>.</para>
</listitem>
</varlistentry>
<varlistentry id="cuba.backgroundWorker.maxActiveTasksCount">
<term>cuba.backgroundWorker.maxActiveTasksCount</term>
<listitem>
<para>Максимальное количество активных <link linkend="background_tasks">фоновых задач</link>.</para>
<para>Значение по умолчанию: <literal>100</literal></para>
<para>Интерфейс: <code>WebConfig</code></para>
<para>Используется в блоке <structname>Web Client</structname>.</para>
</listitem>
</varlistentry>
<varlistentry id="cuba.backgroundWorker.maxClientLatencySeconds">
<term>cuba.backgroundWorker.maxClientLatencySeconds</term>
<listitem>
<para>Задержка в секундах, которая добавляется к таймауту <link linkend="background_tasks">фоновой задачи</link>, прежде чем она будет прервана механизмом <code>WatchDog</code>. Отражает возможные сетевые задержки при опросе статуса задачи.</para>
<para>Значение по умолчанию: <literal>60</literal></para>
<para>Интерфейс: <code>WebConfig</code></para>
<para>Используется в блоке <structname>Web Client</structname>.</para>
</listitem>
</varlistentry>
<varlistentry id="cuba.backgroundWorker.uiCheckInterval">
<term>cuba.backgroundWorker.uiCheckInterval</term>
<listitem>
<para>Период опроса состояния <link linkend="background_tasks">фоновых задач</link>.</para>
<para>Значение по умолчанию: <literal>2000</literal></para>
<para>Интерфейс: <code>WebConfig</code></para>
<para>Используется в блоке <structname>Web Client</structname>.</para>
</listitem>
</varlistentry>
<varlistentry id="cuba.confDir">
<term>cuba.confDir</term>
<listitem>

View File

@ -14,6 +14,9 @@ import com.haulmont.cuba.gui.executors.impl.TaskHandlerImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.annotation.ManagedBean;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.swing.*;
import java.util.Collections;
import java.util.List;
@ -29,12 +32,14 @@ import static com.google.common.base.Preconditions.checkNotNull;
* @author artamonov
* @version $Id$
*/
@ManagedBean(BackgroundWorker.NAME)
public class DesktopBackgroundWorker implements BackgroundWorker {
private Log log = LogFactory.getLog(DesktopBackgroundWorker.class);
private WatchDog watchDog;
@Inject
public DesktopBackgroundWorker(WatchDog watchDog) {
this.watchDog = watchDog;
}
@ -98,6 +103,7 @@ public class DesktopBackgroundWorker implements BackgroundWorker {
}
@Override
@Nonnull
public Map<String, Object> getParams() {
return params;
}

View File

@ -9,12 +9,14 @@ package com.haulmont.cuba.desktop.gui.executors.impl;
import com.haulmont.cuba.gui.executors.impl.TaskHandlerImpl;
import com.haulmont.cuba.gui.executors.impl.TasksWatchDog;
import javax.annotation.ManagedBean;
import javax.swing.*;
/**
* @author artamonov
* @version $Id$
*/
@ManagedBean(TasksWatchDog.NAME)
public class DesktopTasksWatchDog extends TasksWatchDog {
@Override

View File

@ -85,27 +85,4 @@
</property>
</bean>
<!-- Background Tasks -->
<bean id="cuba_BackgroundWorker_WatchDog" class="com.haulmont.cuba.desktop.gui.executors.impl.DesktopTasksWatchDog"/>
<bean id="cuba_BackgroundWorker" class="com.haulmont.cuba.desktop.gui.executors.impl.DesktopBackgroundWorker">
<constructor-arg index="0" ref="cuba_BackgroundWorker_WatchDog"/>
</bean>
<!--
To use BackgroundWorker in a project add to {app}-desktop-spring.xml this scheduler definition:
<bean id="backgroundWorkerScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<property name="daemon" value="true"/>
<property name="poolSize" value="1"/>
</bean>
<task:scheduled-tasks scheduler="backgroundWorkerScheduler">
<task:scheduled ref="cuba_BackgroundWorker_WatchDog" method="cleanupTasks" fixed-delay="2000"/>
</task:scheduled-tasks>
-->
</beans>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
* Copyright (c) 2013 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
@ -15,13 +15,26 @@ import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Background task for execute in {@link BackgroundWorker}
* <p>
* <b>It is strongly recommended to be able to interrupt working thread. <br/>
* Don't ignore {@link InterruptedException} or its ancestors.</b>
* </p>
* Background task for execute by {@link BackgroundWorker}.
* <p/> If the task is associated with a screen through ownerWindow constructor parameter, it will be canceled when
* the screen is closed.
* <p/> If timeout passed to constructor is exceeded, the task is canceled by special {@link WatchDog} thread.
*
* @param <T> measure unit which shows progress of task
* <p/> Simplest usage example:
* <pre>
* BackgroundTask<Integer, Void> task = new BackgroundTask<Integer, Void>(10, this) {
* public Void run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
* for (int i = 0; i < 5; i++) {
* TimeUnit.SECONDS.sleep(1);
* }
* return null;
* }
* };
* BackgroundTaskHandler taskHandler = backgroundWorker.handle(task);
* taskHandler.execute();
* </pre>
*
* @param <T> task progress measurement unit
* @param <V> result type
* @author artamonov
* @version $Id$
@ -37,11 +50,11 @@ public abstract class BackgroundTask<T, V> {
Collections.synchronizedList(new ArrayList<ProgressListener<T, V>>());
/**
* Task with timeout
* Create a task with timeout.
*
* @param timeout Timeout
* @param timeUnit Time unit
* @param ownerWindow Owner window
* @param timeout timeout
* @param timeUnit timeout time unit
* @param ownerWindow owner window
*/
protected BackgroundTask(long timeout, TimeUnit timeUnit, Window ownerWindow) {
this.ownerWindow = ownerWindow;
@ -49,10 +62,11 @@ public abstract class BackgroundTask<T, V> {
}
/**
* Task with timeout
* Create a task with timeout.
* <p/> The task will not be associated with any window.
*
* @param timeout Timeout
* @param timeUnit Time unit
* @param timeout timeout
* @param timeUnit timeout time unit
*/
protected BackgroundTask(long timeout, TimeUnit timeUnit) {
this.ownerWindow = null;
@ -60,9 +74,10 @@ public abstract class BackgroundTask<T, V> {
}
/**
* Task with timeout in default SECONDS unit
* Create a task with timeout in default SECONDS unit.
* <p/> The task will not be associated with any window.
*
* @param timeoutSeconds Timeout in seconds
* @param timeoutSeconds timeout in seconds
*/
protected BackgroundTask(long timeoutSeconds) {
this.ownerWindow = null;
@ -70,10 +85,10 @@ public abstract class BackgroundTask<T, V> {
}
/**
* Task with timeout in default SECONDS unit
* Create a task with timeout in default SECONDS unit.
*
* @param timeoutSeconds Timeout in seconds
* @param ownerWindow Owner window
* @param timeoutSeconds timeout in seconds
* @param ownerWindow owner window
*/
protected BackgroundTask(long timeoutSeconds, Window ownerWindow) {
this.ownerWindow = ownerWindow;
@ -81,112 +96,148 @@ public abstract class BackgroundTask<T, V> {
}
/**
* Main tasks method
* Main method that performs a task.
* <p/> Called by the execution environment in a separate working thread.
*
* @param taskLifeCycle Task life cycle
* @return Result
* @throws Exception exception in worker thread
* <p/> Implementation of this method should support interruption:
* <ul>
* <li/> In long loops check {@link TaskLifeCycle#isInterrupted()} and return if it is true
* <li/> Don't swallow {@link InterruptedException} - return from the method or don't catch it at all
* </ul>
*
* @param taskLifeCycle lifecycle object that allows the main method to interact with the execution environment
* @return task result
* @throws Exception exception in working thread
*/
public abstract V run(TaskLifeCycle<T> taskLifeCycle) throws Exception;
/**
* Task completed handler
* Called by the execution environment in UI thread when the task is completed.
*
* @param result of execution
* @param result result of execution returned by {@link #run(TaskLifeCycle)} method
*/
public void done(V result) {
}
/**
* Task canceled handler
* Called by the execution environment in UI thread if the task is canceled by
* {@link BackgroundTaskHandler#cancel()} invocation.
* <p/> This method is not called in case of timeout expiration or owner window closing.
*/
public void canceled() {
}
/**
* Task canceled by watchdog handler
* Called by the execution environment in UI thread if the task timeout is exceeded.
*
* @return True if handled
* @return true if this method implementation actualy handles this event. Used for chaining handlers.
*/
public boolean handleTimeoutException() {
return false;
}
/**
* Handle exception
* Called by the execution environment in UI thread if the task {@link #run(TaskLifeCycle)} method raised an
* exception.
*
* @param ex Exception
* @return True if exception handled
* @param ex exception
* @return true if this method implementation actualy handles the exception. Used for chaining handlers.
*/
public boolean handleException(Exception ex) {
return false;
}
/**
* On progress change
* Called by the execution environment in UI thread on progress change.
*
* @param changes Changes list
* @param changes list of changes since previous invocation
*/
public void progress(List<T> changes) {
}
/**
* Synchronous get parameters for run from UI
* Called by the execution environment in UI thread to prepare some execution parameters. These parameters can be
* requested by the working thread inside the {@link #run(TaskLifeCycle)} method by calling
* {@link TaskLifeCycle#getParams()}.
*
* @return Run parameters
* @return parameters map or null if parameters are not needed
*/
public Map<String, Object> getParams() {
return null;
}
/**
* @return owner window
*/
public final Window getOwnerWindow() {
return ownerWindow;
}
/**
* @return timeout in ms
*/
public final long getTimeoutMilliseconds() {
return timeoutMilliseconds;
}
/**
* @return timeout in sec
*/
public final long getTimeoutSeconds() {
return TimeUnit.MILLISECONDS.toSeconds(timeoutMilliseconds);
}
/**
* Add additional progress listener.
* @param progressListener listener
*/
public final void addProgressListener(ProgressListener<T, V> progressListener) {
if (!progressListeners.contains(progressListener))
progressListeners.add(progressListener);
}
/**
* Additional progress listeners.
* @return copy of the progress listeners collection
*/
public final List<ProgressListener<T, V>> getProgressListeners() {
return new ArrayList<>(progressListeners);
}
/**
* Remove a progress listener.
* @param progressListener listener
*/
public final void removeProgressListener(ProgressListener<T, V> progressListener) {
progressListeners.remove(progressListener);
}
/**
* Listener for task life cycle
* Listener of the task life cycle events, complementary to the tasks own methods:
* {@link BackgroundTask#progress(java.util.List)}, {@link BackgroundTask#done(Object)},
* {@link com.haulmont.cuba.gui.executors.BackgroundTask#canceled()}.
*
* @param <T> Progress unit
* @param <V> Result
* @param <T> progress measurement unit
* @param <V> result type
*/
public interface ProgressListener<T, V> {
/**
* On task progress changed
* Called by the execution environment in UI thread on progress change.
*
* @param changes Progress units
* @param changes list of changes since previous invocation
*/
void onProgress(List<T> changes);
/**
* On task completed
* Called by the execution environment in UI thread when the task is completed.
*
* @param result Result
* @param result result of execution returned by {@link #run(TaskLifeCycle)} method
*/
void onDone(V result);
/**
* On task canceled
* Called by the execution environment in UI thread if the task is canceled.
*/
void onCancel();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
* Copyright (c) 2013 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
@ -7,7 +7,7 @@
package com.haulmont.cuba.gui.executors;
/**
* Task handler for {@link BackgroundTask}
* Task handler for {@link BackgroundTask}.
*
* @author artamonov
* @version $Id$
@ -15,43 +15,37 @@ package com.haulmont.cuba.gui.executors;
public interface BackgroundTaskHandler<V> {
/**
* Execute<br/>
* If the task appears to hang then it will be canceled
* Execute the {@link BackgroundTask}.
* <p/> This method must be called only once for a handler instance.
*/
void execute();
/**
* Try to cancel task
* Cancel task.
*
* @return True if canceled
* @return true if canceled, false if the task was not started or is already stopped
*/
boolean cancel();
/**
* Synchronous get result from execution
* Wait for the task completion and return its result.
*
* @return Result
* @return task's result returned from {@link BackgroundTask#run(TaskLifeCycle)} method
*/
V getResult();
/**
* Done flag
*
* @return True if task is already done
* @return true if the task is completed
*/
boolean isDone();
/**
* Canceled flag
*
* @return True if task has been canceled
* @return true if the task has been canceled
*/
boolean isCancelled();
/**
* Alive flag
*
* @return True if task is running
* @return true if the task is running
*/
boolean isAlive();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
* Copyright (c) 2013 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
@ -9,6 +9,8 @@ package com.haulmont.cuba.gui.executors;
import com.haulmont.cuba.gui.AppConfig;
/**
* Simple wrapper to a {@link BackgroundTask} to support restarting execution of the same task.
*
* @author artamonov
* @version $Id$
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
* Copyright (c) 2013 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
@ -7,20 +7,22 @@
package com.haulmont.cuba.gui.executors;
/**
* Task executor service for GUI layer
* Entry point to {@link BackgroundTask} execution functionality.
*
* @author artamonov
* @version $Id$
*/
public interface BackgroundWorker {
String NAME = "cuba_BackgroundWorker";
/**
* Create handler for background task
* Create handler for a background task. The handler is used to control the task execution.
*
* @param <T> progress measure unit
* @param task heavy background task
* @return Task handler
* @param <V> task result type
* @param task background task instance
* @return task handler
*/
<T, V> BackgroundTaskHandler<V> handle(BackgroundTask<T, V> task);
}

View File

@ -1,24 +1,27 @@
/*
* Copyright (c) 2012 Haulmont Technology Ltd. All Rights Reserved.
* Copyright (c) 2013 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
package com.haulmont.cuba.gui.executors;
import javax.annotation.Nonnull;
import java.util.Map;
/**
* Life cycle object for task
* Lifecycle object that is passed to {@link BackgroundTask#run(TaskLifeCycle)} method to allow working thread to
* interact with the execution environment.
*
* @param <T> task progress measurement unit
*
* @param <T> measure unit which shows progress of task
* @author artamonov
* @version $Id$
*/
public interface TaskLifeCycle<T> {
/**
* Publish changes from working thread
* Publish changes to show progress.
*
* @param changes Changes
*/
@ -26,12 +29,13 @@ public interface TaskLifeCycle<T> {
void publish(T... changes);
/**
* @return True if working thread is interrupted
* @return true if the working thread has been interrupted
*/
boolean isInterrupted();
/**
* @return Read-only run parameters
* @return execution parameters that was set by {@link BackgroundTask#getParams()}
*/
@Nonnull
Map<String, Object> getParams();
}

View File

@ -1,29 +1,33 @@
/*
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
* Copyright (c) 2013 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
package com.haulmont.cuba.gui.executors.impl;
import com.haulmont.cuba.core.global.TimeProvider;
import com.haulmont.cuba.core.global.TimeSource;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.gui.executors.BackgroundTaskHandler;
import com.haulmont.cuba.gui.executors.WatchDog;
import javax.inject.Inject;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* WatchDog for BackgroundWorker
* WatchDog for {@link com.haulmont.cuba.gui.executors.BackgroundWorker}.
*
* @author artamonov
* @version $Id$
*/
public abstract class TasksWatchDog implements WatchDog {
@Inject
protected TimeSource timeSource;
private final Set<TaskHandlerImpl> watches;
public TasksWatchDog() {
@ -38,7 +42,7 @@ public abstract class TasksWatchDog implements WatchDog {
if (!AppContext.isStarted())
return;
long actual = TimeProvider.currentTimestamp().getTime();
long actual = timeSource.currentTimestamp().getTime();
List<BackgroundTaskHandler> forRemove = new LinkedList<>();
for (TaskHandlerImpl task : watches) {

View File

@ -6,7 +6,10 @@
package com.haulmont.cuba.web.gui.executors.impl;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.TimeSource;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.core.sys.SecurityContext;
import com.haulmont.cuba.gui.components.Timer;
@ -19,6 +22,9 @@ import com.haulmont.cuba.web.gui.WebTimer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.annotation.ManagedBean;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.*;
import static com.google.common.base.Preconditions.checkNotNull;
@ -29,12 +35,20 @@ import static com.google.common.base.Preconditions.checkNotNull;
* @author artamonov
* @version $Id$
*/
@ManagedBean(BackgroundWorker.NAME)
public class WebBackgroundWorker implements BackgroundWorker {
private Log log = LogFactory.getLog(WebBackgroundWorker.class);
private WatchDog watchDog;
@Inject
private UserSessionSource userSessionSource;
@Inject
private Configuration configuration;
@Inject
public WebBackgroundWorker(WatchDog watchDog) {
this.watchDog = watchDog;
}
@ -192,7 +206,7 @@ public class WebBackgroundWorker implements BackgroundWorker {
this.params = Collections.emptyMap();
securityContext = AppContext.getSecurityContext();
userId = UserSessionProvider.getUserSession().getId();
userId = userSessionSource.getUserSession().getId();
}
@Override
@ -216,6 +230,7 @@ public class WebBackgroundWorker implements BackgroundWorker {
}
@Override
@Nonnull
public Map<String, Object> getParams() {
return params;
}
@ -304,7 +319,7 @@ public class WebBackgroundWorker implements BackgroundWorker {
@Override
public final void startExecution() {
WebConfig webConfig = ConfigProvider.getConfig(WebConfig.class);
WebConfig webConfig = configuration.getConfig(WebConfig.class);
int activeTasksCount = watchDog.getActiveTasksCount();
if (activeTasksCount >= webConfig.getMaxActiveBackgroundTasksCount())

View File

@ -1,27 +1,33 @@
/*
* Copyright (c) 2012 Haulmont Technology Ltd. All Rights Reserved.
* Copyright (c) 2013 Haulmont Technology Ltd. All Rights Reserved.
* Haulmont Technology proprietary and confidential.
* Use is subject to license terms.
*/
package com.haulmont.cuba.web.gui.executors.impl;
import com.haulmont.cuba.core.global.ConfigProvider;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.gui.executors.impl.TaskHandlerImpl;
import com.haulmont.cuba.gui.executors.impl.TasksWatchDog;
import com.haulmont.cuba.web.WebConfig;
import javax.annotation.ManagedBean;
import javax.inject.Inject;
import java.util.concurrent.TimeUnit;
/**
* @author artamonov
* @version $Id$
*/
@ManagedBean(TasksWatchDog.NAME)
public class WebTasksWatchDog extends TasksWatchDog {
@Inject
protected Configuration configuration;
@Override
protected boolean checkHangup(long actualTimeMs, TaskHandlerImpl taskHandler) {
WebConfig webConfig = ConfigProvider.getConfig(WebConfig.class);
WebConfig webConfig = configuration.getConfig(WebConfig.class);
long timeout = taskHandler.getStartTimeStamp();
long latencyMs = TimeUnit.SECONDS.toMillis(webConfig.getClientBackgroundTasksLatencySeconds());

View File

@ -100,12 +100,4 @@
<property name="poolSize" value="10"/>
</bean>
<!-- Background tasks support -->
<bean id="cuba_BackgroundWorker_WatchDog" class="com.haulmont.cuba.web.gui.executors.impl.WebTasksWatchDog"/>
<bean id="cuba_BackgroundWorker" class="com.haulmont.cuba.web.gui.executors.impl.WebBackgroundWorker">
<constructor-arg index="0" ref="cuba_BackgroundWorker_WatchDog"/>
</bean>
</beans>