LoginSignup
5
5

More than 5 years have passed since last update.

Play2.4で追加された「Splitting your web application into several parts」について

Last updated at Posted at 2015-08-28

SBTSubProjects

過去のバージョンでもアプリケーションを分割し、依存関係を定義することはできたのですが、
Play2.4ではroutesファイルを親子で分割、親から子を呼び出すことができるようになりました。

こちらの公式ドキュメントに詳しい構成等が書いてありますが、下記の様なroutes定義をすることにより「/admin/index」は子プロジェクトのroutesファイルが参照される様になります。

親 routes

GET /index                  controllers.Application.index()

->  /admin admin.Routes

子 routes

GET /index                  controllers.admin.Application.index()

で、いくつかの問題点

Play2.4からは、routesから呼び出されるメソッドがインスタンスメソッド(動的参照)に変更されました。
Play2.3までのstaticメソッドでの参照ができません。

なのですが、今回の子プロジェクト化ではstaticでないと参照できないという矛盾?が発生しています。
ちょっと気持ち悪いです。

こちら、コメントにて回答を頂きました。
親プロジェクトとは別に、小プロジェクトのbuild.sbtに「routesGenerator := InjectedRoutesGenerator」を挿入することで解決しました。
ありがとうございます。
下記のjavaのコードに関しては、staticを外すだけで対応は終わりです。

そして、親と子でroutesの切り分けができたのですが、Assetsに関してはもう少しゴニョゴニョする必要があります。
こちらも先ほどの公式ドキュメント内に記載されているのですが、こんなコードが書かれています。
(当方はplay2.4 javaを使用中です。scalaについては最後に追記しました。[2015/09/01])

GET /assets/*file           controllers.admin.Assets.at(path="/public", file)
// Assets.java
package controllers.admin;
import play.api.mvc.*;

public class Assets {
  public static Action<AnyContent> at(String path, String file) {
    return controllers.Assets.at(path, file);
  }
}

子プロジェクトのAssetsは自身で再定義してね!ってことだと思うのですが、
何が問題って、これビルド通らないんですよ…

まずこのままビルドをしてみると、引数の数が足りないと怒られます。
Playの内部(atメソッド)を見てみましょう。

  def at(path: String, file: String, aggressiveCaching: Boolean = false): Action[AnyContent] = Action.async { implicit request =>
    assetAt(path, file, aggressiveCaching)
  }

第三引数に、デフォルト引数が定義してあるんですね。

なのでこう書き直します。

public class Assets {
  public static Action<AnyContent> at(String path, String file) {
    return controllers.Assets.at(path, file, false);
  }
}

そしてもう一度ビルドをしてみると、

staticでない うんぬんかんぬん をstaticコンテキストから参照することはできません。

( ゚д゚)ポカーン

Scalaはやったこと無いのでひとますググったら、ScalaにはStaticというものが存在せず、シングルトンで頑張るらしいのです。

でも、シングルトンで実装されているならインスタンスを取得する方法が絶対にあるはずだ!とググり続けるとScalaのシングルトンをJavaの視点で読み解くという記事を発見!インスタンスは取得できそうです。

で、また書き直すと…

public class Assets {
  public static Action<AnyContent> at(String path, String file) {
    controllers.Assets$ assets$ = Assets$.MODULE$;
    return assets$.at(path, file, assets$.at$default$3());
  }
}

これでビルドが通りました。

一安心と思いきやこんどはstaticファイルが取れてきません…
ここまで来たらあとには引けなにので、さらにPlayの中身をデバッガーで入り込んで探っていきます。
なんで回っているのかわからないfor分とかいろいろ見ていきます。どんどん潜ります。

その結果!よく分かりませんでした!(ソースリーディング力不足&Scala初心者でもう大変!)

で、結局のところtargetディレクトの中身を漁ってファイル階層を確認、四苦八苦した結果、以下の様なコードで所得出来ました。

package controllers.admin;

import controllers.Assets$;
import play.api.mvc.Action;
import play.api.mvc.AnyContent;

public class Assets {

    private static String moduleAssetsLibLary  = "/lib";
    private static String thisModuleName = "admin";

    public static Action<AnyContent> at(String path, String file) {
        controllers.Assets$ assets$ = Assets$.MODULE$;
        String moduleAssetsPath = new StringBuilder()
                .append(path)
                .append(moduleAssetsLibLary)
                .append("/")
                .append(thisModuleName)
                .toString();
        return assets$.at(moduleAssetsPath, file, assets$.at$default$3());
    }
}

ちなみに

Play2.4ではatではなくversionedがデフォルトとなっているので、こっちの方がいいかもしれません。

admin.routes

GET    /assets/*file    controllers.admin.Assets.versioned(path="/public", file: Asset)

Assets.java

package controllers.admin;

import controllers.Assets$;
import play.api.mvc.Action;
import play.api.mvc.AnyContent;

public class Assets {

    private static String moduleAssetsLibLary  = "/lib";
    private static String thisModuleName = "admin";

    public static Action<AnyContent> at(String path, controllers.Assets.Asset file) {
        controllers.Assets$ assets$ = Assets$.MODULE$;
        String moduleAssetsPath = new StringBuilder()
                .append(path)
                .append(moduleAssetsLibLary)
                .append("/")
                .append(thisModuleName)
                .toString();
        return assets$.at(moduleAssetsPath, file);
    }
}

最後に

最後になりますが、これは僕の悪あがきの記録であり、正しいコードだとは限りません。
この記事を参考にして生じた損害に関しましては何ら責任を持てませんので、もし困っている人がいたら自己責任で頑張ってください。

また、これは間違ってるよ!とか、こっちのやり方の方がいいよ!というのがありましたら、コメントをいただけると大変嬉しいです。

環境メモ

java 1.8

scala 2.11.6

playPlugin "com.typesafe.play" % "sbt-plugin" % "2.4.2"

追記 Scalaを試しました

こちらはちょっと変更が必要でした。

公式ドキュメントのコード

package controllers.admin
object Assets extends controllers.AssetsBuilder

実際に動くコード

package controllers.admin

import play.api.http.DefaultHttpErrorHandler

object Assets extends controllers.AssetsBuilder(DefaultHttpErrorHandler)

scala版は「/public/lib/moduleName/file」の形式のpathに変換しなくても大丈夫みたいですね。

5
5
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5