この章の内容は次のとおりです。
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アプリケーションの開発』を参照してください。
TimerManager
は、suspend
メソッドおよびresume
メソッドを使用して中断および再開できます。TimerManager
を中断すると、未処理のタイマーはすべてTimerManager
が再開されるまで保留されます。
この項では、アプリケーション内のTimer APIの使用に必要な手順を説明します。
アプリケーションをデプロイする前に、タイマー・マネージャへのリソース参照を含むデプロイメント記述子が作成済であることを確認してください。
これによって、JNDIを使用してTimerManager
をアクセスできるようになります。JNDIルックアップの詳細は、Oracle WebLogic Server JNDIアプリケーションの開発を参照してください。
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
はデータベース名を示します。
注意:
WEBLOGIC_TIMERS
表は、ClusterMBeanの属性jobSchedulerTableName
を使用して構成することもできます。
必要なスキーマで表を作成した後には、クラスタ構成内から参照されるデータ・ソースを定義する必要があります。ジョブ・スケジューラ機能は、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管理コンソール・オンライン・ヘルプのジョブの取消しを参照してください。
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
です。
デフォルトのCommonJワーク・マネージャのjava:comp/env/wm/default
への自動バインディングは、WebLogic Server 12.2.1で削除されました。
デフォルトCommonJ Work Managerを使用するアプリケーションがある場合、次のいずれかのようにします。
デプロイメント記述子で、wm/default
のためのresource-refエントリを追加します。例:
<resource-ref> <res-ref-name>wm/default</res-ref-name> <res-type>commonj.work.WorkManager</res-type> <res-auth>Container</res-auth> </resource-ref>
CommonJワーク・マネージャを、アプリケーション・コンポーネントにインジェクトします。例:
@Resource commonj.work.WorkManager myWorkManager;
次の例は、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");} }