Playframework2でjavaでフィルタを実装するメモ

備忘録です。Playのバージョンは2.1.1

GlobalSettingクラス(java用)にfilterを登録するらしきメソッドが生えていたので気になって調べていました。
filter作って登録できるなら色々出来る事もありそうだなーと思って調べてたんですが、ざっくりコード読んだ感じだと、自作のフィルタを作るための仕組みはScala用にはAPIがあるけれど、Java用には用意されていない様子で、Javaオンリーで自作フィルタを実装するには、ScalaAPIjavaから呼び出す必要がある模様。

で、実装したコードが以下。
結論から言うとかなりつらみがありました。

解説*1 *2

実装する必要があるメソッドは

public EssentialAction apply(final EssentialAction next){}

このメソッド。
多分引数として与えられるアクションはリクエストに本来適用すべきアクションを表していて、「フィルタを処理を実装する」とは、このアクションを再定義した新規のアクションを返すってことなんだと思います。
java側からはこのEssentialActionはinterfaceとして見えます

return new EssentialAction(){ /*(ry)*/ }

最初は無名クラスとして返そうとしてこう書いたんですが、これだとよく分からないメソッドの実装を大量に要求されて死んだので

return EssentialAction$.MODULE$.apply(f)

こんな感じで、EssentialActionオブジェクトのファクトリメソッドを利用してEssentialActionのインスタンスを生成しました。
上記のメソッドに指定する関数は「リクエストヘッダを引数に取り、byte[](リクエストボディ)をResult(レスポンス)に変換するIterateeを返す」関数で、この関数を実装することが、おそらく実質的なフィルタ処理の実装ということなんだと思います。

フィルタを適用しない場合に本来得られたであろうIterateeは、next.apply(requestHeader)の戻り値として得られるはずで、つまり「何もしないフィルタ」を実装するとは、多分以下のようにすることです。

return EssentialAction$.MODULE$.apply(new AbstractFunction1<RequestHeader, Iteratee<byte[], Result>>(){
  @Override public Iteratee<byte[], Result> apply(RequestHeader rh) { return next.apply(rh); }
});


処理の終わりに何らかの処理を実行したい場合*3、next.apply(rh)の戻り値として得られるIterateeのmapメソッドを利用するっぽいです。
mapメソッドに指定する関数は「Resultを引数に取り、"何らかの型"の値を返す」関数ですが、EssentialAction$.MODULE$.apply()に指定できる関数は最終的にIteratee型を返さないといけないので、実質的にmapメソッドに指定できるのは「Resultを引数に取り、Resultを返す」関数です。
mapメソッドに指定した関数は、IterateeがResultを生成し終わった段階で実行されるとのことなので、「アクション実行後に実行したい処理」は、それを定義した関数をここで渡せばよさそうです。


ちなみに、ScalaAPIが要求する「関数」はjavaからはFunction1*4インタフェースとして見えます。
これを無名クラスとしてapplyメソッドを実装して渡そうとすると、上記のEssentialActionの時と同じように、よく分からない大量のメソッドの実装も一緒に要求されて死にます。
こういう時はAbstractFuntion1を利用するといいようです。


そんなこんなで出来上がったコードが冒頭のコードなんですが、見ての通りかなりのつらみが溢れ出た感じになってるので、直接EssentialFiterのAPIを利用するみたいなことはせずに、何らかのラッパをかぶせたほうがよさそうです。

*1:ものすごく理解が足りてないので、以下の解説は間違った理解のもとに成り立っている恐れがあるので注意してください。

*2:間違ってたらツッコミください

*3:例えばHogeFilterは処理時間計測フィルタなので「アクション実行後に現在時刻を取得してログに出す」みたいな処理を書く必要がある

*4:関数の引数が1個の場合