kagamihogeの日記

kagamihogeの日記です。

JJUG ナイトセミナ 「Java EE 特集」に行ってきた

JJUG ナイトセミナ 「Java EE 特集」 - 日本Javaユーザーグループ | Doorkeeper で聞いてきたことのメモとか、感想とかです。
なお、アノテーションやプロダクトが正確な名称になってないところもありますが、推敲がめんどくなったんでそのままにしてあり、そこは適宜読み替えをお願い致します。。

CDIをはじめよう

スピーカーは見習いプログラミング日記id:n_agetsuma氏。

Java EE 6からついにEJBを使わないDI/AOP仕様が導入。Springに行った人もそろそろ帰ってきてもいいのよ? という枕詞から始まる。

CDIとは、Context(スコープを持った)DI(依存性の注入)である。なので、まずDIの復習か入る。

JavaでWebアプリケーション作る場合、エンドポイントから外部システムへの三層レイヤーを取るのが一般的。では、レイヤー間はどう接続するか。newしてしまうと、ユニットテストに必要なクラスが芋づる式に必要になってしまい、ユニットテストしにくい。時間を食うのでユニットテスト困難と化し、結果としてテストのないレガシーコードが生まれてしまう。

解決策。まずはインタフェースを切り出す。しかし、コードのどこかでnewしてはアンマリ意味が無い。よって、誰かが実装クラスを探して自動でnewしてくれや、となる……ところに、DIコンテナというニーズが生まれることになった。

よって、このコンテナの基本的な機能は、インタフェースに対応する実装を勝手に探してnewする、というものになる。CDIでは、@Injectアノテーションを使うことがそれに相当する。これが特に効果を発揮するのはユニットテストのとき。その理由は、ユニットテストは、手段は何でもいいからダミークラスに差し替えすることさえできるのが最低条件なため。

このあたりの説明が素晴らしいのは、DIのメリットを、レイヤー間の結合度がうんぬん、ではなく、ユニットテストの観点からしてる点にあると俺は思う。

というわけでCDIについて。Java EE 6からCDI1.0導入、Java EE 7でCDI1.1として小規模アップデート。昔は、Java EE試そうと思ったら高価な製品買わないとダメだったけど、いまはオープンソースのものが幾つもあるので、気軽に試せる時代になった。

Java EE仕様でのCDIの位置づけ。レイヤー間を繋ぐのが主な役割。また、EJBを使わないビジネスロジックの実現。Java EE 5まではEJBのみDIができた。

具体的なコードを用いたCDIのサンプル説明。Java EE 6では、@EJBではなく@InjectでEJBもフィールドインジェクションが可能に。

インジェクションされる側の条件について。コンテナが管理(自分でnewしてない)しているインスタンスであること。具体的には、Servlet関連、EJBJAX-RSのエンドポイント、CDIで生成されたクラス、などなど。また、CDIによって生成されたインスタンスには、さらに別のクラスをインジェクション可能。とはいえ、途中で自分でnewしちゃったりしたらダメ。なお、スピーカーの苦い過去として、Strutsのアクションクラスにインジェクションしようとしたけどされなかった、など。わかるわ。

生成される側の条件。引数なしコンストラクタがあればどんなクラスでも注入可能。

インタフェースの実装が複数ある場合は? 答え:デプロイに失敗する。どのAPサーバもおなじ挙動するらしい。

この解決には、@Qualifierが付与されたアノテーションを自分で作成。例えば、RDBMS製品ごとのDAOを作る場合、@Oracleとか、@PostgreSQLとか自前のアノテーションつくる。これは静的な解決方法。

実行時に実装ガタの解決をしたい場合。たとえばユーザによって決済のクラスを動的に決定したい、など。
こちらは@Producesをつかう。

スコープについて。基本的にはJavaのwebアプリの開発者であれば馴染み深いもの。

@requestscoped リクエスト受付〜応答まで。インジェクション対象クラスにこれがついていると、リクエストごとにインジェクションしなおす。スピーカーのJBossでの実験だと、キャッシュされているのかどうかよくわからないが、すくなくともオブジェクトIDが違うものは返ってくる、とのこと。

@sessionscoped 同一セッション中は、状態が保持された同一インスタンスがインジェクションされる。

@conversionscoped session未満の任意の長さ

@dependent デフォルト。なんもつけないと、インジェクション先にあわせたスコープになる。たとえばサーブレットの場合、一度インスタンスができるとそれはずっとコンテナに存在し続けるはずなので、サーブレット生成時に一度だけインジェクションされることになる。

EL式との組み合わせ。JSF 2.x/CDI @namedによるELバインディング。value="#{password.now}" @namedってついてるクラスにだけこーいうバインドが可能になる。このバッキングビーンをコントローラーにインジェクションする、といった使い方をする。

インターセプター。メソッドの開始終了に割り込み、業務とは直接関係ないシステム処理を実行する。ロギング、セキュリティ関連、などなど。

インターセプターの作り方。自前アノテーションを作り、インターセプターの実装を作り、最後に@intercoptorbindingで実装と対象を関連付け、といった手順。

インタセプタの優先順位。Java EE 6ではbeans.xmlが必須、Java EE 7では@priorityが導入。基本的には、beans.xmlに先にかかれたものから実行される。

コンフィグ。Java EE 6ではインターセプターがなくてもbeans.xmlが必須。7ではオプション化……なのだが、なくても動くが、スコープ指定をしないとインターセプターがうごかないので、明示的に@dependentをかかなければならない。などなど、この辺何気にややこしい問題が潜んでいるらしい。

CDIJava EE 7のアプリケーションでどう使うか。基本的には三層構造のレイヤ分離。特にweb層(JSF、jaxrs,websocket,servlet)、Service(CDI Bean)、永続化のJPA2.x。Web層は変化が特に激しいので、ここから分離できるものはなるべく分離しておきたい。

EJBとの使い分け。@transactionalにより、Java EE 7からEJBの宣言的トランザクションCDIだけでもできるようになった。モノはSpringのそれと同じ。RintimeExceptionがスローされたらロールバックとか。そもそもトランザクションをコンテナにまかせたいがためにEJBを使いたかったのだけど、CDIの登場でその意義が薄れてきている。

とはいってもまだまだEJBにしかできないこともある。非同期実行、MQ連携、リモート実行EJB、タイマ機能(cron使うこと多いだろうけど) 通常のwebアプリだとEJBいらないんじゃね、って趣きもあるが、企業システムだとMQとか非同期実行とかはまだまだ必要であり、EJBはそうそうなくなるものではない、と思われる。

なお、CDIを勉強するにはweldのドキュメントがオススメらしい。CDI1.1をいますぐためせるのはglassfish 4なので、ダウンロードして試しましょう、とのこと。

単体テストはどうしている? JUnitなら、自分でダミーの実装クラスをnewしてつっこんでしまえばいい。
arqullian使うのもいい。

JSF(は俺が良く知らないのだけど)に似たような名前のmanagedアノテーションがあるらしいが? CDIアノテーションつかったほうがよい、とjavadocにかかれている。manageとかスコープはすべてCDIのものに移行すべき、とのこと。

デスクトップアプリケーションでCDIするには? DIが目的であれば、guiceとかもうちょっと軽いものを選択したほうが良いかもしれない。

arqullianのembeddebはよくない? remoteでいいんじゃね、とのこと。俺もそう思います。

おっぴろげJavaEE DevOps

スピーカーは @nagaseyasuhito 氏。

Java EE 77とか、Java 8のもうすぐリリースとかニュースは聞く。けど、実際に使ってるとこの話とか、運用事例とかはあんまりない気がするので、話すことにした、とのこと。GREEでの運用事例ということで、俺はGREEってRubyとかLL言語の会社ってイメージだったんで、そこの中の人が「エンタープライズ」という名のつくものを使ってなんか作った、というのは素直に驚いた。

事例紹介としては、2013/11/27に上がってた初めてのJava EE 6開発! 最初の壁をどう乗り越えた?──最新Java EE開発“虎の穴” 第1回 菊田洋一氏 - builder by ZDNet Japanもオススメ、とのこと。

この発表で使用されたソースコードはコチラ http://bit.ly/jjug-camellia

作ったものは、GlassFish経由のジョブワーカー的なもの。JAX-RSでリクエスト投げて、CDIでロジックに繋げて、JPAで永続化。それと、JMSでメッセージ投げる、EJB Timerで非同期実行もアリ。

JAX-RSで投げて、CDIのロジックになげて、JPAで永続化。JMSでメッセージなげる。あと、EJB Iimerで非同期実行

JAX-RSの部分。LombokいいよLombok。@setter,@getter,@EqualsAndHashCodeとか、見たまんまのものを自動生成してくれる。JAXBのアノテーションをフィールドにつけると、いろいろ勝手にやってくれる。@xmltransientで除外、型変換クラスは(マーシャルとアンマーシャルの実装)を指定する、など。

Bean Validation @patternで宣言的に正規表現を指定。

JPAのcriteria APIを使う……けどまぁめんどくさいよね、これ。で、クライテリアのビルダーやらなんやらをラップするベースクラスを作って、一個のメソッド呼び出しできるようにした、とのこと。この辺はコード見たほうが早いんで、そっちを見ていただきたい。無名クラス使うので少々長々しくなるが、ラムダ式つかえば一行で終わるので、のちのち幸せになれる、かもしれない。

サービスクラス。クラスに@transactionalつけると、すべてのメソッドがトランザクション協会になる。このクラスに、@injectでdaoクラスを注入する。また、メソッドバリデーションで、お決まりのチェックコードを排除する。

テスト。ユニットテストとインテグレーションテストをどう分けるか。
failsafe-plugin デフォルトでは、クラス末尾にITがついているものだけテストされる。
surefire-plugin **/*IT.javaをexcludeして実行する。。

コンテナつかったテストは重いので、できるだけユニットテストに寄せてしまいたい。

JUnitで、entitymanagerfactoryをテスト用のプロパティで自前のインスタンス作って、そっちを使うようにする。で、JMockitでentitymangaerを注入する。

インテグレーションテスト。arqullianを使う。サービスクラスとかを@injectしてテストする。

リリース&デプロイ。git でバージョン管理している。maven-release-pluginを使うと、自動的にタグつけしてくれる。

ユニットテスト、ITテスト、開発環境へのデプロイまでは自動でやってくれる。ITテストがOkだったら、手動でリリースにはいる。

clone workspace SCM。ある段階でのリポジトリの状態をコピーする。これが役に立つのは、途中でコミットされたものが混じってきたりすることが防げる。

デプロイするとき。asadmin deployコマンド enabled=falseにすると、まずGlassFishのドメインにのみ配備される。つぎに、リバースプロキシからAP1を外す。アクティブがなくなったあと、enableコマンドでインスタンスを指定してデプロイする。次は、AP1をもどして、AP2を外して、以下略。

運用フェーズ。asadmin set-log-levels オンラインでログレベルを変更できる。たとえば、なんか重いクエリがきたとき、eclipselinkのログレベルを変更したりして、あやしいクエリを追う。

監視。JMX。@MXBeanというアノテーションをつけたインタフェースをつくって、実装をつくる。今回はDBアクセスしてユーザ数負えるものを作るためにEJBにして、@startup, @singletonをつける。イニシャライズでMBeanServer登録などなどの処理を書く。

監視はgangliaってのを使ってる。

スキーマ変更があったときはどうするか? ダブルライトで対応。たとえば、カラム名を変更(name -> userName)と言った場合。アプリケーション側では二つのフィールドに書き込むようにする。そのあと、古いフィールドを消したアプリをデプロイしなおす、といった手段をとっている。

まとめ。エンタープライズって名前からは重厚な響きを受けるけど、実際つかってみたら意外とライトでカジュアルであった。エンタープライズって名前ついてる時点で拒絶反応ある人も多いだろうけど、触ってみてもいいんじゃなかろーか、といった締めくくり。テンポの良い発表は聞いてて面白かったです。




最後に。みんな↓とかのAdvent Calendarに参加するといいんじゃないかな、とのことでした。
Java EE Advent Calendar 2013 - Adventar
GlassFish Advent Calendar 2013 - Adventar