cronとTask Queueによるリマインダメールの送信
今度は、イベントの開催日の前日になったら、参加者にリマインダメールを送信する機能を追加します。
このような機能は、通常のアプリケーションではバッチ処理などで実現するところです。Google App Engineでは、設定されたスケジュールをもとに処理を実行させるcronという機能があり、これを利用してメールを送信させます。
また、Google App Engineでは、通常のリクエスト処理やcronによる処理は、すべて30秒以内で終わらせなければならないという制限があります。メール送信のような処理は件数によって処理時間が30秒を超えることもありえるので、この制限にはTask Queueというバックグラウンド処理を行う機能を利用して対応します。
Modelで指定日に開催されるイベントを検索できるようにする
実際のcronやTaskQueueの処理の作成に入る前に、Modelに指定日に開催されるイベントを検索する関数を追加しておきます。
def findByDate( date:Date ) = { val c = Calendar.getInstance c.setTime( date );c.set( Calendar.HOUR, 23 ) c.set( Calendar.MINUTE, 59 );c.set( Calendar.SECOND, 59 ) val q = new Query( "event" ). addFilter( "toDate", FilterOperator.GREATER_THAN_OR_EQUAL, date.getTime ). addFilter( "toDate", FilterOperator.LESS_THAN_OR_EQUAL, c.getTime.getTime ) dsService.prepare( q ).asList( FetchOptions.Builder.withLimit( 1000 ) ).map { e => entity2Event( e ) } }
このfindByDate関数は、引数で受け取ったDateに開催されるイベントを検索します。前回説明したデータストアのLow Level APIを利用して、指定日付の00:00:00~23:59:59の範囲に開催日があるデータを検索して返すようになっています。
cronとTaskQueueのための設定
cronとTaskQueueのための設定ファイルを作成します。"src/main/webapp/WEB-INF"ディレクトリに、以下の内容でcron.xmlを作成します。
<?xml version="1.0" encoding="UTF-8"?> <cronentries> <cron> <url>/mailSender/cron</url> *1 <description></description> <schedule>every days of 00:00</schedule> *2 </cron> </cronentries>
このファイルは、cronによって処理をどのようなスケジュールで行うか設定するものです。cronは、指定した時刻に定義したURLをGoogle App Engineが呼び出してくれる機能です。よって、*1は、cron処理によってスケジュールされた時刻に呼び出されるURLを指定しています。*2は、スケジュールの定義です。ここでは、毎日00:00に処理を実行するように定義しています。スケジュールの指定方法は開発ドキュメントをご覧ください。
次に、TaskQueueで使用するQueueの定義を行います。"src/main/webapp/WEB-INF"ディレクトリに、以下の内容でqueue.xmlを作成します。
<queue-entries> <queue> <name>default</name> <rate>10/s</rate> </queue> </queue-entries>
このファイルは、queueの名前の定義と、queueから処理を実行させる間隔を指定します。詳細はドキュメントを参照ください。
MailSenderクラス - cronとTaskQueueの処理
では、実際の処理の作成に入ります。cronで指定時刻に呼び出されたときに、翌日開催されるイベントを検索してメール送信処理をTaskQueueに追加するcron関数と、TaskQueueから呼び出されて参加者にメールを送信するtask関数の2つの処理を作成します。
"src/main/scala/demo/model/MailSender.scala"を以下の内容で作成します。
package demo.model ※importについては省略します。ダウンロードサンプルをご覧ください object MailSender { def cron = { val c = Calendar.getInstance c.add( Calendar.DAY_OF_MONTH, 1);c.set( Calendar.HOUR, 0 );c.set( Calendar.MINUTE, 0 ) c.set( Calendar.SECOND, 0 );c.set( Calendar.MILLISECOND, 0 ) val queue = QueueFactory.getDefaultQueue // Queueを取得 *1 val events = Event.findByDate( c.getTime ) // 翌日開催のイベントを検索 *2 events.foreach{ e => e.members.foreach{ m => // TaskQueueに渡すパラメータの設定。イベントのIDとメールアドレスを指定 val taskOptions = TaskOptions.Builder.param("id", e.id.get.toString ). param("address", m.getEmail).url("/mailSender/task") *3 queue.add( taskOptions ) // TaskQueueに追加 *4 println("add queue %s, %s".format( e.id.get,m.getEmail ) ) }} Full(OkResponse()) } def task = { param("id").foreach{ id => param("address").foreach{ address => Event.find( id.toLong ).foreach{ e => // イベント情報を検索 *5 val formatter = new java.text.SimpleDateFormat("yyyy/MM/dd HH:mm") formatter.setTimeZone( TimeZone.getTimeZone( "Asia/Tokyo" )) val ms = MailServiceFactory.getMailService // MailServiceを取得 *6 // メールの内容を設定 val msg = new MailService.Message() msg.setSubject( "[リマインダ] 明日は%sが開催されます。" format e.name ) msg.setTo( address ) msg.setSender("test@example.com") val body = """ イベント名:%s 日時 :%s - %s 場所 :%s 主催者 :%s """.format( e.name, formatter.format( e.fromDate ), formatter.format( e.toDate ), e.place, e.user.getEmail ) msg.setTextBody( body ) ms.send( msg ) // メール送信を実行 *7 println("Task done %s, %s \n %s".format( e.name, address, body ) ) } }} Full(OkResponse()) } }
cron関数は、*1で処理を追加するQueueオブジェクトを取得し、*2で翌日開催のイベントを検索します。対象となるイベントの参加者ごとにループして、TaskQueueに1件分のメール送信処理を追加しています。
*3では、TaskQueueを追加する処理に、パラメータとしてイベントIDと送信先のメールアドレスを渡しています。ここで渡したパラメータは、TaskQueueの処理を行うtask関数でリクエストパラメータから取得して利用されます。*4で、追加した処理を"/mailSender/task"というURLで呼び出すようにして、TaskQueueに追加しています。ここでは、Queueに追加するだけで、実際の処理の呼び出しは非同期に行われます。
task関数は、リクエストパラメータからイベントIDと送信先アドレスを受け取って1件メールを送信する処理です。この関数は非同期にGoogle App Engine側から"/mailSender/task"というURLで呼び出されて実行されます。
*5で、パラメータとして受け取ったイベントIDでデータストアからイベント情報を取得します。*6で、メール送信を行うためにGoogleから提供されるAPIである、MailServiceを取得します。*7でメールの件名や宛先などを設定し、*8で、取得したMailServiceのsendメソッドを呼び出してメールを送っています。このsendメソッドによるメール送信は非同期で行われます。