You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 15 Next »


エンタープライズ・メッセージングは信頼性のあるエンタープライズ・フレームワークに緩やかに結びつけられたコンポーネントとして重要性が増しています。これはエンタープライズ・アプリケーションや異種のエンタープライズ・リソースの急増に重要な要素であり、これらのアプリケーションを密接にまとまったシステムとして統合するニーズを増大させています。長年に渡って、メッセージングやメッセージング指向ミドルウェア (MOM) はこの統合に対して独占的な方法となっていました。標準化された Java メッセージング・サービス (JMS) の導入は、独占的な MOM ベースの製品の多くの不便さを解消します。加えて、J2EE アプリケーション・サーバーにエンタープライズ Java Beans 2.0 から導入されたメッセージ駆動 Beans (MDBs) は投資削減に役立ちます。現在のほとんどの J2EE アプリケーション・サーバーは、いっさいの価値を JMS に加えられた MOM として実行します。J2EE 1.4 認定のアプリケーション・サーバーとして、Apache Geronimo はオープン・ソースでよく成長したメッセージング・フレームワークの一つである ActiveMQ を統合して JMS のサポートをしています。この文章では Geronimo と ActiveMQ を使ってローカルとリモート両方の環境におけるエンタープライズ・アプリケーションのシナリオでの JMS の利用方法を示します。

このサンプル・アプリケーションでの会社は、ある物品を小売市場、卸売市場という違う市場の両方で販売しています。このアプリケーションでのすべての注文は顧客への配送前に会社の営業担当社員によって認証されます。卸売市場では、この会社はすべての国に渡って仲介者へ販売します。彼らは一度にまとめて注文を送ってきます。これを委託販売品と呼びます。エンド・ユーザーはこの会社のウェブ・サイトを利用して物品を注文し、仲介者は委託販売品を店舗でインストールした特別なソフトウェアを利用して送付します。すべての委託販売品は営業担当社員が扱う前にこの会社のゼネラル・マネージャーによって承認されます。

これは、委託販売品と注文の依頼を非同期で処理する典型的な JMS を利用したアプリケーションです。

この文章を読むと、メッセージ・キュー、Geronimo/ActiveMQ 環境でのコネクション・ファクトリー、簡単にエンタープライズ・アプリケーションにおける異なる種類のアプリケーションを利用しているメッセージの送受信の定義ができるようになるでしょう。

この文章は以下のセクションで構成されています。 

  • Geronimo/ActiveMQ 環境での JMS の概略
  • アプリケーションの概略
  • サンプル・アプリケーションの構成、ビルド、デプロイ
  • サンプル・アプリケーションのテスト
  • まとめ

Geronimo/ActiveMQ 環境での JMS の概略

Geronimo サーバーは JMS サーバーとアプリケーション・コンポーネントを持っていて、JMS リソースにコネクション・ファクトリーやトピックス、キューなどのように接続できます。また、この JMS サーバーはメッセージ・ブローカーとしても知られています。Geronimo がデフォルトでサポートしているメッセージ・ブローカーは ActiveMQ です。これは十分に成熟した機能豊富な JMS プロダクトですので、通常は変更する必要はありません。この実装では組み込みの Derby データベースをメッセージ永続化機能のために利用しています。

ActiveMQ はさまざまなトランスポート(例えば TCP、SSL、UDP、マルチキャスト、内部 JVM、NIOなど)とクライアント相互処理(例えばプッシュ、プル、パブリッシュ/サブスクライブ)をサポートします。Geronimo での ActiveMQ は、JMS メッセージを処理する EJBs である MDBs をサポートします。ActiveMQ は Geronimo の J2EE 仕様の機能を JMS アプリケーションに保持させ、例えば JSP やサーブレットや EJB に JMS を利用させることができるようになります。Geronimo はこの JMS API を抽象レイヤーに実装しているので、いずれの JMS プロバイダーもサポートします。J2EE コネクター (JCA) 仕様をサポートすることでこの機能が含まれています。JCA 1.5 の詳細仕様により、アプリケーション・サーバーとドライバーの間に ActiveMQ(リソース・アダプター)による決まりごとがあります。Geronimo にデプロイしたアプリケーションは、このリソース・アダプター (RA) を通じてのみ ActiveMQ メッセージ・ブローカーへ接続します。

アプリケーションの概略

Order processing アプリケーションは2つのメッセージ・キューを定義して注文や委託販売品を受信しています。注文リクエストはこの会社のウェブ・アプリケーション経由で生成され、送信されます。注文リクエストが注文キューで受信されると、MDB が処理開始となります。これらのリクエストをサーバー・リポジトリーに保存することによって、注文リクエストは次の段階へすすみます。これらの保存された注文リクエストは後でその会社の社員によって処理されます。

この会社の販売仲介者は委託販売品送信アプリケーションを利用して、委託販売品注文(まとまった注文)を自分の所から送信します。まず、XML ファイルに注文情報を準備し、これをアプリケーションのパラメーターとして渡します。委託販売品送信アプリケーションは XML ファイルの中身(注文依頼)を読み込み、委託販売品キューに送信します。この会社のゼネラル・マネージャーは委託販売品受注アプリケーションを利用して委託販売品依頼を確認します。委託販売品の依頼を委託販売品キューが受信すると、委託販売品受信リスナー・アプリケーションがゼネラル・マネージャーのコンピューターにこれらのリクエストをダウンロードします。彼はこれを承認し、営業担当社員に回して次の段階へ進みます。

アプリケーションの内容

注文処理アプリケーションのコアは、アプリケーション・サーバーに EAR としてデプロイされます。以下に EAR の内容の概略を示します。

|-Order.ear
   |- geronimo-activemq-ra-2.0-SNAPSHOT.rar
   |- jms-mdb-sample-ejb-2.0-SNAPSHOT.jar
       |-META-INF
           |- openejb-jar.xml
   |- jms-mdb-sample-ejb-2.0-SNAPSHOT.war
       |- index.jsp
       |- error.jsp
       |- WEB-INF
           |- web.xml
           |- classes
   |- META-INF
       |- application.xml
       |- geronimo-application.xml

MDB の実装

ejb-jar.xml ファイル内に MDB の宣言をする代わりに、メッセージ駆動 Bean には @MessageDriven アノテーションを利用します。さらなる情報をアノテーションに与えることによって、送信先(今回はキューに入れる)を探して処理する方法がわかります。この MDB は 'OrderQueue' にメッセージを送信する処理をします。終了結果として画面にこのメッセージを表示します。

OrderRecvMDB.java
//
// MessageDrivenBean that listens to items on the
// 'OrderQueue' queue and processes them accordingly.
//
@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
    @ActivationConfigProperty(propertyName="destination", propertyValue="OrderQueue")
})
public class OrderRecvMDB implements MessageListener{

    private static final String ORDER_MGMT_INFO = "order_mgmt.properties";
    private static final String ORDER_REPO = "order.repo";

    public OrderRecvMDB() {
    }

    public void onMessage(Message message) {
        TextMessage textMessage = (TextMessage) message;
        try {
            System.out.println("Order Received \n"+ textMessage.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

このアプリケーションには、OrderQueue でリッスンする MDB があります。openejb-jar.xml では Geronimo に MDB が jms-resources という JMS リソース・グループに所属していることを通知します。これは OrderRecvMDBOrderQueueCommonConnectionFactory 経由でリンクしています。

openejb-jar.xml
<openejb-jar
    xmlns="http://www.openejb.org/xml/ns/openejb-jar-2.1"
    xmlns:naming="http://geronimo.apache.org/xml/ns/naming-1.1"
    xmlns:security="http://geronimo.apache.org/xml/ns/security-1.1"
    xmlns:sys="http://geronimo.apache.org/xml/ns/deployment-1.2">
    <sys:environment>
        <sys:moduleId>
	    <sys:groupId>${pom.groupId}</sys:groupId>
	    <sys:artifactId>${pom.artifactId}</sys:artifactId>
	    <sys:version>${version}</sys:version>
	    <sys:type>jar</sys:type>
        </sys:moduleId>
        <sys:dependencies>
        <sys:dependency>
            <sys:groupId>org.apache.geronimo.configs</sys:groupId>
            <sys:artifactId>activemq-broker</sys:artifactId>
            <sys:type>car</sys:type>
        </sys:dependency>
        </sys:dependencies>
        <sys:hidden-classes/>
        <sys:non-overridable-classes/>
    </sys:environment>
    <enterprise-beans>
        <message-driven>
            <ejb-name>OrderRecvMDB</ejb-name>
            <resource-adapter>
                <resource-link>jms-resources</resource-link>
            </resource-adapter>
        </message-driven>
    </enterprise-beans>
</openejb-jar>

geronimo-application.xmlapplication.xml は EAR の主たるコンポーネントを定義しています。EJB コンポーネントと Web アーカイブ情報は通常これらのファイルにかかれます。この geronimo-application.xmlはまた、JMS キューとそれへ接続するための一般的なキューのコネクション・ファクトリーを定義するセクションも含みます。これは ear に組み込まれた geronimo-activemq-ra.rar のデプロイに利用されます。

geronimo-application.xml
<application xmlns="http://geronimo.apache.org/xml/ns/j2ee/application-1.1">
    <environment xmlns="http://geronimo.apache.org/xml/ns/deployment-1.2">
        <moduleId>
            <groupId>${pom.groupId}</groupId>
            <artifactId>${pom.artifactId}</artifactId>
            <version>${version}</version>
            <type>ear</type>
        </moduleId>
    </environment>
    <module>
       <connector>geronimo-activemq-ra-2.0-SNAPSHOT.rar</connector>
        <connector xmlns="http://geronimo.apache.org/xml/ns/j2ee/connector-1.2">
            <dep:environment xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2">
                <dep:moduleId>
                    <dep:groupId>${pom.groupId}</dep:groupId>
                    <dep:artifactId>jms-resources</dep:artifactId>
                    <dep:version>${version}</dep:version>
                    <dep:type>rar</dep:type>
                </dep:moduleId>
                <dep:dependencies>
                    <dep:dependency>
                        <dep:groupId>org.apache.geronimo.configs</dep:groupId>
                        <dep:artifactId>activemq-broker</dep:artifactId>
                        <dep:type>car</dep:type>
                    </dep:dependency>
                </dep:dependencies>
            </dep:environment>
            <resourceadapter>
                <resourceadapter-instance>
                    <resourceadapter-name>jms-resources</resourceadapter-name>
                    <nam:workmanager xmlns:nam="http://geronimo.apache.org/xml/ns/naming-1.2">
                        <nam:gbean-link>DefaultWorkManager</nam:gbean-link>
                    </nam:workmanager>
                </resourceadapter-instance>
                <outbound-resourceadapter>
                    <connection-definition>
                        <connectionfactory-interface>javax.jms.ConnectionFactory</connectionfactory-interface>
                        <connectiondefinition-instance>
                            <name>CommonConnectionFactory</name>
                            <implemented-interface>javax.jms.QueueConnectionFactory</implemented-interface>
                            <implemented-interface>javax.jms.TopicConnectionFactory</implemented-interface>
                            <connectionmanager>
                                <xa-transaction>
                                    <transaction-caching/>
                                </xa-transaction>
                                <single-pool>
                                    <match-one/>
                                </single-pool>
                            </connectionmanager>
                        </connectiondefinition-instance>
                    </connection-definition>
                </outbound-resourceadapter>
            </resourceadapter>
            <adminobject>
                <adminobject-interface>javax.jms.Queue</adminobject-interface>
                <adminobject-class>org.apache.activemq.command.ActiveMQQueue</adminobject-class>
                <adminobject-instance>
                    <message-destination-name>OrderQueue</message-destination-name>
                    <config-property-setting name="PhysicalName">OrderQueue</config-property-setting>
                </adminobject-instance>
            </adminobject>
            <adminobject>
                <adminobject-interface>javax.jms.Topic</adminobject-interface>
                <adminobject-class>org.apache.activemq.command.ActiveMQTopic</adminobject-class>
            </adminobject>
        </connector>
    </module>
</application>
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd" version="5">
  <description>Geronimo Sample EAR for jms-mdb-sample</description>
  <display-name>Geronimo Sample EAR for jms-mdb-sample</display-name>
  <module>
    <connector>geronimo-activemq-ra-2.0-SNAPSHOT.rar</connector>
  </module>
  <module>
    <ejb>jms-mdb-sample-ejb-2.0-SNAPSHOT.jar</ejb>
  </module>
  <module>
    <web>
      <web-uri>jms-mdb-sample-war-2.0-SNAPSHOT.war</web-uri>
      <context-root>/order</context-root>
    </web>
  </module>
</application>

クライアントの実装

OrderSenderServlet.java サーブレットはウェブのフォームを分析し、メッセージを生成し、CommonConnectoryFactory 経由で OrderQueue へメッセージを送信します。

Geronimo は @Resource の 'mappedName' 属性を無視することに注意してください。その代わりにアノテーションは 'name' を使ってください。

OrderSenderServlet.java
public class OrderSenderServlet extends HttpServlet {

    @Resource(name="CommonConnectionFactory")
    private ConnectionFactory factory;

    @Resource(name="OrderQueue")
    private Queue receivingQueue;

    public void init() throws ServletException {
        super.init();
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        manageOrders(req,res);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        doGet(req,res);
    }

    private void manageOrders(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException{
        String path = "/error.jsp";
        Connection connection = null;
	MessageProducer messageProducer = null;
	Session sess = null;
	try
        {
	    String customerId = req.getParameter("customerId");
	    String orderId = req.getParameter("orderId");
	    String qty = req.getParameter("quantity");
	    String model = req.getParameter("model");

	    if(!customerId.equals("") && !orderId.equals("") && !qty.equals("")){
	    	System.out.println("Start Sending Order Request");
	    	// creating online order request
	    	String orderRequest = "<Order orderId=\""+orderId+"\" custId=\""+customerId+"\" qty=\""+qty+"\" model=\""+model+"\"/>" ;
		connection = factory.createConnection();
		sess = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		path = "/index.jsp";
		TextMessage msg = sess.createTextMessage("<OrderId=" + orderId + " CustomerId=" + customerId
							+ " Quantity=" + qty + " Model=" + model + ">" );
		messageProducer = sess.createProducer(receivingQueue);
		messageProducer.send(msg);
		System.out.println("Order Request Send");
	    } else{
	    	String error = "";

	    	if(customerId.equals("")){
	    	    error = "Customer Id Cannot be Empty";
	    	}else if(orderId.equals("")){
	    	    error = "Order Id Cannot be Empty";
	    	}else if(qty.equals("")){
	    	    error = "Quantity Cannot be Empty";
	    	}
	    	req.setAttribute("error",error);
	    }
        } catch (Exception e)
        {
            System.out.println("Error "+e);
            e.printStackTrace();
        } finally {
            try {
                if(messageProducer != null) messageProducer.close();
                if(sess != null)sess.close();
                if(connection!= null)connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
        getServletContext().getRequestDispatcher(path).forward(req,res);
    }
}

このアーカイブの web.xml では、キューのコネクション・ファクトリーとキューとの関連する構成を記述しています。これはリソースをローカル環境で参照するためには必須です。

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <description>JMS Servlet Sample</description>
    <servlet>
        <servlet-name>OrderSenderServlet</servlet-name>
        <servlet-class>org.apache.geronimo.samples.order.OrderSenderServlet</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>OrderSenderServlet</servlet-name>
        <url-pattern>/order</url-pattern>
    </servlet-mapping>

    <resource-ref>
	<res-ref-name>CommonConnectionFactory</res-ref-name>
	<res-type>javax.jms.QueueConnectionFactory</res-type>
	<res-auth>Container</res-auth>
	<es-sharing-scope>Shareable</res-sharing-scope>
    </resource-ref>

    <message-destination-ref>
	<message-destination-ref-name>OrderQueue</message-destination-ref-name>
	<message-destination-type>javax.jms.Queue</message-destination-type>
	<message-destination-usage>Produces</message-destination-usage>
	<message-destination-link>OrderQueue</message-destination-link>
    </message-destination-ref>

    <welcome-file-list>
        <welcome-file>/index.jsp</welcome-file>
    </welcome-file-list>

</web-app>

このウェブ・アプリケーションはサーブレット 2.5 仕様をサポートしていることに注意してください。古いバージョン(2.4)での構成のいくつかは上記 web.xml とは若干違います。

アノテーションがキューやコネクション・ファクトリーの参照解決に利用されるので、 geronimo-web.xml は今回は不要です。

ツールの利用

この注文アプリケーションのデプロイとビルドには以下のツールを利用しています。

Apache Maven 2

Maven はエンタープライズ Java プロジェクト向けの有名なオープンソースのビルドツールです。 ビルド作業の負荷を軽減できるように設計されました。Maven では Ant や他の伝統的な make ファイルなどで利用されるタスク・ベースの手法ではなく、宣言的手法を利用して、プロジェクトの構成や内容が定義されます。このことにより、企業全体で開 発標準を適用することや、ビルド用スクリプトの記述やメンテナンスに必要な時間を削ることの助けとなります。宣言的で、ライフサイクルに基づく手法を使っ ていた Maven 1 は、伝統的なビルド方法よりも、多くの人にとって、根本的な発展となり、さらに Maven 2 はこの点を高めました。Maven 2 は次の URL からダウンロードできます。
http://maven.apache.org

サンプル・アプリケーションの構成、ビルド、デプロイ

以下のリンクから Time Reporting アプリケーションをダウンロードしてください。
jms-mdb-sample

ファイルを解凍すると、jms-mdb-sample ディレクトリーが作られます。

ソース・コード

SVN からサンプルのソースコードをチェックアウトすることができます。

svn checkout http://svn.apache.org/repos/asf/geronimo/samples/trunk/samples/jms-mdb-sample

ビルド

jms-mdb-sample にはすでにデプロイできる ear ファイルが含まれています。ですが、ソースからビルドすることもできます。

コマンド・プロンプトを利用して jms-mdb-sample ディレクトリーへ移動し、mvn install site コマンドを入力するとビルドされます。jms-mdb-sample フォルダーの下に jms-mdb-sample-ear-2.0-SNAPSHOT.ear が作られます。

デプロイ

Order processing サンプル・アプリケーションのデプロイは、JMS リソースのデプロイとほとんど同じです。

  1. Console Navigation パネルから Deploy New を選択してください。
  2. Archive 入力欄に jms-mdb-sample フォルダーの jms-mdb-sample-ear-2.0-SNAPSHOT.ear を読み込んでください。
  3. Install ボタンを押してアプリケーションをサーバーへデプロイしてください。

サンプル・アプリケーションのテスト

サンプル・ウェブ・アプリケーションをテストするには、ブラウザーを開いて http://localhost:8080/order を入力してください。注文管理の Welcome ページが表示されます。ユーザーは注文に必要な情報を入力し、サブミットします。


注文処理の後、コンソール上にメッセージが表示されているでしょう。 

まとめ

この文章では ActiveMQ JMS サーバーを持つ Apache Geronimo の JMS 機能の利用をデモンストレーションしました。JMS 機能を広く利用する仮定的な例となります。

この文章での重要なことは以下の通りです。

  • Geronimo 環境における JMS コネクション・ファクトリーと関連するキューの定義
  • メッセージ駆動 Beans は J2EE コンテナーによって与えられた JMS キュー上でコンポーネントのリスニングをしている
  • No labels