unnamed

Javaとか、http://twitter.com/sugarlife

退職しました

本月末で某インフラグループのSI会社を退職しました。次は同グループ持株組織の研究所系のなんたらセンターに行きます。

2009年度に新卒で入社してから4年間お世話になりました。初っ端から燃え上がったJavaの鉄火場に突っ込まされたり、いつの間にかAndroidJavaScriptの性能改善に入ったり、セキュリティコンサルをしてたりしてました。手広過ぎますが、一言でまとめれば主に性能問題に対するトラブルシュータ的な位置付けだったように思います。何人もの諸先輩・後輩方に助けられつつ、故障解析に追われながらも主にインフラ面の知識を吸収しながら直ぐに試せる場を与えられたのは僥倖でした。

ここ一年はJava EE実装の検証も始め出し、GlassFish User Group Japan(#glassfishjp) での発表や、JJUGでLTするなど、Javaへの恩返しを兼ねてユーザ活動も取り組み始めました。当退職エントリの投稿を勧めて頂いた先輩の id:sh2 さんを見習ってブログももう少し真面目に取り組みたいと思います…多分。

新しい職場ではもう少しコアにJavaをやります。より広い範囲と携われるので色々と試しながら楽しみます:)

Apache TomEE 事始め

この記事は Java EE Advent Calendar 2012 の5日目です!

Apache TomEE

Apache TomEEは去年のJavaOne 2011で発表された、TomcatベースのJava EE 6 (Web Profile)対応のJava EEアプリケーションサーバです。とみーと読みます。
これにRESTfulなWebを作るのに必須なJAX-RSを載せたJAX-RS版とみー君もいます。また、Certifitedではありませんが、Full Profileに近づけた機能を持つとみーぷらす (TomEE Plus) というものもあります。
必要に応じて使いやすいとみー君を選ぶことができます。それぞれの違いはここの比較表が解りやすいです。

華々しい(?)発表の後はあまり進展のない日々が続きましたが、今年のJavaOne 2012前にバージョン1.5がリリースされました。以前は手持ちのJavaEE6サンプルが一発で動かなかった&NetBeansで動かなかったため、TomcatにOpenXXXをつなぎ合わせた『ふらんけん』状態なのかと思い、そっと閉じた経験があったのですが改めて見ていきたいと思います。

TomEEのインストール

落として、解凍。おわり。tomcatと一緒ですね。簡単な動かし方としては、以下の通りに実行できます。

  • 実行する:bin/startup.sh
  • デプロイする:bin/tomee.sh deploy
  • 終了する:bin/shutdown.sh

Web管理コンソールはTomcatと一緒で、http://localhost:8080にアクセスすると確認できます。ポートは tomcatと同様に conf/server.xml を書き換えることで変更できます。

NetBeansでTomEE

次にNetBeansでTomEEを動かします。

  1. サービスタブ > サーバー > 右クリック > サーバを追加
  2. サーバーを選択
    1. Apache Tomcat を選択。
  3. インストールとログインの詳細
    1. サーバーの場所:Apache TomEEを配置した場所
    2. ユーザ名/パスワード:デフォルトならtomee/tomee。設定はconf/tomcat-users.xmlを直接編集する。


これで終わりです。Tomcatと同じ方法でインストールできます。

Java EE 6 サンプルアプリを動かす

NetBeansに収録されている Java EE 6 (JSF & JPA)のサンプルアプリである Pet Catalog を TomEE で実行してみます。

プロジェクトを作って何も考えずに実行する
  • 新規プロジェクトでサンプル > Java EE > Pet Catalog からプロジェクトを作る。
  • プロジェクトを選んで右クリック > プロパティ > 実行 > サーバ に先ほど登録した TomEE を選択 > OK
  • 実行
12 05, 2012 6:32:51 午前 org.apache.catalina.core.StandardWrapperValve invoke
重大: Servlet.service() for servlet [Faces Servlet] in context with path [/petcatalog] threw exception [javax.el.ELException: Error reading 'pagingInfo' on type controller.Catalog] with root cause
java.net.ConnectException: Connection refused: connect
	at java.net.DualStackPlainSocketImpl.connect0(Native Method)
(略)
<openjpa-2.2.0-r422266:1244990 fatal general error> org.apache.openjpa.persistence.PersistenceException: java.net.ConnectException: Connection refused: connect
(略)

そうは問屋が卸さない。しかし今回はとみー君が原因ではなく、JPA周りの設定していなかったのが原因であり、ここの設定を進めれば動きそうです。

TomEE の JPA 関係の設定をする

まずはpetcatalogの永続化ユニットの設定を確認します。

  • PetCatalog > 構成ファイル > persistence.xml (実体はsrc/conf/persistence.xml)
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="catalogPU" transaction-type="JTA">
        <jta-data-source>jdbc/petcatalog</jta-data-source>
        <properties>
            <property name="eclipselink.jdbc.user" value="root" />
            <property name="eclipselink.jdbc.password" value="nbuser" />
        </properties>
    </persistence-unit>
</persistence>

今回はとみー君に含まれるOpenJPAを使っているのでeclipselinkの設定を消します。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="catalogPU" transaction-type="JTA">
        <jta-data-source>jdbc/petcatalog</jta-data-source>
    </persistence-unit>
</persistence>

次にとみー君のDataSourceは conf/tomee.xml で設定しています(参考)。これを開くと、以下のようなHSQLDBを使う設定がありました。

<Resource id="My DataSource" type="DataSource">
  JdbcDriver org.hsqldb.jdbcDriver
  JdbcUrl jdbc:hsqldb:file:data/hsqldb/hsqldb
  UserName sa
  Password
  JtaManaged true
</Resource>

<Resource id="My Unmanaged DataSource" type="DataSource">
  JdbcDriver org.hsqldb.jdbcDriver
  JdbcUrl jdbc:hsqldb:file:data/hsqldb/hsqldb
  UserName sa
  Password
  JtaManaged false
</Resource>

OSSのPostgreSQLの設定などに変更しても良いのですが、折角なのでHSQLDBを使います。他のデータベースを使う場合はこちらを参考に書き換えます
今回はJTAで管理しているデータソースとして jdbc/petcatalog をしているので、JtaManaged が true になっている Resource の id を合わせて変更します。

<Resource id="jdbc/petcatalog" type="DataSource">
  JdbcDriver org.hsqldb.jdbcDriver
  JdbcUrl jdbc:hsqldb:file:(適当なファイルのパス)
  UserName sa
  Password
  JtaManaged true
</Resource>

今回使うデータベースにテーブルを作る必要があります。各データベースに合わせて、PetCatalog > サーバー・リソースにある catalog.sql に書かれている SQL を実行してテーブルを作成します。今回はHSQLDBを使っているので、「java -cp lib/hsqldb-2.2.8.jar org.hsqldb.util.DatabaseManager」から HSQLDB の管理ツールを呼び出してテーブルを作成しました。

改めて実行する

サービスタブからTomEEを選択して右クリック→再起動を実施後、PetCatalogをもう一度実行してみます。

動きました!

まとめ

実際にここまでの作業をしていてストレスは余り感じないほど起動・再起動が高速でした。Tomcatに慣れているが、他のJavaEEサーバはちょっと…という人には良いかもしれません。しかし、管理コンソール/ツールが貧弱、IDEとの連携があまりなく他のJava EEサーバと比べるとIDEの恩恵が弱いという点が気になりました。普段はGlassFishを使っているのでその部分がより顕著に感じました。
余談ですが、JavaOne 2012のTomEEセッションで、「実際に商用で使えるのか?」という質問にはげふんげふんな感触でした。商用でも安定していくようであれば、戦略的に選択していく所も増えるのではないかと思います。がんばれとみー君!
同じオープンソースであるGlassFishの勉強会(#glassfishjp)が12/13(木)にあります!こちらもよろしくお願いします!

次回は @masafumi_ohta さんです!よろしくお願いします!

G1GCのログの読み方

この記事は Java Advent Calendar 2012 の3日目です!
前の日は @btnrouge さんの「HotSpotの変遷からみたJava SE 7の考察」でした。次の日は @making さんです!

G1GCのログ

G1GC (Garbage First Garbage Collection)JDK 7u4 から正式に導入されました。この新しいGCの詳細は、幸いなことに @nari3 さんが日本語でG1GC本を執筆されており、これを読むことで殆どを把握することができます。やったね!ちなみに英語の論文等で読もうとすると、例えばこうなります。
学習の高速道路は整っていますが、更に理解の掘り下げやチューニングのために、実際に動かしながらログを読もうとすると、今までの形式とはすっかり変わってしまっているのでその読みにくさに絶望します。そのため、ここではログの出力がそれぞれどんな意味なのかをまとめていきたいと思います。
なお、ここでは以下のJVMを利用し、JDKに付属しているサンプル java2demo.jar を利用してログを出しました。

$ java -server -version
java version "1.7.0_09-icedtea"
OpenJDK Runtime Environment (fedora-2.3.3.fc17.1-x86_64)
OpenJDK 64-Bit Server VM (build 23.2-b09, mixed mode)

G1GC のサイクル

ログを見る前にG1GCの挙動を軽くおさらいしましょう。G1GCは以下のような2つのフェーズに分けられ、6つの処理をサイクルしています。

  • Concurrent Marking Cycle Phase
    1. Initial Mark (Stop the world)
    2. Root Region Scanning (Concurrent)
    3. Concurrent Marking (Concurrent)
    4. Remark (Stop the world)
    5. Cleanup (Stop the world & Concurrent)
  • Evacuation Pause Phase
    1. Copying (Stop the world)
      • このCopyingはYoung Regionを対象としたYoungモードとYoungとOldの両方を対象としたMixedの二種類ある。

-XX:+PrintGC

-XX:PrintGC (-verbosegc) を付けた場合のログは次のようになります。

:
5.325: [GC pause (young) 19M->12M(27M), 0.0270580 secs]
6.184: [GC pause (young) (initial-mark) 18M->12M(27M), 0.0112730 secs]
6.196: [GC concurrent-root-region-scan-start]
6.197: [GC concurrent-root-region-scan-end, 0.0013980]
6.197: [GC concurrent-mark-start]
6.217: [GC concurrent-mark-end, 0.0201330 sec]
6.217: [GC remark, 0.0021420 secs]
6.220: [GC cleanup 13M->13M(27M), 0.0001140 secs]
6.946: [GC pause (young) 18M->13M(27M), 0.0089250 secs]
7.464: [GC pause (mixed) 17M->13M(27M), 0.0118070 secs]
8.388: [GC pause (young) 20M->13M(27M), 0.0104410 secs]
:

まだ読みやすいですね。
Evacuation Pause Phase(と、initial-mark)ごとに「処理名 GC前->GC後(最大)のヒープサイズ, その処理にかかった時間(sec)」が記述されています。Concurrent Marking Phaseは各処理の開始時点(処理名-start)と終了時点(処理名-end)の2回出力され、終了の時点でその処理にかかった時間(sec)が記されています。ただし、Cleanup処理に関しては、オブジェクトの生存情報が再計算されるため、Evacuation Pause Phaseと同様に処理前後のヒープサイズが記されています。
このログからは全体ヒープ量とGC時間、そしてGCによる停止時間の概算が解ります。単純なメモリリークと停止時間を見るだけなら、さしあたり十分な情報は取れます。

-XX:+PrintGCDetails

-XX:+PrintGCDetailsを付けた場合、Evacuation Pause Phase の各 GC Worker Thread が、それぞれの処理に懸かった時間を詳細に出力するため、途端に読みづらくなります。

:
//-XX:PrintGCと一緒。GC Pauseに懸かった総処理時間(secs)
4.685: [GC pause (young), 0.01147000 secs] 
   // 並列動作しているGC worker threadsの経過時間。これ以降の1インデント下がっている部分は並列で実行されており、その処理にかかった時間。
   [Parallel Time:  11.4 ms] 
      // 各workerの処理開始時間(Javaプロセス開始時点からの経過時間(ms))
      [GC Worker Start (ms):  4685.4  4685.5  4685.5  4685.5
       Avg: 4685.5, Min: 4685.4, Max: 4685.5, Diff:   0.1] 
      // 各workerがroot情報をスキャンするのにかかった時間。
      [Ext Root Scanning (ms):  5.4  5.3  5.3  5.6
       Avg:   5.4, Min:   5.3, Max:   5.6, Diff:   0.3] 
      // 各workerがRemembered Sets(RS)を更新するのにかかった時間。
      [Update RS (ms):  3.1  3.2  3.1  3.0
       Avg:   3.1, Min:   3.0, Max:   3.2, Diff:   0.2] 
         [Processed Buffers : 2  3  3  2
          Sum: 10, Avg: 2, Min: 2, Max: 3, Diff: 1] 各workerのバッファーを更新した数。
      //  各workerがRemembered Sets(RS)をスキャンするのにかかった時間。
      [Scan RS (ms):  0.0  0.0  0.0  0.0
       Avg:   0.0, Min:   0.0, Max:   0.0, Diff:   0.0]
      // 各workerが生存オブジェクトをRegionsからCollection Set(CSet)にコピーするのに懸かった時間
      [Object Copy (ms):  2.9  2.8  3.0  2.9
       Avg:   2.9, Min:   2.8, Max:   3.0, Diff:   0.2] 
      // 各workerの終了処理にかかった時間。
      [Termination (ms):  0.0  0.0  0.0  0.0
       Avg:   0.0, Min:   0.0, Max:   0.0, Diff:   0.0]
         [Termination Attempts : 1  1  1  0
          Sum: 3, Avg: 1, Min: 0, Max: 1, Diff: 1]
      // 各workerが上の一連の処理を終わらせた時間(Javaプロセス開始時点からの経過時間(ms))
      [GC Worker End (ms):  4696.8  4696.8  4696.9  4697.0
       Avg: 4696.9, Min: 4696.8, Max: 4697.0, Diff:   0.2] 
      // 各workerの一連の処理時間。StartとEndから単純に計算すると小数点以下の誤差がでる。
      [GC Worker (ms):  11.3  11.3  11.4  11.4
       Avg:  11.4, Min:  11.3, Max:  11.4, Diff:   0.1]
      // 各workerの一連の処理以外の時間。この時間はParallel Timeに含まれる。
      [GC Worker Other (ms):  0.1  0.1  0.1  0.1
       Avg:   0.1, Min:   0.1, Max:   0.1, Diff:   0.1]
   // Card Tableを初期化するのにかかった時間。1スレッドで実行される。
   [Clear CT:   0.0 ms] 
   // 以降のCollection Set関係の合計処理時間。
   [Other:   0.1 ms]
      // Collection Setの選択に懸かった時間。
      [Choose CSet:   0.0 ms] 
      // Reference Objectsに懸かった時間。
      [Ref Proc:   0.0 ms]
      // ReferenceQueueに参照を追加するのに懸かった時間。
      [Ref Enq:   0.0 ms]
      // Collection Setを開放するのに懸かった時間。
      [Free CSet:   0.0 ms]
   // ヒープに対する各領域の占有量。
   [Eden: 5120K(5120K)->0B(3072K) Survivors: 1024K->1024K Heap: 15M(20M)->10M(20M)] 
 [Times: user=0.01 sys=0.00, real=0.01 secs]
4.805: [GC pause (mixed), 0.02731400 secs]
   [Parallel Time:  27.2 ms]
      [GC Worker Start (ms):  4805.2
			:(中略)
   [Eden: 3072K(3072K)->0B(5120K) Survivors: 0B->1024K Heap: 13M(20M)->12M(20M)]
 [Times: user=0.03 sys=0.00, real=0.03 secs]
5.284: [GC pause (young) (initial-mark), 0.02324400 secs]
   [Parallel Time:  22.9 ms]
      [GC Worker Start (ms):  5284.3
			:(中略)
   [Eden: 4096K(4096K)->0B(3072K) Survivors: 1024K->1024K Heap: 15M(20M)->12M(20M)]
 [Times: user=0.02 sys=0.00, real=0.02 secs]
5.308: [GC concurrent-root-region-scan-start]
5.314: [GC concurrent-root-region-scan-end, 0.0054690]
5.314: [GC concurrent-mark-start]
5.396: [GC concurrent-mark-end, 0.0818220 sec]
// ref-procはReference processingに懸かった時間。
5.398: [GC remark 5.398: [GC ref-proc, 0.0000310 secs], 0.0069840 secs]
 [Times: user=0.01 sys=0.00, real=0.01 secs]
5.410: [GC cleanup 13M->13M(20M), 0.0001110 secs]
 [Times: user=0.00 sys=0.00, real=0.00 secs]
5.566: [GC pause (young), 0.01728500 secs]
   [Parallel Time:  17.2 ms]
:

長々と出力されたログ(とOpenJDKのソース)を見ていきましたが、重要なのはGC pause処理部分に以下の一文が追加されたことです。

[Eden: 5120K(5120K)->0B(3072K) Survivors: 0B->1024K Heap: 15M(20M)->10M(20M)] 

これにより、GC処理前後のヒープ全体だけではなく、各ヒープ領域の状況も確認できます。上記の例では、Eden領域がGCによって5120K占有していたものが全てEden領域から解放/移動されて0Bになり、最大値が5120Kから3072Kに減っていることを示しています。また、Survivors領域は今回のGCによって0Bから1024KBに拡張されたことが解ります。

おまけ

-XX:UnlockDiagnosticVMOptions を使うことで有効になる出力用のオプションです。

-XX:G1PrintRegionLivenessInfo

全ての各Regionに存在する生存オブジェクトをConcurrent Marking Cycle Phaseの最後(Cleanup中)に出力します。

-XX:+G1PrintHeapRegions

Regionに関するイベントを各Regionがcommit/allocated/reclaimedした際に出力します。かなりの量のログが出力されます。

-XX:G1LogLevel=(fine|finer|finest)

JavaOne2012 の G1 Garbage Collector Performance Tuning というセッションで紹介されたJVMオプションです。fine==-XX:+PrintGC、finer==-XX:PrintGCDetails、finest≒-XX:+PrintGCDetails (いくつかの細かい情報が追加されている)として出力します。が、現在未対応のため、導入された際にどうなるかは不明です。


以上、G1GCのログの読み方でした。
思った以上にGCサイクルの挙動が詳細に出力されますが、より詳細に追いかけるためにはDiagnosticなJVMオプションも使う必要があります。しかし、人の目で追いかけるにはもはや苦痛を伴うレベルであるため、トラブルシュートやパフォーマンスチューニングを行うにあたっては、@kimuchi583 さんのようにG1GCに最適化された(情報をザッピングできる)ビューワー*1を開発する必要があるなと感じています。
次は @making さんの番です!

*1:http://www.slideshare.net/kimuchi583/jjug-javaone-2012-san-francisco-ltg1gc

GlassFish Users Group Japan 勉強会 June 2012 に参加してきた

6/4に開催されたGlassFish Users Group Japan 勉強会 June 2012 で喋らせて頂きました。
今回のテーマは 「Java EE 6 / GlassFish の再入門」でした。

atnd: http://atnd.org/events/28235
togetter: http://togetter.com/li/315293

最近、公私でGlassFishを触っていますが、その前の段階でOSSアプリケーションサーバの調査を一通り行いました。自分以外の方々はどのように見られているのか興味があったので、まずは自分から発表させて頂こうと思い参加させていただきました。
自分の採った方法の一つとして、各コミュニティのJIRAを眺めて、そのサーバのバグの状況や偏り方、コミュニティの盛況ぶりがどんなもんかと見ていました。時間の都合上、今回は「GlassFishと他のサーバーの比較」と「Issuesの消化状況を見ることで、コミュニティの盛況振りが解るよ」という内容に絞って発表させて頂きました。

加藤田 益嗣 (@den2sn) 氏「GlassFishを4年間使ってきて思う事」

資料:http://prezi.com/su961lcsfymk/glassfish-javaee/
blog:http://d.hatena.ne.jp/den2sn
GlassFishの基本的な情報を丁寧にお話されていました。
特に4年間使われていたということで、V2からV3に移行する際の変更点(やりたいこと)に言及されていたのがためになりました。

久保 智 (@megascus) 氏「JavaEE6 First Application」

資料:http://www.slideshare.net/SatoshiKubo1/javaee6-first-application-glassfishjp
NetBeans+GlassFishな開発環境で、NetBeansの自動生成を利用したアプリケーションを作成。どのようなコードが生成されるかを具体的に説明しつつ、最終的に投げ飛ばしてました:)
NetBeansの自動生成は結構前に試したことがあるのですが、PagingHelperまで生成されるのは思いもよらなかった。。

久保田 (@sugarlife) 氏「Glassfishと他の (OSS) APP Server の取り巻く状況の比較」

資料:http://www.slideshare.net/YujiKubota/application-server-13194633
自分の発表です。JIRAのダッシュボードやレポートをあれこれ弄ってグラフを表示させて調べていました。JIRAが有料版(?)じゃないとダッシュボードの共有できない(誰でも見ることが出来ない)のですが、GlassFish(java.net)のみ共有できるタイプだったので、以下から見ることが出来ます。
JIRA:http://java.net/jira/secure/Dashboard.jspa?selectPageId=10311
実際はバグの傾向等を調べる上で、上のグラフ以外にも色々表示させていたのですが、JIRAの操作に慣れていなためうっかり保存するのを忘れてました。。
また、Tomcatについて喋ってる間、表情がそれまでと変わっていたり、頷かれた方が多かったりと、やっぱり思うところはあるよねと感じましたw

蓮沼 賢志 (@btnrouge) 「GlassFishユーザー認証ワンポイントレッスン」

blog:http://www.coppermine.jp/documents/programming/
ワンポイントレクチャー。GlassFishのユーザー認証機能の利用方法について具体的に説明がされていました。資料は公開されていません(?)が、blogに詳細が掲載されています。
特に、JAASが推奨できないというのは知らなかったのでとてもためになりました。

追記(2012/6/17)
資料が公開されました。
資料:http://www.coppermine.jp/documents/programming/2012/06/presentation-20120604.html
また、blogのユーザ認証記事はこちらとなります:http://www.coppermine.jp/releases/20120604/index.html

Toshiaki Maki (@making) 氏 「Pure Java EE or Spring?」

資料:http://www.slideshare.net/makingx/pure-javaee-orspring
blog:http://blog.ik.am/
Javaフレームワークは宗教論争だ!ファイッ!!
色々とぶっちゃけ話が展開されていて聞いていて面白かったです。少数精鋭なら好きなものを使えば良いよ。慣れているのを使えば良いよという結論はうなずけました。
EJB2時代はSpringがJavaEEをdisり、今はJavaEEがSpringをdisる傾向がちらほら見えるけど、結局自分が使いやすいのを使えたら良いですね。

終わりに

勉強会主催者であり発表の機会を頂いた@btnrougeさん、ならびに会場係等の開催関係者の皆様ありがとうございました。
花粉症で喉を痛めてたので懇親会は残念ながら不参加でしたが、また是非次の機会に:)

Google's Guava Libraries で EventBus

この記事は Java Advent Calendar 2011 の10日目です。wikiばかりで全くblogを書かないので企画の力に頼らせていただきました!
昨日の記事 << | >>明日の記事

今日はGoogleJavaユーティリティライブラリであるGuava Librariesの最新版 10.0.1 で、新たにパッケージごと追加されたAPIであるEventBusについて紹介します。このAPIJavaDocに@Betaアノテーションが付いており、ベータ機能であるため、将来的に内容が変更される可能性があります。

EventBusって?

レイヤー分割されたコンポーネントで構成されたシステムでは、上位層から下位層を呼び出す場合はメソッドを直接呼出し、下位層から上位層を呼び出す場合は相互依存を避けるためイベント通知の機構(publish-subscribeモデル、コールバック)を利用することが多くあります。この機構としてはオブジェクトの内部状態を観察するObserverパターンが著名です。
このような機構のJavaによる実装としては、Observerパターンの実装(java.util.Observerjava.util.Observable)や、SWTに代表されるイベント通知モデルの作りこみがあります。しかし、継承や実装の制約、イベント通知種類(ボタンクリック、スライドバー移動...)ごとのリスナーの管理が必要など多大な手間が懸かります。また、同一レイヤ間の呼び出しにも、疎結合を保つためにイベント通知機構を利用したいと考える時があります。
EventBusとは、このような publish-subscribe モデルをシンプルに実装する1つの手段です。過去には、JavaBeansの連携手法の1つとしてhttp:/eventbus.org で公開されていたと聞いていたのですが、消えてるだと...? 多分こちら(java.net/projects)に移られたのだと思いますが、同じものかは解りません。

長々と書きましたが、纏めるとシンプルにイベント駆動が書けます。一番新しいGoogleさんのEventBusならかなり簡単に書けるのではないだろうか?!というわけで試してみました。

EventBus@Guava Libraries

シンプルなサンプル

簡単に何かしらの文章を発行するイベントの駆動を書いてみます。

イベントを受け取るクラスとメソッド(event handler)は以下のように書けます。

import com.google.common.eventbus.Subscribe;
public class Subscriber {
	@Subscribe
	public void getMessage(String msg){ 
		// @Subscribeのメソッドは必ずpublic, 引数は1つのみ。
		System.out.println("I got a message:" + msg);
	}
}

@Subscribeアノテーションを使うことでevent handlerとなる公開メソッドを実装することができます。イベントである引数は必ず1つのみであることに注意が必要です。このevent handlerメソッドを持つクラスを登録し、イベントを流すのは次のように書きます。

import com.google.common.eventbus.EventBus;
public class Publisher {
	public static void main(String[] args) {
		EventBus bus = new EventBus();
		// EventBusにSubscribe(event handler)を登録。
		bus.register(new Subscriber());
		// EventBusにイベントを流す。
		bus.post("Java Advent Calendar [12/10]");
	}
}

Publisherを実行すると、I got a message:Java Advent Calendar [12/10]と出力されます。

DeadEvent

EventBusに流したイベントがどこにも受け取られなかった場合、DeadEventとして再度流されます。このDeadEventを受け取るSubscriberを実装することで、誰にも受け取られなかった悲しいイベントを受け取ることができます。

import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;
public class DeadEventSubscriber {
	@Subscribe
	public void getDeadEvent(DeadEvent event){
		System.out.println("I got a DeadEvent:" + event.getEvent().toString());
	}
}

先ほどのPublisherにこのSubscriberを登録して、誰にも受け取られないイベントを流してみます。

import com.google.common.eventbus.EventBus;
public class Publisher {
	public static void main(String[] args) {
		EventBus bus = new EventBus();
		bus.register(new Subscriber());
		bus.register(new DeadEventSubscriber()); // 追加
		bus.post("Java Advent Calendar [12/10]");
		bus.post( new Integer(0) ); // 追加
	}
}

実行すると、

I got a message:Java Advent Calendar [12/10]
I got a DeadEvent:0

と、出力されます。デバッグやエラー処理に利用することができるので便利ですね。

その他

非同期に実行するにはAsyncEventBusを利用することで実現できます。

おわりに

Guava LibrariesのEventBusを使ってみました。思ったよりもかなり簡単にイベント駆動が書けますね!
実際に運用するには、Subscriberに親子関係にあるイベントを受け取るようなevent handlerメソッド(getMessage(String)とgetMessage(Object)など)を複数書いたらどうなるんだ(環境依存..?)とか、AsyncEventBusの使い方、スレッドセーフに記述するにはどうするのか、性能は?等、調べるところは多いのですが、後々機会があれば書いていきたいと思います。

以上、Java Advent Calendar 10日目でした!次はs_kozakeさんの番です!

JavaOne 2011 1day

Technical Keynote

  • Doug Sommer / Intel の講演
    • Registration中のため聞けず
  • Oracle NoSQL Database
    • Ashok Joshi
    • Java Implements
    • Oracle coherenceとの違いは聞けず
  • Java SE 7
    • Mark ReinHold
    • Project Coin, InvokeDynamic, Fork/Join Framework
    • Coin : diamond, string switch, multi catch, try with resources
    • InvokeDynamic : Churls Nutter より JRuby の速度向上1について
      • Oracleと強調してJRuby on JDK6より2-3倍の速度向上
    • Fork/Join Framework : 図と一緒にアーキテクチャの説明。
  • Java SE 8
    • Project Lambda, Jigsaw
    • Lambda : Iterableインターフェースにfilter, map, reduce追加。
    • Jigsaw : モジュール依存関係をmodule-info.java内で必要なjarとversionを書くことで動的に整理
      • module-info.javaからパッケージ(jar, jmod, rpm, dev,…)の作成も可能
      • セッション紹介でJVMで動くJavaScriptの開発 Project Nashornの紹介も。
  • Java SE 9
    • Idea
      • Self-Tuning JVM
      • Improved Native Integration
      • Big Data
      • Reification
      • Tail Calls/Continuations
      • Meta-Object Protocol
      • Multi-Tenancy
      • Resource Management
      • Heterogeneous Compute Modules
  • Java FX 2.0
    • Why Java FX? -- need Cross Platform
    • JavaFX 2.0 GA release
    • Developer Preview for Mac OSX
    • JavaFX Schene Builder EA release
    • ここからDemo
      • WEBブラウザ(Chrome?)上で動くApplet
      • 3D Rendering
      • Motion Capture by WebCam
  • Java EE 7
    • Cloud - for Public, Private, Hybrid, PaaS
    • Roleの大量追加
      • 追いかけられたのだけで @DataSourceDefinition, @JMSConnectionFactory, @JMSDefinition, @ConnectionService (Connectorかも)
    • Multitenancy - Multi Tenant対応。特定テナント向けにインスタンスをカスタマイズする。
    • GlassFishの方よりデモ。JSF + EJB + JPA + ServiceMetadata
      • GlassFishのコンソールより、war upload, configuration (loadbalancer, javaee instance num, database)
      • コンソール(ウィザード)上から複数ノード/インスタンスの管理ができるということが売り?インスタンスは負荷に応じて動的に増減する。
    • 2012 Q3 にリリース目標。
  • Java ME
    • 時間過ぎたため退席。
    • TLを見ると、時間過ぎて退席者が多かったようだ。

Google Developer Days 2011 JapanのDevQuizの分野別クイズのうち、Web Gameの解答を晒してみる。
ソースをさっと見ると解答送信以外では通信をしていないようなので、単純に解答を送信するようにした。

// カードの中身を引っ張り出す
var scripts = document.getElementsByTagName("script"); 
var cards = scripts.item(2).innerHTML.split("[")[1].split("]")[0].split(",");
// カードを内容順にソートする
var holder = new Array();
for ( var i=0; i<cards.length; i++ ) {
  holder.push({key:i,card:cards[i]});
}
holder.sort ( function(a,b){return a.card > b.card ? 1 : -1;} );
// 答えを書く
var answer = new Array(cards.length);
for ( var i=0; i<cards.length; i+=2 ) {
  answer[holder[i].key] = holder[i+1].key;
  answer[holder[i+1].key] = holder[i].key;
}
// 答えを送る
var solver = document.getElementById("solve");
solver.answer.value =answer.join(",");
solver.submit();

スライドパズルはA* + 双方向と幅優先探索 + 双方向で実装したところ、2時間程度で300問程度しか解けず。
もともとそんなものだろうと予測していたので、反復深化深さ優先探索を実装して、実行しようとしたところで時間切れ。
ということで結果として103点でした。あまり時間が取れなかったこともありますが、短い時間で効率的に問題を解かねばと反省しきり。