Java EE 5からJava EE 7へのアップデート

注)このエントリは前置きです。

Java EE 5で作ったアプリケーションをJava EE 7にアップデートすることを検討中。

実は、3年近く前に、Java EE 6 (JBoss AS 7)へのアップデートを試みたのだけど、諸々の事情があって保留になっていました。。

その間に、Java EE 7がローンチされたし、せっかく今やるならJava EE 6ではなくて、きちんとJava EE 7(Java SE 8)に対応しておきたいと思います。

というわけで、Wildfly 8.1.0.Final上で動くJava EE 7アプリケーションについて、色々と検証していきます。

アップデートの進め方

移行する際のガイドラインとしては、こちらが参考になります。

この記事を呼んで変更のポイントをつかんでおくことはもちろん重要なのですが、前回の反省点として、既存のアプリをいきなりWildfly 8.1.0.Finalに載せて、動くようになるまでカスタマイズしていく方法は茨の道のように感じてます。

というのも、実際にやるとなると、アプリの作りによって記事に紹介されていないようなカスタマイズが必要になることもあり、「デプロイ→エラー→原因分析・修正」のサイクルをまともに動くようになるまで繰り返す必要があるからです(そもそもアプリの作りが悪い、ということも原因なのですが…)。

というわけで、既存のアプリのことはとりあえず忘れて、小さく動くJava EE 7アプリケーションを作って、きちんと動く状態を保ちつつ、既存のアプリを少しずつ移植していくアプローチを採用したいと思います。

Java EE 7アプリケーションのひな形を作る

まずはWildfly 8.1.0.Finalで動くJava EE 7アプリケーションのひな形を作ります。

やるべき(やりたい)こと:

  • 自動化された結合テストの仕組みを最初の段階で用意する。
  • 普段の開発をさくさく行うために、一発でデプロイ(ホットデプロイ)、アンデプロイができるようにする。
  • ビルドやデプロイは、メンテナンスとカスタマイズのしやすさから、MavenではなくGradleを使う。
    • プロジェクトの構成はMavenやGradleの標準レイアウトに準じる。
    • 公開されているドキュメントやサンプルは基本的にMavenを前提として書かれているので、悩ましいところではあるのですが…。
  • アプリケーションのパッケージングはEARでなくてWAR
    • Java EE 5の時は必然的にEARでパッケージングしてましたが、EJBもwarに含めることができるようになった昨今、あえてEARでパッケージングする必要ってあるのかな…?

次回からのエントリで、詳しい内容について書いていきます。

参考サイト

最近のJava関係イベントの個人的なまとめと感想

デブサミJava Day Tokyoなど、会社の許可をもらってイベントにありがたく行かせてもらっているけど、ちゃんとレポートできていなかったということで、社内勉強会で話すことにしました。

まとめ資料を作ったので、このブログにて先行して公開してしまいます*1少し前のエントリで資料をアップロードしたのはこのための準備でした。

資料の位置づけ

古いJavaの知識で止まっている人(自分含め)やこれから本格的にJavaを学ぼうと思っている人に、最近のJava関連の新機能などをざっくりつかんでもらうための資料です。内容の深さ・広さは、聴く人のJavaスキルに合わせた一応合わせたつもりです。

Java SE 8がリリースされて、その界隈では「for文禁止」などと言われる一方で、会社の新人研修で教えるJavaでは、「ループはforやwhileを使って書きます」みたいに教えるわけで、その辺のギャップを埋める助けになればいいなぁと思っています。

と言いながら、何より、この資料を作るにあたり、色々と調べたり *2NetBeansでせっせと検証コードを書いて試したりしたので、自分の勉強になりました。

2014-06-17 追記

yamadamnさんの指摘を受けて、GlassFishの商用サポートに関する記述、ラムダ式の説明のスライド(と細かいtypo)を修正しました。
yamadamnさん、ありがとうございました。

*1:会社の情報は出ていないし、業務時間に作ったわけではなく休日に個人の活動として作ったものなので、問題はあるまい、うん。

*2:JSRも恥ずかしながら初めてきちんとダウンロードして目を通しました…。

Redmineのカスタムクエリを少し便利にするプラグイン

会社の業務で必要に迫られているものとは別に、Redmineプラグインを作りました。

Redmine Query with Version Plugin

プラグインの概要

カスタムクエリと任意のバージョンの組み合わせによるチケット検索を可能にするプラグインです。

特定のバージョンのチケットだけを抽出するカスタムクエリをよく作るんですが、バージョンごとにカスタムクエリを作るとなると、素のRedmineでは、以下の理由で大変です*1

  • 既存のカスタムクエリをコピーしての作成ができないので、一からカスタムクエリを作成しないとならない*2
  • カスタムクエリの数が増えてくるとサイドバーがゴチャゴチャしてきて、目的のカスタムクエリが探しにくい。

そこで、ベースになるカスタムクエリだけを用意しておき、バージョンに関するフィルタ条件だけは切り出してチケットの検索が行えるようにするのが、このプラグインの用途です。

最低限使える形にはなったので0.1.0として公開しているのですが、もう少し手を加えて、本家に登録したいなぁ…。
Redmineプラグインのライセンスの扱いってどうするのがよいのだろう…?公開されているプラグインを見ると、ライセンスについて明記されているプラグインの中ではGPLとMITが6:4くらいだけど…。

*1:よく使っているRedmineのバージョンが2.3.0なので、新しいバージョンだと事情が違うかも。。

*2:別途Query Duplicate Pluginを導入することでこちらの問題は軽減可能ですが。

Qiitaのブログパーツのカスタマイズ&設置

伊藤さんのブログの「Qiitaのブログパーツをはてなブログのサイドバーに設置する方法」を参考に、便乗してこのブログのサイドバーにもQiitaのブログパーツを貼り付けてみました。

同じく、@suinさんの作られているQiita Widgetをカスタマイズしています。

カスタマイズしたのは以下の点。

  • 表示件数を10件→5件に。
  • フォントのスタイルを変更。font-familyはこのブログのfont-familyをそのまま利用し、非boldにしました。
  • ブログパーツのヘッダ部分のバーを削除。削除した経緯:
    1. font-familyを定義したら、コンパイル後の script.js がエラーになってしまった*1
    2. そこで、 script.js の圧縮は行わないようにした。
    3. 圧縮を行わないので、なるべくスクリプトサイズを抑えるため、バーを削除*2

フォークしたリポジトリのREADMEに、コンパイル手順のメモを書いています。

https://github.com/tq-jappy/qiita-widget

ちなみに、ChromeFirefoxでしか確認していないので、IEでは見られるか不明…。また後で直そう…。

Qiita Widgetを作られた@suinさん、参考にさせていただいた伊藤さん、ありがとうございました。

*1:詳しく見ていないのですが、圧縮後のscript.jsに対して<!--%css%-->を置換するところでポカしてそうなので、単純にlessの書き方が悪いだけの可能性大です。

*2:バーに表示するQiitaのロゴ(Base64での画像埋め込み)が比較的容量を食っていたので。

いいコードをみんなで書く

Keynoteに慣れるために、適当なスライドを作ってアップロードしてみました。

このスライドは、1年くらい前にチームで行ったLT大会の初回に発表したテーマです。プログラミングをする上で考えていることとか、これまでの反省点とか、LTみたいな場がないと話さないであろうことを話した、気がします。

当時はGoogleドキュメントのプレゼンテーションで作ったんですが、それをリメイクしました。旧スライドは「今でしょ!」で締めていたのですが、さすがに時期を過ぎたので破棄しましたw

中身が薄いのと、自分でも全然できていないことを偉そうに書いてるのは相変わらずですね…orz

Keynoteのテンプレートはsanographixさんの「Azusa」を利用させていただきました。すばらしいテンプレートをありがとうございます!

続きを読む

Dropwizard 0.7.0でWebSocket

Dropwizard 0.7.0では組み込みWebサーバとしてJetty 9.0.7が使われており、Jettyであれば非Java EE7環境でもWebSocketが使えるはずだと思ったので、一番簡単なechoを作って試してみました。

build.gradle

まず、websocket-server への依存関係を追加します。

compile 'org.eclipse.jetty.websocket:websocket-server:9.0.7.v20131107'

これで、推移的な依存関係により、websocket-commonwebsocket-clientwebsocket-servlet が追加されます。

WebSocketクラスの作成

@org.eclipse.jetty.websocket.api.annotations.WebSocket アノテーションをつけたPOJOを作ります。

package example.websocket.echo;

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;

@WebSocket
public class EchoWebSocket {

    @OnWebSocketConnect
    public void onConnect(Session session) {
        // do nothing
    }

    @OnWebSocketMessage
    public void onText(Session session, String message) {
        session.getRemote().sendStringByFuture(message);
    }

    @OnWebSocketClose
    public void close(Session session, int statusCode, String reason) {
        // do nothing
    }
}

今回のメインロジックですが、これだけです。メッセージを受け取った処理は@OnWebSocketMessageアノテーションをつけたメソッドで実装し、受け取ったメッセージをそのまま同じクライアントに送るようにしています。

ちなみに、このように@WebSocketクラスを作る方法と別の方法として、org.eclipse.jetty.websocket.api.WebSocketListener インタフェースを実装する方法もあるみたいです。

アプリケーションクラスにWebSocketHandlerをセットする

io.dropwizard.Applicationを継承して作ったアプリケーションクラスの run メソッドを修正。

アプリケーションコンテキストにWebSocketHandlerを追加し、先ほど作った EchoWebSocket クラスを登録します。

environment.getApplicationContext().setHandler(new WebSocketHandler() {

    @Override
    public void configure(WebSocketServletFactory factory) {
        factory.register(EchoWebSocket.class);
    }
});

今回一番頭を悩ませたのがこの辺り。最初は WebSocketServlet を作って登録させてみたのですが、うまく動かず、色々試行錯誤した結果、この方法に落ち着きました。

クライアントの作成

JavaScriptでオーソドックスに書きます。

  var ws = new WebSocket("ws://" + location.host + "/");

  ws.onopen = function(event) {
      console.log("connected.");
      ws.send("hello!");
  }

  ws.onmessage = function(event) {
      console.log("message received from server.");
      console.log(event.data);
  }

HTMLでの見せ方は色々(省略)。

一通り書いたらあとはいつも通り ./gradlew jar でビルドしてアプリケーションを起動後、 http://localhost:18080 にアクセスすれば、ブラウザのコンソールログとしてサーバからエコーバックされた "hello!" が出力されると思います。

補足

今回のJettyのバージョンは9.0.7ですが、9.1からは、JettyのWebSocket対応がJSR 356ベースに変わっているみたいです。逆に言うと、Jetty9.0.7のWebSocket対応はJava標準のものではなく、JettyのAPIに依存した形になっています。そのため、今後Dropwizardが対応するJettyのバージョンが9.1以上になれば、ここでの記事の情報は古くなります。

ちなみに最初はJSR 356の参照実装であるTyrusを試したのですが、断念しました。。

参考

Dropwizard0.7.0でのassets

Dropwizard続き。

Dropwizardで作るアプリケーションのビューとしては、「Dropwizard Views」にあるように、FreeMarkerとMustacheが使えるみたいなのですが、せっかくJAX-RSベースのRESTfulアプリケーションを開発しているので、ビューとロジックは分けたいなと思いました。ビュー側で、Javaへの依存を抑えて、いわゆるSingle Page Application(SPA)を作るための下地を作ります。

Dropwizard0.7.0の場合、以下のようになります。

  • dropwizard-coreにはdropwizard-assetsへの依存関係が含まれていないので、 build.gradle に追加(dropwizard系のライブラリの構成が整理されたのも0.6→0.7での変更点の一つ)。
compile 'io.dropwizard:dropwizard-assets:0.7.0'
  • src/main/resource 以下に assets ディレクトリ(名前は設定次第なので何でもよい)を作り、 index.html を配置(中身は確認ができる最低限のもの)。
  • Applicationクラスの initialize メソッド内で AssetsBundleaddBundle する。 AssetsBundle には5パターンのコンストラクタがあるのですが、 AssetsBundle で定義されているデフォルトのインデックスファイルのファイル名が index.html ではなく index.htm なので、 index.html にさせるために必要なパラメータを全て渡しています。
public void initialize(Bootstrap<HelloAppConfiguration> bootstrap) {
    bootstrap.addBundle(new AssetsBundle("/assets", "/app", "index.html", "assets"));
}