Javaで定期的な処理を動かしたい(1)
Javaで定期的な処理を動かしたい(2) – JMXとcronを使う
の最後、以下のリストで言えば(2)の方法です。
(リストの順番と説明の順番がちょっとおかしいのですが・・・)

1)cronでJavaを起動し、このJavaVM上で処理を行う。
2)デーモンプロセス(としてのJavaVM)上でスケジューリングして、そのVM上で処理を行う。
3)cronでJavaを起動し、処理を行っているデーモンプロセス(としてのJavaVM)上で処理を行う。

今回はquartzというオープンソースを使います。
これは、今までの流れでわかるように、
crondの変わりをするようなものですが、それだけでなく、atdのような事もできます。
crondは知っていても、atdをしらない人は私の経験上、結構いるようですが、
ようは、1回しか実行されないスケジュール処理です。

たとえば、ホテルで100円入れたら1時間だけテレビを見れるような処理を作りたいとしたら、
100円を入れた時点で、1時間後に電源を落とす処理を予約すればいいわけです。
このような処理にatdを使えば簡単にできるという訳です。
(実際にテレビのハードウェア的処理をソフトウェアに渡す処理が必要ではありますが・・・)

まあ、もっと身近なサーバ運用などでの使い方で言えば、
攻撃をしているipを見つけたら(iptablesのログなどを使って)、1時間はそのipからの処理は受け付けない。
みたいなことをatdを使って簡単に構築できるという訳です。

さて、話はもどって、quartzの使い方です。
quartzはだいたい4つの役割にわけられるのではないかと思います。
1)処理を実行するクラス
2)共有するデータ
3)処理をいつするのかを決めるクラス
4)quartzサーバ

では1)処理を実行するクラスを見ていきます。

package com.coltware.java.samples.quartz;

import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class SimpleJob implements Job {
	
	@Override
	public void execute(JobExecutionContext arg) throws JobExecutionException {
		
		JobData data = JobData.getInstance();
		
		int cnt = data.getCount();
		
		JobDetail detail = arg.getJobDetail();
		String name = detail.getName();
		String grp  = detail.getGroup();
		
		System.out.println(grp + " - " +  name + " - counter: " + cnt);
		
		data.add();

	}
}

前回と同様、処理した回数を表示するだけの処理です。
サンプルの為のサンプルコードです。

ここで中点として、実際に処理するクラスでは処理するたびにオブジェクトが作成されます。
なので、何回実行したのかは別にもつ必要があります。

quartzでもここに説明があるように機能があるのですが、
私はどうせ1つのJAVA VM上でしか共有しないのだから、シングルトンなどの機能を使ってしまえ、
というわけで、JobDataというクラスをサンプルでは用意しました。

package com.coltware.java.samples.quartz;

public class JobData {
	
	//  共有できるインスタンスを作る
	private static final JobData instance = new JobData();
	
	private int count;
	
	private JobData(){
		count = 0;
	}
	
	public static JobData getInstance(){
		return instance;
	}
	
	public int getCount(){
		return count;
	}
	
	public synchronized void add(){
		count++;
	}
}

とこんな感じですが、実際に使う場合にはもっと複雑化して、管理する項目やデータなども多くなるでしょう。

残りは3)と4)です。
この処理はmain処理で記述してしまいます。

package com.coltware.java.samples.quartz;

import java.util.Calendar;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzMain {
	
	private Scheduler scheduler = null;
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		
		QuartzMain server = new QuartzMain();
		server.start();
		
                //  5秒スリープ
		Thread.sleep(5000);
		// ここで後で決めるとしていたスケジュールを決定する
		// 10秒後に実行する
		System.out.println("booked job2");
		server.execAfter10sec();
		
		// このサンプルは勝手には止まらない。
		// さて、初めてしまったサービスを止めるにはどうしたらいいのだろうか・・・・
	}
	
	public void start() throws Exception{
		StdSchedulerFactory factory = new StdSchedulerFactory();
		scheduler = factory.getScheduler();
		
		// Quartzのサーバを起動する前からいつ実行すればよいのかわかっている処理
		JobDetail job1 = new JobDetail("job1","grp",SimpleJob.class);
		CronTrigger cron1 = new CronTrigger("cron1", "cron", "*/5 * * * * ?");
		scheduler.scheduleJob(job1, cron1);
		
		// 後でスケジュールが決まる処理
		JobDetail job2 = new JobDetail("job2","grp",SimpleJob.class);
		job2.setDurability(true);
		scheduler.addJob(job2,false);
		
		scheduler.start();
	}
	
	/**
	 * このstopメソッドはこのサンプルからは決して呼ばれることがなくなっている
	 * @throws Exception
	 */
	public void stop() throws Exception{
		if(scheduler != null && scheduler.isStarted()){
			scheduler.shutdown();
		}
	}
	
	public void execAfter10sec() throws SchedulerException{
		//  現在時より10秒後の時間を作成する
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.SECOND, 10);
		
		//
		SimpleTrigger trigger = new SimpleTrigger("trigger", cal.getTime());
		trigger.setJobName("job2");
		trigger.setJobGroup("grp");
		
		scheduler.scheduleJob(trigger);
		
	}
}

このメイン処理ではcron(のような処理)で5秒毎に処理する例と、
ある起点(この例では実行後約15秒後)に、処理をする例を示しています。

これを実行すると

grp - job1 - counter: 0
booked job2
grp - job1 - counter: 1
grp - job1 - counter: 2
grp - job2 - counter: 3
grp - job1 - counter: 4
grp - job1 - counter: 5
grp - job1 - counter: 6

のようになります。

さて、ここでサンプルのソースの中に記述をしましたが、
このサンプル上の問題は、quartzのサービスを止めるための処理であるstop()メソッドがどこからも呼べない事です。
サンプルの都合上、一応止める為の処理は書きましたが・・・・

まあ、これはサーバーをつくると決めた以上はつきまとう問題ではあります。
私はこれがjavaでなければ、素直にシグナルを使います。
が、javaの場合にはJMXを使うようにしています。

理由は2つ。
1)環境に依存しない(現実的な範囲で)。
2)実行の内部状態をみたい(サンプルではJobData内のcount変数の値)ので、そこをJMXを使って監視する。

という事です。
結局はJMXを使ってしまうという話ですが、cronでの制御は自由に記述できる人間にとってみやすく管理しやすいのですが、
アプリケーションから制御するにはいろいろと面倒なのでこのquartzでの制御も残してあげるのはそれはそれでいいのではないかと思っています。

お仕事のご依頼・相談を承ります
この記事に関連するお仕事のご依頼やご相談をお待ちしております。 詳しくは、こちら

Leave a Reply

お仕事のご依頼・相談
この記事に関連するお仕事のご依頼やご相談をお待ちしております。 詳しくは、こちら
ソフトウェア&ライブラリ




ライブラリ
airxmail(en)
AIR版メール送受信ライブラリ
airxzip
AIR版ZIP圧縮・解凍ライブラリ
執筆書籍
本、雑誌等

WEB記事:CodeZine
執筆記事はこちら
カレンダー
2011年2月
« 1月   3月 »
 123456
78910111213
14151617181920
21222324252627
28  

カスタム検索
RSS
Add to Google < !–adsense–>
アーカイブ
カテゴリ
にほんブログ村 IT技術ブログへ