SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Google App Engine/JavaによるScala/Liftアプリケーション開発

Google App Engine/JavaでのScala/Liftアプリケーション開発(後編)

GoogleサービスのAPIを利用してアプリケーションを構築

  • X ポスト
  • このエントリーをはてなブックマークに追加

cronとTask Queueによるリマインダメールの送信

 今度は、イベントの開催日の前日になったら、参加者にリマインダメールを送信する機能を追加します。

 このような機能は、通常のアプリケーションではバッチ処理などで実現するところです。Google App Engineでは、設定されたスケジュールをもとに処理を実行させるcronという機能があり、これを利用してメールを送信させます。

 また、Google App Engineでは、通常のリクエスト処理やcronによる処理は、すべて30秒以内で終わらせなければならないという制限があります。メール送信のような処理は件数によって処理時間が30秒を超えることもありえるので、この制限にはTask Queueというバックグラウンド処理を行う機能を利用して対応します。

Modelで指定日に開催されるイベントを検索できるようにする

 実際のcronやTaskQueueの処理の作成に入る前に、Modelに指定日に開催されるイベントを検索する関数を追加しておきます。

[リスト11]Event.scalaへのfindByDate関数追加
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を作成します。

[リスト12]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を作成します。

[リスト13]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"を以下の内容で作成します。

[リスト14]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メソッドによるメール送信は非同期で行われます。

次のページ
動作確認

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Google App Engine/JavaによるScala/Liftアプリケーション開発連載記事一覧
この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 尾崎 智仁(オザキ トモヒト)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5103 2010/05/21 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング