Seasar DI Container with AOP

SMART deploy

目次

SMART deployについて

「SMART deploy」とは、 「HOT deploy」、「COOL deploy」、「WARM deploy」の3種類の総称です。 Seasar2.4から奨励されているパッケージ構成等の規約を守る事によって、設定ファイルを追記する必要なく開発を行うことができます。 初期設定として必要なことは、プロジェクトのルートパッケージとデータベースへの接続情報を指定するだけです。 規約を守らない場合には、デメリットはなく従来のSeasar2.3の機能はそのまま使う事が出来ます。

SmartDeployの種類

SmartDeployにはデプロイするタイミングによってHOT、COOL、WARMの三種類が用意されています。次にそれぞれの説明をしていきます。

HOT deploy

概要

A. HOT deployの概念

HOT deployとは、プログラムソースの変更時にアプリケーションサーバーをRebootしなくてもコンポーネントをオンデマンドでデプロイ/リデプロイできる技術です。

B. HOT deployのメリット

webシステムの開発を行う場合に発生するRebootによる無駄な待ち時間が無くなります。ソースの変更がダイレクトに結果として確認できる為、小刻みな反復による修正作業を行うことが出来ます。その結果、品質が向上します。

C. 注意点

HOT deployを使用する為には、パッケージの構成に沿う必要があります。リクエストが来るたびにコンポーネントをコンテナに登録しなおすため、マルチスレッドでの利用は適していません。

リファレンス

A. 作成ファイル

HOT deployを使用する為には、s2container.dicon(ユーザー任意)にhotdeploy.diconを<include>する必要があります。

B. 設定ファイルの説明

s2container.diconにhotdeploy.diconを<include>します。hotdeploy.diconはS2container内に含まれています。環境による条件付includeを使うと、後に設定ファイルを書き直す必要がなくなります。

例)s2container.diconにhotdeployを使う条件を記述します

<components>
	<include condition="#ENV == 'ut'" path="hotdeploy.dicon"/>
	<include condition="#ENV != 'ut'" path="cooldeploy.dicon"/>
</components>

同じ階層にenv.txtを作成することで,#ENVの値をenv.txt内から取りに行きます。

標準的な環境名としては、次のようなものを用意してありますが、名前は自由に設定することができます。
必ずしもこの環境名に沿う必要はありません。
  ut :単体テスト用
  ct :結合テスト用
  it :総合テスト用
  product :本番用
  batch   :バッチ用

C. 用意されている機能

コンポーネントは自動で登録されます。
リクエストが有る度に、コンテナに必要なコンポーネントを登録しなおします。よって、常に以前の登録内容は破棄されることになります。

COOL deploy

概要

A. COOL deployの概念

COOL deployとは、アプリケーションの起動とともにデプロイを完了する機能です。

B. COOL deployのメリット

初期化時にすべてのコンポーネントをコンテナに登録するため、初期化後のパフォーマンスが向上します。初期化後のパフォーマンスはSMART deployの中で最速となります。

C. 注意点

COOL deployを使用する為には、パッケージの構成に沿う必要があります。全てのコンポーネントをコンテナに登録するため、登録するClassの数が膨大になると、初期化に必要な時間が長くなります。

リファレンス

A. 作成ファイル

COOL deployを使うには、s2container.diconにcooldeploy.diconを<include>する必要があります。

B. 設定ファイルの説明

s2container.diconにcooldeploy.diconを<include>します。
環境による条件付includeを使うと、後に設定ファイルを書き直す必要がなくなります。
例)s2container.diconにcooldeployを使う条件を記述します

<components>
	<include condition="#ENV == 'ut'" path="hotdeploy.dicon"/>
	<include condition="#ENV != 'ut'" path="cooldeploy.dicon"/>
</components>

env.txtに設定を書き込むことで、環境を操作することができます。

C. 用意されている機能

コンポーネントは自動で登録されます。
S2ContainerのInit()のタイミングでコンテナに全てのコンポーネントを登録します。リクエストを受けても登録内容は破棄されません。

WARM deploy

(1) 概要

A. WARM deployの概念

WARM deployは、COOL deployとHOT deployの丁度中間的な特性を持っています。HOT deployの様にオンデマンドでデプロイします。しかし、一度登録したものを破棄しません。登録後はCOOL deployと同じように登録内容を保持します。

B. WARM deployのメリット

登録するコンポーネントの量が膨大なシステムでCOOL deployを使った場合に発生するデメリット(初期化の時間)を押さえることができます。GUIアプリケーションの起動の場合など、最初から全てのコンポーネントを使う必要がない場合に威力を発揮します。

C. 注意点

WARM deployを使用する為には、パッケージの構成に沿う必要があります。HOT deployの様にソースの変更をダイレクトに反映することは出来ません。

(2) リファレンス

A. 作成ファイル

WARM deployを使うには、s2container.diconにwarmdeploy.diconを<include>する必要があります。

B. 設定ファイルの説明

s2container.diconにwarmdeploy.diconを<include>します。
環境による条件付includeを使うと、後に設定ファイルを書き直す必要がなくなります。

例)s2container.diconにWARM deployを使用する設定をする

<components>
	<include condition="#ENV == 'ut'" path="hotdeploy.dicon"/>
	<include condition="#ENV != 'ut'" path="warmdeploy.dicon"/>
</components>

C. 用意されている機能

コンポーネントは自動で登録されます。
リクエストが有る度に、コンテナにコンポーネントを登録します。ただし、既に同じコンポーネントが登録されている場合には登録しません。

パッケージの構成

「Seasar2.4」推奨の規約として、まずはパッケージの構成を説明致します。 推奨のパッケージ構成を利用する事によって、SMART deployが使えるようになります。また、プロジェクトメンバの情報共有の点においても、どのクラスがどのパッケージにあるか探す事が容易になります。 もちろん、推奨のパッケージ構成を使わずに自由に決めることもできます。その場合は、HOT deployは使えませんが、Seasar2.3の自動登録は、そのまま使うことができます。

パッケージ構成の例
examples.aaaa
examples.aaaa.logic
examples.aaaa.logic.impl
examples.aaaa.dao
examples.aaaa.web.xxxx

まずはルートパッケージを定める必要があります。「パッケージ構成の例」では、examples.aaaaがルートパッケージになります。 ルートパッケージ以下には2つのルールがあります。一つは「ルートパッケージ」+「個別のパッケージ」の構成を持つことです。 個別のパッケージには、logic、daoなどがそれに当たります。詳しくは個別パッケージの要素の処で説明いたします。

もう一つはwebパッケージ以下にサブアプリケーションを持つ構成です。ここで言うサブアプリケーションとは、一つのアプリケーションを幾つかの論理的な固まりに分割したものです。ユースケースと読み替えても良いでしょう。 例えば、examples.aaaa.web.xxxxというサブアプリケーションにはxxxxというユースケースをまとめます。


(例)パッケージ構成の2パターン

①ルートパッケージ+個別パッケージ

examples.aaa.logic
⇒examples.aaa : ルートパッケージ
⇒logic : 個別パッケージ

②ルートパッケージ+Web+サブアプリケーション

examples.aaaa.web.xxxx
⇒examples.aaaa : ルートパッケージ
⇒web : サブアプリケーションのルートパッケージ
⇒xxxx : サブアプリケーション

パッケージの要素

次にパッケージの要素をしていきます。 サブアプリケーション固有のものをサブアプリケーション配下に分類し サブアプリケーション共通のものを個別パッケージとして管理しています。

Web

webは、サブアプリケーションのルートパッケージです。

サブアプリ クラスの命名規約 説明
page任意の名前+Pagepageクラスとは、HTMLと1対1に対応するクラスで、画面の情報と、イベント処理のロジックで構成されています。命名規約は、対応するHTMLの名前+Page となります。HTMLのファイル名の最初を大文字にして、拡張子を取り除き、後ろにPageをつけます。HTMLが格納されるディレクトリ名がパッケージ名になります。例えば、view(Viewのルートディレクトリ)/add/addInput.htmlという画面は、examples.aaa.web.add.AddInputPageに対応します。
dao任意の名前+Daopageクラスの固有のdaoです。命名規約は、任意のクラス名の後ろにDaoをつけます。サブアプリケーション固有のDaoを作成するならば、サブアプリケーションの名前+Daoと命名します。
dxo任意の名前+DxoMapとJavaBeans、JavaBeansとJavaBeansといったオブジェクト間のデータの相互変換を定義するインターフェースです。インターフェースにメソッドを書いておくだけで、モデル変換のロジックは、アスペクトが自動生成します。 実装部分を作る必要はありません。
dao.impl,dxo.impl任意のDao,Dxo+Impldao、dxoは通常、実装クラスは必要ありませんが、自動的に変換できないような複雑なロジックは、明示的に実装することもできます。その場合、抽象クラスとし、必要なメソッドだけを実装してください。命名規約は、dao,dxoのインターフェース名+Implとなります。例えば、AaaDxoの場合、AaaDxoImplになります。

HTML、Page、RDBMSのテーブルの関係

HTMLの入出力項目(のid属性) が Pageクラスのプロパティに対応し、Pageクラスのプロパティがテーブルのカラムに対応します。また、HTMLのアクション(ボタン等)(のid属性) が Pageクラスのメソッドに対応します。サブアプリケーション(ユースケース)固有のロジックは、Pageクラスに記述します。複数のサブアプリケーションで共通に使われるロジックは、Logicクラスに記述します。ドメインモデルを使う場合、もう少し複雑な構成になりますが、ここでは、最もシンプルなパターンについて説明します。
ドメインモデルで作成する場合には、service、actionパッケージも含まれます。

ルートパッケージ直下の要素

サブアプリケーションパッケージの中で共通に扱われる要素を個別パッケージとして管理します。 例えば、サブアプリ内のパッケージ内のdaoは、サブアプリ固有のもので、個別パッケージのdaoはサブアプリ全てに共通するような機能を抽出、管理するものとして分類しています。

個別パッケージ クラスの命名規約 説明
dao任意の名前+Daoデータベースにアクセスするためのインターフェースです。インターフェースにメソッドを書いておくだけで、データアクセスのロジックは、アスペクトが自動生成します。実装部分を作る必要はありません。Daoについての詳しい説明はS2Daoをご覧下さい。
dxo任意の名前+Dxoサブアプリケーションの中のDxoとは別に、複数のサブアプリケーションで共通に使われるDxoです。
logic任意の名前+Logicアプリケーションの業務処理を行うインターフェースです。実装クラスはLogic.Implに作成します。エンタープライズシステムの場合、Logicは複雑な構成になります。Pageクラスだけで管理出来ない処理やサブアプリケーション共通の処理を、個別パッケージで管理するために使用します。
validator任意の名前+Validatorアプリケーションで入力されるRDBMSへのデータを検証するクラスです。JSFのvalidatorと同じものです。独自実装が必要な場合に使用します。
converter任意の名前+Converterデータ型の変換フォーマット等、入出力のフィルターを定義するクラスです。JSFのconverterと同じものです。独自実装が必要な場合に使用します。
interceptor任意の名前+Interceptorアスペクトの振る舞いを記述するクラスです。独自のinterceptorを実装する場合に使用します。interceptorについての詳しい説明はS2Aopをご覧下さい。
dto任意の名前+dtoプレゼンテーションのモデルとしてPageクラスの持つデータを保管して、オブジェクト層との相互変換を行う為のオブジェクトの入れ物です。基本モデルを選択した場合、Dtoは使いませんが、必要に応じて使用することは可能です
util任意の名前+Utilアプリケーションで処理されない手続き型の汎用的な処理のクラスです。staticなメソッドの集まりです。
helper任意の名前+Helperアプリケーションで処理されない追加的な処理のクラスです。instanceなメソッドの集まりです。

パッケージ構成サンプル

Seasar2.4の開発を支援するためのツールとしてDolteng(どぅるてん)というeclipseプラグインが用意されています。 Doltengにはscaffoldというプロジェクトのテンプレートを作成する機能が実装されています。 これまでに説明しました、Seasar2.4の命名規約に沿ったパッケージ構成はDoltengによって簡単に作成することができます。

次の例では、Doltengのscaffoldを使って生成されたパッケージ構成を元に、CRUDアプリの解説を行います。 今回の例は、Teeda+Daoの構成で作成されています。 Doltengを使えば、データベースのテーブルから、CRUDアプリを自動生成してくれます。

命名規約

サブアプリ名はテーブル名が小文字で入ります。サブアプリ内のクラス名の先頭にはサブアプリ名が入ります。Pageクラス名には、対応するHtmlファイル名が入ります。
Htmlファイル 内容
empList.htmlEMPテーブルのレコード内容をSELECTで一覧表示する画面
empEdit.htmlEMPテーブルのレコード内容に対してINPUT、UPDATE、DELETEを行う画面
empConfirm.htmlempEditページでレコード内容を変更する時に変更内容の確認表示をする画面

クラスについて

examples.aaaa.web.empパッケージ
クラス名 内容
AbstructEmpPage.javaEMPテーブルの全カラムをフィールドに持っているPageクラス
EmpListPage.javaempList.htmlに対応したPageクラス
EmpEditPage.javaempEdit.htmlに対応するPageクラス
EmpConfirmPage.javaempEdit.htmlに対応するPageクラス
EmpDxo.javaサブアプリ固有のDxo
examples.aaaa.webパッケージ
クラス名 内容
AbstractCrudPage.javaページにまたがる処理のうち、CRUDタイプの管理を行うクラス
CrudType.javaページにまたがる処理のうち、CRUDタイプの宣言クラス

AbstractCrudPage.java

ページにまたがる処理のうち、CRUDタイプの管理を行うクラス
examples.aaaa.daoパッケージ
クラス名 内容
EmpDao.javaEMPテーブルのカラムを関連付けるDaoインターフェース
examples.aaaa.entityパッケージ
クラス名 内容
Emp.javaEMPテーブルのbean

Diconファイルの構成

Seasar2.3ではXmlHellを解消する為の取り組みの一つとしてLessConfigrationに取り組んできました。 その機能として、AutoRegister等の自動登録が代表的なものです。 Seasar2.4では、Convention Over Configurationの考え方により、 LessConfigrationをさらに突き詰めて、設定ファイル自体を書く必要がほとんど無くなりました。

diconファイル名 設定内容
app.diconプロジェクトで任意の設定を行う場合に使用する設定ファイルで、プロジェクトで特別な設定が必要な時にはここに記述を追記します。。
s2container.dicons2containerの環境を読み込む為の設定ファイルで、SMART deployの使用条件をここで定めている。使用条件は条件インクルードを使用して設定します。
jdbc.diconデータベース接続各種設定を行います。
convention.dicon初期設定としてルートパッケージを設定するファイルです。
creator.dicon初期設定としてSMART deployにおいて、自動登録されるコンポーネントのコンポーネント定義を作成するファイルです。SMART deployを使用すると、 規約に基づいてコンポーネント定義が作成されます。 これらのコンポーネント定義を作成する場合に、 org.seasar.framework.container.customizerを実装したクラスを使用します。
customizer.dicon初期設定としてSMART deployにおいて、 自動登録されるコンポーネントのコンポーネント定義をカスタマイズするファイルです。

S2.4では命名規約に沿っていれば、 開発当初に(convention.dicon)のrootPackageNameを指定する事とデータベース接続以外に 設定ファイルに追記する作業はありません。

diconファイルの設定例

convention.dicon

ルートパッケージaaaaaを指定した場合

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
	"http://www.seasar.org/dtd/components21.dtd">
<components>
	<component class="org.seasar.framework.convention.impl.NamingConventionImpl">
		<initMethod name="addRootPackageName">
			<arg>"aaaaa"</arg>
		</initMethod>
	</component>
</components>

creator.dicon

エンタープライズシステムの構成。単純なアプリでは必要なコンポーネントだけ設定する。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
	"http://www.seasar.org/dtd/components21.dtd">
<components>
	<include path="convention.dicon"/>
	<include path="customizer.dicon"/>
	<component class="org.seasar.framework.container.creator.PageCreator"/>
	<component class="org.seasar.framework.container.creator.ActionCreator"/>
	<component class="org.seasar.framework.container.creator.DaoCreator"/>
	<component class="org.seasar.framework.container.creator.DtoCreator"/>
	<component class="org.seasar.framework.container.creator.DxoCreator"/>
	<component class="org.seasar.framework.container.creator.HelperCreator"/>
	<component class="org.seasar.framework.container.creator.LogicCreator"/>
	<component class="org.seasar.framework.container.creator.ServiceCreator"/>
	<component class="org.seasar.framework.container.creator.InterceptorCreator"/>
	<component class="org.seasar.framework.container.creator.ValidatorCreator"/>
	<component class="org.seasar.framework.container.creator.ConverterCreator"/>
</components>

customizer.dicon

creator.diconにおいての、コンポーネントをカスタマイズする場合、ここで設定する。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
	"http://www.seasar.org/dtd/components21.dtd">
<components>
	<component name="defaultCustomizer" 
		class="org.seasar.framework.container.customizer.AspectCustomizer">
		<property name="interceptorName">"aop.traceInterceptor"</property>
	</component>
	<component name="commandAspectCustomizer" 
		class="org.seasar.framework.container.customizer.AspectCustomizer">
		<property name="interceptorName">"aop.traceInterceptor"</property>
		<property name="pointcut">"do.*, initialize, prerender"</property>
	</component>

<!--   省略   -->

	<component name="interceptorCustomizer" 
		class="org.seasar.framework.container.customizer.CustomizerChain">
	</component>
	<component name="validatorCustomizer" 
		class="org.seasar.framework.container.customizer.CustomizerChain">
	</component>
	<component name="converterCustomizer" 
		class="org.seasar.framework.container.customizer.CustomizerChain">
	</component>
</components>

HOT deployサンプルプログラム

頻繁に Seasar 関連プロダクトを取り上げてくださる MYCOM ジャーナルを参照しています。
http://journal.mycom.co.jp/articles/2006/11/16/seasar/

サンプルプログラムで作成するファイルは以下の通り


<ソースファイル>
	org.example.service        <-----------------パッケージ
		MessageService.java    <-----------------Javaソースファイル
		PrintService.java
		
	org.example.service.impl
		MessageServiceImpl.java
		PrintServiceImpl.java
		
	test
		Test.java

<設定ファイル>
	convention.dicon
	creator.dicon
	customizer.dicon
	s2container.dicon
	app.dicon
	

上記作成する必要のあるファイルの内、設定ファイルは準備されているものをそのまま使うことが出来る。

org.example.service(Impl)パッケージに含まれるファイル

MessageServiceImpl.java

package org.example.service.impl;

import org.example.service.MessageService;

public class MessageServiceImpl implements MessageService {
    public String getMessage() {
        return ("Hello HOT deploy");
    }
}

MessageService.java

package org.example.service;

public interface MessageService {
    public String getMessage();
}

PrintService.java

package org.example.service;

public interface PrintService {
    public void print();
}

PrintServiceImpl.java

package org.example.service.impl;

import org.example.service.MessageService;
import org.example.service.PrintService;

public class PrintServiceImpl implements PrintService {
    private MessageService messageService;

    public void setMessageService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void print() {
        System.out.println(messageService.getMessage());
    }
}

testパッケージに含まれるファイル

Test.java

package test;

import org.example.service.PrintService;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;
import org.seasar.framework.container.hotdeploy.HotdeployUtil;

public class Test {
    public static void main(String[] args) throws Exception {
        SingletonS2ContainerFactory.init();
        S2Container container = SingletonS2ContainerFactory.getContainer();

        Class.forName(PrintService.class.getName());
        for (int i = 0; i < 3; ++i) {
            HotdeployUtil.start();
            PrintService printer = (PrintService) container
                    .getComponent("printService");
            printer.print();
            HotdeployUtil.stop();
        }
    }
}
  • main()メソッドでコンテナを初期化。
  • Class.forName()は 無くすとClassCastException が起こります。
  • for()ループ内はHOT deployの効果を確認するための処理。
  • HotdeployUtil#start() ~ stop() までがHOT deployのコンテキスト。

このコンテキスト内では HotdeployClassLoader がスレッドのコンテキストクラスローダーに設定され、新たなクラスはこのクラスローダーにロードされます。 このクラスローダーは HotdeployUtil#stop() で破棄されます。 次の繰り返しになると、また新たにクラスローダーが作成され、クラスも改めてロードされます。 簡単に言うと、これが HOT deploy の全てです。 Web アプリの場合は HotdeployServletFilter に HotdeployUtil#start() と stop() の役割を受け持ちます。 つまり、一つのリクエストが一つのコンテキストになります。


diconファイル

app.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
</components>

プロジェクト任意の環境を設定する

今回は特に設定する必要は無い。

s2container.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="hotdeploy.dicon"/>
</components>

s2containerの環境を設定するdiconファイル

  • S2container内hotdeploy.diconをincludeする。
  • ここで SMART deploy を使用するための設定をする。
  • (hot,cool,warm)deploy.dicon は s2-framework-2.4.x.jar の中に含まれています。
  • 条件インクルードを使うと,env.txt の内容によってSMART deployを切り替えることができます。

convention.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <component class="org.seasar.framework.convention.impl.NamingConventionImpl">
        <initMethod name="addRootPackageName">
            <arg>"org.example"</arg>
        </initMethod>
    </component>
</components>

初期設定として、ルートパッケージを設定する。

creator.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="convention.dicon"/>
    <include path="customizer.dicon"/>

    <component class="org.seasar.framework.container.creator.ServiceCreator"/>
</components>

コンポーネント定義を作成するコンポーネントクリエータを設定する。

今回のサンプルではサービスしか使用しないので,サービス用のクリエータのみを定義しています。

customizer.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <component 
    	name="serviceCustomizer" 
    	class="org.seasar.framework.container.customizer.CustomizerChain"/>
</components>

コンポーネント定義をカスタマイズする、コンポーネントカスタマイザを設定する。

今回のサンプルではサービスしか使用しないので,サービス用のカスタマイザのみを定義しています。

実行結果

DEBUG 2006-11-21 17:16:17,050 [main] BEGIN org.example.service.impl.PrintServiceImpl#print()
DEBUG 2006-11-21 17:16:17,050 [main] BEGIN org.example.service.impl.MessageServiceImpl#getMessage()
DEBUG 2006-11-21 17:16:17,050 [main] END org.example.service.impl.MessageServiceImpl#getMessage() 
: Hello HOT deploy
Hello HOT deploy
DEBUG 2006-11-21 17:16:17,050 [main] END org.example.service.impl.PrintServiceImpl#print() : null
//・・・for()ループの設定により3回繰り返される

testクラスのfor()ループの中にブレークポイントを仕掛けて、実行途中にMessageServiceImpl.java内のメッセージを変更した場合、 HOT deployによって、その変更が実行結果に反映されることを確認できる。