Oracle® Fusion Middleware Oracle WebLogic Server Timer and Work Manager API (CommonJ)プログラマーズ・ガイド 11gリリース1 (10.3.6) B61632-05 |
|
![]() 前 |
この章では、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
タイマー・オブジェクト
TimerManager
は、管理対象の環境においてタイマーを作成および使用するためのフレームワークを提供します。TimerListener
は、タイマー通知を受信します。TimerManager.schedule()
メソッドを使用して、TimerListener
を特定の時刻または間隔において実行するようにスケジューリングします。
タイマーの実装方法の詳細は、「Timer APIの使用」を参照してください。
TimerManager
インタフェースは、アプリケーション内の全般的なスケジューリング・フレームワークを提供します。管理対象の環境では、複数のTimerManager
インスタンスをサポートできます。1つのアプリケーション内に複数のTimerManager
インスタンスを設定できます。
TimerManager
は、デプロイ中にデプロイメント記述子で構成されます。TimerManager
の定義には、実装固有の付加的な構成情報が含まれることもあります。
デプロイメント記述子にTimerManager
が定義されると、そのインスタンスはローカルJava環境でJNDIルックアップを使用してアクセスできるようになります。JNDI lookup()
でTimerManager
を呼び出すたびに、TimerManager
の新しい論理インスタンスが返されます。
TimerManager
インタフェースはスレッドセーフです。
JNDIの仕様の詳細は、『Oracle WebLogic Server JNDIのプログラミング』を参照してください。
この節では、アプリケーション内でTimer APIを使用するために必要な手順を簡単に示します。
アプリケーションをデプロイする前に、タイマー・マネージャへのリソース参照を含むデプロイメント記述子が作成済みであることを確認してください。
これにより、TimerManager
がJNDIを介してアクセス可能になります。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の基本実装であり、これについては「Timer APIの概要」で説明しています。
クラスタ全体タイマー
クラスタ全体タイマーはクラスタ内の各サーバーを含む他のJVMを認識し、そのため、ロード・バランシングとフェイルオーバーが可能です。クラスタ全体タイマーのライフ・サイクルは作成したサーバーにとらわれず、クラスタのライフ・サイクルにわたって機能し続けます。クラスタ・メンバーの少なくとも1つが有効である限り、このタイマーは機能を続けます。この機能はジョブ・スケジューラと呼ばれます。
ジョブ・スケジューラのTimer APIの実装では、「Timer APIの実装」にリストされている要件の他に、以下の要件が追加されます。
タイマー・リスナー・クラスはシリアライズ可能でなければなりません。
タイマー・リスナー・クラスはサーバー・システム・クラスパスに指定されていなければなりません。
タイマーの再試行の最小時間は30秒。これは、ジョブ・スケジューラが30秒ごとに実行するタイマーを取得するためです。「ジョブ・スケジューラの使用」を参照してください。
各タイマーにはそれぞれメリットとデメリットがあります。ローカル・タイマーでは、複数のジョブをより短い時間間隔で処理できます。ジョブ・スケジューラでは、クラスタ内の永続性要件のために同様の精度ではジョブを処理できません。一方で、初めにタスクを作成したサーバーが失敗した場合にもそのタスクを実行する必要がある場合には、ジョブ・スケジューラの方が適しています。
この節では、アプリケーション内にジョブ・スケジューラを実装し、それを利用するようにWebLogic Server環境を構成するための基本的な手順を簡単に示します。
永続性を保持し、タイマーがクラスタを認識できるようにするため、ジョブ・スケジューラにはデータベース接続が必要です。ジョブ・スケジューラ機能では、サーバーの移行でサポートされるのと同じデータベース・ベンダーおよびバージョンがサポートされます。
便宜上、セッション永続性、サーバーの移行などに使用されているのと同じデータベースを使用できます。たとえば、サーバーの移行におけるソース・データの選択および作成の手順については、Oracle WebLogic Server管理コンソール・ヘルプのクラスタ内のサーバーの移行の構成に関する項を参照してください。
データベースには、WEBLOGIC_TIMERS
という表を作成する必要があります。サポートされるデータベースにこの表を作成するためのスキーマは、以下にあります。
WL_HOME/server/db/dbname/scheduler.ddl
where dbname is the name of the database vendor.
必要なスキーマで表を作成した後には、クラスタ構成内から参照されるデータ・ソースを定義する必要があります。ジョブ・スケジューラ機能は、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>
ジョブ・スケジューラでは、リースが有効になっている必要があります。リースは、高可用性データベース・リースまたは非データベース・コンセンサス・リースのいずれかを使用できます。
リースの詳細は、『Oracle WebLogic Serverクラスタの使用』のリースに関する項を参照してください。
高可用性データベース・リースを使用する場合、データベースにリース表を作成する必要があります。サポートされるデータベースにこの表を作成するためのスキーマは、以下にあります。
WL_HOME/server/db/dbname/leasing.ddl
dbname
はデータベース・ベンダーの名前です。
クラスタリングされたタイマー内で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
ジョブは、プログラムまたは管理コンソールを使用して取り消すことができます。
プログラムを使用してジョブを取り消すには、そのジョブの対応するJobRuntimeMBean
のcancel()メソッドを呼び出します。JobRuntimeMBeansには、以下のいずれかの方法でアクセスできます。
スケジュールされたジョブのIDを使用してJobSchedulerRuntimeMBean.getJob(id)を呼び出します。IDは、JobScheduler.schedule()
メソッドを呼び出して取得したTimerオブジェクトのtoString()
メソッドから取得します。
JobSchedulerRuntimeMBean.getExecutedJobs()
を呼び出して、少なくとも1回は実行したすべてのジョブのJobRunTimes
の配列を取得します。
cancel()
メソッドを使用して、1回も実行していないスケジュールされたジョブを取り消すことはできません。
管理コンソールを使用してジョブを取り消す方法については、管理コンソール・オンライン・ヘルプの「ジョブの取消し」を参照してください。
commonj.timer
APIのメソッドおよびインタフェースの中には、ジョブ・スケジューラ環境でサポートされていないものもあります。以下のメソッドおよびインタフェースはサポートの対象外です。
CancelTimerListener
インタフェース
StopTimerListener
インタフェース
TimerManager
インタフェースの以下のメソッド
suspend()
resume()
scheduleAtFixedRate()
stop()
waitForStop()
waitForSuspend()
Work Manager (commonj.work
) APIは、アプリケーションがコンテナ内で複数の作業項目を同時に実行できるようにするインタフェースを提供します。
基本的に、このAPIはjava.lang.Thread
APIのコンテナ管理の代替機能を提供します。後者は、管理対象のJava EE環境でホストされるアプリケーション内では使用しないようにする必要があります。Work Manager APIを使用するとコンテナが実行中のすべてのスレッドを可視性および制御できるので、そうした環境ではかわりにWork Manager APIを使用するようお薦めします。
注意: Work Manager APIでは、フェイルオーバーや永続性メカニズムは提供されません。管理対象サーバー環境が失敗または停止した場合、現在の作業はすべて失われます。 |
この節では、Work Manager APIに定義されているインタフェースの全般的な概要を説明します。これらのインタフェースの詳細な使用方法については、commonj.workパッケージのjavadocを参照してください。
Work Manager APIには以下のインタフェースがあります。
WorkManager - このインタフェースは、実行する作業のスケジューリングに使用するスケジューリング・メソッド群を提供します。
WorkManager
はサーバー・レベルでシステム管理者が定義します。WorkManager
インスタンスはJNDIルックアップを実行して取得します。管理対象の環境では、複数のWorkManager
インスタンスをサポートできます。『Oracle WebLogic Server JNDIのプログラミング』を参照してください。WorkManager
はデプロイメント中にresource-ref
として構成します。「ワーク・マネージャのデプロイメント」を参照してください。
アプリケーション・レベルでは、WorkManager
の各インスタンスがWorkItem
を返します。アプリケーション内にWorkManager
を実装する方法の詳細は、WorkManagerのjavadocを参照してください。
Work - このインタフェースを使用すると、アプリケーション・コードを非同期的に実行できます。このインタフェースを実装するクラスを作成することで、コード・ブロックを作成して特定の時刻または定義した間隔で実行するようにスケジューリングできます。これがすなわち、Work Manager API内で処理される「作業」です。
WorkItem - Work
インスタンスがWorkManager
に送信された後で、WorkManager
はWorkItem
を返します。WorkItem
は完了したWork
インスタンスのステータスの判断に使用されます。
WorkListener - WorkListener
インタフェースは、Work
インスタンス内に定義されたスケジューリング済み作業とWorkManager
との通信機能を提供するコールバック・インタフェースです。
WorkListener
を使用するとWork
項目の現在のステータスを判断できます。詳細については、WorkListenerのjavadocを参照してください。
注意: WorkListener インスタンスは、WorkManager 経由でWork をスケジューリングするのに使用した元のスレッドと常に同じJVMで実行されます。 |
WorkEvent - WorkEvent
は、WorkManager
によってWork
が処理されたときに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サーブレット内で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");} }