【IT168 技术文章】JCA 1.5 是 J2EE 连接器体系结构的最新版本,它包含许多重要的改进和一些重要增强。本文介绍的是 JCA 新的工作管理合约。该合约允许资源适配器为延迟执行或定期执行的工作创建计时器,并允许计时器使用应用服务器的线程同步或异步执行处理。本文将描述事务流入支持如何在资源适配器导入到服务器的事务中进行这种处理,以及资源适配器随后如何控制事务的完成。
如果想在现有资源适配器中使用这项功能,或者正在考虑编写新的 JCA 1.5 资源适配器,那么本文是一份必不可少的读物。如果要编写使用资源适配器的应用程序,想了解更多幕后的情况,那么本文也会吸引您。
让工作完成
在本系列的第 1 部分中,我们介绍了 ResourceAdapter 接口,它提供了一种机制,能够在应用服务器内部为资源适配器提供生命周期。您可能还记得 start 方法被用来传递一个叫做 BootstrapContext 的对象。上次我们介绍了这个对象,但是 BootstrapContext 接口的三个方法才是工作管理和事务流入合约的关键,如清单 1 所示:
清单 1. 用来得到三个工具对象的 BootstrapContext 接口
2 WorkManager getWorkManager();
3 XATerminator getXATerminator();
4 Timer createTimer() throws UnavailableException;
5 }
WorkManager 允许资源适配器对工作进行调度,在应用服务器线程上同步或异步执行调度。这个工作可以在资源导入的事务中执行,在这种情况下,XATerminator 有助于完成工作。Timer 负责延迟工作或定期工作的执行。本文将更深入地研究这三个类,并说明如何使用它们。
WorkManager 接口提供了三套处理工作的方法(doWork、 startWork 和 scheduleWork),如清单 2 所示:
清单 2. 用来提交工作项目的WorkManager 接口
2
3 void doWork(Work work) throws WorkException;
4 void doWork(Work work, long startTimeout, ExecutionContext execContext,
5 WorkListener workListener) throws WorkException;
6 long startWork(Work work) throws WorkException;
7 long startWork(Work work, long startTimeout, ExecutionContext execContext,
8 WorkListener workListener) throws WorkException;
9 void scheduleWork(Work work) throws WorkException;
10 void scheduleWork(Work work, long startTimeout,
11 ExecutionContext execContext, WorkListener workListener)
12 throws WorkException;
13
14 }
每个方法接收的第一个参数,都是实现 Work 接口的对象的一个实例,如清单 3 所示:
清单 3. 资源适配器实现的 Work 接口
2 void release();
3
4 }
Work 接口扩展了 Runnable 接口,您应当像直接进行 Java 线程编程时所做的那样,实现run 方法中执行的工作。您很快就会看到 release 方法发挥其作用的地方。
WorkManager 上的 doWork 方法可以让一些工作同步执行、一直受阻塞或者直到某些工作完成才执行。这看起来可能不是特别有用——这不就是直接调用 run 方法时发生的事情吗?并不完全如此。首先,它让应用程序服务器说明现在不是做这项工作的恰当时候。例如,如果在 ResourceAdapter start 方法的范围内调用 doWork,那么您可能发现它将抛出 WorkRejectedException 异常。应当尽快从这个方法返回,如果可能的话,应当把工作安排成异步处理。
第二,如果应用服务器特别繁忙,那么它可能会推迟这项工作的启动。可以用第 2 个startTimeout 参数指明资源适配器准备为工作启动等候多长时间。如果应用服务器没能在这个时间内启动工作,那么就会抛出 WorkRejectedException 异常。WorkManager 接口定义了常量 IMMEDIATE 和 INDEFINITE,它们允许资源适配器指明自己根本不准备等候或者准备一直等候下去。
第三,正如下一节解释的,有可能让工作片断在资源适配器导入的事务上下文中执行,而不是在与当前线程关联的上下文中执行。这正是第 3 个参数的用途,该参数是一个可选的 ExecutionContext。
最后,使用 doWork 方法让应用服务器对资源适配器执行的工作拥有更多控制。在关闭应用服务器时,如果资源适配器夹在一个漫长的、复杂的操作中间,那么服务器不需要等待资源适配器完成,或者被清理干净。相反,服务器可以通过调用 Work 对象的 release 方法,给资源适配器发信号。然后资源适配器应当尽快完成处理。清单 4 显示了 release 方法使用的一个示例:
清单 4. Work 对象的示例
2 private volatile boolean _released;
3 void run() {
4 for (int i = 0; i < 100; i++) {
5 if (_released) break;
6 // Do something
7 }
8 }
9 void release() {
10 _released = true;
11 }
12
13 }
注意清单 4 中使用的关键字 volatile。在 run 方法正进行其处理的同时,会在一个独立的线程上调用 release 方法,volatile 修饰符确保 run 方法能够看到更新的字段。
doWork 方法也可能失败,抛出一个名称古怪的 WorkCompletedException 异常。这个异常是在将 run 方法分配给一个线程时抛出的,但这个异常或者是上下文设置失败,或者是通过抛出运行时异常退出方法。doWork 方法会提供一个错误代码,表示这些故障发生的路径,还把问题原因作为链接的异常提供。