Scala

Microsoft翻訳APIをPlayframework2(Scala)で使うサンプル

概要

 自分のアプリの改修に伴い翻訳APIを使った時の事をまとめた記事です。Microsoft Translatorを使いました。他にも翻訳APIは例えばGoogle Translate APIがありますが2011年末に有料化したので、Microsoft Translatorです。
 API使用には、使用登録、APIからaccess_tokenを取得、そのaccess_tokenを使い翻訳API使用、の手順が必要です。

登録

 Windows Azure Marketplaceから登録を行って下さい。使うのはMicrosoft Translatorです。この記事を書いている時点で2,000,000文字/月までは無料ですがよく確認しましょう。
 この後アプリケーションを登録すると、「顧客の秘密」という値が受け取れます。これが後でclient_secretになります。登録したアプリケーションはここから確認できます。

access token取得

 以下のAPIから返ってくるjsonより、access_tokenを取得します。

・エンドポイントURL
https://datamarket.accesscontrol.windows.net/v2/OAuth2-13
・パラメタ
client_id アプリケーションを登録したときに登録した「クライアントID」
client_secret アプリケーションを登録したときに登録した「顧客の秘密」
scope http://api.microsofttranslator.com
grant_type client_credentials
・戻り値
{"token_type":"http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0","access_token":"ここにアクセストトークンが入ります","expires_in":"599","scope":"http://api.microsofttranslator.com"}

戻り値のaccess_tokenを次に使います。なおこのtokenの有効時間は10分だけなので、キャッシュして使い回しましょう。時間が来たら再取得が必要です。

翻訳APIの使用

 翻訳APIに翻訳元と先の言語コードと、翻訳したい文字列を渡して翻訳します。なお、ここで使う言語コードはTranslator Language Codesで定義されています。対応している言語もこちらで確認してください。

・エンドポイントURL
http://api.microsofttranslator.com/V2/Http.svc/Translate
・パラメタ
from 翻訳元テキストの言語コード
to 翻訳先の言語コード
text 翻訳したい文字列
・ヘッダ
キーは「Authorization」、値は「Bearer」+ 半角スペース + 先程取得したaccess_token
・戻り値
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">ここに翻訳結果の文字列</string>

これで翻訳結果が取得できます。

サンプルサイト

サンプルのサイトです。入力されたキーワードを一度他の言語に翻訳し、それを再び元に戻して返します。いわゆる再翻訳です。
http://honyaku.herokuapp.com/

実装サンプル

github

リンク

ドキュメント 
Microsoft Translator

finagle入門 - 複数Serviceの作り方

前回:finagleを動かすまで。
今回:複数のServiceを持たせる。

Serviceについて

 finagleでは、Serviceというものを使いアプリを作っていきます。Serviceはリクエストを受け取り、Futureの形で処理結果を返すものです。これにより非同期な処理も実現しています。
 ここで、finagleにリクエストが来たときにどのServiceを使うか振り分けるのに使うのが、RoutingService.byPathです。finagleのソースで軽く触れられていますが、どれにも該当しなかった場合は404が返ります。

サンプル

今回作成したサンプルはこちらですので、これを参照してください。
tattyamm/finagle-http-sample at v0.2
RoutingService.byPathを使ったのはこの辺りです。
  val routingService =
    RoutingService.byPath {
      case "/hello" => new HelloService
      case "/echo" => new EchoService
    }

  val server: Server = ServerBuilder()
    .codec(RichHttp[Request](Http()))
    .bindTo(new InetSocketAddress(8080))
    .name("httpserver")
    .build(routingService)

finagle入門 - サンプルを動かすまで

finagle入門記事です。finagleについて調査して最初のサンプル作るところまでをまとめました。

finagleとは

Finagle, from Twitterを見ましょう
・読み方はフィネーフル、ネにアクセント。
・Twitter社が公開したRPCフレームワーク。
・クライアント、サーバー。
・Scalaです。
・Nettyの薄いラッパー。Netty側もたまに必要になる。
・プロトコルとしてHTTP、Streaming HTTP などをサポート。
・ドキュメントだけではよくわからない事もある。ソース読む必要も。
・Futureという物を使い、非同期処理が書ける。

ドキュメント

・公式のドキュメント。一読する必要がある。
Finagle Developer Guide
Finagle Developer Guide (December 15, 2011 Draft) の適当な和訳 — Gist
・公式のAPIリファレンス
API Reference (Scaladoc)
・Twitterが公開しているScala Schoolの中にfinagle入門のドキュメントがある
Scala School - An introduction to Finagle
・日本語でFuture等についての解説が読める。
Effective Scala

Futureについて

・Futureは、まだ完了していない計算結果に対する約束(Promise)を保持する。
・同期的にブロックして結果が欲しいなら、Futureを.getすれば結果を受け取れる。
・非同期に結果が欲しいなら、Futureをmapして結果を受け取る。Effective Scalaの例に考える。
val result: Future[Int]
val resultStr: Future[String] = result map { i => i.toString }
ここでresultは、なんか処理した結果Intが結果として返ってくるFurure。このresultの結果を受け取りたいときは、Future型のresultをmapすれば良い。mapの中の値iより先は、iが受け取れるようになったら実行される。
・いくつかの処理を並列で実行し、その結果を受け取る為の仕組みも準備されている。Future.collect()やFuture.join()を使う。
・日本語ドキュメントが最近できましたので、こちらもお読みください。 Future と Promise - Scala Documentation
・Serviceとは、requestを受け取りFutureを返すもので、これを組み合わせてアプリケーションを作っていく。

finagleをはじめる準備

finagleはScalaで作ります。Scalaのビルドツールであるsbtを用意しましょう。またテンプレートを使いたいのでgiter8も入れてください。まずg8コマンドでscalaプロジェクトを作成します。
$ g8 typesafehub/scala-sbt

Scala Project Using sbt 

organization [org.example]: jp.tattyamm.finagle.sample
name [Scala Project]: finagle-sample
scala_version [2.9.2]: 2.9.2
version [0.1-SNAPSHOT]: 0.1-SNAPSHOT

Applied typesafehub/scala-sbt.g8 in finagle-sample
プロジェクトのディレクトリが作成されます。これをInteliJで読み込むためにsbt-ideaを準備しましょう。プロジェクトのディレクトリ下にproject/plugins.sbtを作成。内容は以下の通り。なお、com.github.mpeltonenの最新バージョンを確認して導入した方が良いです。(sbt-assemblyもついでに入れちゃいましょう。必須ではありません。)
resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"

addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")

//sbt-assembly
resolvers += Resolver.url("artifactory", url("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns)

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.8.4")
このプラグインを読み込ませ、InteliJ用のファイルを作成します。
$sbt update
$sbt gen-idea
これでInteliJのOpen Projectからプロジェクトを読み込めるようになりました。InteliJの設定がまだ残っているので、以下のことをやってください。
・File -> Project Structure -> Project -> Project SDKをJavaのバージョン(1.7)に設定。
・File -> Project Structure -> Modules -> 表示されているやつ両方 -> DependenciesのModule SDKをJavaのバージョン(1.7)に設定。
・File -> Project Structure -> Facets -> Scala -> Compiler instantiationでUse ordinary compilerを選択し、Scalaのバージョン等を設定。
これでビルドできるようになります。次は実行する為の設定です。
・Build -> Edit Configuration -> 左上のプラスボタンからApplicationを選択 -> Main classを適切に設定、Use classpath of moduleをプロジェクト名のものに設定してOKで確定
これで実行もできるようになりました。Runして確かめられます。
あと、sbtのバージョンを定義する必要があるので、project/build.propertiesに以下の1行を書いたファイルを作成してください。バージョンは環境依存ですので、sbt --versionでバージョンを確認してください。
sbt.version=0.11.3


finagleのはじめ方

さて、ここからがfinagleの準備です。finagleにはfinagle-coreというものが必要です。今回はfinagle-httpも使います。その為の準備をbuild.scalaで行います。安定板がどれかわからないので、とりあえず現時点で最新の5.3.20を入れました。最終的にこのような感じになります。resolversとlibraryDependenciesがポイント。
import sbt._
import sbt.Keys._
import scala._

object FinaglesampleBuild extends Build {

  lazy val finaglesample = Project(
    id = "finagle-sample",
    base = file("."),
    settings = Project.defaultSettings ++ Seq(
      name := "finagle-sample",
      organization := "jp.tattyamm.finagle.sample",
      version := "0.1-SNAPSHOT",
      scalaVersion := "2.9.2",
      // add other settings here
      resolvers ++= Seq(
        "Twitter Repository" at "http://maven.twttr.com/"
      ),
      libraryDependencies ++= Seq(
        "com.twitter" % "finagle-core" % "5.3.20",
        "com.twitter" % "finagle-http" % "5.3.20"
      )
    )
  )
}
ここに書いたものがInteliJに読み込まれなければ$ sbt gen-ideaなどをすれば解消されるはずです。またhttp://maven.twttr.com/は接続できない事も多いので、何度かリトライして下さい。

サンプルはfinagleのサイトにあるHttpというものを使います。ただしFilterの例としてAuthorizeクラスがあるのですが、シンプルにする為に取り除きました。ほぼコピーですがソースコードはこんな感じになります。(ソース全体はgithubのtattyamm/finagle-http-sampleにあります。)
package jp.tattyamm.finagle.sample

import com.twitter.finagle.{Service, SimpleFilter}
import org.jboss.netty.handler.codec.http._
import org.jboss.netty.handler.codec.http.HttpResponseStatus._
import org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1
import org.jboss.netty.buffer.ChannelBuffers.copiedBuffer
import org.jboss.netty.util.CharsetUtil.UTF_8
import com.twitter.util.Future
import java.net.InetSocketAddress
import com.twitter.finagle.builder.{Server, ServerBuilder}
import com.twitter.finagle.http.Http

/**
 * This example demonstrates a sophisticated HTTP server that handles exceptions
 * and performs authorization via a shared secret. The exception handling and
 * authorization code are written as Filters, thus isolating these aspects from
 * the main service (here called "Respond") for better code organization.
 */
object HttpServer {
  /**
   * A simple Filter that catches exceptions and converts them to appropriate
   * HTTP responses.
   */
  class HandleExceptions extends SimpleFilter[HttpRequest, HttpResponse] {
    def apply(request: HttpRequest, service: Service[HttpRequest, HttpResponse]) = {

      // `handle` asynchronously handles exceptions.
      service(request) handle { case error =>
        val statusCode = error match {
          case _: IllegalArgumentException =>
            FORBIDDEN
          case _ =>
            INTERNAL_SERVER_ERROR
        }
        val errorResponse = new DefaultHttpResponse(HTTP_1_1, statusCode)
        errorResponse.setContent(copiedBuffer(error.getStackTraceString, UTF_8))

        errorResponse
      }
    }
  }

  /**
   * The service itself. Simply echos back "hello world"
   */
  class Respond extends Service[HttpRequest, HttpResponse] {
    def apply(request: HttpRequest) = {
      val response = new DefaultHttpResponse(HTTP_1_1, OK)
      response.setContent(copiedBuffer("hello world", UTF_8))
      Future.value(response)
    }
  }

  def main(args: Array[String]) {
    val handleExceptions = new HandleExceptions
    val respond = new Respond

    // compose the Filters and Service together:
    val myService: Service[HttpRequest, HttpResponse] = handleExceptions andThen respond

    val server: Server = ServerBuilder()
      .codec(Http())
      .bindTo(new InetSocketAddress(8080))
      .name("httpserver")
      .build(myService)
  }
}
これを実行し、http://localhost:8080/にアクセスした結果、「hello world」と表示されれば成功です。ServerBuilderのcodecにHttpを渡すだけでHTTPサーバーとして機能し、ここにDefaultHttpResponseを返すServiceを定義する事でサーバーに何をさせるかを定義しています。

まとめ

今回はfinagleを動かすところまでをまとめました。次回以降はもうちょっとfinagleを使った具体的な使い方を書きたいです。

参考、または参考になりそうなサイト

・最もシンプルと思われる、Herokuによるfinagleの始め方。
Heroku | Scala on Heroku
・全体の解説
steps to phantasien(2011-11-04)
・Herokuでfinagleを動かしている
Nihitok's Tumbir - finagleで作るtwitterのRESTAPIのようなサーバー1

更新履歴

2013.11.14 バージョン依存の部分を少しだけ加筆。

TwitterクライアントをPlayframework2+Scalaで作ってHerokuで動かした

Playframework2とScalaでちょっとしたアプリケーションを作ってみたく、Twitterクライアントを作成しました。Herokuで動かしています。
playframework2_logo Heroku | Logos

実物

Play2TwitterClient

ソースコード

githubにて公開しました。 tattyamm/Play2TwitterClient

Herokuへのデプロイ手順

Play2のアプリケーションをHerokuへデプロイする手順の詳細は以前ブログに書きましたので、そちらをご覧下さい。
Playframework2(Scala)をHerokuで動かす手順 - えんたつの記録
今回は手順の概要だけ書きます。

Herokuアプリケーションの作成。(play2clientは名前)
$ heroku create --stack cedar play2client
プロジェクトのルートディレクトリにProcfileという名前で以下のファイルを作成
web: target/start -Dhttp.port=${PORT} ${JAVA_OPTS}
gitにcommitした状態で、Herokuにデプロイ。(herokuappはブランチ名)
$ git push heroku herokuapp:master
リアルタイムなログ確認
$ heroku logs --tail

その他技術情報

Twitter API関係
Playframework2とScalaでTwitter APIを操作する - えんたつの記録
他はPlay2のドキュメント読んでました。

感想

・とりあえず動いているが、直すべき所は多い。
・もっとライブラリ使って行く必要がありますね。util-evalやTwitter4Jなど。ここは次回以降か、もしアップグレードするならこのアプリにて。

Playframework2(Scala)をHerokuで動かす手順

今回はPlayframework2とScalaで作ったアプリケーションをHerokuで動かします。Add onも試したかったので、memcachedもHerokuの無料の範囲で使用しました。

Herokuとは

Heroku | Logos
・PaaS
・Ruby、Clojure、Java(Enterprise)、Python、JavaScript(Node.js)、Scalaが動く。
・基本無料で、後から機能の追加や性能のアップが可能。
・デプロイにはgitを使う。
・AWSの上で動いている。(なのでAWS障害のときには停止した)
・Salesforce.comにより2011年に買収された。

今回の環境や条件

・Mac OS X 10.6.8
・Playframework2 + Scalaのアプリ
・アプリからmemcachedを利用する

まとめ

・手順は簡単
・Heroku向けの設定を書く必要があるが、量はあまり多く無い。
・ある程度無料で使える
・ただしAdd On追加にはクレジットカード登録が必須
・今回完成したものはこちらです。
HerokuGithubを参照してください。

Herokuを使う準備

 まずHerokuにユーザー登録を行う。Sign Upからメール認証をします。
 次にherokuコマンドを使えるようにする為、heroku toolbeltをインストールします。Heroku Toolbeltからダウンロードしたソフトをインストール($ gem install herokuでも良い。OSのバージョンアップ時には注意が必要)。

 公開鍵と秘密鍵の生成を行います。
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/username/.ssh/id_rsa): /Users/username/.ssh/heroku_rsa
Enter passphrase (empty for no passphrase): (パスワード)
Enter same passphrase again: (パスワード)
ここで~/.ssh/configの設定もしておいて、herokuに接続するときにこの鍵が使われるようにします。~/.ssh/configを編集。
Host heroku
User git
HostName heroku.com
IdentityFile    ~/.ssh/heroku_rsa
Port 22
TCPKeepAlive yes
IdentitiesOnly yes
秘密鍵の登録を行います。
$ ssh-add ~/.ssh/heroku_rsa
Enter passphrase for /Users/username/.ssh/heroku_rsa: 
Identity added: /Users/username/.ssh/heroku_rsa (/Users/username/.ssh/heroku_rsa)
これでHerokuに接続するための下準備は完了です。

 herokuがインストールできたか確認しつつセットアップを行います。herokuコマンドを最初に打ったときにはメールアドレスなどの入力が求められます。
$ heroku login
Enter your Heroku credentials.
Email: (アドレス)
Password (typing will be hidden): (パスワード)
Found the following SSH public keys:
1) github_rsa.pub
2) heroku_rsa.pub
3) id_rsa.pub
Which would you like to use with your Heroku account? 2 (さっきつくった鍵を指定)
Uploading SSH public key /Users/username/.ssh/heroku_rsa.pub... done
Authentication successful.
確認のためにherokuのアプリ一覧を見るコマンドを入れてみましょう。
$ heroku list
=== My Apps
stark-dawn-hogehoge
こんな感じに表示されれば完了です。

herokuの準備

herokuにアプリケーションをつくります。$ heroku createコマンドだけだと自動で名前が決定されるのですが、今回は名前を指定します。(ちなみに名前の変更は$ heroku rename 名前、というコマンドです)
$ heroku create --stack cedar checktrend
Creating checktrend... done, stack is cedar
http://checktrend.herokuapp.com/ | git@heroku.com:checktrend.git
Git remote heroku added
この時点でheroku側にwelcomeページが作成されている事を確認しましょう。

ここでAddOnを導入します。今回はMemcacheを使います。(ほんとはRedis To Goを導入したかったのですが、Play側の設定が上手く行かなかった)。無料の5MBプランを使用するので、以下のコマンドを実行。
$ heroku addons:add memcache:5mb
Adding memcache:5mb on checktrend... failed
 !    Please verify your account to install this add-on
 !    For more information, see http://devcenter.heroku.com/categories/billing
 !    Verify now at https://heroku.com/verify
失敗です。Verifyしないとだめなようです。調べてみると、無料でもAdd Onの使用にはクレジットカード登録が必要だそうです。なのでdashboardなどからクレカ登録を済ませてから再度コマンドを実行します。
$ heroku addons:add memcache:5mb
Adding memcache:5mb on checktrend... done, v2 (free)
Use `heroku addons:docs memcache:5mb` to view documentation.
追加できました。確認しましょう。
$ heroku addons
=== checktrend Configured Add-ons
memcache:5mb
追加されているのが確認できました。

Play2のアプリをHeroku向けに書き換える

 Play2のプロジェクトのルートディレクトリにProcfileという名前で以下の内容を作ります。
web: target/start -Dhttp.port=${PORT} ${JAVA_OPTS}

 次にmemcached向けの設定を行います。memcachedをPlay2から使う方法はこのブログでも紹介したことがありますのでそちらを参照してください。このmumoshu/play2-memcachedを使います。
 memcachedの接続先のサーバー情報はapplication.confに記述します。ここにHerokuの環境変数を使っ設定を書くようです。playframeworkのドキュメントに書いてありました。「my.key = ${?MY_KEY_ENV}」という形式だそうです。しかしこのように書くのだと考えていたのですが上手く行きません。その時はこの内容をapplication.confに追記しました。
# memcached
# デフォルトのEhCachePluginを無効にします
ehcacheplugin=disabled
# memcachedの設定
#memcached.host="${?MEMCACHE_SERVERS}:11211"
#memcached.user=${?MEMCACHE_USERNAME}
#memcached.password=${?MEMCACHE_PASSWORD}
環境変数からの設定が上手くいかないので、接続先サーバーを直接指定します。まずこのコマンドで環境変数を確認します。
$ heroku config --app checktrend
=== checktrend Config Vars
JAVA_OPTS:                    -Xmx384m -Xss512k -XX:+UseCompressedOops
MEMCACHE_PASSWORD:            uwaaaaaaaa
MEMCACHE_SERVERS:             nantoka.host
MEMCACHE_USERNAME:            memcachedusername
PATH:                         .sbt_home/bin:/usr/local/bin:/usr/bin:/bin
REPO:                         /app/.sbt_home/.ivy2/cache
SBT_OPTS:                     -Xmx384m -Xss512k -XX:+UseCompressedOops
この中からmemcachedに関係しそうなものをメモしておきます。そしてこの値を直接application.confに書き込みます。
# memcached
# デフォルトのEhCachePluginを無効にします
ehcacheplugin=disabled
# memcachedの設定
memcached.host="nantoka.net:11211"
memcached.user="username"
memcached.password="password"


デプロイ

gitにアプリケーションを登録します。
$ git init
$ git add .
$ git commit -m "init"
これをherokuにpushすることでデプロイができます。
$ git push heroku master
なおこのコマンドはデフォルトではローカルのmasterブランチをデプロイします。ブランチを指定したい場合は、次のように書く必要があります。 herokuに任意のブランチをpushするにはこのようにしてください。これはherokuappというブランチをデプロイする場合の例です。
$ git push heroku herokuapp:master 
大量にログが表示され、最後の方で「http://checktrend.herokuapp.com deployed to Heroku」などと表示されていれば成功です。動いたか実際に見てみましょう。ブラウザからURLを開いても良いですし、以下のコマンドでもブラウザが開きます。
$ heroku open

これで動作確認をすれば全て完了です。私のアプリはこのように動いています。
Twitter Trends Available
ここでログも確認しましょう。課金していないので最新のものだけがこのように表示されていきます。
$ heroku logs
2012-10-08T09:44:26+00:00 heroku[router]: Blank App -> GET checktrend.herokuapp.com/ dyno= queue= wait= service= status=200 bytes=
2012-10-08T09:44:26+00:00 heroku[router]: Blank App -> GET checktrend.herokuapp.com/favicon.ico dyno= queue= wait= service= status=200 bytes=
2012-10-08T10:06:23+00:00 heroku[api]: Add redistogo:nano add-on by メアド
2012-10-08T10:06:23+00:00 heroku[api]: Release v2 created by メアド
(以下略)


必要に応じてやること

Herokuは1時間アクセスが無いとIdle状態に入り、その後アクセスしたときに30秒ほどページ表示までかかります。頻繁にアクセスしないページの場合、1時間おきにアクセスするよう、Herokuのschedulerをセットしておきましょう。
HerokuのDynoが寝ないようにする #heroku - Qiita

ログを見る

 リアルタイムにログを見たい場合はこうします
$ heroku logs --tail
現在はこのコマンドを使うのにadd-on入れる必要はありません。またこのコマンドは直近のある程度の量しか見る事ができません。それ以上使いたい場合はHerokuに課金する必要があるらしいです。

 ログを収集、解析見る為のAdd onもHerokuは提供しています。この2つが使われている例を見たので、とりあえず両方入れてどちらかを選べば良いのではないでしょうか。ここはあまり調査をしていません。
Loggly
$ heroku addons:add loggly:mole
Papertrail
$ heroku addons:add papertrail:choklad


その他コマンド

psコマンドも用意されています。
$ heroku ps
=== web: ``
web.1: created 2012/10/08 19:06:23 (~ 1h ago)

サーバー上でplayコマンドを実行することができます。
$ heroku run sbt play
(大量のログは略)
       _            _ 
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/ 
             
play! 2.0.2, http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[TwitterProxy] $ 
この状態ではPlayのコマンドが使用できるので、例えばtestコマンドも実行できます。

herokuのアップデートはこのように行います。
$ heroku update


参考

Getting Started with Scala on Heroku | Heroku Dev Center 第3回 クラウドプラットフォーム「Heroku」の活用 | Think IT
HerokuでWebアプリ開発を始めるなら知っておきたいこと (1)無料のスペック - アインシュタインの電話番号☎
無料のPaaS、HerokuでPlay frameworkを使ったWebアプリケーションを動かす | クラスメソッド開発ブログ
play2.0 on herokuでmemcachedを使う #Memcached #heroku #Scala #Play - Qiita
第4回 Herokuのアドオンと外部サービスを活用しよう | Think IT
$HOME/.ssh/configを活用していますか? — ディノオープンラボラトリ
HerokuでSSH公開鍵(public key)を登録する方法(と削除して再登録する方法) | ID-Blogger
heroku用の鍵設定を~/.ssh/configとかに書いてログインできるようにする。 | かなりすごいブログ
play2.0 on herokuでmemcachedを使う #Memcached #heroku #Scala #Play - Qiita

補足情報:redisの場合(失敗したが途中までの記録)

play2側の設定がわからず途中で断念しました。その手前までの記録です。
$ heroku addons:add redistogo:nano
Adding redistogo:nano on checktrend... done, v2 (free)
Use `heroku addons:docs redistogo:nano` to view documentation.
追加できました。確認しましょう。
$ heroku addons
=== checktrend Configured Add-ons
redistogo:nano

 次はredisの設定です。ドキュメントを見ていきましょう。これによるとredisの接続先URLはREDISTOGO_URLという環境変数に格納されるようです。
 このプロジェクトで使っているredisについては過去にまとめました。使っているのはこのプラグインです。このドキュメントによると、redisの接続先はapplication.confにredis.hostを追記すればよさそうです。
 環境変数により設定ができるようなので、application.confに以下の行を追記すれば良いようです。なおこの環境変数が無かった場合これは無視されるので、ローカルでの場合とコードを書き換える必要はありません。
redis.host=${?REDISTOGO_URL}
 これで準備できたのでデプロイを行っても、どうしてもredisに接続できないという内容や、hostの指定が間違っているというエラーになります。例えばこういうのです。
Caused by: com.typesafe.config.ConfigException$Parse: application.conf: 49: Expecting end of input or a comma, got ':' (if you intended ':' to be part of the value for 'redis.host', try enclosing the value in double quotes, or you may be able to rename the file .properties rather than .conf)
検索した感じだとこのStack Overflowぐらいしか近そうなケースが見つからず、redis.hostには何を設定するのか、他に方法があるのかなど対処法がまだわかりません。

Playframework2(Scala)でmemcachedを使う

 Play2でmemcachedを使うにはどうすれば良いか調べたらStack Overflowに質問がありました。これによるとmumoshu/play2-memcachedが使えるらしいです。公式でも紹介されていますね。これを試してみました。
 今回作成したプロジェクトはこちらです。
tattyamm/Play2MemcachedSample

準備

まずmemcachedをbrewでインストール。
$ brew install memcached
You can enable memcached to automatically load on login with:
    mkdir -p ~/Library/LaunchAgents
    cp /usr/local/Cellar/memcached/1.4.13/homebrew.mxcl.memcached.plist ~/Library/LaunchAgents/
    launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist

If this is an upgrade and you already have the homebrew.mxcl.memcached.plist loaded:
    launchctl unload -w ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist
    cp /usr/local/Cellar/memcached/1.4.13/homebrew.mxcl.memcached.plist ~/Library/LaunchAgents/
    launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist

Or start it manually:
    /usr/local/bin/memcached

Add "-d" to start it as a daemon.
memcachedを起動します。
/usr/local/bin/memcached

今回の為のプロジェクトをplayアプリをScalaで作成し、InteliJで読み込めるようにします。
$ play new Play2MemcachedSample
$ cd Play2MemcachedSample
$ play idea


Play2からmemcachedを使う準備

Build.scalaにいくつか追加します。
import sbt._
import Keys._
import PlayProject._

object ApplicationBuild extends Build {

  val appName = "Play2MemcachedSample"
  val appVersion = "1.0-SNAPSHOT"

  val appDependencies = Seq(
    // Add your project dependencies here,
    "com.github.mumoshu" %% "play2-memcached" % "0.2.1-SNAPSHOT"
  )

  val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
    // Add your own project settings here
    resolvers += "Sonatype OSS Snapshots Repository" at "http://oss.sonatype.org/content/groups/public",
    resolvers += "Spy Repository" at "http://files.couchbase.com/maven2" // required to resolve `spymemcached`, the plugin's dependency.
  )

}
conf/に新しくplay.pluginsというファイルを新規作成し、以下の内容を書きます。
5000:com.github.mumoshu.play2.memcached.MemcachedPlugin
application.confに以下の内容を追記します。
# デフォルトのEhCachePluginを無効にします
ehcacheplugin=disabled
# memcachedのアドレスとポートを指定します
memcached.host="127.0.0.1:11211"

Play2からmemcachedを使う

準備ができたら、通常のCache APIとほぼ同じように扱う事ができます。Application.scalaに以下のように書いて試してみました。
package controllers

import play.api._
import cache.Cache
import play.api.mvc._
import play.api.Play.current

object Application extends Controller {

  def index = Action {

    // set
    try {
      Cache.set("item.key", "item.value")
    } catch {
      case _ => println("Cacheをsetするとこでエラーのはずだが、memcachedを起動していなくてもここに来ない")
    }

    // setにキャッシュの有効期限を秒で指定できる。
    try {
      Cache.set("item.key2", "item.value2", 30)
    } catch {
      case _ => println("Cacheをsetするとこでエラーのはずだが、memcachedを起動していなくてもここに来ない")
    }

    // get 方法1
    //   方法1と2のどちらのように値を決めても良い。
    val valueFromCache1: Option[String] = Cache.getAs[String]("item.key")
    println(valueFromCache1.getOrElse("item.keyがなかった"))

    // get 方法2
    val valueFromCache2: String = Cache.getOrElse[String]("item.key") {
      "item.keyがなかった"
    }
    println(valueFromCache2)

    // get キーが存在しない場合の挙動
    val valueFromCache3: String = Cache.getOrElse[String]("hogehogeohoge") {
      "item.keyがなかった(正常)"
    }
    println(valueFromCache3)

    Ok(views.html.index("Your new application is ready."))
  }

}
コンソールを確認すると、上手く扱えている事がわかります。

キーの削除はCache APIの方法ではなく次のように行います。
play.api.Play.current.plugin[MemcachedPlugin].get.api.remove("keyToRemove")
なお、memcachedの中身を確認するときは$ telnet localhost 11211で接続し、キーを指定するならget keynameで値を取り出すことができます。

 使ってみた上での注意点は、memcachedが起動していなくてもCache.setなどで例外が発生しないことです。例外が出ず気づきにくいので注意したいところ。
 これでmemcachedがPlay2のCache APIとして使えるようになりました。redis同様に自然に使う事ができて非常に便利なプラグインですね。

サンプルプロジェクト

githubに置きました。一応テストも書いています。
tattyamm/Play2MemcachedSample

参考

mumoshu/play2-memcached
Mac OS X LionでMemcachedをインストール | ましましブログ
playframework 2.0 - play framework 2.0 support memcached? - Stack Overflow
Scalacache ・ playframework-ja/Play20 Wiki
memcache_logo

ScalaとPlayframework2で作成したアプリケーションのJenkinsでの管理

 Scalaで書いたPlayframework2のアプリケーションをJenkinsで管理する為に行った事をまとめました。今回はGithubを使いソースコード管理するケースを想定しています。Github上でソースコードが更新されたかを監視し、自動でテスト(sbt test)を行います。Jenkinsの勉強として行った事の記録です。間違いがあればご指摘お願いします。
 なお、Jenkinsのインストール方法はこちらにまとめました。
さくらVPS(CentOS6.3)へJenkinsをインストールして認証つけてURLを変更する - えんたつの記録
jenkins_logo

プラグインのインストール

 Jenkinsのプラグイン管理画面から、「sbt plugin」と「Git Plugin」と「Github Plugin」にチェックを入れ、「ダウンロードして再起動後にインストール」をクリック。
 「プラグインのインストール/アップグレード」の画面になるので、「インストール完了後、ジョブがなければJenkinsを再起動する」にチェックを入れて放置します。
 再起動が完了したら、プラグイン管理画面から、目的のプラグインがインストール済みの状態になっているかを確認しましょう。

設定

 システムの設計の中に、今回インストールしたプラグインに対応する設定を行います。
 「sbt」の「sbt launch jars」で、「Name」と「Path」を設定します。Nameは適当に、Pathにはsbt-launch.jarの場所を示します。今回は /usr/lib/sbt/0.11.3/sbt-launch.jar にあるので、そこを指定。Nameは0.11.3とでもしておきます。

ジョブの作成

・トップページの「新規ジョブ作成」をクリック。
・ジョブ名を入れ、「フリースタイル・プロジェクトのビルド」にして次へ。
・リポジトリURLを設定します。Gitプラグインを入れたので、「ソースコード管理システム」の中に「Git」という選択肢があります。ここの「Repository URL」にgit cloneなどで使うURLを入力。
・ビルドの設定を行います。「ビルド」から「ビルド手順の追加」の中の「Build using sbt」を選択。このように編集します。
JVM Flags
-XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss16M

sbt Flags
-Dsbt.log.noformat=true   (デフォルトのまま)

Actions
compile test publish-local
・いつビルドするかを設定します。「ビルド・トリガ」の「SCMをポーリング」にチェックを入れ、適切な時間を設定します。「* * * * *」とすると1分毎になり、「15 * * * *」なら15分毎になります。ほんとはWebhookにしたかったのですが上手く行かなかったので、今回は1分前に設定しました。
・テスト結果のレポートを出力するために、「ビルド後の処理」の「JUnitテスト結果の集計」の「テスト結果XML」を「target/test-reports/*.xml」にする。

 これで定期的にJenkinsがGithubを見に行き、更新があったらtestを行い、結果を記録してくれます。

メモ

・Play1系には対応したプラグインが存在しますが、Play2系にはまだ対応したプラグインはまだありません。

これとは別の方法によるもの(github hook)

Github Hooks for Jenkins Continuous Integration
・Jenkins側でシステム設定を開き、「GitHub Web Hook」の「Override Hook URL」にチェックを入れるとWebhookするためのURLがわかる。
・github側ではWebHook URLsに先程のWebhookするためのURLを入れる。
・そうすると、github側でプロジェクトが更新されるとJenkinsが自動的に動作するようになります。
今回はこの方法では通りませんでした。原因調査中。Digest認証だろうか。

参考

Configuring Jenkins plugin for SBT | Cake Solutions Team Blog
JenkinsでPlay Frameworkのプロジェクトを扱う - ひとりWEB開発日記
第二回Jenkins勉強会でLTしてきました - 複雑系スパゲティソース(はてな版)
Testing Rails apps with Jenkins - komagata

Playframework2とScalaでTwitter APIを操作する

Playframework2からOauthログインしてTwitter APIを操作するサンプルアプリです。githubにてソースコードは公開しています。
tattyamm/Play2TwitterSample

手順

1.Oauthログインの為に、Twitterのページへリダイレクトさせる
2.Twitter側で認証してもらったらアプリケーションにリダイレクトされ戻ってくる
3.ここでAccess TokenとAccess Token Secretを取得する
4.これを使ってTwitter APIにアクセスする

準備

playアプリをScalaで作成し、InteliJで読み込めるようにします。
$ play new Play2TwitterSample
$ cd Play2TwitterSample
$ play idea
また、Twitter DevelopersからTwitterアプリを登録し、Cunsumer KeyとConsumer Secretを取得し、必要なら書き込み権限を与えておきましょう。この時、Callback URLはhttp://localhost:9000/authとは設定できないようなので、http://192.168.0.1:9000/authのように自分のローカルなIPを記述しておきます。

Oauthログイン

Oauthログイン方法はこちらに記載されています。また、ここで使われているセッションについてのURLも参考にして下さい。
ScalaOAuth ・ playframework-ja/Play20 Wiki
ScalaSessionFlash ・ playframework-ja/Play20 Wiki
このコードではこのような事が行われています。
0.サイト上でTwitterログインボタンが押されたら、http://localhost:9000/authに飛ばす処理があったと想定。
1.http://localhost:9000/authにアクセスがあると、authenticateメソッドが呼ばれる(routes側の設定)。
2.Twitterからの認証済みのリダイレクトだった場合は、その内容からAccess TokenとAccess Token Secretを取得し、セッションに入れておく。
3.Twitterからの認証済みのリダイレクトでない場合は、Twitterにリダイレクトを行ってユーザーにログインしてもらう。

こちらのコードのほぼコピーですが、これに補足する形でサンプルアプリケーション全体を作成してみました。githubにて公開しています。
tattyamm/Play2TwitterSample

Twitter APIを操作

通常のHTTPリクエストはこのように行いPromiseを取得します。
WS.url("http://mysite.com").get()
今回はOauthで認証された状態でアクセスしたいので、signメソッドにOAuthCalculatorを渡して使用します。consumerKey,consumerSecret,accessToken,accessTokenSecretはあらかじめ取得した状態でこのようにリクエストしPromiseを取得します。
val oauthCalculator = OAuthCalculator(ConsumerKey(consumerKey, consumerSecret), RequestToken(accessToken, accessTokenSecret))
val resultPromise = WS.url(url).sign(oauthCalculator).get()
ここから取得した値を取り出す場合はこのようになります。
val oauthCalculator = OAuthCalculator(ConsumerKey(consumerKey, consumerSecret), RequestToken(accessToken, accessTokenSecret))
val resultPromise = WS.url(url).sign(oauthCalculator).get()
val resultBody = resultPromise.await.get.body
一般的に非同期通信が基本になると思います。そのようにする場合はこうなります。
Async {
  WS.url(url).sign(oauthCalculator).get().map { response =>
    Ok(views.html.index(response.body))
  }
}
こんな感じで値を取り出し、あとはjsonを解釈すれば完成です。全体像はgithubを見て下さい。なお今回はOauthログインする事がテーマだった為、jsonのパースは行っていません。

補足、コメント

・play.api.libs.ws.WSを使います。私がやった時にplay.libs.WSと混同してしまい時間がかかりました。

参考

OAuthプロトコルの中身をざっくり解説してみるよ - ( ꒪⌓꒪) ゆるよろ日記
ScalaWS ・ playframework-ja/Play20 Wiki
ScalaOAuth ・ playframework-ja/Play20 Wiki
ScalaSessionFlash ・ playframework-ja/Play20 Wiki

Playframework2でHTTPリクエストを行う

インターネット上にあるjsonやxmlやhrmlなどのコンテンツを取得する時にどのように書けば良いかのメモです。非同期、同期、Oauthについてまとめます。play.api.libs.ws.WSをimportしてから使って下さい。

非同期

Async {
  WS.url(url).get().map { response =>
    Ok(views.html.index(response.body))
  }
}

同期

val resultBody = WS.url(url).get().await.get.body

Oauth付き非同期

signメソッドの有無だけが違います。
val oauthCalculator = OAuthCalculator(ConsumerKey(consumerKey, consumerSecret), RequestToken(accessToken, accessTokenSecret))
Async {
  WS.url(url).sign(oauthCalculator).get().map { response =>
    Ok(views.html.index(response.body))
  }
}

Oauth付き同期

val oauthCalculator = OAuthCalculator(ConsumerKey(consumerKey, consumerSecret), RequestToken(accessToken, accessTokenSecret))
val resultPromise = WS.url(url).sign(oauthCalculator).get()
val resultBody = resultPromise.await.get.body

参考

ScalaWS · playframework-ja/Play20 Wiki
ScalaAsync · playframework-ja/Play20 Wiki

Playframework2からredisを操作する(Play2へのプラグインインストール方法)

redis_log
 Playframeworkにはredisを扱う方法がプラグインとして提供されています。今回はPlayframework2のCache APIとしてredisを利用できるようにするプラグインを導入する方法を通して、Playframework2へのプラグイン導入方法をまとめます。

 CentOSへのredisインストール方法はこちらにまとめました。なおMacの場合はHomebrewが入っているなら$ brew install redisでインストールできます。
さくらVPS(CentOS6.3)へのredisインストールとchkconfigへの登録 - えんたつの記録
 Play2にはモジュールという形で機能を追加することができ、今回は以下のプラグインを使用します。
play-plugins/redis at master · typesafehub/play-plugins

 また、今回作成したサンプルアプリはgithubにて公開しています。
tattyamm/Play2RedisSample

手順

1.playアプリの作成
2.プラグインの導入
3.sbtでプラグインを読み込ませる
4.redisプラグインの使い方

playアプリの作成

適当な名前でScalaのPlayアプリケーションを作成します。play newコマンドにてScalaの新規プロジェクトを作成しましょう。
$ play new Play2RedisSample
とりあえずInteliJで読み込むために、sbtでideaコマンドを実行します。
$ cd Play2RedisSample
$ play idea


プラグインの導入

InteliJでプロジェクトを開き、以下のファイルを編集します。

・project/Build.scala
appDependenciesに以下を追加します。
"com.typesafe" %% "play-plugins-redis" % "2.0.2"
こうなります。
import sbt._
import Keys._
import PlayProject._

object ApplicationBuild extends Build {

    val appName         = "Play2RedisSample"
    val appVersion      = "1.0-SNAPSHOT"

    val appDependencies = Seq(
      // Add your project dependencies here,
      "com.typesafe" %% "play-plugins-redis" % "2.0.2"
    )

    val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
      // Add your own project settings here      
    )

}

・conf/play.plugins
confディレクトリにplay.pluginsという名前で新しいファイルを作成し、優先度とプラグインクラスを記述します。
550:com.typesafe.plugin.RedisPlugin


そしてプラグインを読み込ませ、InteliJにも読み込ませます。
$ play
$(playのプロンプトで) reload
$(playのプロンプトで) update
$(playのプロンプトで) idea

実行できるか確認しておきましょう。この段階ではエラーが出なければ良いでしょう。
$(playのプロンプトで) idea

(最初試行錯誤していた時はsedisも必要なのかと思ってBuild.scalaのappDependenciesに"org.sedis" %% "sedis" % "1.0.1"を書き、mainにresolvers += "Sedis repository" at "http://guice-maven.googlecode.com/svn/trunk"を追加し、sedisの為にjedisが必要かと思いBuild.scalaのappDependenciesに"Redis.clients" % "jedis" % "2.0.0"を追加した。結局不要だった。)

redisプラグインの使い方

play2のCache APIの仕組みが使えます。redisへのsetとgetを試してみましょう。app/controllers/Application.scalaを編集します。(よく理解していないのですが、play.api.Play.currentが必要です。)
package controllers

import play.api.mvc.{Controller, Action}
import play.api.Play.current
import play.api.cache.Cache

object Application extends Controller {

  def index = Action {

    // set
    try {
      val valueSet = Cache.set("item.key", "item.value")
    } catch {
      case _ => println("Cache.setでエラー")
    }

    // get 方法1
    //   方法1と2のどちらのように値を決めても良い。
    //   ただしkeyが無い場合playには警告が出る。([warn] application - could not deserialize key:item.key ex:java.lang.NullPointerException)
    val valueFromCache1: Option[String] = Cache.getAs[String]("item.key")
    println(valueFromCache1.getOrElse("item.keyがなかった"))

    // get 方法2
    val valueFromCache2: String = Cache.getOrElse[String]("item.key") {
      "item.keyがなかった"
    }
    println(valueFromCache2)


    Ok(views.html.index("Your new application is ready."))
  }

}
コンソールに適当な結果が表示されれば問題ありません。redis-cliコマンドからから「keys *」でredisに値が入っているか確認しておきましょう。これでPlayframework2にredisプラグインを導入することで、Cache API経由でredisを操作することができました。

補足

・confに設定として記述できるのは以下の値です。
redis.host デフォルト:localhost
redis.port デフォルト:6379
redis.timeout デフォルト:2000
redis.password デフォルト:null
・redisの他の機能を使う必要があればJedisやSedisを直接使う必要がありそうです。
・なおPlayframeworkのCache APIはデフォルトでEHCacheが使用されます。
・今回作成したサンプルはgithubで公開しています。
tattyamm/Play2RedisSample

コメント

簡単な内容となりましたが、右も左もわからぬうちはこういう情報が欲しく、適切な調べ方もわからず苦労しました。

参考

・プラグイン導入に関するドキュメント
始める sbt - プラグインの使用
・このプラグインのgithubリポジトリと基本的なドキュメント
play-plugins/redis at master · typesafehub/play-plugins · GitHub
・プラグインの導入方法
Play framework 2.0 での自作プラグイン - Scala版 - なんとなくな Developer のメモ
・playのキャッシュについて
Scalacache · playframework-ja/Play20 Wiki
・モジュールの使い方
言葉をポッケに持ち歩こう: PlayframeworkのPDFモジュール
・サンプル
play-plugins/redis/sample/project/Build.scala at master · typesafehub/play-plugins · GitHub
・sedisも入れる必要があるみたいな事が書いてあるらしいが、今回サンプルをつくった時には要求されなかった
[2.0] Redis Plugin - Google グループ
・memcachedの場合
Play! framework 概要 Tipsもあるよ! - ikeike443のブログ
・MySQLの場合
lambda me : Play2からMySQLにつなぎたい - Playframework2
このサイトについて
Webアプリケーション開発のことや、iPhone・Android向けアプリ開発の話題が中心です。
管理:えんたつ twitter: @tattyamm
mimage
一部のリンクにはアフィリエイトが含まれます。
カテゴリ別アーカイブ
RSS
プログラミング本
古い本含めてメモです
iPhoneプログラミングUIKit詳解リファレンス iPhoneプログラミングUIKit詳解リファレンス Android Layout Cookbook アプリの価値を高める開発テクニック パーフェクトPHP (PERFECT SERIES 3) JavaプログラミングBlack Book 2nd Edition (Black Bookシリーズ)
表記
当サイトではGoogle Analyticsを使用しております。詳細はこちらを御覧ください