DBMS_ALERT
在DB端Signal一个ALERT,在Application端取得ALERT,再做相应处理。
注:如果要在DB端使用DBMS_ALERT,必须有Object Privileges对应权限。
grant execute on dbms_alert to user
DB端:
--PROCEDURE
CREATE OR REPLACE PROCEDURE proc_batch IS
BEGIN
DBMS_ALERT.signal(
name => 'batch_send_mail',
message => 'BATCH');
COMMIT;
END;
--JOB.SUBMIT
VARIABLE jobno number;
BEGIN
--每隔一天的10点执行。 具体时间是几分之几天。 (1/1440代表每一分钟)
DBMS_JOB.SUBMIT(:jobno,'proc_batch;',SYSDATE, 'trunc(sysdate)+1+10/24');
COMMIT;
END;
--実行job
BEGIN
DBMS_JOB.RUN(:jobno);
END;
--非JOB方式的临时测试 (测试App端是否取得ALERT)
BEGIN
DBMS_ALERT.signal('batch_send_mail', 'BATCH');
COMMIT;
END;
Application端:
一、BatchSendMailServlet (由于使用了Spring框架,如果在init同线程中不停等待DB,会使得Bean无法create,后续无法执行,另外新建一个线程,专门用来等待DB)
package com.hp.gdcc.jddc.famas.framework.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
public class BatchSendMailServlet extends HttpServlet {
public void init() throws ServletException {
logger.debug("init() method!");
ListenerThread lt = new ListenerThread();
Thread thread = new Thread(lt);
thread.setDaemon(true);
thread.start();
}
}
二、ListenerThread (专用于DB等待)
package com.hp.gdcc.jddc.famas.framework.servlet;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Types;
import java.util.Date;
import com.hp.gdcc.jddc.famas.com.batch.SendMailService;
import com.hp.gdcc.jddc.famas.com.consts.BeanConstants;
import com.hp.gdcc.jddc.famas.com.service.ICommonBusinessService;
import com.hp.gdcc.jddc.famas.com.util.ApplicationUtil;
public class ListenerThread implements Runnable {
private static final String ALERT = "batch_send_mail";
private static final String ALERT_STATUS = "BATCH";
private static Connection con;
public void run() {
//由于使用weblogic的JNDI,先去调用一个方法访问DB,如果没有取得或是抛出异常,让线程进行Sleep,10秒后继续访问DB,直到JNDI可用,break出死循环
this.threadSleep();
//调用DBMS_ALERT,取得返回的ALERT
this.callDatabase();
}
private void threadSleep() {
ICommonBusinessService commonService = (ICommonBusinessService) ApplicationUtil
.getBean(BeanConstants.BEAN_COM_BUSI_SERVICE);
while (true) {
try {
Date date = commonService.getSysDate();
if (date != null) {
logger.debug("date =" + date);
break;
} else {
logger.debug("sleep wait JNDI - 1");
Thread.sleep(10000);
}
} catch (Exception e) {
try {
logger.debug("sleep wait JNDI - 2");
Thread.sleep(10000);
} catch (Exception ex) {
logger.debug(ex);
}
}
}
}
private void callDatabase() {
try {
logger.debug("callDatabase() method!");
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url = "jdbc:oracle:thin:@jddccq:1521:FAMAS";
con = DriverManager.getConnection(url, "FAMAS", "FAMAS");
// DBMS_ALERT.REGISTER
// - 指定した名前のアラートをこのセッションで受け取ることを定義する
String storedproc = "{call DBMS_ALERT.REGISTER(?)}";
CallableStatement cstmt1 = con.prepareCall(storedproc);
cstmt1.setString(1, ALERT);
cstmt1.executeUpdate();
// ALERT MSG
String outputStr = null;
CallableStatement cstmt2;
// DBMS_ALERT.WAITANY
// メッセージの受信待ち
// ここでは、一つの待ちプロセスで複数の種類のメッセージを受け取り、
// メッセージの内容で処理を分ける。
String storedproc2 = "{call DBMS_ALERT.WAITANY(?,?,?,?)}";
cstmt2 = con.prepareCall(storedproc2);
// 第1パラメータ(OUT) - アラートの名前
cstmt2.registerOutParameter(1, Types.VARCHAR);
// 第2パラメータ(OUT) - メッセージの内容
cstmt2.registerOutParameter(2, Types.VARCHAR);
// 第3パラメータ(OUT) - 実行結果。0ならアラート受信。1ならタイムアウト。
cstmt2.registerOutParameter(3, Types.INTEGER);
// 第4パラメータ(IN) - タイムアウト時間(秒数)
cstmt2.setInt(4, 300);
// stopSyncが実行されると completeフラグがセットされる。
// それまでは無限ループとなる。
while (true) {
logger.debug("Listenning!!!");
// アラートはトランザクションがコミットした時点で送受信される
con.commit();
// 上で指定したDBMS_ALERT.WAITANYプロシジャの実行
cstmt2.executeUpdate();
if (cstmt2.getInt(3) == 1) {
// タイムアウトによって終了した場合の処理
logger.debug("TIME OUT! WAITING!");
} else {
// アラート受信で終了した場合、アラートの名前で処理を分ける
// cstmt2.getString(1) == ALERT
outputStr = cstmt2.getString(2);
logger.debug("msg = " + outputStr);
// メール送付機能を呼び出し
if (ALERT_STATUS.equals(outputStr)) {
// 得到了DB送出的ALERT,这里执行自定的处理
this.doSomething();
}
}
}
} catch (Exception e) {
logger.debug(e);
}
}
}
三、web.xml
<servlet>
<servlet-name>batchServlet</servlet-name>
<servlet-class>com.hp.gdcc.jddc.famas.framework.servlet.BatchSendMailServlet</servlet-class>
<!--启动优先级低于ActionServlet,越大越低-->
<load-on-startup>3</load-on-startup>
</servlet>