![]() ![]() ![]() ![]() |
この節では、Timer and Work Manager API の概要を説明し、これらをアプリケーション内に実装する方法を示します。
Timer and Work Manager API は、Oracle と IBM が共同作成した仕様で定義されています。この API を使用すると、Java EE アプリケーション内に EJB とサーブレットを同時にプログラミングできます。この API はしばしば CommonJ と呼ばれます。
CommonJ API には、次のコンポーネントが含まれます。
Timer API を使用すると、アプリケーション内に定義された特定のリスナに対し、アプリケーションでタイマー通知コールバックをスケジューリングして受信できます。タイマー機能を利用すると、特定の時刻または間隔において作業するようにスケジューリングおよび実行することが可能です。「Timer API の概要」を参照してください。
この API は、commonj.timer
パッケージをインポートして実装します。
Work Manager API を使用すると、アプリケーションで EJB またはサーブレット内の作業の優先順位を決められます。アプリケーションは、複数の作業項目をコンテナ内部でプログラム的に実行できます。「Work Manager API とは」を参照してください。
この API は、commonj.work
パッケージをインポートして実装します。
WebLogic Server には、CommonJ Work Manager API 以外にもサーバレベルのワーク マネージャが複数あり、優先順位の決定やスレッド管理を行えます。これらは、グローバルにコンフィグレーションすることも、アプリケーションの特定のモジュール用にコンフィグレーションすることも可能です。
commonj.timer
と commonj.work
は同じ API の一部ですが、それぞれ別の機能を提供します。どちらの API を実装するかは、アプリケーション固有のニーズによって決定します。CommonJ Timer API は、作業を特定の間隔でスケジューリングする場合に理想的です。たとえば、あるジョブを特定の時刻に実行する必要がある場合などです。CommonJ Work Manager API は、作業を優先順位に基づいて処理する場合に理想的です。たとえば、特定のジョブの発生時期をはっきりと予測できないが、発生した場合には高い (または低い) 優先順位を与える場合などです。
Timer API は次の 3 つのインタフェースで構成されています。
TimerManager
は、管理対象の環境においてタイマーを作成および使用するためのフレームワークを提供します。TimerListener
は、タイマー通知を受信します。TimerManager.schedule()
メソッドを使用して、TimerListener
を特定の時刻または間隔において実行するようにスケジューリングします。
タイマーの実装方法の詳細については、「Timer API の使用」を参照してください。
TimerManager
インタフェースは、アプリケーション内の全般的なスケジューリング フレームワークを提供します。管理対象の環境では、複数の TimerManager
インスタンスをサポートできます。1 つのアプリケーション内に複数の TimerManager
インスタンスを設定できます。
TimerManager
は、デプロイ中にデプロイメント記述子でコンフィグレーションされます。TimerManager
の定義には、実装固有の付加的なコンフィグレーション情報が含まれることもあります。
デプロイメント記述子に TimerManager
が定義されると、そのインスタンスはローカル Java 環境で JNDI ルックアップを使用してアクセスできるようになります。JNDI lookup()
で TimerManager
を呼び出すたびに、TimerManager
の新しい論理インスタンスが返されます。
TimerManager
インタフェースはスレッドセーフです。
JNDI の使用方法の詳細については、『WebLogic JNDI プログラマーズ ガイド』を参照してください。
TimerManager
は、suspend()
メソッドおよび resume()
メソッドを使用して中断および再開できます。TimerManager
を中断すると、未処理のタイマーはすべて TimerManager
が再開されるまで保留されます。
TimerManager
は、stop()
メソッドを使用すると停止できます。stop()
が呼び出されると、アクティブなタイマーはすべて停止され、TimerManager
インスタンスはすべての TimerListener
インスタンスのモニタを停止します。
commonj.timers
パッケージを使用するすべてのアプリケーションは、TimerListener
インタフェースを実装する必要があります。
Timer
インタフェースのインスタンスは、TimerManager
を介してタイマーがスケジューリングされたときに返されます。
この節では、アプリケーション内で Timer API を使用するために必要な手順を簡単に示します。
アプリケーションをデプロイする前に、タイマー マネージャへのリソース参照を含むデプロイメント記述子が作成済みであることを確認してください。
これにより、TimerManager
が JNDI を介してアクセス可能になります。JNDI ルックアップの詳細については、『WebLogic JNDI プログラマーズ ガイド』を参照してください。
commonj.timers.*
パッケージをインポートします。InitialContext
を作成します。これにより TimerManager
が JNDI でルックアップ可能になります。InitialContext 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()
メソッドは Timer
オブジェクトを返します。
timerExpired()
メソッドを実装します。public void timerExpired(Timer timer) {
//このメソッド内で
//ビジネス ロジックが実行される
}
クラスタワイド タイマーの 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 は 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 を実装する場合、タイマーにコンフィグレーションできるライフ サイクルには 2 種類あります。
ローカル タイマーは単一のサーバ JVM 内でスケジューリングされ、ライフ サイクル全体にわたってこの JVM 内で処理されます。ローカル タイマーはこの JVM が動作している限り動作し続け、JVM が終了すると失敗します。サーバ起動後のタイマーの再スケジューリングはアプリケーションが行います。
これは commonj.timers の基本実装であり、これについては「Timer API の概要」で説明しています。
クラスタワイド タイマーはクラスタ内の各サーバを含む他の JVM を認識し、そのため、ロード バランシングとフェイルオーバが可能です。クラスタワイド タイマーのライフ サイクルは作成したサーバにとらわれず、クラスタのライフ サイクルにわたって機能し続けます。クラスタ メンバーの少なくとも 1 つが有効である限り、このタイマーは機能を続けます。この機能はジョブ スケジューラと呼ばれます。
ジョブ スケジューラの Timer API の実装では、「Timer API の実装」にリストされている要件の他に、以下の要件が追加されます。
各タイマーにはそれぞれメリットとデメリットがあります。ローカル タイマーでは、複数のジョブをより短い時間間隔で処理できます。ジョブ スケジューラでは、クラスタ内の永続性要件のために同様の精度ではジョブを処理できません。一方で、初めにタスクを作成したサーバが失敗した場合にもそのタスクを実行する必要がある場合には、ジョブ スケジューラの方が適しています。
この節では、アプリケーション内にジョブ スケジューラを実装し、それを利用するように WebLogic Server 環境をコンフィグレーションするための基本的な手順を簡単に示します。
永続性を保持し、タイマーがクラスタを認識できるようにするため、ジョブ スケジューラにはデータベース接続が必要です。ジョブ スケジューラ機能では、サーバの移行でサポートされるのと同じデータベース ベンダおよびバージョンがサポートされます。
便宜上、セッション永続性、サーバの移行などに使用されているのと同じデータベースを使用できます。たとえば、サーバの移行におけるソース データの選択および作成の手順については、Administration Console オンライン ヘルプの「クラスタ内のサーバの移行のコンフィグレーション」を参照してください。
データベースには、WEBLOGIC_TIMERS
というテーブルを作成する必要があります。サポートされるデータベースにこのテーブルを作成するためのスキーマは、以下にあります。
WL_HOME
/server/db/
dbname
/scheduler.ddl
必要なスキーマでテーブルを作成した後には、クラスタ コンフィグレーション内で参照されるデータ ソースを定義する必要があります。ジョブ スケジューラ機能は、ClusterMBean の DataSourceForJobScheduler
属性に有効なデータ ソースが定義されている場合にのみ利用できます。これは、WebLogic Server Administration Console でコンフィグレーション可能です。Administration Console オンライン ヘルプの「ジョブ スケジューラのデータ ソースのコンフィグレーション」を参照してください。
この定義の仕方について、以下の 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>
ジョブ スケジューラでは、リースが有効になっている必要があります。リースは、高可用性データベース リースまたは非データベース コンセンサス リースのいずれかを使用できます。
リースの詳細については、『Using Clusters』の「リース」を参照してください。
高可用性データベース リースを使用する場合、データベースにリース テーブルを作成する必要があります。サポートされるデータベースにこのテーブルを作成するためのスキーマは、以下にあります。
WL_HOME/
server/db/
dbname
/leasing.ddl
クラスタ化されたタイマー内で 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);
// 30 秒ごとにこのジョブを実行
ジョブは、プログラムまたは Administration Console を使用してキャンセルできます。
プログラムを使用してジョブをキャンセルするには、そのジョブの対応する JobRuntimeMBean の cancel()
メソッドを呼び出します。JobRuntimeMBeans には、以下のいずれかの方法でアクセスできます。
JobSchedulerRuntimeMBean.getJob(id)
を呼び出す。ID は、JobScheduler.schedule()
メソッドを呼び出して取得した Timer オブジェクトの toString()
メソッドから取得します。JobSchedulerRuntimeMBean.getExecutedJobs()
を呼び出して、少なくとも 1 回は実行したすべてのジョブの JobRunTimes
の配列を取得する。
cancel()
メソッドを使用して、1 回も実行していないスケジュールされたジョブをキャンセルすることはできません。
Administration Console を使用してジョブをキャンセルする方法については、Administration Console オンライン ヘルプの「ジョブの取り消し」を参照してください。
以下のデバッグ フラグを使用すると、より冗長な出力が可能になります。
-Dweblogic.debug.DebugSingletonServices=true -Dweblogic.JobScheduler=true
commonj.timer
API のメソッドおよびインタフェースの中には、ジョブ スケジューラ環境でサポートされていないものもあります。以下のメソッドおよびインタフェースはサポートの対象外です。
Work Manager (commonj.work
) API は、アプリケーションがコンテナ内で複数の作業項目を同時に実行できるようにするインタフェースを提供します。
基本的に、この API は java.lang.Thread
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
インスタンスは JNDI ルックアップを実行して取得します。管理対象の環境では、複数の WorkManager
インスタンスをサポートできます。『WebLogic JNDI プログラマーズ ガイド』を参照してください。WorkManager
はデプロイ中に resource-ref
としてコンフィグレーションします。「ワーク マネージャのデプロイメント」を参照してください。
アプリケーション レベルでは、WorkManager
の各インスタンスが WorkItem
を返します。アプリケーション内に WorkManager
を実装する方法の詳細については、WorkManager の javadoc を参照してください。
Work
インスタンスが WorkManager
に送信された後で、WorkManager
は WorkItem
を返します。WorkItem
は完了した Work
インスタンスの状態の判断に使用されます。WorkListener
インタフェースは、Work
インスタンス内に定義されたスケジューリング済み作業と WorkManager
との通信機能を提供するコールバック インタフェースです。
WorkListener
を使用すると Work
項目の現在の状態を判断できます。詳細については、WorkListener の javadoc を参照してください。
注意 : | WorkListener インスタンスは、WorkManager 経由で Work をスケジューリングするのに使用した元のスレッドと常に同じ JVM で実行されます。 |
WorkEvent
は、WorkManager
によって Work
が処理されたときに WorkListener
に送信されます。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>");
// 出力ストリームを閉じないこと - 出力ストリームはサーブレット エンジンが閉じられるようにする
// これはパフォーマンス向上のため
System.out.println("finished execution");}
}
![]() ![]() ![]() |