长任务(LongTask)功能是在构建在 长连接(SEUG) 基础之上的。用于完成那些需要在后台执行较长时间的任务,这些任务的执行时长可以是几十秒、几分钟、几小时、甚至更长,对于这种很长的任务用户几乎不可能一直在网页端等待他们执行结束,但是他们往往又需要随时了解该任务的执行状况 ,或者对正在执行的任务进行一些调度管理。长任务(LongTask)功能正是为了解决用户的上述困扰而提供的。
LongTask的Client端
使用LongTask的方法与使用AjaxAction的方法有些类似,在IDE工具栏的Action中可以找到LongTask这样一个组件,它有如下几个重要的属性...
- taskName - 该属性的含义与AjaxAction中的service属性是一致的,表示一个后台长任务的服务名称。
- appearence - 用于指定系统如何向用户展示当前正在执行任务的信息。其中mainTask表示显示一个模态的提示器;daemonTask表示显示一个非模态的提示器;none表示不现实任何提示,此时用户可以利用LongTask的事件自行确定如何展示任务的执行状态。
- disableOnActive - 表示是否要在检测到有正在执行的任务时自动禁用本控件。
作为一个最简单的例子,我们可以直接在LongTask的taskName中定义一个字符串,如 #simpleTask,根据Dorado7.3.2中开始提供的新特性,此种简略写法实际表示的值是:<小写字符开头的View名称>#simpleTask。假设我们的View的文件名是TestLongTask.view.xml,那么#simpleTask实际表示testLongTask#simpleTask。(此规则同样适用于AjaxAction.service,DataSet.dataProvider,UpdateAction.dataResolver等属性)
LongTask的Server端
按照默认的开发讨论,我们接下来可以直接在一个名为TestLongTask的JavaBean编写LongTask后端逻辑代码了。例如...
@Component public class TestLongTask { @Expose public LongTask simpleTask() { return new LongTask() { public Object call() throws Exception { Thread.sleep(5000); return null; } }; } }
从上面的代码可见,定义的LongTask后端的方法与Ajax后端的方法非常相似,唯一的要求是此方法必须返回一个LongTask对象。LongTask是java.util.concurrent.Callable<Object>的实现类,会由LongTask的调度器在另一个线程中执行。在最为简单的示例中,我们只需要实现LongTask的call()方法,就已经完成了一个长任务。
LongTask类中有这样几个常用的方法...
- setStateInfo() - 每次设置的TaskStateInfo对象包含三部分的信息——state、text和data。其中state是enum类型的状态代码,而text和data是当前状态附带的信息,您可以根据自己的需要给text和data设置任意的值。目前Dorado支持的任务状态有....
- waiting - 等待,例如当任务调度器发现目前正在执行某个任务的实例已经达到了上限,而用户仍希望再开启新的长任务,那么新的长任务将会进入等待队列,即等待状态。
- running - 正在执行。
- suspending - 正在尝试挂起。
- suspended - 挂起。
- resuming - 正在尝试恢复执行。
- terminated - 执行结束。
- aborting - 正在中止,即取消执行。
- aborted - 已终止,即已取消。
- error - 出错并以退出。
- appendLog() - 向客户端输出日志信息,通常是TaskLog对象封装的一段文本。
LongTask的调度
在声明一个LongTask的方法时,我们还可以利用@TaskScheduler来为长任务配置简单的调度信息。@TaskScheduler包含以下几个属性...
- scope - 任务的可见范围,目前包含两种取值 session(默认)和application。
- maxRunning - 最大可运行实例数。
- maxWaiting - 最大等待队列的长度。
- impl - 自定义任务调度器的实现类。(待补充...)
实时控制
在LongTask执行的过程中,我们可以通过客户端LongTask的suspend()、resume()、abort()等方法来暂停、恢复或中止LongTask的执行。不过这三个方法都不能自动的完成所有的工作,通常用户都需要编写一定的代码才能真正的实现对LongTask的执行控制。
以abort()操作为例,当用户在Client端调用了abort()方法后,Server端的LongTask对象会自动进入aborting状态,但是LongTask并不会自动的停下来,它仍会以正常的方式继续执行。因为我们不能强行的杀死LongTask目前的执行线程,这种做法是不受推荐且存在隐患的。开发者需要自行判断aborting状态,并且决定以何种方式让LongTask优雅的退出执行。这听起来有些麻烦,不过好在在绝大部分情况下我们并不需要去控制LongTask的执行过程。
下面是一个支持suspend()、resume()、abort()这三种操作的LongTask的代码实例,可以从这里获得一些启发...
@Expose public LongTask copyFiles() { return new LongTask() { public Object call() throws Exception { for (File file: files) { synchronized (this) { if (isSuspendRequired()) { setStateInfo(new TaskStateInfo(TaskState.suspended); wait(); } if (isAbortRequired()) { setStateInfo(new TaskStateInfo(TaskState.aborted)); break; } FileUtils.copy(file, destDir); } } return null; } protected void doResume() { notify(); setStateInfo(new TaskStateInfo(TaskState.running)); } protected void doAbort() { notify(); } }; }