Play2.2(Java)からPlay2.3(Java)にマイグレーションする際、SecureSocialで苦労したとこ

社内コミュニティで作っている とあるアプリ をPlay2.2からPlay2.3にマイグレーションしました。 Playのマイグレーションと言うよりは、SecureSocialのバージョンアップで苦労したので、その記録を残しておきます。

ご利用上の注意事項

網羅した情報ではありません。私が使っているせまーい範囲での情報です。

  • 利用している認証方式はUserPasswordのみです。OAuthの情報はありません。
    • この時点でいらない子のような気もしますが、まぁ整理するための練習と思って書きます。
  • Viewに関する情報はありません。
    • SecureSocialはフロントから呼び出す認証APIを実装するために利用しています。

お品書き

  • どのバージョンを使うか?
  • 参考になるコード
  • 変わってたところ
  • Scalaで書くしかなさそうなとこ
  • 実装で悩んだところ
    • build.sbt
    • グローバル設定
    • UserService
    • コントローラー
    • Future
    • テスト (Cookieどうするの?)

どのバージョンを使うか?

sonatype-snapshots/ws/securesocial/securesocial_2.11/master-SNAPSHOT を使えばよいようです。

参考になるコード

SecureSocialのリポジトリにあるサンプルアプリが参考になります。

securesocial/samples/java/demo at master · jaliss/securesocial

変わってたとこ

  • プラグインの管理方法
  • Identityクラス
  • UserServiceのインターフェース
  • ctx().args.get(SecureSocial.USER_KEY)が返すオブジェクト

プラグインの管理方法

従来のバージョンではplay.pluginsで管理していました。 master-SNAPSHOTでは、securesocial/RuntimeEnvironment クラスで利用するプラグインを管理します。

RuntimeEnvironment.Defaultクラスを継承すると、楽に作れます。

注意点

グローバル設定を行うクラスで、getControllerInstanceをオーバーライドする必要があります。

参考になるコード

securesocial/MyEnvironment.scala です。 ※ このクラスはScalaで書くしかないようです。

Identityクラス

Identityクラスがなくなりました。代わりにBasicProfileクラスを使います。

UserServiceのインターフェース

  • 戻り値にF.Promiseを使うようになった
  • doSaveに引数SaveModeが追加された
  • doSave(Token) -> doSaveToken(Token)に変更された
  • 新しいメソッドが追加された
    • doLink
    • doPasswordInfoFor
    • doUpdatePasswordInfo

実装はサンプルアプリを参考にするとよいです。

ctx().args.get(SecureSocial.USER_KEY)が返すオブジェクト

従来は、Identityクラスのオブジェクトでした。
master-SNAPSHOTでは、開発者が指定したクラスのオブジェクトになりました。地味に便利。

Scalaで書くしかなさそうなとこ

Javaで書く方法が分からなかったとこです。
難しいことをしなかったので、サンプルアプリのコピペでほぼ対応できます。

  • ViewTemplatesを継承したクラス
  • RuntimeEnvironmentを継承したクラス

実装で悩んだところ

build.sbt

"ws.securesocial" %% "securesocial" % "master-SNAPSHOT"

でいいんだ。と分かるまでに一苦労しました。 ググると、以下のようなコードを見つけることができますが、2015年4月時点ではここまでしなくてもよいようです。

lazy val root = (project in file(".")).enablePlugins(PlayScala)
    .dependsOn(ProjectRef(uri("https://github.com/ewiner/securesocial.git#play-2.3"), "mainModule"))

もう1点、SNAPSHOTを参照する場合は、

resolvers += Resolver.sonatypeRepo("snapshots")

を付けるのを忘れないようにしましょう。

グローバル設定

サンプルコードにあるgetControllerInstanceがいるのかどうかに悩みました。

結論、必要です。

private RuntimeEnvironment env = new MyEnvironment();

@Override
public <A> A getControllerInstance(Class<A> controllerClass) throws Exception {
    A result;

    try {
        result = controllerClass.getDeclaredConstructor(RuntimeEnvironment.class).newInstance(env);
    } catch (NoSuchMethodException e) {
        // the controller does not receive a RuntimeEnvironment, delegate creation to base class.
        result = super.getControllerInstance(controllerClass);
    }
    return result;
}

UserService

何を実装すればいいのかよく分からなかったものがあります。

  • SaveModeのLoggedInでやること
  • doLink, doPasswordInfoFor, doUpdatePasswordInfo
    • doLinkはサンプルアプリに実装例がある
    • doPasswordInfoFor, doUpdatePasswordInfoは

が、空実装でもマイグレーション前と同等の機能は提供できているので、気にしないことにしました(オイオイ)。

コントローラー

サンプルコード securesocial/Application.java を見ると、

のような実装になっています。
いずれもプラグインを管理するRuntimeEnvironmentを参照するためなのかな? と思います。

ただ、コントローラーでSecuredActionまたは、UserAwareActionアノテーションを使っていれば、securesocial.core.java.SecureSocialのevnメソッドを使ってRuntimeEnvironmentが参照できるようなので、上記の対応はしませんでした。

以下、調べてみたことです。

  • securesocial.core.java.SecureSocialクラスのenvメソッドでは Http.Context.current().argsのキー"securesocial-env"に入っているオブジェクトを返す
  • Http.Context.current().argsにオブジェクトをセットするのは SecuredクラスのinitEnvメソッド
  • SecuredクラスのinitEnvは以下のアノテーションで使われている
    • SecuredAction
    • UserAwareAction (で使われているUserAwareクラス)
  • SecuredActionまたは、UserAwareActionアノテーションを使っていれば、SecuredクラスのinitEnvが呼び出されるので、securesocial.core.java.SecureSocialクラスのenvメソッドを使ってRuntimeEnvironmentを呼び出すことができる

Future

SecureSocialだからと言うわけではないのですが、絡みで苦労したので。
Futureでラッピングされたオブジェクトをどう参照すればいいか? で悩みました。
例えばこんなの。

Future<Option<Authenticator<LocalUser>>> futureAuthenticator ...

futureAuthenticator.value().get().get() で Option<Authenticator> を取り出せるはずなのですが、単に呼び出すだけだとNoneが返ってきます。

concurrency - Scala - futures does not run - Stack Overflow を見ると、待ちが必要らしいです。

で、こんな感じでまってみました。が、これでいいのか良く分かっておりません…orz

Await.result(futureUser, Duration.create(10, TimeUnit.MILLISECONDS));

テスト (Cookieどうするの?)

コントローラーのテストをするのに、リクエストにCookieを付けたい場面があります。 このCookieをどう作るの? と言う話です。

従来は java - Unit-testing methods secured with Securesocial annotation - Stack Overflow を参考に実装していました。

master-SNAPSHOTでは、Cookieの作成に必要なプラグインをRuntimeEnvironmentから参照する必要があります。 なので、実装方法を変更しなければいけません。

残念ながらその方法が良く分からなかったので、playframework 2.3 - Unit testing securesocial authentication for play2.3.x controllers in scala - Stack Overflow を参考に、実際にAPIを呼び出し、受け取ったCookieを使うことにしました。強引ではありますが…。

Scalaな方は、この辺 securesocial/WithLoggedUser.scala を利用にすると幸せになれそうです。
最新のバージョンには対応していない(Identityクラスを使っている)ようなので、修正が必要と思います。

社内のコミュニティのメンバーと、開発合宿に行ってきました @山喜旅館

f:id:couger:20141129121302j:plain

動機

これまで3回ほど合宿に行ってたんですが、2011年を最後に3年ほどやってませんでした。 このまま合宿しなくなるのも寂しいし、新しい人も増えてきたし、ここを逃す手はないんじゃないか? ということで、一念発起。 合宿に行ってきたというわけです。

日時

2014年11月28日(金)、29日(土)の1泊2日。

メンバー

社内のコミュニティの人。私を含め8人。 コミュニティの名前は「NESアジャイルコミュニティ」。アジャイルについて興味のある人が集まって、ごそごそしてます。 2007年にスタートしたので、今年で7年目。 ぐだぐだ のんびり続けてます。

8人のうち、5人は付き合いの長い人。3人が今年入ってきてくれた人 :D うち2名は、今年入社で、先週コミュニティに参加して、その場で合宿参加を決めたというなかなか行動力のある人たち。

場所

伊東にある 山喜旅館 です。

候補は、メンバーが紹介してくれたFindJobさんの エンジニアが選ぶ。開発合宿で泊まりたい日本の宿7選【2013年版】 を参考にしました。 「開発合宿」と銘打っているだけあって、Wifiや会議室、プロジェクタなどが標準装備のようです。ありがたい。 私たちは、

  • 参加者は東京と、静岡に分かれていたので、交通費が最小になるところ
  • 宿泊費がお財布に優しい ということで、山喜旅館にしました。

プランは「研修・ゼミプラン」です。1泊2食で6480円(税込)。 安い代わりに 「浴衣・バスタオル・タオル・歯ブラシなどのアメニティは自分で持っていく」 のような制限があります。 サイト で注意事項を確認しましょう。

プランに加えて、2日目用の会議室(9:00~17:00)と、プロジェクタ1台を借りました。 プロジェクタはVGA接続です。Macな方とかは変換コネクタお忘れなく。

ちなみに、これまでの合宿は、完全自腹のため、公共の設備を使っていました。 が、今回は 会社から支援を受けることができ 、個人の負担を減らすことができたので、 せっかくだから、ネットでよく見かける開発合宿で行ってるところにしてみました。 この仕組みを作ってくれた人事総務部の方に感謝‼︎

やったこと

テーマは「最近の開発を体験しよう」です。 対象をWebアプリにして、社外の開発サービスも使ってみようということでGitHub,Slack, TravisCIを使ってみました。

(TODO 全体の図)

準備

コミュニティの人たちとの情報交換用にRedmineが立ち上がっているので、そこを使って連絡を。

開発環境のセットアップ方法、旅しおり作り、備品担当などを決めて行きました。

移動

東京からは新幹線こだまを使って熱海まで、そのあと伊東線を使います。

伊 東駅についたら、徒歩10分ほどで宿に着きます。みんな大好きGoogle先生道案内 してもらえば、迷うことはほぼないでしょう。 しかし、なぜ、Google先生はくねくね曲がるルートが好きなのか。

食料などの調達は駅前のコンビニ(セブンイレブン)が楽そうです。 コンビニに寄るのを忘れる私のような人は、 途中のお店 角田商店 で、お酒、つまみを調達する手もあります。

宿

貫録ある入口。ほんとにここでいいんだっけ? と一瞬迷うくらい。

f:id:couger:20141128173846j:plain

部屋は2Fの「大島、初島」と、「大?」の3部屋。 大島は、8人で集まってだべっても大丈夫な大きさです。

(TODO: 写真なかったっけ?)

夕飯

2Fにある宴会場で夕飯です。てんぷら、刺身、お肉と、ボリュームもしっかりしてます。

f:id:couger:20141128200332j:plain

合宿1日目

これから始まる開発に向けて、懇親会でメンバーの絆を深めます。 合間をぬって、開発環境の準備を。

山喜旅館はWiFi完備。部屋でも使えるはずなんですが、電波が弱いのか、たびたび切れます。 ので、自前のWiFiルータで乗り切りました。WiMaxはあんまりスピード出ませんでした。 他の人のレポートで、事前にお願いしておくとつながりやすい部屋を紹介してもらえるという話がありました。 単なる連絡不足かも…。みんな、ごめん…orz

f:id:couger:20141128214718j:plain

しかし、この後3時まで話し込むとは…。明日は開発なのに!!

合宿2日目

朝食を食べた後、チェックアウト。会議室に移動して、開発スタートです。 旅館の中に会議室がある(2Fの宴会室のそば)ので、移動が楽で助かります。

会議室には

  • ホワイドボード2つ (壁に備え付けが1つと移動式が1つ)
  • 長机8つくらい
  • いすたくさん
  • 有線LAN

があります。(私たちが借りたのは第二会議室)

当日のタイムスケジュールはこんな感じです。 12:00で離脱する人がいるので、なるべく開発時間がとれるよう、ライトニングトークを午前、午後に分割しました。

時間 内容
09:00~9:30 ライトニングトーク(1)
09:30~12:30 開発
12:30~13:30 お昼
13:30~14:00 ライトニングトーク(2)
14:00~16:00 開発
16:00~17:00 片づけ

会議室の様子です。

f:id:couger:20141129102320j:plain

開発は、インフラチームと、アプリチームに分かれて行いました。 アプリチームは、午前中、仕組みの説明を、午後はバグの修正と、いくつかの改修を、 インフラチームは、githubリポジトリの引越しから始まり、slack, travisciのアカウント取得、連携を。

人数と興味の範囲が増えると、できることが増えていいですね‼︎ :D

最後に

開発合宿で気づいたことを適当に。

良かったこと、続けたいこと

  • 色々な人に会えた。色々な考えに触れられて刺激を受けました。
  • みなさん、いい人でよかった!

宿

  • 1日目、部屋が和室なのでみんなが部屋でワイワイやりやすかった
  • 値段の割にはおいしい料理
  • 2日目は会議室のWiFiの早くてよかった
  • 長い間、温泉に浸かることができた
    • ぬるい温泉のおかげ
  • 温泉に入れたこと
    • 温泉好きなんですw

内容

  • 宴会が盛り上がって、楽しかった
  • LTが面白かった
  • なんかいろいろ触れた(興味領域を探せた)
    • Bootstrap, AngularJS, Play Framework
    • GitHub + Travic CI + Slackの連携
    • アジャイル開発っぽいの(ターン制度?)
  • 1つのものをみんなで作るという体験ができた
  • 興味に応じて作業ができた(「インフラチーム」と「アプリチーム」に分かれて作業できた)
    • Javaコードなんて書きたくないんですが・・・」という欲求に対応できた
  • お弁当アプリのリポジトリGitHubの組織リポジトリに移行できた
  • issueをかなり見つける事ができた
  • 合宿前にSlackとGithubのアカウントを取得していたので、スムーズに進めることができた
  • データの受け渡しに以外とUSBメモリが役立った

その他

  • 楽しく活動できた
  • 欠席者が出なかった
  • いきなりでもなんとか参加できた事(ありがとうございます)
  • あんまり寒くなかった
  • 終電で参加出来た!!
  • Facebookメッセンジャーは良かった
  • エクスプレス予約で、Webから領収証が発行できることを知った

困ったこと

宿

  • 温泉がぬるかった
  • 1日目の夜は旅館のWiFiがつながりにくく、持参したモバイルルータのスピードもあまり出なかった
  • プロジェクタの入力がVGAしかなかったのでDVIのみのPCは繋がらなかった
  • (若干)宿が古くて、歩いた時に音がしたり、ちっちゃい虫がいたり

内容

  • 到着したのが9:00pmで、本格的に始まったのが11:00pmだった
    • 金曜日開始が良くない??
  • 作業時間がちょっと少ないように感じた(午前中までしかいられなかったからかも)
  • LT資料は用意しとくべきだった
  • 慣れないMacはダメだった
  • 以前作成したElasticsearchコンテナを動作させようとしたところが正常に起動しなかった
  • 色々触っただけだった(最初だし仕方無い)
  • Webアプリの構造について、よく分からない部分があった
    • もうちょっと、予習しておけばよかった
  • 環境のセットアップに手間取った
    • 前日にやっておくべきでした…
  • コーディングの経験不足が明らかになった
    • ペアの方のお蔭で何とかやっていけた感じです。ありがとうございました<( )>
  • インフラチームの作業が、(ちょっと)行き当たりばったりだった [2票]

その他

  • 3時に寝た次の日は眠たかった
  • ちょっと食べ過ぎてしまい、日曜日に胃が重かった。
  • ビールを飲みすぎた
  • 誰かが会議室の鍵を隠した
  • 2日目、天気が悪かった
  • 最後まで居たかった(2日目午前中までしかいられなかった)
  • 清算間違えたかも、もうちょっと注意を払ってお金のやり取りをすれば良かった

改善したいこと

宿

  • プロジェクタの入力仕様について確認しておく

内容

  • 合宿でやることを事前に整理しておく
  • 作業内容を前日までにもう少し詰める
    • 少なくとも項目だけでも挙げておく(やり過ぎないのは大事)
  • Slackをもっと活用する
  • Gitの使い方を事前に調べておく or 使い方を共有する + 何やかんやでgitで躓いてた気がしたので...(私だけ?)

その他

  • 旅館からあずかったものはちゃんと管理しましょう
  • 無線LANルータがあればメンバ間のデータ送信が楽になる?
  • 予め、messangerのコミュニティは作っておくべき?
  • 3連休の前半に設定する?
    • 「次の日仕事」はちょっとキツイので

f:id:couger:20141129170214j:plain

ろくろ回す練習しよっと…。

Play Framework 2.2.xのbuild.sbtにcucumberを実行するタスクを追加

背景

  • Play Frameworkでjavaを使って開発している。
  • UIのテストにcucumber-jvmを使っている。
  • it:testでcucumberを実行している
  • 開発中は特定のタグ @current のついたシナリオだけ実行したい
  • play it:current とかやりたいけど、方法が分からん
  • skipoleschris/xsbt-cucumber-plugin · GitHub を入れてみたが、Scalaが前提のようなので、使えない感じ

仕方がないので、タスクを作ってみた

実行方法

play cucumber

でcucumberが実行できる

play "cucumber [タグの名前]"
(ex play "cucumber @current")

タグの名前を指定して実行することもできる。

build.sbtに入れ込むコード

lazy val cucumberTask = InputKey[Unit]("cucumber", "Run Cucumber tests.")

cucumberTask := {
  val logger = streams.value.log
  logger.info("Running Cucumber tests.")
  val args: Seq[String] = Def.spaceDelimited("<arg>").parsed
  val cucumberOpts = args.isEmpty match {
    case false => "-Dcucumber.options=" + args.map("--tags " + _).mkString(" ")
    case true => ""
  }
  logger.info("Cucumber.Options: "  + cucumberOpts)
  val cucumberRunner = "features.RunCucumber"
  val classPassArgs = (fullClasspath in Test).value.map(_.data).mkString(";")
  Fork.java(ForkOptions(runJVMOptions=Seq(cucumberOpts)), Seq("-cp", classPassArgs, "org.junit.runner.JUnitCore", cucumberRunner))
}
  • クラスパスはfullClasspath in testを使っている
  • CucumberのRunnerはコードに埋め込み
  • tags以外のオプションは対応していない

自分で使う分にはここから始めればいいから十分だ。

  • 引数とりたい場合はTaskKeyじゃなくて、InputKeyに突っ込む
  • Def.spaceDelimited("").parsedで引数が取れる
  • Fork.javaでjvmを起動できる

がポイント?

PlayFrameworkをHerokuにデプロイするまで

  • Herokuでサインアップ
  • HerokuのClientをインストール
  • 以下のコマンドを実行して、デプロイ先を作成
heroku create [HerokuアプリのID]
  • デプロイに使うgitリポジトリのURLが出力されるので、それを使って以下のコマンドを実行
git remote add heroku [URL]
  • Procfileを作成。Playアプリ名には、build.sbtのnameで指定している名前を入れる。

今回はdevモードで動かしかったので%prod.application.modeを指定。

web: target/universal/stage/bin/[Playアプリ名] -Dhttp.port=${PORT} -DapplyEvolutions.default=true -Ddb.default.driver=org.postgresql.Driver -Ddb.default.url=${DATABASE_URL} -D%prod.application.mode=dev
  • system.propertiesを作成 (SecureSocialを使ってるのでJava7が必要)
java.runtime.version=1.7
  • Pushする (developブランチをPushする場合)
git push heroku develop:master

Pushするときに以下のメッセージが出る場合は、msysgitのインストールディレクトリにある.sshフォルダにキーを置くこと。
Heroku – Trouble with Windows and SSH Keys | Ganeshji Marwaha による。

Permission denied (publickey).

SublimeTextを入れてみる

Play Frameworkについてのメモ

社内のコミュニティでPlay Frameworkを使ってちょっとしたアプリを作ることになったので、メモ。

  • いつものようにWindows
  • なるべくJavaを使う (手伝ってくれる人が多くなるかもしれない)
  • EclipsePleiades All in One 4.3.1.v20130926を利用する

Play Frameworkのインストール

Download Play Framework でZip形式のファイルをダウンロード。
適当なフォルダに解凍して終了。
パスを通しておくとよい。

Eclipseを使うための準備

Webアプリを作った後、以下のコマンドで.projectファイルを作ってくれる。

> play eclipse

Eclipseで[ファイル -> インポート -> 一般 -> 既存プロジェクトをワークスペースへ]を選択、インポートすればOK。
プラグインとして"ScalaIDE for Eclipse"をインストールしておく。

Eclipseを使ってデバッグ

Play Framework 2.0 で Eclipse を使ってデバッグする | ときどきウェブサイト構築記 を参考に。

> play debug run
  • 最後に以下のようなメッセージが出力されるので、9999にあたる部分の数字を覚えておく
Listening for transport dt_socket at address: 9999
  • Eclipseで[実行 -> Debugの構成 -> リモートJavaアプリケーション]に新しい設定を追加する
    • プロジェクト : 現在開いているプロジェクト
    • 接続プロパティ ホスト : localhost
    • 接続プロパティ ポート : 覚えておいた数字

テスト

ここくらいはScalaで書いても良いんじゃないかなぁ…。無理かなぁ。
モデルのテストをJavaで書いて、コントローラーのテストをScalaで書くか。

(ちょっと考える)

PlayFramework - Play Framework (Java) の fakeApplication を使ったテストをスッキリ書く - Qiita
Play Javaでspecs2を使う

データベースにデータをロードするのにEbeanを使うのか。
Yaml何となく苦手。DBUnit+Excelさんに登場してもらおうかなぁ…。

おまけ

社内のリポジトリSubversion。社外からアクセスするのは無理。
SubversionGitHubを連携させればなんとかなるのかな?

git-svnを利用した運用を考える - Qiita

Windows 8 に Rails 3.2.13 + Ruby 1.9.3 をインストールする

以前にWindowsでやろうとして死亡したんだけど、
またちょっと手を出すことになりそうなので、今どうなってるか確認。

インストール

Ruby をインストールする

http://rubyinstaller.org/downloads/ から Ruby 1.9.3-p429 をダウンロード、インストールする。

ruby --version

でバージョンの確認をする。

DevKit をインストールする

http://rubyinstaller.org/downloads/ から DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe をダウンロード、インストールする。

DevKitは解凍後、

cd [解凍したフォルダ]
ruby dk.rb init
ruby dk.rb install

でインストールができる。

Rails をインストールする

以下のコマンドを実行する。

gem install rails --version "3.2.13"

最後に file 'lib' not found と言う若干不安なメッセージがでているけども進む。

rails --version

でバージョンを確認。

Git for windows (msysgit) をインストールする

http://code.google.com/p/msysgit/downloads/list?q=full+installer+official+git から Git-1.8.3-preview20130601.exe をダウンロード、インストールする。
※ therubyracer のために必要。

アプリケーション作成

ガワを作る
rails new fuga --skip-test-unit --skip-bundle

fuga フォルダにアプリができる。

therubyracer のおぜん立てをする

Windows環境でtwitter-bootstrap-railsを利用する - Qiita を見ながらインストールする。
3 の Gemfile を変更するところまでやればOK。

変更点は以下。

  • v8.dll, v8preparser.dll を [Rubyをインストールしたディレクトリ]\bin に入れる
とりあえず動かしてみる
bundle install --path vendor/bundle

会社ではこれの前にひと工夫いるわけだが、家からやるのであれば大丈夫。

rails server -p 3003

3003 ポートでスタート。ファイアウォールの警告がでるので接続を許可する。
http://localhost:3003/ でアクセスして、Welcome aboard が出ればOK。

シャットダウンする。CTRL+C でOK。

アプリを作る

簡単!Rails3.2を使って10分でブログアプリを構築する方法 - DQNEO起業日記 をみてもくもくと作る。
"Hello Rails"を表示するためのコントローラを作成 以降を流せばよし。

おー、できる!! 素晴らしい!!!

テストを作ってみる

あとで書き足す。前回はここでもはまったんだよなぁ。
あとこれが WindowsXP/7 でも動くかどうか…。