2016年7月25日月曜日

【JavaでPlayFramwork2.4.6】Grobal.java

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

最近はPlayFrameworkについて勉強を進めています。

前回まででプロジェクトの作成に成功したところで、今回はいよいよ細かい機能の確認に入っていきたいと思います。
今回のテーマは「Grobal.java」です。

Grobal.java


公式の参考ページは以下です。


上記はバージョンPlay2.3の話です。
本ブログで取り扱うのは2.4.6ですが、公式そのものが枯れていないので公式に無い話を取り扱うのもやむなし。
頑張っていきましょう。

役割


まず、Grobalクラスの役割は全てのコントローラクラスの総合継承クラスとでも言いましょうか。
本来コントローラが担当するURLリクエスト活動の親クラスとしての他、システムの初回起動時専用など全体的な共通処理を担当するクラスです。

従って、

  • システム初回起動時に一回だけ行いたい処理
  • 各URLのリクエスト時に毎回実行したい処理

この辺りがGrobalクラスの役割と言えるでしょう。

特徴


Grobalクラスの特徴は、やはりクラス名が「Grobal」であるということです。
「Grobal2」とかちょっとクラス名を変えただけで動きません。
そう、Grobalクラスは、場所がルート直下で、クラス名がGrobal.javaであるというネーミング規約があります。

後は、「GlobalSettingsクラス」を継承しているという点くらいでしょうか。

public class Global extends GlobalSettings {


私が特徴に思うのは、やっぱり「Grobal.java」であることというネーミング規約が敷かれている点でしょうか。
「Grobal2」とかちょっとでも違うネーミングにするともう動きません。

外部ファイル名ではなくJava本体のクラス名にこのような規約を設定するのはJavaプログラミングでは余り見かけない特徴だと思います。
この辺りがscalaベースとしているPlayFramworkの特徴でしょう。

このように、JavaがJavaクラスのクラスやメソッドを「文字列として認識」して振り分けるという機能を「リフレクション」と言います。

メタプログラミングと言い換えても良いでしょうか。

普通にシステム開発者として従事している間は余り需要はありあせんが、ライブラリ開発社の立場からすると偶に裏技として出番がある時があります。
「こういう使い道もある」という程度に覚えておくと良いかもしれませんね。


各メソッド


Global.javaが実現する機能は親クラスであるGlobalSettingsの実装に準拠しています。


public class GlobalSettings {

    /**
     * Executed before any plugin - you can set-up your database schema here, for instance.
     */
    public void beforeStart(Application app) {
    }

    /**
     * Executed after all plugins, including the database set-up with Evolutions and the EBean wrapper.
     * This is a good place to execute some of your application code to create entries, for instance.
     */
    public void onStart(Application app) {
    }

    /**
     * Executed when the application stops.
     */
    public void onStop(Application app) {
    }

    /**
     * Called when an exception occurred.
     *
     * The default is to send the framework's default error page. This is achieved by returning null,
     * so that the Scala engine handles the excepetion and shows an error page.
     *
     * By overriding this method one can provide an alternative error page.
     *
     * @param t is any throwable
     * @return null as the default implementation
     */
    public F.Promise onError(RequestHeader request, Throwable t) {
        return null;
    }

    /**
     * Call to create the root Action of a request for a Java application.
     * The request and actionMethod values are passed for information.
     *
     * @param request The HTTP Request
     * @param actionMethod The action method containing the user code for this Action.
     * @return The default implementation returns a raw Action calling the method.
     */
    @SuppressWarnings("rawtypes")
    public Action onRequest(Request request, Method actionMethod) {
        return new Action.Simple() {
            public F.Promise call(Context ctx) throws Throwable {
                return delegate.call(ctx);
            }
        };
    }

    /**
    *
    * Called when an HTTP request has been received.
    * The default implementation (return null) means to use the application router to find the appropriate action
    *
    * By overriding this method one can provide an alternative routing mechanism.
    * Please note, though, this API is very low level, useful for plugin/module authors only.
    *
    * @param request the HTTP request header as seen by the core framework (the body has not been parsed yet)
    * @return an action to handle this request - if no action is returned, a 404 not found result will be sent to client
    */
    public play.api.mvc.Handler onRouteRequest(RequestHeader request) {
        return null;
    }

    /**
     * Called when no action was found to serve a request.
     *
     * The default behavior is to render the framework's default 404 page. This is achieved by returning null,
     * so that the Scala engine handles onHandlerNotFound.
     *
     * By overriding this method one can provide an alternative 404 page.
     *
     * @param request the HTTP request
     * @return null in the default implementation, you can return your own custom Result in your Global class.
     */
    public F.Promise onHandlerNotFound(RequestHeader request) {
        return null;
    }

    /**
     * Called when an action has been found, but the request parsing has failed.
     *
     * The default behavior is to render the framework's default 400 page. This is achieved by returning null,
     * so that the Scala engine handles onBadRequest.
     *
     * By overriding this method one can provide an alternative 400 page.
     *
     * @param request the HTTP request
     * @return null in the default implementation, you can return your own custom Result in your Global class.
     */
    public F.Promise onBadRequest(RequestHeader request, String error) {
        return null;
    }

    /**
     * Get the filters that should be used to handle each request.
     */
    public  Class[] filters() {
        return new Class[0];
    }

}


メソッドを抜き出すと以下です。

  • beforeStart
  • onStart
  • onStop
  • onError
  • onRequest
  • onRouteRequest
  • onHandlerNotFound
  • onBadRequest
  • filters

色々ありますね……。

このうち、私が現在のシステム開発で必要としているのは以下です。

  • beforeStart:システム初回起動
  • onRequest:各種リクエスト実行時
  • filters:フィルター

beforeStartメソッドというのはシステムで最初にリクエストを受けた時だけ実行されるメソッドです。
シングルトンクラスの初期化などで実行するのが良いでしょう。

onRequestメソッドはリクエスト毎に毎回実行。

filtersメソッドは、onRequestに連動しているものです。

各種リクエストの開始終了ログなど、システムの最初に用意するログ機構についてはこの辺りが活躍するわけですね。

終わりに


次回はログ出力の手法についてご紹介したいと思います。

2016年7月4日月曜日

【JavaでPlayFramwork2.4.6】Eclipseへの取り込み

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

最近はPlayFrameworkについて勉強を進めています。

前回にて一応、プロジェクトの作成には成功しました。
次はEclipseへの取り込みに行ってみましょう。

開発環境はEclipse


まず大前提ですが、Javaで開発するんだからそりゃEclipse使いたいですよね?
と言いますのも、PlayFrameworkは根っこの思想がScalaなので、根本的には「Eclipseみたいな派手な総合開発環境(IDE)が無くてもテキストエディターだけで実装出来るよ」というものがあります。

いや、確かにそうなのかもしれませんけど、何をするにもやっぱIDEはあった方が良いと思います。

これは他の言語でも同じ。
PHPでもRubyでもその他でも、テキストエディターとかviとかだけで実装する熟練の戦士は確かに一定数いるとは聞きますが、今は文明の利器であるIDEがあるんだから使えばいいしょ。

「最近の若いもんはEclipseが無ければコード一つマトモに書けやしない……」

別にいいだろ! あるんだから使えば!!

と言う訳で、実装パワーをIDEに頼り切るスタイルは今後も継続です。

しかし、問題はどのIDEを使うか、ですね。

  • Eclipse
  • IntelliJ IDEA

ご存じのように、日本ではEclipseがぶっちぎりの一強、事実上のスタンダード。
しかし世界を見渡すとIntelliJ IDEAもなかなかのシェアを誇る。

最近はAndroidアプリ開発専用IDE「AndroidStudio」がIntelliJ IDEAのカスタマイズで作られている辺り、日本人でもIntelliJ IDEAを無視できなくなってきました。

「IntelliJ IDEAの方がEclipseより優れている部分もあるんだぜ!!」

みたいな話も調べると出てきますし、IntelliJ IDEAの習得は将来性を鑑みたJavaエンジニアの課題として一考に値するものだと言えるでしょう。

しかし、今回の所はEclipseでやらせて頂きたいと思います。
私自身がEclipseに慣れっこなのでEclipse以外は気が進まないというのもありますが、何より、私以外の要員も私と同じくEclipse派ばかりですので。
IntelliJ IDEAを導入するには説明出来るだけの合理性と、要員の教育、全員のモチベーションが必要で、そういった体制も考えるとEclipse以外の選択は厳しいのです。

プロジェクトのインポート

では、さっそくEclipseに取り込んでいきましょう。
前回までで「Activator new」で作ったプロジェクトをEclipseのworkspace配下に置いて、プロジェクトを取り込みます。

当然ながら、Eclipseの「プロジェクトの作成」で作るわけでは無いということです。

結果がこちら。


この時点ではプロジェクトにEclipse設定ファイルがありませんので、ご覧のようにJavaプロジェクトではなくファイル一覧のように見えてしまっています。

これをEcipseに対応させるには、Play公式にやり方が掲載されています。


まずプラグイン「sbteclipse」を入れます。project/plugins.sbt に以下の記述を入れるだけでOK。

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")

この『プラグイン』というものに他にどんなものがあるのか、探していくのも面白そうですね。

次に、Activatorで「Eclipse」というコマンドを発行します。


すると、以下のようにプロジェクトにクラスパス等が通ってすっかりJavaプロジェクト風になりました。


特にエラーも起きていませんね……。
おかしいな……。

いえ、実はですね、私が最初に行った時はここまでやってもEclipseにエラーが発生していたのです。
原因はこちら。


赤枠の部分。

  • target/scala-2.11/twirl/main
  • target/scala-2.1.1/routes/main

これが自動的に入ってくれないのでscalaクラスの変数がコンパイルエラーを起こすというものがあったのです。


Application.javaの変数「index」が変数解決不能のエラーを起こしていたら、それはソースフォルダに「scala-2.11/twirl/main」「scala-2.1.1/routes/main」が入っていないことが原因だから手動で追加しろという内容の記事を書こうと思っていたんですが……。

ちくしょー。
私が最初に検証を行っていた4月の頃から今記事を書いている7月の現時点まででこの問題が解消されてやがる。

だからこれからPlayを始める人はこんなことに悩まなくて良いんだ……。

すいませんね。
私は画像キャプチャを取る為に過去に行った作業を一からやり直しながら記事を執筆しており、当時存在していたEclipseコマンドのバグがいつの間にか修正されていることに今気付いたのですよ。

ここまで書いて記事を消すのも悲しいので、本日はこれにて終了……。

PlayFramworkは非常にホットなライブラリなので、過去にあった問題がサラッと直っていることもあるということです。

終わりに


やられましたね、これは!!

しかし過去に存在した問題がアップデートで解決するのは望ましいこと。
Tacyは人柱になったと思って祈りを捧げて下さい。

引き続きPlayの連載を継続します。