计时器和工作管理器 API (CommonJ) 编程人员指南
本文整理自 WebLogic 10.0中文文档。
更多CommonJ规范的信息参考:http://www.ibm.com/developerworks/library/specification/j-commonj-sdowmt/index.html
计时器和工作管理器 API
此文档概述计时器和工作管理器 API,并说明其在应用程序中的实现方法。
计时器和工作管理器 API 的描述
计时器和工作管理器 API 是按照 BEA Systems 和 IBM 共同创建的规范进行定义的。通过此 API,可以并发编写在 Java EE 应用程序中的 EJB 和 Servlet 程序。此 API 经常被称为 CommonJ。
CommonJ API 包含下列组件:
通过计时器 API,应用程序可以为在某一应用程序中定义的特定监听器调度和接收计时器通知回调。通过计时器,您可以在特定时间或间隔调度和执行工作。请参阅计时器 API 概述。
此 API 通过导入 commonj.timer 包来实现。
通过工作管理器 API,应用程序可以确定 EJB 或 Servlet 中工作的优先级。应用程序可采用编程方式,在一个容器中执行多个工作项。请参阅工作管理器 API 的描述。
此 API 通过导入 commonj.work 包来实现。
除 CommonJ 工作管理器 API 外,WebLogic Server 还包含服务器级别的工作管理器,这些工作管理器可提供优先级和线程管理。这些可以通过全局方式进行配置,也可以针对应用程序的特定模块进行配置。
虽然 commonj.timer 和 commonj.work 属于同一个 API 的一部分,但它们的功能却各不相同。您可以根据应用程序的特定需要实现其中的一种。CommonJ 计时器 API 适用于按特定间隔调度工作的情形;例如,您知道某一项作业应在特定时间运行的情形。而 CommonJ 工作 API 适用于根据优先级处理工作的情形。例如,您不能准确预测一项特定作业的发生时间,而是希望这项作业被赋予更高或更低的优先级。
下列部分将详细描述 CommonJ API。
计时器 API 概述
计时器 API 包含以下三个接口:
计时器管理器提供在受管环境中创建和使用计时器的框架。计时器监听器接收计时器通知。TimerManager.schedule() 方法用于调度 TimerListener,使其在特定时间或间隔运行。
有关如何实现计时器的详细描述,请参阅使用计时器 API。
TimerManager 接口
TimerManager 接口在应用程序中提供常规调度框架。受管的环境可以支持多个 TimerManager 实例。这就是说,一个应用程序可以包含多个 TimerManager 实例。
创建和配置计时器管理器
可通过部署描述符在部署期间配置 TimerManager。TimerManager 定义还可以包含其他实现特定的配置信息。
一旦在部署描述符中定义了 TimerManager,您就可以在本地 Java 环境中使用 JNDI 查找访问它的实例。每次调用 JNDI lookup() 访问 TimerManager 时,都将返回 TimerManager 的一个新逻辑实例。
TimerManager 接口是线程安全的。
有关使用 JNDI 的详细信息,请参阅"WebLogic JNDI 编程"。
挂起 TimerManager
可使用 suspend() 和 resume() 方法,挂起和恢复 TimerManager。当挂起 TimerManager 时,所有待定的计时器将延迟到 TimerManager 恢复时为止。
停止 TimerManager
可使用 stop() 方法停止 TimerManager。在调用 stop() 方法后,所有活动的计时器都将停止,而且 TimerManager 实例将停止监视所有 TimerListener 实例。
TimerListener 接口
使用 commonj.timers 包的所有应用程序都需要实现 TimerListener 接口。
Timer 接口
当通过 TimerManager 调度计时器时,将返回 Timer 接口的实例。
使用计时器 API
此部分将简述在应用程序中使用计时器 API 时的必需步骤。
在部署应用程序前,确保已创建了包含计时器管理器资源引用的部署描述符。
这样可以通过 JNDI 访问 TimerManager。有关 JNDI 查找的详细信息,请参阅"WebLogic JNDI 编程"。
下面描述计时器 API 的实现过程:
创建一个 InitialContext,以便在 JNDI 中查找 TimerManager。
IntialContext inctxt = new InitialContext();
有关 JNDI 查找的详细信息,请参阅"WebLogic JNDI 编程"。
基于 TimerManager 的 JNDI 查找,新建一个 TimerManager。
TimerManager mgr = (TimerManager)ctx.lookup(`java:comp/env/timer/MyTimer');
在此语句中,将 JNDI 查找结果强制转换成 TimerManager。
实现 TimerListener 来接收计时器通知。
TimerListener listener = new StockQuoteTimerListener(`abc', `example');
调用 TimerManager.schedule()
mgr.schedule(listener, 0, 1000*60)
schedule() 方法返回计时器对象。
public void timerExpired(Timer timer) {
//此方法执行
//业务逻辑
}
计时器管理器示例
package examples.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import commonj.timers.*;
/**
* TimerServlet 演示了 commonj 计时器的简单使用
*/
public class TimerServlet extends HttpServlet {
/**
* 非常简单地实现服务方法
* 该实现安排了 commonj 计时器。
*/
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
try {
InitialContext ic = new InitialContext();
TimerManager tm = (TimerManager)ic.lookup
("java:comp/env/tm/default");
// 立即开始每 10 秒执行一次计时器
tm.schedule (new MyTimerListener(), 0, 10*1000);
out.println("<h4>Timer scheduled!</h4>");
} catch (NamingException ne) {
ne.printStackTrace();
out.println("<h4>Timer schedule failed!</h4>");
}
}
private static class MyTimerListener implements TimerListener {
public void timerExpired(Timer timer) {
System.out.println("timer expired called on " + timer);
// 此处一些有用的工作...
// 只取消计时器
System.out.println("cancelling timer ...");
timer.cancel();
}
}
}
使用作业调度程序
此部分描述作业调度程序功能的用法。通过作业调度程序,您可以在群集环境中实现 commonj.timer API。
作业调度程序实际上就是可以在群集中使用的 commonj.timer API 包的实现。在此上下文,作业被定义为要提交给作业调度程序进行执行的 commonj.timers.TimerListener 实例。
本部分包含下列主题:
计时器的生命周期
在应用程序中实现 commonj.timer API 时,可以为计时器配置两种可能的生命周期。
本地计时器在单个服务器 JVM 中调度,并且这些计时器在它们整个生命期中都是在此 JVM 中进行处理。只要 JVM 运行,计时器就将一直运行;而当 JVM 退出时,计时器将出现故障。在服务器启动后,应用程序负责重新调度计时器。
此为 commonj.timers 的基本实现,其描述见计时器 API 概述。
群集范围的计时器了解包含群集中每一服务器的其他 JVM,因此能执行负载平衡和故障转移。群集范围计时器的生命周期不与创建它的服务器绑定在一起,但是在群集的整个生命周期继续发挥作用。只要还有一个群集成员处于活动状态,计时器就会继续发挥作用。此功能称为作业调度程序。
每一计时器有其自己的优缺点。本地计时器能以短得多的作业处理时间间隔处理作业。由于群集有持久性要求,因此作业调度程序不能如此精确地处理作业。另一方面,作业调度程序更适合于在即使创建任务的初始服务器出现故障的情况下也必须执行的任务。
实现和配置作业调度程序
此部分将简述在应用程序中实现作业调度程序的基本过程以及配置 WebLogic Server 环境以利用作业调度程序的基本过程。
数据库配置
为了维持持久性,并使计时器支持群集,作业调度程序需要数据库连接。作业调度程序功能与服务器迁移支持相同的数据库供应商和版本。
为方便起见,可与会话持久性、服务器迁移等使用相同的数据库。
在数据库中,必须基于以下模式创建一个称为 WEBLOGIC_TIMERS 的表:
CREATE TABLE WEBLOGIC_TIMERS (
TIMER_ID VARCHAR2(100) NOT NULL,
LISTENER BLOB NOT NULL,
START_TIME NUMBER NOT NULL,
INTERVAL NUMBER NOT NULL,
TIMER_MANAGER_NAME VARCHAR2(100) NOT NULL,
DOMAIN_NAME VARCHAR2(100) NOT NULL,
CLUSTER_NAME VARCHAR2(100) NOT NULL,
CONSTRAINT SYS_C00323062 PRIMARY KEY(TIMER_ID, CLUSTER_NAME, DOMAIN_NAME)
)
数据源配置
在创建了采用所需模式的表之后,必须定义一个可在群集配置内引用的数据源。只有为 Cluster MBean 的 DataSourceForJobScheduler 特性定义了有效数据源后,才能使用作业调度程序功能。您可以在 WebLogic Server 管理控制台对此进行配置。
以下的代码摘自 config.xml,用于说明对此进行定义的方法:
<domain>
...
<cluster>
<name>Cluster-0</name>
<multicast-address>239.192.0.0</multicast-address>
<multicast-port>7466</multicast-port>
<data-source-for-job-scheduler>JDBC Data Source-0</data-source-for-job-scheduler>
</cluster>
...
<jdbc-system-resource>
<name>JDBC Data Source-0</name>
<target>myserver,server-0</target>
<descriptor-file-name>jdbc/JDBC_Data_Source-0-3407-jdbc.xml</descriptor- file-name>
</jdbc-system-resource>
</domain>
作业调度程序中的 JNDI 访问
与常规 commonj.timer API 相比,在群集计时器中执行 JNDI 查找的过程有所不同。下面的代码段说明如何将 JNDI 查找强制转换成 TimerManager。
InitialContext ic = new InitialContext();
commonj.timers.TimerManager jobScheduler =(common.timers.TimerManager)ic.lookup
("weblogic.JobScheduler");
commonj.timers.TimerListener timerListener = new MySerializableTimerListener();
jobScheduler.schedule(timerListener, 0, 30*1000);
// 每 30 秒执行一次此作业
不支持的方法和接口
作业调度程序环境并非支持 commonj.timer API 的所有方法和接口。不支持下列方法和接口:
工作管理器 API 的描述
工作管理器 (commonj.work) API 提供的接口允许应用程序在一个容器中并发地执行多个工作项。
实际上,此 API 就是 java.lang.Thread API 的容器管理替代方法。java.lang.Thread API 不适用于受管 Java EE 环境中承载的应用程序。在这样的环境中,工作管理器 API 替代方法的效果更好,因为它允许容器全面查看和控制所有执行线程。
注意: | 工作管理器 API 不提供故障转移和持久性机制。如果受管服务器环境出现故障或关闭,则当前所有工作都将丢失。 |
工作管理器接口
此部分概述工作管理器 API 中定义的接口。有关使用这些接口的详细信息,请参阅 commonj.work package 的 javadoc。
工作管理器 API 包含下列接口:
WorkManager - 此接口提供一组用于调度要执行工作的调度方法。
系统管理员可在服务器级别定义 WorkManager。WorkManager 实例通过执行 JNDI 查找来获取。受管的环境可以支持多个 WorkManager 实例。请参阅"WebLogic JNDI 编程"。您在部署期间将 WorkManager 配置为 resource-ref。请参阅工作管理器部署。
每个 WorkManager 实例在应用程序级别返回一个 WorkItem。有关在应用程序中实现 WorkManager 的详细信息,请参阅 WorkManager javadocs。
Work - 通过此接口,您可以异步运行应用程序代码。通过创建实现此接口的类,您可以创建可通过调度在特定的时间或定义的间隔运行的代码块。换句话说,此为在工作管理器 API 中处理的"工作"。
WorkItem - 在将 Work 实例提交给 WorkManager 后,WorkManager 将返回一个 WorkItem。此 WorkItem 用于确定整个 Work 实例的状态。
WorkListener - WorkListener 接口是一个回调接口,它在 WorkManager 与 Work 实例中定义的调度工作之间建立通信。
可以使用 WorkListener 确定 Work 项的当前状态。有关详细信息,请参阅 WorkListener javadocs。
注意: | WorkListener 实例总与通过 WorkManager 调度 Work 的初始线程在同一个 JVM 中执行。 |
WorkEvent - 在 WorkManager 处理 Work 时,会将 WorkEvent 发送给 WorkListener。
RemoteWorkItem - RemoteWorkItem 接口是 WorkItem 接口的扩展,可用于在远程执行工作。通过此接口,可序列化工作可以在群集中的任何成员上执行。
工作管理器部署
在适当部署描述符中通过 resource-ref 在服务器级别定义工作管理器。在其他描述符中可以是 web.xml或 ejb-jar.xml。
下面的部署描述符片段说明如何配置 WorkManager:
<resource-ref>
<res-ref-name>wm/MyWorkManager</res-ref-name>
<res-type>commonj.work.WorkManager</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
...
注意: | 建议您为 WorkManager 对象的 JNDI 名称空间使用前缀 java:comp/env/wm。 |
工作管理器示例
此部分的工作代码示例在 HTTP Servlet 中使用 CommonJ 工作管理器。
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import weblogic.work.ExecuteThread;
import commonj.work.WorkManager;
import commonj.work.Work;
import commonj.work.WorkException;
public class HelloWorldServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
try {
InitialContext ic = new InitialContext();
System.out.println("## [servlet] executing in: " +
((ExecuteThread)Thread.currentThread()).getWorkManager()
.getName());
WorkManager wm = (WorkManager)ic.lookup
("java:comp/env/foo-servlet");
System.out.println("## got Java EE work manager !!!!");
wm.schedule(new Work(){
public void run() {
ExecuteThread th = (ExecuteThread) Thread.currentThread();
System.out.println("## [servlet] self-tuning workmanager: " +
th.getWorkManager().getName());
}
public void release() {}
public boolean isDaemon() {return false;}
});
}
catch (NamingException ne) {
ne.printStackTrace();}
catch (WorkException e) {
e.printStackTrace();
}
out.println("<h4>Hello World!</h4>");
// 不要关闭输出流 - 而是启用 servlet 引擎关闭输出流
// 以实现更佳性能。
System.out.println("finished execution");}
}
posted on 2012-02-19 19:23
BeanSoft 阅读(4856)
评论(0) 编辑 收藏 所属分类:
WebLogic