yojikのlog

yojikのブログです

2014ブレイク確実!JavaベースのポータブルなWebフレームワーク Dropwizard

最近Dropwizardというフレームワークが海外のJavaおよびJVMベース言語界隈で流行り始めている感しがします。 Thought Works Technology Reader でも Traialに入ってきています。

http://dropwizard.codahale.com/

このフレームワークはYammerのバックエンドWebサービスを提供するために作られたフレームワークで、アプリケーション開発者からみると、

  • JaxRSベースのREST提供フレームワーク
  • ORM
  • Jettyベースの組み込みWebサーバ
  • Metricsを収集するためのライブラリ、管理ツール等

といった機能があります。

ここらへんまでは普通のフレームワークと基本的には違いが無く、むしろWebサービスに特化しているため物足りなく感じるのですが、特筆すべきは、このフレームワークが推奨するデプロイ・運用方法にあります。

dropwizardでは、ビルドをする際に(dropwizard自身を含む)依存ライブラリをすべて一つのjarに固めてしまい、組み込みWebサーバを利用するのが基本的な使い方になります。(この一つjarに固めるということができれば、ビルド手段はmavenでもgradleでもsbtでもなんでもよいです)

実行環境ではこのように通常のアプリケーションとしてjavaコマンドで起動します。

java -jar target/hello-world-0.0.1-SNAPSHOT.jar server hello-world.yml


Dropwizardで作成するアプリはmainメソッドで起動する単なるJavaアプリです。プロダクション環境でも、特定のアプリケーションサーバに専用ツール等でデプロイすることはありません。デプロイは適当な手段で結局どこかのディレクトリに作成されたjarとymlを置くだけです。アプリの起動・停止はOSの標準的なツールを使うだけです。依存ライブラリもすべてjarに固めているので、基本的にはJava実行環境だけあればよいことになります。

複数アプリを扱う場合は、それぞれのアプリを個別に作成し、ポートを変えて起動し、逆プロキシを使ってルーティングすることになるのでしょう。現代のWebアプリケーションはアプリサーバにデプロイするのではなく、通常のプロセスとして起動し、ポートにバインディングするべきなのです。 ( http://twelve-factor-ja.herokuapp.com/port-binding )

気になる環境ごとの差異はymlファイルに外だしして設定します。Configurationというクラスを継承したクラスを作成することによって、ymlにアプリケーション独自の設定項目を追加できます。ある種のDSLを支援する機構ですね。

もちろん、開発中も根本的には単なるmainから起動するJavaアプリケーションなのでIDEとの統合も簡単です。デバッグも気楽にできるはず。お好きなJVM言語処理系のライブラリや実行機構を組み込むことも可能です。すでにScalaプラグイン等は存在します。

まとめ

JavaでWeb開発するときの「めんどくささ」「痛み」のかなりの部分はアプリケーションサーバに由来します。

アプリケーションサーバでの運用にありがちなのは、PermGenが足りなくなる問題*1、サーバ独自の設定項目の理解が難しい、共有するライブラリのバージョンアップ問題、独自開発ツール問題、クラスローダ問題、アプリケーションログが埋もれる問題、複数アプリケーションが同時起動している場合のガベージコレクションのチューニングなどなど。

Dripwizardで作成するアプリにはそのような問題とは無縁です。

Dropwizardアプリとして作成されるjarファイルは、アプリサーバとWebアプリを一体化した(流行り言葉でいえば)イミュータブルなアプリケーションサーバといってもよい存在です。

本番環境で動いているものと(環境や設定以外)完全に同じ構成のアプリケーションを手元で走らせて再現、検証できます。また、ポータブルなWebアプリケーションをユーザ向けに配布する、みたいなケースでも作成したjarを公開し渡すだけです。

まさに"write once run anywhere(死語)"再びという手軽さですね。

*1:複数アプリがデプロイされているので、クラス数の見積もりが難しいなど

Java8でmixinをがんばってみる

Java8からinterfaeのデフォルト実装が使えるようになります。インタフェースは複数implemntsできます。したがって複数のdefault実装付インタフェースを組み合わせてクラスを構成することができそうです。mixiinによる実装の再利用です。(この場合implementと呼んでよいのか疑問ですが)。うまく使えばScalaのトレイトみたいなことが出来るはず。

それでは実例を。Userオブジェクトを検索して、その人にメールを送るという架空のアプリを考えます。

まずはUserオブジェクト。
いちいち複数ファイルを書くのは面倒なので、今回はすべてstaticなネストクラス(とネストインタフェース)として定義します。

public class MyTest {
    public static class User {
        String id;
        User(String id) {this.id = id;}
        @Override  public String toString() {return id;}
    }

はい適当ですね。
そしてUserを検索してくるUserRepositoryのインタフェースと、文字列をメール送信するMailGatewayを定義します。こちらには実装をつけません。(今回のポイント)

   //ステップ1
    public interface UserRepository {
        User findUser(String id);
    }
    public interface MailGateway {
        void send(String  str);
    }

こちらも適当です。
そしてUserRepositoryやMailGatewayに依存し、これらのサービスを組み合わせて必要な処理をおこなうMailSenderクラスを定義します。

    //ステップ2
    /** UserRepositoryとMailGatewayに依存するサービス */
    public static abstract class  MailSender implements  UserRepository ,MailGateway {
        User u ;
        public void sendMail(String id) {
            u  = findUser(id);     //UserRepositoryのメソッド
            send(u.id);               //MailGatewayのメソッド
        }
    }

こちらは抽象クラスとして定義します。必要なインタフェースを定義していないので当然ですね。(抽象クラスなのでなんとなくインスタンス変数も持たせました)

ここからが重要なところです。肝心の各種インタフェースを実装したインタフェースを定義します(上手い呼び名なし)

    //ステップ3
    /**実装*/
    public interface  UserRepositoryDefault  extends UserRepository {
        public default  User findUser(String id) {
            return new User(id);
        }
    }
    /**実装*/
    public interface  MailGatewayDefault  extends MailGateway {
        public default  void  send(String  str) { 
            System.out.printf("%sにメールを送ったよ\n",str);
        }
    }

まあ適当です。ここまでで準備完了。

そしてここからが美味しいところ、それぞれの実装を組み合わせたクラスを定義します。

    //ステップ4
    //以下ですべてを繋ぐ
    /**
     * defaultメソッドで実装されたインタフェース宣言を組み合わせたもの
     * 基本実装は必要無いが、mixinされたもの同士実装がぶつかるときは、ここでケアする
     *      */
    public static class MailSenderImpl extends  MailSender implements  UserRepositoryDefault, MailGatewayDefault {}

ちょっとScalaっぽい見た目です。
ここではMailSenderを継承し、さらに実装が書かれたインタフェースをimplentsしています。中身は必要ないので一行でかけます。

もしデフォルト実装インタフェース同士のメソッドシグネチャがかぶってしまった場合、コンパイルエラーが出るので、このクラスでオーバーライドして調整する必要があります。(きちんとしたトレイトがある言語ならば、ここら辺を上手くケアしてくれるのです)

そして、このクラスの利用はこんな感じです。(本来ならばMailSenderImplの生成はクラスの利用者から見ないところで行われるでしょう)

    /** 実行*/
    public static void main(String[] args) {
        MailSender ms  = new MailSenderImpl();
        ms.sendMail("id");
    }

この仕組みを利用することによってたとえばテスト時にはMailGatewayだけをMockにするといったことが簡単にできます。

また、簡易AOP的なこともできます。たとえばMail送信時にログをとりたいなら、、、、

    /**実装*/
    public interface  LogMailGateway  extends MailGatewayDefault {
        public default  void  send(String  str) { 
            System.out.println("メールを送信しそう");
            MailGatewayDefault.super.send(str);
            System.out.println("メールを送信したみたい");
        }
    }

上記のようなデコレータをつくっておいて

    public static class MailSenderImpl extends  MailSender implements  UserRepositoryDefault, LogMailGateway {}

このように組み合わせることが可能です。簡易DIとかにも応用できそうですね。

Scalaのトレイトのようにインスタンス変数をもったり、線形化によって仮想の継承関係をつくって、メソッド衝突を防いだりすることはできませんが、その分シンプルで理解しやすいでしょう。局面を限れば非常に有効なパターンだと思います。

というわけでラムダ式によるコレクションAPIの改良のおまけで入ったようなこの機能ですが、かなり便利に使えそうです。

パシフィックリムを観た

いい意味で観た後に何も残らない、完全燃焼の面白さでした。(いい意味で)

すべてのシーンが怪獣映画ロボット映画でよくあるかっこいいシーン、ベタの積み重ねなんだけど、圧倒的なハリウッドパワーで作られてるので、クオリティが半端ない。シナリオもかっこいいシーンに論理的な説得力を与えるために構築されています。最初からロボット戦たっぷり描くために怪獣とのファーストコンタクトをナレーションでさらっと飛ばしたり、パイロット二人の脳を直結するドリフトの設定も主人公たちの絆を(ある意味手っ取り早く)深めたり説明を飛ばすために有効でした。

大人たちが莫大な予算と時間を使って、「剣がチェーンになってて、それがガシーンくっついてぶっさすとかっこいい」「そのときに女性パイロットが「これは家族の分」とか言ってぶっさすと燃える」、「ロシア代表のロボットは原発っぽい形にしようぜ」とかを真剣に考え実現化する、たぶん普通の企業の社長が自社の方針決めるより何百倍も真剣に考えてる、という事実がたまらなく良いと思います。

あ、あと芦田プロも良い演技でした。

追記:

劇中ではすでに破壊された設定で出てこなかったけど、この日本製イェーガーがホントかっこいい。日の丸ついてるし

http://images1.wikia.nocookie.net/__cb20130723033132/pacificrim/images/thumb/7/78/Tacit_Ronin.PNG/250px-Tacit_Ronin.PNG

http://pacificrim.wikia.com/wiki/Tacit_Ronin