2013年10月15日火曜日

play-pac4j : Play! Framework 2.2 で OAuth(失敗 -> 2.1 では成功)

ちょっと前のエントリで Play! Framwork で twitter4j を使うのに SecureSocial というのを使ってみましたが、別のものということで今度は pac4j というのを使ってみます。定義によると
pac4j is a Profile & Authentication Client for Java (it's a global rebuilding of the scribe-up library)
だそうです。それをさらに Play! Framework から利用しやすいように play-pac4j というものがあるようですね。早速使ってみます。

まずは dependency の解決です。上のガイドに従うと、Twitter を使うためには、play-pac4j_java と pac4j-oauth が必要なようですね。Maven の central repo では最新はそれぞれ 1.1.1 と 1.4.1 です。以下のように build.sbt に追加します。(このファイル、2.x では project/Build.scala だったはずなんですが、変わったんですね・・・)

libraryDependencies ++= Seq(
  javaJdbc,
  javaEbean,
  cache,
  "org.twitter4j" % "twitter4j-core" % "3.0.4",
  "org.pac4j" % "play-pac4j_java" % "1.1.1",
  "org.pac4j" % "pac4j-oauth" % "1.4.1"
)  

前回やったように、IntelliJ IDEA を一度止めて play idea してから開きなおすのが手っ取り早いようです。ではコードの変更です。
  1. コントローラーが継承しているクラスを Controller から JavaController に
  2. Global.onStart() で TwitterClient を登録
  3. ログインを要求したいコントローラーメソッドに @RequiresAuthentication を追加
  4. routes に callback を追加
具体的には
1
import org.pac4j.play.java.JavaController;

public class Application extends JavaController {
2
public class Global extends GlobalSettings {
    @Override
    public void onStart(Application application) {
        final TwitterClient twitter = new TwitterClient(CONSUMER_KEY, CONSUMER_SECRET);
        final Clients clients = new Clients("http://localhost:9000/callback", twitter);
        Config.setClients(clients);
3
    @RequiresAuthentication(clientName="TwitterClient")
    public static Result top() {
4
# For pac4j
GET   /callback               org.pac4j.play.CallbackController.callback()
POST  /callback               org.pac4j.play.CallbackController.callback()
GET   /logout                 org.pac4j.play.CallbackController.logoutAndRedirect()
おお、結構作業量が少なくていい感じ。早速実行してみましょう。
[info] play - Application started (Dev)
Uncaught error from thread [play-akka.actor.default-dispatcher-7] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[play]
java.lang.AbstractMethodError
あっさりオワタw
まだ 2.2 がサポートされてないんですね。この方も嘆いてますが、Play! Framework ってバージョン間の互換性がなさすぎる・・・。

仕方ないので Play! Framework を 2.1.5 にしてみます。2.1.5 で play new して作った雛形から、Build.scala をコピーして、build.sbt 内に加えた変更を移動します。あとは念のため diff で差分が出た build.properties とか plugins.sbt とかを直していざテスト。

おっ!
うまくいきました。これは結構簡単。

最後にお約束の twitter4j のインスタンス作り。
    @RequiresAuthentication(clientName="TwitterClient")
    public static Result login() throws TwitterException {
        final TwitterProfile profile = (TwitterProfile) getUserProfile();
        TwitterFactory factory = new TwitterFactory(new ConfigurationBuilder().setOAuthConsumerKey(CONSUMER_KEY).setOAuthConsumerSecret(CONSUMER_SECRET).build());
        Twitter tw = factory.getInstance(new AccessToken(profile.getAccessToken(), profile.getAccessSecret()));
これでうまくいきました。

2013年9月30日月曜日

Heroku を試す(7)~ Play! Framework 2.2 を IntelliJ IDEA で動かしてみる

実は前の記事はだいぶ前に書いていて公開し忘れていたものでした。このたびもう一度使ってみようと思ったので、新しいエントリを書く前にとりあえず公開したものです。で、もう一度やろうとしたらいろいろバージョンも変わっていたので変化部分を。

まず前回はあきらめた git のバージョンですが、ちゃんとバージョンをあげておきました。

$ git --version
git version 1.7.11.3

これなら文句は言われまいw IntelliJ IDEA を起動しましたが、確かに何も警告は出ませんでした。勝った!(←何に?w)これで IDEA のほうも最新バージョンで進めます。

Play! Framework のほうですが、最新は 2.2.0 になっていました。なにやら嫌な予感が・・・。Play! Framework 自体は好きなんですが、バージョンアップ時の挙動が結構変わるのが難儀するんですよね。まあいいか、早速プロジェクトを作ってみます。


orz
こんな予想的中してもうれしくないw まだIDEAのプラグインが対応してないのかな。ここは気を取り直して、Play側から作って IDEA 対応を試みます。

$ play new sample-app
$ cd sample-app
$ play
[sample-app] $ idea

これを IDEA から開くと、無事開けました。

前回なぜはまらなかったのか不明ですが、デフォルトでは起動すると 9000 番ポートにつなぎに行きます。しかし他のアプリケーションで使っていると当然失敗します。直接コマンドラインから指定するときは

$ play run --http.port=9001

のようにすればいいのですが、IDEA からの起動だと play に直接コマンド渡せるわけではないので。結論としては Run/Debug Configurations のところで、Play 2 の起動構成を出し、 JVM Options に -Dhttp.port=9001 のように足せばポートが指定できます。

これで起動、デバッグ、あとは heroku へのデプロイ等は問題なくできました。しかし初回の push は長い・・・。いろんなライブラリの解決してるからっぽいですけど。あとはデータベースへの接続やバージョン管理(GAEで version.application.appspot.com で試せるやつのような)のやり方を学ばねば。

2013年9月29日日曜日

Heroku を試す(6)~ Play! Framework 2.0 を動かしてみる

さてなんとなく感じがつかめてきたところで今度は Play! Framework を使ってみようと思います。せっかくなのでちゃんと対応している(はずの)IntelliJ IDEA を使ってみましょう。Eclipse でやると結局のところ Java 以外のところはテキストエディタとして使うことが多くていまいち便利になった感がないので。

早速起動してプロジェクトを作成・・・ってあれ?前はここに Play! って出てた気がしたんですが・・・。恐らく完全な気のせいで、プラグインを入れなければいけないようです。それじゃ Eclipse と同じとかは思わないことにしますさくっと入れますw すると、おお、確かにでてきましたね!

実は最初何度やってもバージョンを変えてもうまくいかなくて相当悩んだんですが、ここに問題があがっていてこれかと思って普通にコマンドラインから play new してもエラーになったんです。なんだろうと思ったら Play! Framework 自体を /opt (つまり書き込み不可な場所)に入れていたのが原因だそうで。どうせ読み書き可能にしなきゃいけないのならユーザーのディレクトリに入れて指定したところうまくいきました。この思想がいいかどうかは別として。

さてこれを Heroku に上げてみましょう。Heroku のほうと Play! Framework のほうと両方に異なる文書がありますが、スクロールバーが短い(w) Play! のほうにしたがってみます。

まずは Store your application in git の部分。git コマンドをたたけばいいわけですが、IDEA から行きましょう。VCS > Import into Version Control > Create Git Repository で・・・

orz

厳しいっすw

git は rpm 経由で入れていて上げるのはまあまあ面倒なので、12.0.1 を再度引っ張り出してきて構成しました。するとコマンド自体は動いていたのか、既に git の管理下になっているようです。

では heroku の設定を。
$ heroku create sdk0815-sample2
Creating sdk0815-sample2... done, region is us
http://sdk0815-sample2.herokuapp.com/ | git@heroku.com:sdk0815-sample2.git
Git remote heroku added
これでリモートがセットされたはず。 しかし
$ git push heroku master
Warning: Permanently added the RSA host key for IP address '50.19.85.154' to the list of known hosts.
error: src refspec master does not match any.
error: failed to push some refs to 'git@heroku.com:sdk0815-sample2.git'
うーむ。これは何のエラーだろう。いろいろ調べたらどうやら何も push するものがないときに出るエラーみたいですね。 git commit したら確かにコミットされたので commit まではされてなかったんですね。
#IDEA からやるべきだったんですがついコマンド打っちゃいました。プロジェクトを右クリックで Git > Commit Directory... で全部 commit できます。

さて今度は push したらずっと待たされました。いけー。どりゃー。おおおお!

やったー!・・・ってあれ、こんな殺風景なはずは・・・w

どうもフレームワーク動いてないですね、これ。前に作ったときにあった Procfile にあたるものが必要なのかも。と思ったらちゃんと書いてありました。 これを作って再度挑戦です。また待たされました。いつ終わるのかな~。終わった!

変化なしw

よく考えてみると、この "Your new application is ready." という文字列って、ソースコードの中では
@(message: String)

@main("Welcome to Play 2.1") {
    
    @play20.welcome(message, style = "Java")
    
}

となっていて、文字列はコントローラである Application.java から送られてきています。ということはフレームワークは動いているということかな? @play20 というカスタムタグ(?)が動いていないようだと。

まあちょっと玉虫色な感じもしますが、とりあえず動かせるところまではもってこれました。実際に何か作るかは意欲と時間とネタ次第です。

2013年8月2日金曜日

IntelliJ IDEA で maven の dependency を解決する

ほんのちょっとした Tips 的ですが、しばらく悩んだので。

IntelliJ IDEA で Maven Module を指定して Project を作成します。ここまではよし。しかし実際に作成された pom.xml に dependency を追加する方法がわかりませんでした。すると、いくつか方法はあるようですが、そのうちの1つはエディターから Alt+Enter 等で出てくるメニューから "Add Maven Dependency..." を選べばよいと。ふむふむ。出てくるダイアログで文字列検索すれば・・・
 でてこない。 orz
全く出てこないわけではなく、何も入れてないときは ant とか commons なんとかとかそのへんは出てるんですよね。うーむ。どうもローカルにあるリポジトリだけを見てるっぽいですね。

そんなときは File > Settings から Maven > Repositories を選ぶと以下のような画面になるので、central repo を選んで Update を押せばよいようです。
その後同じ操作をすると、
めでたしめでたし。

2013年6月26日水曜日

Play! Framework で twitter4j を使う

久しぶりにちょっと時間ができたので遊んでみました。

表題のとおりです。とりあえず OAuth でログインできるところまで行きたいですね。

まずは twitter4j を追加します。これまでは jar をコピーしていたのですが、そんなことするよりずっと簡単な方法があります。Managed Dependency という項に書かれているように Build.scala に依存を書けば勝手に解決してくれます。フレームワークって大事ですね。
  val appDependencies = Seq(
    "org.twitter4j" % "twitter4j-core" % "3.0.3"
  )
これだけ・・・。便利になりました。

早速コードを書き始めると・・・おや、Play! を再起動しても IntelliJ IDEA では解決してくれないですね。うーむ。File > Invalidate Caches なんかもやってみましたがだめです。どうやら
$ play idea
を実行しないとだめみたいです。このへんはもう少し便利になってくれるとうれしいですけどね。まあこれで特に問題はないです。

では OAuth のページを作成しましょう。

と思ったらなんか便利そうなものを見つけました。 ここに書かれている SecureSocial なるものを使ってみますか。また dependency をつけます。
  val appDependencies = Seq(
    "org.twitter4j" % "twitter4j-core" % "3.0.3",
    "securesocial" %% "securesocial" % "master-SNAPSHOT"
  )

  val main = play.Project(appName, appVersion, appDependencies).settings(
    // Add your own project settings here
    resolvers += Resolver.url("sbt-plugin-snapshots", new URL("http://repo.scala-sbt.org/scalasbt/sbt-plugin-snapshots/"))(Resolver.ivyStylePatterns)
  )
この時点では Play! 2.1 には spapshot を使えとあるのでそうしてみました。これで同じように play idea も実行して解決させておきます。routes は書いてあるものそのままで。play.plugins からは不要な Provider を消します。
9994:securesocial.core.DefaultAuthenticatorStore
9995:securesocial.core.DefaultIdGenerator
9997:securesocial.controllers.DefaultTemplatesPlugin
9998:service.InMemoryUserService
10000:securesocial.core.providers.TwitterProvider
こんな感じで。あ、sample から InMemoryUserService を持ってきました。動作確認に使えます。次に securesocial.conf を作って application.conf から include します。
securesocial {
  onLoginGoTo=/
  onLogoutGoTo=/login
  ssl=false

  twitter {
     requestTokenUrl="https://twitter.com/oauth/request_token"
     accessTokenUrl="https://twitter.com/oauth/access_token"
     authorizationUrl="https://twitter.com/oauth/authenticate"
     consumerKey="****"
     consumerSecret="****"
  }
}
いよいよ実行です・・・。とその前に、ログインしてないとだめなメソッドに @SecureSocial.SecuredAction アノテーションをつけます。
    @SecureSocial.SecuredAction
    public static Result index() {
        Identity user = (Identity) ctx().args.get(SecureSocial.USER_KEY);
        final String userName = user != null ? user.fullName() : "guest";
        return ok(index.render("Logged on as " + userName + "."));
    }
では実行です。/login にアクセスすると
まずはこのようにログインの画面が出ます。ここで t のアイコンをクリックするといつものツイッターの認証画面に飛ばされます。そこで認証を行うと
のようになります。やったー。・・・ツイッターだけだと正直楽になったかどうか微妙ですがw ほかのサービス(facebook とか)でもログインできるのでその手のサービスを作りたいときは便利なのは間違いないですね。

さてこれでログインはできましたが、twitter4j が使えなければ意味がないわけで。ログインした後に実行されるメソッドの中身を変えます。

        String ret = "Unknown";
        Identity user = (Identity) ctx().args.get(SecureSocial.USER_KEY);
        if (user != null) {
            Twitter twitter = new TwitterFactory().getInstance();
            twitter.setOAuthConsumer(Play.application().configuration().getString("securesocial.twitter.consumerKey"),
                    Play.application().configuration().getString("securesocial.twitter.consumerSecret"));
            AccessToken accessToken = new AccessToken(user.oAuth1Info().get().token(), user.oAuth1Info().get().secret());
            twitter.setOAuthAccessToken(accessToken);
            User u = twitter.verifyCredentials();
            ret = "Logged on as " + u.getName() + "(@" + u.getScreenName() + ") followed by " + u.getFollowersCount() + " followers.";
        }
        return ok(index.render(ret));
実行すると・・・
 できたー。

2013年5月24日金曜日

Heroku を試す(5)~コードを変更してデプロイしてみる

ずいぶん間があいてしまいました。興味の向く先がいろいろ変わるのはいいのか悪いのかわからないですが・・・。前々回の続きです。

しかしこのページ、殺風景ですね。
と思ったら
 あらら、エラーになってますね。早速直してみましょう。

直すためにはどうやってこのコードが動いているのかを知る必要があります。ソースコードを開いてみます。右にあるように、Main.java と HelloServlet.java があります。どう見ても Main が怪しい(w)ので開いてみると、思いっきり main メソッドがありますね。へぇ、これって任意の(サーブレットコンテナだけでなく)Java コードを実行できちゃったりするんですかね?それはそれで面白いような怖いような・・・。

ひとつ疑問なのは、この main メソッドをどうやって見つけているのか、ということ。これがわからないと間違ってどこかのクラスに main メソッドを作ってしまうととたんにアプリが動かなくなったりということもありうるのでは。ファイルを眺めていると大体のファイルは予想がつくものですが、中に Procfile なるファイルがあります。これは何だろう。
web:    java $JAVA_OPTS -cp target/classes:target/dependency/* com.example.Main
ほほう。どうやらこれがその答えだったようですね。なので一安心w

で、本題の修正です。もう twitter.github.com は twitter.github.io に変わっているために殺風景なページになっているのでしょうね。ローカルで編集して試します。Main クラスを Java Application として起動します。Eclipse 内では m2e のおかげでクラスパス等は解決されているはずです。 起動しました。8080 にアクセスしたらちゃんと同じページが出てきます。よしよし。Edit > Find/Replace で github.com を github.io に Replace します。保存してリロードすると・・・
めちゃめちゃ今どきになったじゃないですかw

さてこの変更をコミットしましょう。チュートリアルに戻って、まずは変更をコミットです。それから Push to Upstream をクリックしてしばし待ちます。すると無事サイトのほうも更新されます。なるほど。

2013年4月29日月曜日

Eclipse のプロジェクトを IntelliJ IDEA に

ちょっと Heroku の話はお休みして。#f1yosou のサイトはとうとう GAE/J の無料分では Stats が計算できなくなり、あえなく Stats を閉鎖する運びとなりました。で、この際裏で進めていたサイト全体のリニューアルを先にしてしまおうかと思ったのです。なぜなら新しいデザインのほうはまだ Stats ができてなくて時間がかかるなと思っていたんですが、現行のほうも Stats が使えないならもう移行してしまってもいいわけですね。

とはいえまだ公開できるレベルではないのでもう少し実装・テストをする必要があります。なのでこの際現状は Eclipse で開発しているのですが、IntelliJ IDEA を使ってみようと思いました。Java 部分はそれほど大差ないかと思うのですが、Velocity とかこれまではほぼ自前/手書きだったのが便利になるのではという期待もこめて。


ソースコードは subversion にチェックインされているので現状 Eclipse にあるコードはすべてチェックインしておきます。そこからチェックアウトする流れで。 IntelliJ IDEA から VCS > Checkout from Version Control > Subversion を選びます。すると右のようなダイアログがでるので、リポジトリの情報を入力して選択します。その後どこに展開するか場所をしていします。


場所を指定すると次は右のようなダイアログが出るので、私は gae というフォルダ以下に gae 関連のプロジェクトを全部まとめておきたいので、真ん中のやつを選びました。これで gae というフォルダ以下に f1yosou というフォルダができて、そのファイルが中にチェックアウトされます。

するとどうでしょう。
お、Web プロジェクトで、GAE とさらに GWT も使ってることを検出してくれました。Configure を・・・

と思ってこのキャプチャをとっていたらこのポップアップは消えてしまいました!w でもしたの Event Log をクリックしたら同じメッセージとリンクがでていました。ナイスw
Configure をクリックするとこんなダイアログがでるので、OK。
すると File > Project Structure を表示して Facets を見ると、
のように定義されています。あとはここに SDK へのパスを入れたりすればよい。

・・・と思ったんですが、いろいろ問題が。そもそも Dev Server が起動しない(ブランクになる)とか。クラスパス関連がごしゃごしゃになってたりとか。そこでいろいろ苦労しながらなんとかできそうな状態に持っていったやり方を以下に書きます。

まずは新規に GAE/J + GWT のプロジェクトを作成します。File > New Project で Java Module を選択して Next。これで JDO を使う場合はその選択も忘れずに。これで空のプロジェクトができます。空ですがいろいろ設定されているのが大事。

Eclipse でプロジェクトを作った場合は恐らく web ディレクトリではなく war になっていると思うので、ここで web ディレクトリを war に Rename します。web の上で右クリック、Refactor > Rename で war にしましょう。この rename がなぜか反映されてないところがあるのでそこを修正します。プロジェクトを右クリックして Open Module Settings を開くと上にあるようなダイアログが開くので、Facets の Web を選んで、Web Resource Directory 名を変更します。

さていよいよもとのコードを移行します。VCS > Checkout from Version Control > Subversion で上記の作業をします。そのときのチェックアウト先を今作ったプロジェクトそのものにします(つまり上書きします)。そして VCS > Enable Version Control で Subversion を選びます。私の場合 jdoconfig.xml と appengine-web.xml、web.xml が青くなりました。つまりチェックインされているものが変更されているということですね。これらの変更を Revert しましょう。右クリックで Subversion > Revert で戻せます。あと不要な index.jsp も消します。

この状態だとライブラリはパスが通ってない状態なので、WEB-INF/lib に何か入れていた場合はこれらを追加しなければいけません。普通の Java のときと同様ですが右クリックして Add as Library で追加します。また Open Module Settings をして Artifacts の WEB-INF/lib のところに追加するのも忘れずに。

この状態で Run/Debug で AppEngine Dev サーバーを起動したところようやく起動できました。・・・できたんですけどブラウザには悲しいページが。

まだだ!くじけるな!

この話は(たぶん)IntelliJ IDEA 云々ではないようです。既に問題になっていたようで。なので解決方法は Run > Edit Configuration から開くダイアログの VM options に-XX:-UseSplitVerifier をつけるだけ。そしてサーバーを再起動すると・・・
やっとキター!(表示が変なのはデータがまだないからです。)

この勢いで GWT の起動構成も確認します。自分のモジュールの .gwt.xml を右クリックして Run/Debug 。VM Options が必要な場合は上と同様に入れます。そして起動、おりゃーっ!
orz

まあいろいろ起きるもんですね・・・。生みの苦しみかこれは。とはいえこれは簡単に直って、また起動構成に戻り、Server を Default から AppEngine Dev に切り替えて起動しなおすだけでした。ふー。

さあ後は deploy だな! Tools > Upload AppEngine Application を選ぶだけ!
java.lang.RuntimeException: The application contains Java 7 classes, but the --use_java7 flag has not been set.
Unable to update app: The application contains Java 7 classes, but the --use_java7 flag has not been set.
いじめか orz
きっと Java6 にすれば治るんでしょう。GAE って確かまだ Java7 は正式サポートじゃなかったような気がするし。いやもう眠いから調べないですけど。切り替え!もう一度 Upload!

で、できた・・・。

これで恐らく開発できる状態にまではたどり着けた、と思うのですが。しばらく使ってみて今後どうするか考えます。ちなみにこのやり方が正しいのかベストなのか等全く見当もつかないので、もし試す場合は自己責任でお願いします。