Javaのモダンなウェブ・フレームワーク enkan の env 管理についてです。 env 周りのAPIや動作は直感的で使用例を見ればすぐ分かる感じです。 ただ、公式のドキュメントを見る限りだと、そのあたりの説明がまだあまり載っていないようだったので備忘のためまとめました。
enkan については、JJUG CCC 2016 Springのセッションで発表されていたのを聞いて知りました。 enkan 自体の概要は作者のkawasimaさんの http://qiita.com/kawasima/items/723646eb30a91d724352 の記事を参照してください。
実行環境による設定の切り替え
enkanには、実行環境ごとに異なる値(例えばデータベースのURLやメールの送信先など)を外部化して管理するための仕組みが普通にサポートされています。
enkan-coreのEnv.javaがまさにそれを実現しているクラスです。
kotowari-architype
で生成されるソースでも使われています。
例えばこんな感じです。
Env.getInt("PORT", 3000)
上記は実行環境ごとのport
の値を取得しています。
第2引数の3000
は、該当するキーが存在しなかった場合のデフォルト値です。
値を設定できる箇所
Env.javaは環境ごとの値を3つの場所から取得し、順にマージします。 Env.javaのスタティック・イニシャライザで次のように処理されており、
static { readEnvFile(); // 1. <classpath>/env.properties ファイル readSystemEnv(); // 2. システム環境変数 readSystemProps(); // 3. システム・プロパティ(例: java -Dmy.foo.bar=hoge) }
3つのメソッドはそれぞれ取得した値をスタティックなフィールドのマップにputしています。 なので、複数箇所で同じ値が設定されている場合は後勝ちになり(後に設定している値で上書きされ)ます。
middleware predicate
enkanでは、リクエストとレスポンスの処理がミドルウェア・パターンによって、モジュラーに着脱可能になっています。
この各ミドルウェアの有効/無効は、使用するミドルウェアを宣言する際にpredicate
(述語)*1で条件付けをすることによって制御します。*2
enkan-coreでは、基本的な述語のクラスが一通り用意されており、 それらはenkan.util.Predicatesから簡単に利用可能です。
import static enkan.util.Predicates.NONE; import static enkan.util.Predicates.envin; // ・・・(中略)・・・ WebApplication app = new WebApplication(); Routes routes = Routes.define( r -> { r.get("/").to(IndexController.class, "index"); // ・・・(その他ルーティングは省略)・・・ }).compile(); app.use(new DefaultCharsetMiddleware()); app.use(NONE, new ServiceUnavailableMiddleware(new ResourceEndpoint("/public/html/503.html"))); app.use(envIn("development", "test"), new StacktraceMiddleware()); // enkan.env が development かまたは test の場合のみ有効 // ・・・(その他ミドルウェアは省略)・・・ return app; }
各述語の実装はenkan.predicateパッケージ配下のクラスです。
この中のEnvPredicate.javaが、envIn()
メソッド内で呼び出されている実装です。
その他の注意点や細かいこと
env.properties
ファイルの置き場所
クラスパス直下に置きます(プロジェクトのルートとかではないよ)。
キー文字列の正規化
- キー文字列の大文字/小文字は無視されます。
- また、キー文字列内の
_
は.
に変換されて解釈されます。
内部で小文字の.
区切りに正規化(変換)しているためです。つまり、以下は全て同じ結果になります(極端な例ですが)。
Env.get("CLIENT.MAIL.SENDTO")
Env.get("client.mail.SendTo")
Env.get("client.mail.sendto")
Env.get("client_mail_sendto")
Env.get("client.mail_sendto")
propertiesファイルでは.
区切り小文字で、環境変数では_
区切りの大文字で書くのがよく見るスタイルなので、その差を吸収するような挙動なのかなと思います。
実行環境ごとの middleware の切り替え
enkan.util.Predicates#envIn()
は、実行環境によってmiddlewareを切り替えるためのpredicateです。
具体的には、enkan.env
というキーの値が用いられ、その値がenkan.util.Predicates#envIn()
の引数(複数のうちどれか1つ)と一致しているかどうかで判定します。*3
env.properties
にも環境変数にもシステム・プロパティにも該当のキーがない場合は、デフォルト値としてdevelopment
が使用されます。
まとめ
Env.java
クラスで実行環境ごとの値の管理が可能。- (1)
<classpath>/env.properties
→ (2)環境変数 → (3)システム・プロパティ の順に読み込まれ、同じキーに紐づく値は上書きされる。 enkan.util.Predicates#envIn()
メソッドでenkan.env
の値による Middleware の制御が可能。
感想
REPLドリブンで開発できるのはやっぱり楽しい。