技术开发 频道

JCA 1.5: 工作管理和事务流入

        JCA WorkManager 和 Timer 的替代方案

  正如前一小节提到过的,在 JCA 中,使用 Timer 有一个问题:执行 TimerTask 的线程不在应用服务器的控制之下。因为 Timer 是类而不是接口,而且线程实际是在类的构造函数中产生的, 所以应用服务器不能修改这种行为。允许应用服务器实现这一目的一个替代方案就是使用由 IBM 和 BEA 联合开发的 Timer and Work Manager for Application Servers 规范的接口。

  在使用这个规范的接口时,会从 Java 名称与目录服务接口(JNDI)得到一个 TimerManager 实现。可以通过给管理器安排了一个 TimerListener 实现来返回一个 Timer (是一个commonj.timers.Timer 而不是 java.util.Timer)。在安排好的时间上会调用 TimerListener,可以用 Timer 执行诸如取消预定操作之类的操作。

  顾名思义,这个规范还提供了 JCA 工作管理的一个替代。这里的接口与 JCA 的那些接口很相似,除了初始的 commmonj.work.WorkManager 是从 JNDI 得到的。这个合约提供的附加行为还包括:阻塞到一个或全部预定工作完成的能力、在远程 JVM 上执行可以序列化的工作的可能性。与 commonj Timer 一样,commonj WorkManager 的应用不限于资源适配器。任何服务器端 J2EE 组件都可以使用这项功能。

  清单 9 显示了为了使用 commonj 的类而被重写的 清单 8 的示例:

  清单 9. 使用 commonj 接口

1 final InitialContext context = new InitialContext();
2 final WorkManager workManager = (WorkManager)
3         context.lookup("java:comp/env/wm/MyWorkManager");
4 final TimerManager timerManager = (TimerManager)
5     context.lookup("java:comp/env/timer/MyTimer");
6 timerManager.scheduleAtFixedRate(new TimerListener() {
7     public void timerExpired(final Timer timer) {
8         try {
9             workManager.schedule(new ExampleCommonjWork());
10         } catch (final WorkException exception) {
11         }
12     }
13 }, 0, 60 * 1000);
14

  ExampleCommonjWork 类与 清单 4 的 ExampleWork 相同,此外,它还实现了 commonj Work 接口要求的额外的 isDaemon 方法。如果工作长期存在,那么该方法应当返回 true。

  导入事务并完成事务

  在 JCA 1.5 之前,应用服务器总是充当事务的协调者。应用程序负责器负责启动事务,而且在每个资源管理器通过 JCA 连接管理器登记了自己的 XAResource 之后,还通过一起提交或回滚所有资源来协调事务的完成。JCA 1.5 的事务流入合约允许企业信息系统(EIS)启动和完成事务,充当事务的协调者。这样 EIS 就能通过资源适配器把事务导入应用服务器,并在事务范围内在服务器上执行工作。例如,它可能使用 Supports 的容器管理器事务属性来调用消息驱动 bean(MDB)。这样,MDB 方法执行的工作,包括对其它 EJB 的调用,就会成为事务的一部分。

  资源适配器通过第 3 个 ExecutionContext 参数导入事务,在 清单 2 中可以看到它被传递给 WorkManager。正如在清单 10 中可以看到的,这个类拥有设置事务 ID (XID) 和事务超时的方法:

  清单 10. 传递给 WorkManager 的 ExecutionContext 类

1 public class ExecutionContext {
2     public ExecutionContext() { ... }
3     public void setXid(Xid xid) { ... }
4     public Xid getXid() { ... }
5     public void setTransactionTimeout(long timeout)
6         throws NotSupportedException { ... }
7     public long getTransactionTimeout() { ... }    
8     
9 }

  XID 惟一地标识事务,它由三部分构成:格式标识符、事务界定符和分支界定符。如果 EIS 还没有事务的 XID,那么就必须根据 XA 规范构建一个 XID。然后应用服务器会在调用工作对象的 run 方法之前把这个事务与执行线程关联起来。

  把事务导入应用服务器之后,接下来就由资源适配器负责把属于这个事务的事件通知给服务器。具体地说,它必须把事务完成通知给应用服务器。它是通过清单 11 中的 XATerminator 接口做到这一点的,可以从 BootstrapContext 中得到它的实现:

  清单 11. 用于事务完成的 XATerminator 类

1 public interface XATerminator {
2     void commit(Xid xid, boolean onePhase) throws XAException;
3     void forget(Xid xid) throws XAException;
4     int prepare(Xid xid) throws XAException;
5     Xid[] recover(int flag) throws XAException;
6     void rollback(Xid xid) throws XAException;
7     
8 }

  XATerminator 接口的方法与 XAResource 上的方法对应,只是在这个例子中,资源适配器需要调用应用程序服务器。典型情况下,如果事务中包含不止一个资源,那么资源适配器会调用 XATerminator 的 prepare 方法,传递与 ExecutionContext 中传递的 Xid 相同的 Xid。如果所有的资源都返回 XA_OK (或者 XA_RDONLY),那么资源适配器会接着调用 commit;否则就会调用 rollback。

  结束语

  本文介绍了如何用 WorkManager 接口对工作进行调度,以便在应用程序的控制下处理这些工作。您已经看到如何用 Timer 的实例在以后某个时间执行工作或定期执行工作,还了解了使用 JCA 提供的对象的 commonj 替代方案。您已经看到资源适配器如何在自己导入到应用服务器的事务中执行工作,并用 XATerminator 接口控制事务的完成。

0
相关文章