2013/12/18

CQRSの和訳

DDDとCQRSについて

DDD (Domain Driven Design = ドメイン駆動設計)が世間に知られるようになってきましたが、今度はDDDをさらにスケーラビリティにするCQRS (Command Query Responsibility Segregation = コマンドクエリ責務分離)が出てきました。

DDD提唱者の英語本の和訳版「エリック・エヴァンスのドメイン駆動設計」がAmazonにありますが、非常に分厚く高価です。概要をまとめた資料が「Domain Driven Design(ドメイン駆動設計) Quickly 日本語版 - InfoQ」から入手できます。

CQRSはデータベース設計とイベントソーシングも含めた壮大なWebアプリケーションのアーキテクチャですが、日本語の資料がまだ少ないです。CQRSを適用したアプリケーションを構築できるインフラがWindows Azureにあるようですが、Java EEにはまだ無いようです。

Greg Young流CQRSの和訳版

DDDもCQRSもスケールが大きな設計手法で、容易に理解できるものではありませんが、理念や概要を知っておくだけでも、かなり見えてくるものがあると思います。というわけで、Greg Young氏著の「CQRS Documents by Greg Young」を和訳しました。Greg Young氏の許可をいただけましたので、公開します。拙者は英語が苦手なので、和訳内容に誤りや不適切な表現が随所にあることをご了承ください。
翻訳内容の訂正や指摘などございましたら、ご教示いただけると嬉しいです。修正して入れ替えます!!

2013/06/22

JMS 2.0チュートリアル with NetBeans 7.3.1 and GlassFish4


JMS 2.0チュートリアル with NetBeans 7.3.1 and GlassFish4
本チュートリアルは、「NetBeansおよびGlassFishを使用した、簡単なJava Message Service(JMS)プロデューサの作成」を参考にして作成しました。
JMSは一般的には、異なるJava EEアプリケーション間のコミュニケーション手段として使いますが、ここでは、1つのWebアプリケーションでJMS 2.0のキューを利用したメッセージ送信とメッセージ受信の両方を実装します。


【目次】
  1. Webアプリケーションプロジェクトの作成
  2. メッセージ送信処理の実装
    1. メッセージ送信するJSF管理対象Beanの作成
    2. メッセージ送信するJSFページの作成
  3. メッセージ受信処理の実装
  4. 実行してみる

Webアプリケーションプロジェクトの作成

以下の手順でWebアプリケーションのプロジェクトを作成します。
  1. NetBeansのメニューバーから、[ファイル] > [新規プロジェクト]を実行します。

  2. [新規プロジェクト]画面の[1. プロジェクトを選択]ステップにて、
    [カテゴリ]で[Java Web]、[プロジェクト]で[Webアプリケーション]を選択し、[次 >]を押下します。


  3. [新規 Webアプリケーション]画面の[2. 名前と場所]ステップにて、[プロジェクト名]に[JMSSample]を入力し、[次 >]を押下します。
    なお、[プロジェクトの場所]は任意です。


  4. [新規 Webアプリケーション]画面の[3. サーバと設定]ステップにて、
    次の画面のようになっていることを確認し、[次 >]を押下します。


  5. [新規 Webアプリケーション]画面の[4. フレームワーク]ステップにて、[JavaServer Faces]を選択し、「終了]を押下します。
以上で、Webアプリケーションのプロジェクト作成は完了です。

メッセージ送信処理の実装

メッセージを送信する部品として、JSFページとJSF管理対象Beanの2つを作成します。
JSFページでメッセージを入力して送信を実行したら、JSF管理対象BeanでメッセージをJMSキューに入れるという処理になります。
なお、JMSキューではメッセージ送信部品をProducerと呼びます。

メッセージ送信するJSF管理対象Beanの作成

以下の手順で、JSFページで入力したメッセージを取り出して、JMSキューに入れるJSF管理対象Beanを作成します。
なお、JSFページは後で作成します。
  1. [プロジェクト]の[ソース・パッケージ]を右クリックして、ポップアップメニューから[新規] > [その他]を実行します。
  2. [新規ファイル]画面の[1. ファイル・タイプを選択]ステップにて、
    [カテゴリ]で[JavaServer Face]を、[ファイル・タイプ]で[JSF管理対象Bean]を選択し、[次 >]を押下します。
  3. [New JSF管理対象Bean]画面の[2. 名前と場所]ステップにて、
    [クラス名]に[MessageProducerBean]、[パッケージ]に[jms2]を入力し、[スコープ]で[request]を選択し、[終了]を押下します。
  4. 以上のウィザードで生成されたjms2.MessageProducerクラスをプログラミングします。
    messageフィールドとそのgetter/setterを作成し、sendメソッドを入れます。
    (赤字がプログラミングする部分)
    package jms2;
    
    import javax.inject.Named;
    import javax.enterprise.context.RequestScoped;
    
    @Named(value = "messageProducerBean")
    @RequestScoped
    public class MessageProducerBean {
        private String message;
    
        public MessageProducerBean() {
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public void send(){
        }
    }
  5. sendメソッドの閉じかっこ「}」の後ろで[Alt]+[Insert]キーを押下して、ポップアップメニューから[JMSメッセージを送信...]を実行します。
  6. [JMSメッセージを送信]画面の、[プロジェクトの送信先]で[追加]を押下して[jms/queue1]を追加し、[OK]を押下します。
  7. jms2.MessageProducerクラスにコードが自動挿入され、次の内容になります。
    また、[サーバー・リソース]フォルダ配下の[glassfish-resources.xml]にも「jms/queue1」などが自動挿入されます。
    (赤字が自動挿入されたコード)
    package jms2;
    
    import javax.annotation.Resource;
    import javax.inject.Named;
    import javax.enterprise.context.RequestScoped;
    import javax.inject.Inject;
    import javax.jms.JMSConnectionFactory;
    import javax.jms.JMSContext;
    import javax.jms.Queue;
    
    @Named(value = "messageProducerBean")
    @RequestScoped
    public class MessageProducerBean {
        @Resource(mappedName = "jms/queue1")
        private Queue queue1;
        @Inject
        @JMSConnectionFactory("java:comp/DefaultJMSConnectionFactory")
        private JMSContext context;
        private String message;
    
        public MessageProducerBean() {
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public void send(){
        }
    
        private void sendJMSMessageToQueue1(String messageData) {
            context.createProducer().send(queue1, messageData);
        }
    }
  8. sendメソッドをプログラミングします。
    (赤字がプログラミングする部分)
        public void send(){
            FacesContext facesContext = FacesContext.getCurrentInstance();
            sendJMSMessageToQueue1(message);
            FacesMessage facesMessage = new FacesMessage("Message sent: " + message);
            facesMessage.setSeverity(FacesMessage.SEVERITY_INFO);
            facesContext.addMessage(null, facesMessage);
        }
    
  9. jms2.MessageProducerBeanクラスを保存します。
以上で、jms2.MessageProducerBeanクラスの作成は完了です。

メッセージ送信するJSFページの作成

[Webページ]フォルダ配下の[index.xhtml]を次の内容になるようにプログラミングして、保存します。
(赤字がプログラミングする部分)
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <title>JMS Message Producer</title>
    </h:head>
    <h:body>
        JMS Message Producer
        <h:form>
            <h:outputLabel value="Message: " for="message"/>
            <h:inputText id="message" value="#{messageProducerBean.message}"/>
            <h:commandButton value="Send Message" action="#{messageProducerBean.send}"/>
            <h:messages globalOnly="true"/>
        </h:form>
    </h:body>
</html>

メッセージ受信処理の実装

メッセージを受信する部品として、MDB(Message Driven Bean)を作成します。
今回作成するのは、JMSキューからメッセージを取り出して、コンソールに出力するだけの簡単なMDBです。 なお、JMSキューではメッセージ受信部品をConsumerと呼びます。
  1. [ソース・パッケージ]の[jms2]パッケージを右クリックして、ポップアップメニューから[新規] > [その他]を実行します。
  2. [新規ファイル]画面の[1. ファイル・タイプを選択]ステップにて、
    [カテゴリ]で[エンタープライズJavaBeans]を、[ファイル・タイプ]で[メッセージ駆動型Bean]を選択し、[次 >]を押下します。
  3. [New メッセージ駆動型Bean]画面の[2. 名前と場所]ステップにて、
    [EJB名]に[MessageConsumerBean]を入力し、[次 >]を押下します。
  4. [新規ファイル]画面の[3. アクティブ化構成プロパティ]ステップでは、そのまま[終了]を押下します。
  5. jms2.MessageConsumerBeanクラスのonMessageメソッドをプログラミングします。
    (赤字がプログラミングする部分)
    @Override
    public void onMessage(Message message) {
        try {
            TextMessage tm = (TextMessage) message;
            System.out.println("Consumed message: " + tm.getText());
        } catch (JMSException ex) {
            ex.printStackTrace(System.err);
        }
    }
  6. jms2.MessageConsumerBeanクラスを保存します。

実行してみる

  1. [JMSSample]を右クリックし、ポップアップメニューから[実行]を実行します。
  2. ブラウザでJSFページが表示されます。
    [Message]に任意のメッセージを入力して[Send Message]を押下します。
  3. NetBeansのコンソールに、次が出力されていたら成功です。
以上、簡単なJMS 2.0のチュートリアルでした。
JMS 1.1までの開発経験がある方にとっては、プログラミング量がかなり減ったことがわかっていただけるでしょう。

2013/05/22

Java™ 7でのガーベジコレクションの選択

Java 7のガーベジコレクションの入り口

Java 7のガーベジコレクションの仕様や実装を調べるなら、次の2つが入り口となるでしょう。

Java 7でガーベジコレクションを選択する方法(6種類)

Java 7ではエルゴノミクス機能により、自動的にガーベジコレクション(GC)を決定します。
エルゴノミクス機能による決定を無効にして自分でGCを選択したい場合、次の6種類のオプションの指定方法があります。

  • -XX:+UseSerialGC
  • -XX:+UseParallelGC
  • -XX:+UseParallelGC -XX:+UseParallelOldGC
  • -XX:+UseConcMarkSweepGC
  • -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode
  • -XX:+UseG1GC
-XX:+UseSerialGC
New世代領域、Old世代領域ともに、GCを単一スレッドで実行。
-XX:+UseParallelGC
New世代領域では、GCを複数スレッドで実行。
Old世代領域が不足したら、Full GCを単一スレッドで実行。
-XX:+UseParallelGC -XX:+UseParallelOldGC
New世代領域では、GCを複数スレッドで実行。
Old世代領域が不足したら、Full GCを複数スレッドで実行。
-XX:+UseConcMarkSweepGC
New世代領域は-XX:+UseParallelGCと同様の処理。
Old世代領域でCMS GCを実行。
CMS GCは、アプリケーションスレッドと並列してGCを実行することにより、Stop the worldを減らすのが狙い。
CMS GCはコンパクションを行わないので、CMS GCでもメモリが不足する場合は、フォールバックとしてFull GCを実行。
-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode
-XX:+UseConcMarkSweepGCをインクリメンタル・モードで実行。
インクリメンタルモードの詳細は未調査だが、Old世代領域のGCを少しずつ実行するイメージ)
Oracleのドキュメントでは「i-cms」で説明されています。
-XX:+UseG1GC
Java 7新機能のG1 GCを実行。
Javaヒープを細かく細分化し、New世代領域やOld世代領域などを割り当てる方式。
詳細は、上の「Getting Started with the G1 Garbage Collector」を参照。

なお、Full GCはMark Sweep and Compaction方式です。
また「-Xincgc」は上の5つめの「-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode」と同じになります。
なおCMS GCは「Concurrent Mark Sweep [Garbage] Collectorの略称で、CMS GCの説明は「Java SE 6 HotSpot™ Virtual Machine Garbage Collection Tuning」にあります。

Java 7でのSystem.gc()の実行について

十分プログラムを追えていませんが、

  1. \jdk\src\share\classes\java\lang\Runtime.java
  2. \jdk\src\share\native\java\lang\Runtime.c
  3. hotspot\src\share\vm\prims\jvm.cpp
  4. hotspot\src\share\vm\memory\universe.cpp

と追っていくと、

  • GenCollectedHeap::collect(GCCause::Cause cause)と
  • G1CollectedHeap::collect(GCCause::Cause cause)

に辿り着きます。
条件によっては、FullGCを実行するように見えます。「条件によっては」と書いたのですが、基本的にFull GCになると期待しています。
「Java™ Platform, Standard Edition 7 API Specification」のRuntime.gc()のところに

Runs the garbage collector. Calling this method suggests that the Java virtual machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the virtual machine has made its best effort to recycle all discarded objects.

ガベージコレクタを実行します。このメソッドを呼び出すと、Java 仮想マシンは使用していないオブジェクトをリサイクルし、使用中のメモリーをすばやく再利用可能な状態にします。メソッド呼び出しから制御が戻された時点で、仮想マシンは破棄されたオブジェクトをすべて再利用するよう最善を尽くしたことになります。

とあります。Full GCを実行することが明記されていないし、コンパクションを行うかも判りませんが、「-verbose:gc」でログを取るなどしてウラを取ってみたいと考えています。