mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-03 03:38:33 +08:00
BackgroundTasks JavaDocs and documentation. #PL-1930
This commit is contained in:
parent
5852963a75
commit
c0d339e923
@ -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<Integer, Void> progressIndicator = new BackgroundTask<Integer, Void>(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<Integer, Void> task = new BackgroundTask<Integer, Void>(10, this) {
|
||||
@Override
|
||||
public Void run(TaskLifeCycle<T> taskLifeCycle) {
|
||||
// 1 2 3 4 5 :-)
|
||||
public Void run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
|
||||
for (int i = 0; i < 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<Integer> changes) {
|
||||
// индикация прогресса
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getParams() {
|
||||
// передаём параметры
|
||||
return Collections.emptyMap();
|
||||
// Действия в UI потоке для индикации прогресса
|
||||
}
|
||||
};
|
||||
|
||||
// Получение управляющего объекта и запуск
|
||||
BackgroundTaskHandler taskHandler = backgroundWorker.handle(progressIndicator);
|
||||
taskHandler.execute();</programlisting>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
<para><emphasis role="bold">Объект задачи</emphasis></para>
|
||||
<para><code>BackgroundTask<T, V></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<T,V></code> с набором статических методов.
|
||||
В окне можно отображать статус задачи и разрешать/запрещать отмену фонового процесса.</para>
|
||||
<para><emphasis role="bold">Отслеживание исполнения задач</emphasis></para>
|
||||
<para>Если Вы хотите использовать параметры из UI компонентов, то необходимо переопределить метод <methodname>Map<String, Object> 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><listener>
|
||||
<listener-class>com.haulmont.cuba.web.gui.utils.BackgroundWorkerListener</listener-class>
|
||||
</listener></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><bean id="backgroundWorkerScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
|
||||
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<T, V></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><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> </programlisting>
|
||||
<para><emphasis role="bold">Настройки</emphasis></para>
|
||||
<para>Для Web слоя в WebConfig настраивается частота проверки изменений на стороне клиента (браузера): <parameter>cuba.backgroundWorker.uiCheckInterval</parameter> (По умолчанию 2000 мс)</para>
|
||||
</task:scheduled-tasks> </programlisting></para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>В блоке Web Client необходимо прерывать все задачи пользователя при истечении его HTTP сессии. Для этого нужно в файл <filename>web.xml</filename> модуля web добавить следующий элемент:<programlisting><listener>
|
||||
<listener-class>com.haulmont.cuba.web.gui.utils.BackgroundWorkerListener</listener-class>
|
||||
</listener></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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
@ -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$
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
@ -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) {
|
||||
|
@ -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())
|
||||
|
@ -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());
|
||||
|
@ -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>
|
Loading…
Reference in New Issue
Block a user