| Oracle® Fusion Middleware Oracle WebLogic Server CommonJアプリケーションの開発 12c (12.2.1) E72525-01 |
|
![]() 前 |
この章では、Timer and Work Manager APIの概要を説明し、これらをアプリケーション内に実装する方法を示します。
この章の内容は次のとおりです。
Timer and Work Manager APIは、OracleとIBMが共同作成した仕様で定義されています。このAPIを使用すると、Java EEアプリケーション内にEJBとサーブレットを同時にプログラミングできます。このAPIはしばしばCommonJと呼ばれます。
CommonJ APIには、次のコンポーネントが含まれます。
Timer API
Timer APIを使用すると、アプリケーション内に定義された特定のリスナーに対し、アプリケーションでタイマー通知コールバックをスケジューリングして受信できます。タイマー機能を利用すると、特定の時刻または間隔において作業するようにスケジューリングおよび実行することが可能です。「Timer APIの概要」を参照してください。
このAPIは、commonj.timerパッケージをインポートして実装します。
Work Manager API
Work Manager APIを使用すると、アプリケーションでEJBまたはサーブレット内の作業の優先度を決められます。アプリケーションは、複数の作業項目をコンテナ内部でプログラム的に実行できます。「Work Manager API」を参照してください。
このAPIは、commonj.workパッケージをインポートして実装します。
WebLogic Serverには、CommonJ Work Manager API以外にもサーバー・レベルのワーク・マネージャが複数あり、優先度の決定やスレッド管理を行えます。これらは、グローバルに構成することも、アプリケーションの特定のモジュール用に構成することも可能です。
commonj.timerとcommonj.workは同じAPIの一部ですが、それぞれ別の機能を提供します。どちらを実装するかは、アプリケーション固有のニーズによって決まります。CommonJ Timer APIは、作業を特定の間隔でスケジューリングする場合に理想的です。たとえば、あるジョブを特定の時刻に実行する必要がある場合などです。CommonJ Work APIは、作業を優先度に基づいて処理する場合に理想的です。たとえば、特定のジョブの発生時期をはっきりと予測できないが、発生した場合には高い(または低い)優先度を与える場合などです。
以下の節では、CommonJ APIを詳細に説明します。
Timer APIは次の3つのインタフェースで構成されています。
TimerManager
TimerListener
Timer
TimerManagerインタフェースは、管理対象の環境においてタイマーを作成および使用するためのフレームワークを提供します。TimerListenerは、タイマー通知を受信します。TimerManager.scheduleメソッドを使用して、TimerListenerを特定の時刻または間隔において実行するようにスケジューリングします。
タイマーの実装方法の詳細は、「Timer APIの使用」を参照してください。
TimerManagerインタフェースは、アプリケーション内の全般的なスケジューリング・フレームワークを提供します。管理対象の環境では、複数のTimerManagerインスタンスをサポートできます。1つのアプリケーション内に複数のTimerManagerインスタンスを設定できます。
TimerManagerは、デプロイ中にデプロイメント記述子で構成されます。TimerManagerの定義には、実装固有の付加的な構成情報が含まれることもあります。
TimerManagerをデプロイメント記述子で定義したら、そのインスタンスには、ローカルJava環境でJNDI参照を使用してアクセスできます。TimerManagerでJNDI lookup()を呼び出すたびに、TimerManagerの新しい論理インスタンスが返されます。
TimerManagerインタフェースはスレッドセーフです。
JNDIの使用の詳細は、『Oracle WebLogic Server JNDIアプリケーションの開発』を参照してください。
この項では、アプリケーション内のTimer APIの使用に必要な手順を説明します。
アプリケーションをデプロイする前に、タイマー・マネージャへのリソース参照を含むデプロイメント記述子が作成済であることを確認してください。
これによって、JNDIを使用してTimerManagerをアクセスできるようになります。JNDIルックアップの詳細は、Oracle WebLogic Server JNDIアプリケーションの開発を参照してください。
Timer APIを実装するには、次の手順を実行します。
commonj.timers.*パッケージをインポートします。
InitialContextを作成します。これによりTimerManagerがJNDIでルックアップ可能になります。例:
InitialContext inctxt = new InitialContext();
JNDIルックアップの詳細は、Oracle WebLogic Server 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メソッドはTimerオブジェクトを返します。
timerExpiredメソッドを実装します。例:
public void timerExpired(Timer timer) {
//Business logic is executed
//in this method
}
クラスタ全体タイマーのTimer APIの実装では、要件が追加されます(「タイマーのライフ・サイクル」を参照してください)。
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 demonstrates a simple use of commonj timers
*/
public class TimerServlet extends HttpServlet {
/**
* A very simple implementation of the service method,
* which schedules a commonj timer.
*/
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");
// Execute timer every 10 seconds starting immediately
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);
// some useful work here ...
// let's just cancel the timer
System.out.println("cancelling timer ...");
timer.cancel();
}
}
}
この項では、ジョブ・スケジューラ機能の使用方法について説明します。ジョブ・スケジューラを使用すると、クラスタリング環境内にcommonj.timer APIを実装できます。
ジョブ・スケジューラは、基本的にはcommonj.timer APIパッケージの実装であり、クラスタ内で使用できます。ここではジョブはcommonj.timers.TimerListenerインスタンスとして定義され、ジョブ・スケジューラに送信されて実行されます。
この項の内容は次のとおりです。
アプリケーション内にcommonj.timer APIを実装する場合、タイマーに構成できるライフ・サイクルには2種類あります。
ローカル・タイマー
ローカル・タイマーは単一のサーバーJVM内でスケジューリングされ、ライフ・サイクル全体にわたってこのJVM内で処理されます。ローカル・タイマーはこのJVMが動作している限り動作し続け、JVMが終了すると失敗します。サーバー起動後のタイマーの再スケジューリングはアプリケーションが行います。
これは、commonj.timersパッケージの基本実装です。
クラスタ全体タイマー
クラスタ全体タイマーはクラスタ内の各サーバーを含む他のJVMを認識し、そのため、ロード・バランシングとフェイルオーバーが可能です。クラスタ全体タイマーのライフ・サイクルは作成したサーバーにとらわれず、クラスタのライフ・サイクルにわたって機能し続けます。クラスタ・メンバーの少なくとも1つが有効であるかぎり、このタイマーは機能を続けます。この機能はジョブ・スケジューラと呼ばれます。
ジョブ・スケジューラのTimer APIの実装では、「Timer APIの実装」にリストされている要件の他に、次の要件が追加されます。
タイマー・リスナー・クラスはシリアライズ可能である必要があります。
タイマー・リスナー・クラスはサーバー・システム・クラスパスに指定されていなければなりません。
タイマーの再試行の最小時間は30秒。これは、ジョブ・スケジューラが30秒ごとに実行するタイマーを取得するためです。
各タイマーにはそれぞれメリットとデメリットがあります。ローカル・タイマーでは、複数のジョブをより短い時間間隔で処理できます。ジョブ・スケジューラでは、クラスタ内の永続性要件のために同様の精度ではジョブを処理できません。一方で、初めにタスクを作成したサーバーが失敗した場合にもそのタスクを実行する必要がある場合には、ジョブ・スケジューラの方が適しています。
この項では、アプリケーション内にジョブ・スケジューラを実装し、それを利用するようにWebLogic Server環境を構成するための基本的な手順を簡単に示します。この項の内容は、次のとおりです。
永続性を保持し、タイマーがクラスタを認識できるようにするため、ジョブ・スケジューラにはデータベース接続が必要です。ジョブ・スケジューラ機能では、サーバーの移行でサポートされるのと同じデータベースがサポートされます。
便宜上、セッションの持続やサーバー移行などに使用されるものと同一のデータベースを使用できます。たとえば、サーバーの移行におけるデータ・ソースを選択および作成する方法は、Oracle WebLogic Server管理コンソール・オンライン・ヘルプのクラスタ内のサーバーの移行の構成に関する項を参照してください。
データベースで、WEBLOGIC_TIMERSという表を作成する必要があります。この表を作成するためのスキーマは、次の場所にあります。
WL_HOME/server/db/dbname/scheduler.ddl
前述のパスで、dbnameはデータベース名を示します。
必要なスキーマで表を作成した後には、クラスタ構成内から参照されるデータ・ソースを定義する必要があります。ジョブ・スケジューラ機能は、ClusterMBean.DataSourceForJobScheduler属性に有効なデータ・ソースが定義されている場合にのみ利用できます。WebLogic Server管理コンソールを使用してこの属性を構成する方法の詳細は、Oracle 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>
ジョブ・スケジューラでは、リースが有効になっている必要があります。リースは、高可用性データベース・リースまたは非データベース・コンセンサス・リースのいずれかを使用できます。高可用性データベース・リースを使用する場合、データベースにリース表を作成する必要があります。
この表を作成するためのスキーマは、次の場所にあります。
WL_HOME/server/db/dbname/leasing.ddl
前述のパスで、dbnameはデータベース名を示します。
詳細は、Oracle WebLogic Serverクラスタの管理のリースに関する項を参照してください。
クラスタリングされたタイマー内でJNDIルックアップを実行する手順は、一般的なcommonj.timer APIにおける手順とは異なります。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);
// execute this job every 30 seconds
ジョブは、プログラムまたはWebLogic Server管理コンソールを使用して取り消すことができます。
プログラムを使用してジョブを取り消すには、そのジョブの対応するJobRuntimeMBeanのcancelメソッドを呼び出します。JobRuntimeMBeanには、次のいずれかの方法でアクセスできます。
スケジュールされたジョブのIDを使用してJobSchedulerRuntimeMBean.getJob(id)を呼び出します。IDは、JobScheduler.scheduleメソッドを呼び出して取得したTimerオブジェクトのtoStringメソッドから取得します。
JobSchedulerRuntimeMBean.getExecutedJobs()を呼び出して、少なくとも1回は実行したすべてのジョブのJobRunTimesの配列を取得します。
cancelメソッドを呼び出して、1回も実行していないスケジュールされたジョブを取り消すことはできません。
WebLogic Server管理コンソールを使用してジョブを取り消す方法の詳細は、Oracle WebLogic Server管理コンソール・オンライン・ヘルプのジョブの取消しに関する項を参照してください。
以下のデバッグ・フラグを使用すると、より冗長な出力が可能になります。
-Dweblogic.debug.DebugSingletonServices=true -Dweblogic.JobScheduler=true
commonj.timerパッケージの次のメソッドおよびインタフェースは、ジョブ・スケジューラ環境でサポートされていません:
CancelTimerListenerインタフェース
StopTimerListenerインタフェース
TimerManagerインタフェースの以下のメソッド
suspend
resume
scheduleAtFixedRate
stop
waitForStop
waitForSuspend
Work Manager API (commonj.work)は、アプリケーションがコンテナ内で複数の作業項目を同時に実行できるようにするインタフェースのセットを提供します。
基本的に、このAPIはjava.lang.Thread APIのコンテナ管理の代替機能を提供します。後者は、管理対象のJava EE環境でホストされるアプリケーション内では使用しないようにする必要があります。Work Manager APIを使用するとコンテナが実行中のすべてのスレッドを可視性および制御できるので、そうした環境ではかわりにWork Manager APIを使用するようお薦めします。
|
注意: Work Manager APIでは、フェイルオーバーや永続性メカニズムは提供されません。管理対象サーバー環境が失敗または停止した場合、現在の作業はすべて失われます。 |
この項では、Work Manager APIのインタフェースの概要を説明します。これらのインタフェースの使用の詳細は、Oracle WebLogic Server Java APIリファレンスのcommonj.workに関する項を参照してください。
Work Manager APIには以下のインタフェースがあります。
WorkManager - 実行する作業のスケジューリングに使用するスケジューリング・メソッドのセットを提供します。
WorkManagerはサーバー・レベルでシステム管理者が定義します。WorkManagerインスタンスはJNDIルックアップを実行して取得します。管理対象の環境では、複数のWorkManagerインスタンスをサポートできます。WorkManagerはデプロイメント中にresource-refsとして構成します。「ワーク・マネージャのデプロイメント」を参照してください。
アプリケーション・レベルでは、WorkManagerの各インスタンスがWorkItemを返します。アプリケーション内にWorkManagerを実装する方法の詳細は、Oracle WebLogic Server Java APIリファレンスのWorkManagerに関する項を参照してください。
JNDIの詳細は、Oracle WebLogic Server JNDIアプリケーションの開発を参照してください。
Work - アプリケーション・コードを非同期的に実行できます。このインタフェースを実装するクラスを作成することで、コード・ブロックを作成して特定の時刻または定義した間隔で実行するようにスケジューリングできます。これがすなわち、Work Manager API内で処理される「作業」です。
WorkItem - 完了したWorkインスタンスのステータスを判断します。WorkItemは、WorkインスタンスがWorkManagerに送信された後、WorkManagerによって返されます。
詳細は、Oracle WebLogic Server Java APIリファレンスのWorkに関する項を参照してください。
WorkListener - Workインスタンス内に定義されたスケジューリング済作業とWorkManagerとの通信を提供します。WorkListenerは、コールバック・インタフェースです。
WorkListenerを使用するとWork項目の現在のステータスを判断できます。詳細は、Oracle WebLogic Server Java APIリファレンスのWorkListenerに関する項を参照してください。
|
注意: WorkListenerインスタンスは、WorkManager経由でWorkをスケジューリングするのに使用した元のスレッドと常に同じJVMで実行されます。 |
WorkEvent - WorkEventは、WorkManagerによってWorkが処理されたときにWorkListenerに送信されます。
詳細は、Oracle WebLogic Server Java APIリファレンスのWorkEventに関する項を参照してください。
RemoteWorkItem - RemoteWorkItemインタフェースはWorkItemインタフェースの拡張で、作業をリモートに実行できるようにします。このインタフェースを使用すると、クラスタの任意のメンバーで実行される作業をシリアライズできます。
詳細は、Oracle WebLogic Server Java APIリファレンスのRemoteWorkItemに関する項を参照してください。
ワーク・マネージャは、サーバー・レベルで、適切なデプロイメント記述子の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サーブレット内で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>");
// Do not close the output stream - allow the servlet engine to close it
// to enable better performance.
System.out.println("finished execution");}
}