S2Chronosとは?
主に使うクラス
- タスククラス
時間に関連する処理を行うクラス. - トリガークラス
タスククラスの開始,一時停止,終了などの定義したクラス. - スケジューラクラス
スケジューリングによってタスクを実行するクラス.S2Chronosの根幹をなす機能を提供する.ほぼ意識しないで実装可能.
タスククラスの実装
- 基本なタスククラスの実装方法
最も簡単なタスククラスは以下です.メソッド名はdoExecuteとしてください.
このクラスはS2のCreatorによってS2にSMART deploy対象として扱われます.ですので他のS2のコンポーネントと同様にDIやAOPの適用が可能になります.また,タスククラスはPOJOとして実装が可能です. まず,クラス名はTaskというサフィックスをつけて,Taskアノテーションも付けます. 次にこのタスククラスを実行する条件をトリガーアノテーションで指定します.(ここではスケジューラに登録されると即時実行するNonDelayTriggerを指定します.時間指定する場合はCronTriggerなどを利用してください.)@Task @NonDelayTrigger public class BasicTask { private static Logger log = Logger.getLogger(BasicTask.class); // タスク処理 public void doExecute() { log.info(this.getClass().getSimpleName() + ":doExecute"); } }
- 初期化と破棄処理
以下のように初期化処理や,破棄処理のメソッドを定義することも可能です.
この場合は、初期化処理,タスク処理,破棄処理に対応するメソッドを定義していきます.
まず,初期化処理としてinitializeメソッドを定義して初期化処理を実装してください.初期化処理はスケジューラによってタスクが初期化されるときに一度だけ呼び出されます.
破棄処理の場合はdestoryメソッドを実装します. 破棄処理はスケジューラによってタスクが破棄されるときに一度だけ呼び出されます.ここでいう破棄とはVMのGCとは異なります.ただし,isReScheduleTaskがtrueを返すタスクの場合は破棄処理は実行されません.@Task @NonDelayTrigger public class BasicTask { private static Logger log = Logger.getLogger(BasicTask.class); // 初期化処理 public void initialize() { log.info(this.getClass().getSimpleName() + ":initialize"); } // タスク処理 public void doExecute() { log.info(this.getClass().getSimpleName() + ":doExecute"); } // 破棄処理 public void destroy() { log.info(this.getClass().getSimpleName() + ":destroy"); } }
- 開始処理と終了処理
タスクが開始されるときに呼びだされるstartメソッドと,タスクが終了するときに呼び出されるfinishメソッドを定義できます.@Task @NonDelayTrigger public class BasicTask { private static Logger log = Logger.getLogger(BasicTask.class); // 初期化処理 public void initialize() { log.info(this.getClass().getSimpleName() + ":initialize"); } // 開始処理 public void start(){ log.info(this.getClass().getSimpleName() + ":start"); } // タスク処理 public void doExecute() { log.info(this.getClass().getSimpleName() + ":doExecute"); } // 終了処理 public void end(){ log.info(this.getClass().getSimpleName() + ":end"); } // 破棄処理 public void destroy() { log.info(this.getClass().getSimpleName() + ":destroy"); } }
doから始まるタスクメソッドの名前を自由に設定したい場合は,startメソッドを用意しそのメソッドに@NextTaskアノテーションで次に実行するタスクメソッド名を付与してください.@Task @NonDelayTrigger public class BasicTask { private static Logger log = Logger.getLogger(BasicTask.class); // 初期化処理 public void initialize() { log.info(this.getClass().getSimpleName() + ":initialize"); } // 開始処理 @NextTask("taskA") public void start(){ log.info(this.getClass().getSimpleName() + ":start"); } // タスク処理 public void doTaskA() { log.info(this.getClass().getSimpleName() + ":doTaskA"); } // 終了処理 public void end(){ log.info(this.getClass().getSimpleName() + ":end"); } // 破棄処理 public void destroy() { log.info(this.getClass().getSimpleName() + ":destroy"); } }
- タスクの起動条件である
トリガーを設定する
トリガーには標準以下の種類をサポートします.- NonDelayTrigger
即時実行型.スケジューラに登録直後に即時実行されます.@Task @NonDelayTrigger // 即時実行 public class BasicTask {
- DelayTrigger
遅延実行型.指定した時間分経過した後に実行されます.@Task @DelayTrigger(delay = 1000) // 一秒後に実行 public class BasicTask {
- CronTrigger
クーロン型.CROND形式で指定した時間に実行されます.CROND形式はQuartz互換です.詳しい仕様はこちら@Task @CronTrigger(expression = "0 */1 * * * ?") // 1分ごとに実行 public class BasicTask {
- TimedTrigger
日時指定型.指定した日時に実行します.@Task @TimedTrigger(startTime = "2008/05/05 12:00:00") // 指定日時に実行 public class BasicTask {
- ScopedTimeTrigger
トリガーの有効期間を指定できるトリガーです.こちらについてはアノテーションは対応していません.@Task public class BasicTask { private ScopedTimeTrigger trigger = new ScopedTimeTrigger(new CCronTrigger("0 */1 * * * ?")); public void initialize(){ // Date型で範囲を指定する場合 Date date = DateFormat.getDateInstance().parse("2008/12/12 12:12:12"); trigger.setScopedStartTime(date); date = DateFormat.getDateInstance().parse("2008/12/13 12:12:12"); trigger.setScopedEndTime(date); // CCronTriggerで範囲を指定する場合 CROND形式の時刻条件がマッチする際にトリガーが有効となります trigger.setScopedStartCronTrigger(new CCronTrigger("0 0 4 * * ?")); trigger.setScopedEndCronTrigger(new CCronTrigger("0 0 5 * * ?")); } public TaskTrigger getTrigger(){ return trigger; } }
- NonDelayTrigger
トリガークラスの実装
- トリガーのカスタマイズ
トリガーはカスタマイズすることが可能です.以下のようにルートパッケージ+triggerに"C"で始まって"Trigger"で終わるトリガークラスを実装してください.トリガークラスはTaskTriggerインターフェイスを実装している必要があります.実際にはAbstractTriggerクラスを継承すると便利です.public class CExampleTrigger extends AbstractTrigger { private static final long serialVersionUID = 1L; private int div; public boolean isShutdownTask(){ // タスクのシャットダウン条件.trueを返すとタスクは即座にシャットダウンされます. return false; } public boolean isEndTask() { // タスクの終了条件.trueを返した場合はタスクを終了させます.ただし,即時終了ではなくタスクメソッド単位での終了制御となります. ど return false; } public boolean isStartTask() { // trueを返したらタスクが実行されます. return System.currentTimeMillis() % div == 0; } public void setShutdownTask(boolean shutdownTask){ } public void setEndTask(boolean endTask) { } public void setStartTask(boolean startTask) { } @Override public boolean isReSchedule() { // trueを返しすとタスク終了後に再度スケジューリングされます.標準トリガーはトリガーによってはこの値の規定値が異なります. return true; } public int getDiv() { return div; } public void setDiv(int div) { this.div = div; } }
トリガークラスに対応するアノテーションは以下のように同名のプロパティを持たせます.@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ExampleTrigger { int div() default 2; }
タスクへの適用は以下のようになります. アノテーションで指定された属性はトリガークラスの同名のプロパティにDIされます.@Task @ExampleTrigger(div = 3) public class ExampleTriggerTask extends AbstractCommonTask { snip...
- ダイナミックトリガーの実装について
以下のようにタスククラスにトリガーアノテーションを記述しないで,triggerプロパティを持たせることで動的なトリガーを定義することが可能です.もちろん,独自に作成したトリガーにも対応しています.@Task public class ExampleTriggerTask extends AbstractCommonTask { private CCronTrigger trigger = new CCronTrigger("0 */1 * * * ?"); public TaskTrigger getTrigger(){ return trigger; } }
スレッドプールについて
- タスククラスのスレッドプール
タスククラスにスレッドプールを以下の方法で指定することができます.スレッドプールのタイプはJava5のconcurrentパッケージに準拠します.FIXED/CACHED/SINGLE/SCHEDULEDが使えます.スレッドプールタイプを指定しない場合はCACHEDで動作します.@Task @NonDelayTrigger public class BasicTask { private static Logger log = Logger.getLogger(BasicTask.class); // スレッドプールタイプを返します. public ThreadPoolType getThreadPoolType(){ return ThreadPoolType.FIXED; } // スレッドプールのサイズを返します. public Integer getThreadPoolSize(){ return 10; } // タスク処理 public void doExecute() { log.info(this.getClass().getSimpleName() + ":doExecute"); } }
TaskThreadPoolを使っても同様に設定可能です.また,TaskThreadPoolを複数のタスククラスで共有することでスレッドプールを共有することが可能になります.@Task @NonDelayTrigger public class BasicTask { private static Logger log = Logger.getLogger(BasicTask.class); private TaskThreadPool threadPool = new ThreadPool(); public void initialize(){ threadPool.setThreadPoolType(ThreadPoolType.FIXED); threadPool.setThreadPoolSize(10); } // スレッドプールタイプを返します. public TaskThreadPool getThreadPool(){ return threadPool; } // タスク処理 public void doExecute() { log.info(this.getClass().getSimpleName() + ":doExecute"); } }
タスクのフロー制御
- タスクのフロー制御をNexTaskアノテーションとJoinTaskアノテーションで行う
タスクメソッドに対してNetTaskアノテーションを付与することでタスク処理のフローを実装することができます.以下の例では,doTaskAが正常に終了した場合doTaskBに遷移します.
また,タスクメソッドを同期もしくは非同期で実行するかどうかを,JoinTaskアノテーションで指定することができます.JoinType.NoWaitを指定した場合はスケジューラがタスクメソッドを非同期に呼び出します.JoinType.Waitの場合は同期呼び出しになります.デフォルトは同期呼び出しです.doTaskBのようにStringで遷移先のタスクメソッドを指定することもできます.この場合は無条件に同期呼び出しになります./** * Simpleタスクです. *
* NonDelayTriggerアノテーションの場合は,即時実行のトリガーとなります. *
* @author junichi * */ @Task @NonDelayTrigger public class SimpleTask { /** * ロガーです. */ private static Logger log = Logger.getLogger(SimpleTask.class); /** * タスクが開始されるときに最初に呼ばれます. ** 次のタスクはdoTaskAメソッドになります. *
*/ @NextTask("taskA") public void start() { log.info("[[SimpleTask::start]]"); } /** * タスクメソッドAです. ** JoinTaskアノテーションがNoWaitの場合は本メソッドは非同期に実行されます. * 次のタスクはdoTaskBメソッドになります. *
*/ @NextTask("taskB") @JoinTask(JoinType.NoWait) public void doTaskA() { log.info("[[SimpleTask::doTaskA]]"); } /** * タスクメソッドBです. ** JoinTaskアノテーションがWaitの場合は本メソッドは同期的に実行されます. * 次のタスクは,戻り値で設定されます. *
* @return 次のタスク名 */ @JoinTask(JoinType.Wait) public String doTaskB() { log.info("[[SimpleTask::doTaskB]]"); return "taskC"; } /** * タスクメソッドCです. ** JoinTaskアノテーションがNoWaitの場合は本メソッドは非同期に実行されます. * CloneTaskで本メソッドを並列処理数を指定できます. * 次のタスクはありません. *
*/ @JoinTask(JoinType.NoWait) @CloneTask(10) public void doTaskC() { log.info("[[SimpleTask::doTaskC]]"); } /** * すべてのタスクメソッドが終了したら呼ばれます. */ public void end() { log.info("[[SimpleTask::end]]"); } } - CloneTaskアノテーションでタスクメソッドの並列実行
タスクメソッドを並列実行する際は,CloneTaskアノテーションが使えます.上記のように10を指定した場合は,タスクに割り当てられたスレッドプール内で同時10個のタスクメソッドを処理しようとします.
例外のハンドリング
- 例外をスローした際の挙動
タスクメソッド内で例外をスローすると次に実行するタスクメソッドがあっても実行せずに終了処理メソッドを呼び出します。 - スローした例外を処理する方法
setterもしくは例外キャッチ用メソッドで例外を受けとり処理することができます。setterで受け取る方法
@Task @NonDelayTrigger public class BasicTask { private static Logger log = Logger.getLogger(BasicTask.class); // タスク処理 public void doExecute() { thow new RuntimeException(); } public void setException(Exception ex){ this.exception = ex; } public void destory(){ if ( this.exception != null ){ // 例外処理 } } }
catchExceptionメソッドで受け取る方法
@Task @NonDelayTrigger public class BasicTask { private static Logger log = Logger.getLogger(BasicTask.class); // タスク処理 public void doExecute() { thow new RuntimeException(); } public void catchException(Exception ex){ this.exception = ex; } }
その他情報源
リーダコミッタのblog http://d.hatena.ne.jp/j5ik2o/