リリース作業は絶対自動化

商用環境でモジュールや設定ファイルを手作業で変更を入れるのはありえん。
変更を入れなくても、あれやって〜これやって〜っていう手作業はありえん。


必要な作業は全てスクリプト化しろ。と。
手順書書く時間があったらスクリプトを書け。と。
スクリプトは商用リリース直前じゃなくて、開発の初期から作ってそれを、すべての試験環境へのリリースで使ってブラッシュアップする。と。

手作業は常に同じことを再現できないけど、スクリプトならできる。リリースは反復作業だから。


スクリプトにすることで、CVSで管理できるようになるし。


手作業オンリーのリリース作業要員を入れ換えるのは恐いけど。
自動化されたリリース作業の要員を入れ換えるのはさほど恐くない。

@ExportでセッションにいれたオブジェクトをInterceptorで使うとき

まず、@Exportでセッションにオブジェクトを設定する。

public class HogeServiceImpl implements HogeService {
  public MogeDto mogeDto;
  public void hoge() {
    mogeDto = ....
  }
  @Export(storage=StorageType.SESSION)
  public MogeDto getMogeDto() {
    return mogeDto;
  }
}

で、以下のようなInterceptorの中で、上記で設定したmogeDtoを取り出して、instanceofでチェックすると、思ったとおりの結果にならない。

public class SessionCheckInterceptor extends AbstractMethodInterceptor {
  private S2Container container;
  public void setContainer(S2Container container) {
    this.container = container;
  }

  public Object invoke(MethodInvocation invocation) throws Throwable {
    Map sessionMap = container.getExternalContext().getSessionMap();
    Object attrValue = sessionMap.get("mogeDto");
    if (attrValue != null) {
      System.out.println(attrValue instanceof MogeDto);        
    }
  }
}

原因は、以下で説明されているHotDeployClassLoaderがらみ。
http://d.hatena.ne.jp/koichik/20061118#1163869222


HotDeployでなくCoolDeployだと、思ったとおりに動くのだけど、やっぱりHotDeployでさくさく開発したい。
で調べてたら、以下で、HotdeployUtil.rebuildValue() ってのがある。と。
https://www.seasar.org/issues/browse/FLEX-16


で、こんな感じで直したら、うまくいった。
これがスマートな解決方法かわからないけど。。

public class SessionCheckInterceptor extends AbstractMethodInterceptor {
  private S2Container container;
  public void setContainer(S2Container container) {
    this.container = container;
  }

  public Object invoke(MethodInvocation invocation) throws Throwable {
    Map sessionMap = container.getExternalContext().getSessionMap();
    Object attrValue = sessionMap.get("mogeDto");
    if (attrValue != null) {
      attrValue = HotDeployUtil.rebuildValue(attrValue);
      System.out.println(attrValue instanceof MogeDto);        
    }
  }
}

このHotDeployUtil.rebuildValue() だけど、ソースを見ると HotDeployが無効になっているときは、何もせずに、引数のオブジェクトをそのまま返すようになっている。
パフォーマンスの問題にもならなそうだし、このまま製品コードに含めてしまっても、特に問題なさげ。

Proxyクラス

S2Flex2のソースを読んで知った。
http://livedocs.adobe.com/flex/2_jp/langref/flash/utils/Proxy.html

プロパティへのアクセスや、メソッドの呼び出しのプロキシを作れる。

こんな感じでProxyを継承しておくと、

  import flash.utils.flash_proxy;
  import flash.utils.Proxy;
  use namespace flash_proxy;
  public dynamic class HogeProxy extends Proxy {
        flash_proxy override function callProperty(methodName:*, ...args):*{
           trace("callProperty methodName:" + methodName + " args:" + args);
           return methodName;
        }
        flash_proxy override function setProperty(name:*,value:*):void{
           trace("setProperty name:" + name + " value:" + value);
        }
        flash_proxy override function getProperty(name:*):*{
           trace("getProperty name:" + name);
           return name;
        }
  }

定義していないプロパティやメソッドが、存在するかのようにアクセスすることができる。

  var hogeProxy:HogeProxy = new HogeProxy();
  hogeProxy.moge = "もげ";
  var f:String = hogeProxy.fuga;
  hogeProxy.foobar("ふーばー");

S2Flex2を試してみた。(2)

次はサービスを作ってみることにする。

どうやって作るかの説明がサイトにないので、exampleのAddサンプルをみて作ってみる。
とりあえず、こんな感じか。。

まず、Main.mxmlに S2Flex2Serviceとリモート呼び出しの結果を受け取るスクリプトを追加。
ボタンを押したら、MainService の getHoge()メソッドが呼ばれるようにする。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
	xmlns:s2="http://www.seasar.org/s2flex2/mxml"
	layout="absolute">
	<mx:Script>
		<![CDATA[
			import mx.rpc.events.ResultEvent;
			import mx.rpc.events.FaultEvent;
			import mx.controls.Alert;
			import mx.utils.ObjectUtil;
			
			public function getHoge():void {
			    service.getHoge();	
			}
			public function onResult(event:ResultEvent):void {
				Alert.show(ObjectUtil.toString(event));
				var hoge:String = event.result as String;
				message.text = hoge;
			}
			public function onFault(event:FaultEvent):void {
			    Alert.show(ObjectUtil.toString(event));	
			}
		]]>
	</mx:Script>
	<s2:S2Flex2Service id="service" destination="mainService"
		result="onResult(event)" fault="onFault(event)" showBusyCursor="true" />
	<mx:Text id="message" x="26" y="23" text="Hello World !" width="222" height="45" fontSize="30"/>
	<mx:Button x="26" y="76" label="ボタン" click="getHoge()"/>
</mx:Application>

まだサーバ側を作ってないけど、この状態で一回実行してみよう。
実行すると、サービスが見つからないと起こられる。
そりゃそうだ。

ERROR 2007-03-15 16:03:23,046 [http-8080-Processor24] [EFLX0001]適用可能なServiceInvoker[mainService]が見つかりません。
org.seasar.flex2.rpc.remoting.service.exception.InvokerNotFoundRuntimeException: [EFLX0001]適用可能なServiceInvoker[mainService]が見つかりません。

ということで、サービスを作ってみる。
Main.mxmlの上で右クリックしたときのメニューに、"Create Service" というのがあるので、これで作る。

Create Service を実行すると、s2flex2.test.web.flex.MainService と、s2flex2.test.web.flex.MainServiceImpl を生成した。

で、こいつらに、getHoge メソッドを追加。

package s2flex2.test.web.flex;

public interface MainService {
	String getHoge();
}
package s2flex2.test.web.flex.impl;
import org.seasar.flex2.rpc.remoting.service.annotation.RemotingService;
import s2flex2.test.web.flex.MainService;
@RemotingService
public class MainServiceImpl implements MainService {
	public String getHoge() {
		return "ほげ";
	}
}

で、実行してみる。
が、まだ InvokerNotFoundRuntimeException が出る。
exampleのAddサンプルを見ると、どうもconvention.dicon のrootPackageNameの設定が違うっぽい。
Addサンプルのconvention.diconだと、以下のようになっていて、

<initMethod name="addRootPackageName">
  <arg>"examples.flex2.add"</arg>
</initMethod>

サービスクラスは、examples.flex2.add.service.AddService と、examples.flex2.add.service.impl.AddServiceImple になっている。
つまり、Addサンプルでは、ルートパッケージの直下のserviceパッケージにサービスクラスが入っている。

なので、MessageService と MessageServiceImpl のパッケージを、それぞれ s2flex2.test.service と s2flex2.test.service.impl に変更する。


で、実行してみる。

DEBUG 2007-03-15 16:20:22,875 [http-8080-Processor24] HOT deployを開始します
DEBUG 2007-03-15 16:20:22,968 [http-8080-Processor24] クラス(s2flex2.test.service.impl.MainServiceImpl[mainService])のコンポーネント定義を登録します
DEBUG 2007-03-15 16:20:23,468 [http-8080-Processor24] mainService(クラス:s2flex2.test.service.impl.MainServiceImpl)をRemotingServiceとして登録しました。
DEBUG 2007-03-15 16:20:23,500 [http-8080-Processor24] BEGIN s2flex2.test.service.impl.MainServiceImpl#getHoge()
DEBUG 2007-03-15 16:20:23,500 [http-8080-Processor24] END s2flex2.test.service.impl.MainServiceImpl#getHoge() : ほげ
DEBUG 2007-03-15 16:20:23,515 [http-8080-Processor24] HOT deployを終了しました

ようやくうまくいった。

ここまでで、MainServiceを追加して、パッケージ移動したけど、Tomcatは最初に起動したままで再起動はしていない。
HotDeployすげぇ。

続けて、サービスクラスを、s2flex2.test.web.flex.serviceパッケージに、移動してみる。
サービスクラスを、s2flex2.test.web.flex.service.MessageService と s2flex2.test.web.flex.service.impl.MessageServiceImpl に変更。


実行してみる。

DEBUG 2007-03-15 16:42:37,328 [http-8080-Processor24] HOT deployを開始します
DEBUG 2007-03-15 16:42:37,359 [http-8080-Processor24] クラス(s2flex2.test.web.flex.service.impl.MainServiceImpl[mainService])のコンポーネント定義を登録します
DEBUG 2007-03-15 16:42:37,406 [http-8080-Processor24] mainService(クラス:s2flex2.test.web.flex.service.impl.MainServiceImpl)をRemotingServiceとして登録しました。
DEBUG 2007-03-15 16:42:37,406 [http-8080-Processor24] BEGIN s2flex2.test.web.flex.service.impl.MainServiceImpl#getHoge()
DEBUG 2007-03-15 16:42:37,406 [http-8080-Processor24] END s2flex2.test.web.flex.service.impl.MainServiceImpl#getHoge() : ほげ
DEBUG 2007-03-15 16:42:37,406 [http-8080-Processor24] HOT deployを終了しました

おぉ、うまく動いた。
どうやら、serviceという名前のパッケージに入れれば動くということらしい。


追記:

いや、Tomcatを再起動したら、s2flex2.test.web.flex.service だと動かない。

[EFLX0001]適用可能なServiceInvoker[mainService]が見つかりません。
org.seasar.flex2.rpc.remoting.service.exception.InvokerNotFoundRuntimeException: [EFLX0001]適用可能なServiceInvoker[mainService]が見つかりません。

そうなると、ルートパッケージ直下のserviceに入れるしかないのか。。。
サブアプリケーション単位で分けたい場合は、convention.diconに追加していく感じ?

S2Flex2を試してみた。(1)

環境は以下のとおり。

  • Dolteng0.18.0
  • Eclipse3.2.0
  • JDK1.5.0_08
  • Tomcat 5.5.17

Eclipseの新規作成から、Chura Project を選んで、S2Flex2 + S2Dao を選択してプロジェクトを作成。
Root Package Name はとりあえず、s2flex2.test とする。


プロジェクトを作成すると、s2関連のjarが見つからないといわれた。

ビルド・パスのエラーが解決されるまで、プロジェクトをビルドできません
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/commons-logging-1.0.4.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/geronimo-jsp_2.0_spec-1.0.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/geronimo-jta_1.0.1B_spec-1.0.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/geronimo-servlet_2.4_spec-1.0.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/h2-2007-01-30.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/s2-dao-1.0.41-SNAPSHOT.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/s2-dao-tiger-1.0.41-SNAPSHOT.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/s2-extension-2.4.10.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/s2-flex2-1.0.1-SNAPSHOT.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/s2-framework-2.4.10.jar' がありません。
プロジェクト s2flex2 に、必要なライブラリー 'WEB-INF/lib/s2-tiger-2.4.10.jar' がありません。

S2関連のjarがないと言っているので、WEB-INF/lib にjarを入れるために、各種最新版を落としてきたのだが、要求されているバージョンと微妙に違う。。

  • S2Container 2.4.11
  • S2Tiger 2.4.11
  • S2Dao 1.0.41 RC
  • S2DaoTiger 1.0.41 RC
  • S2Flex2 1.0.1
  • S2Flex2Tiger 1.0.0

けど、落とし直すのがめんどくさいので、ビルドパスを設定しなおして、エラーを消去。

で、Doltengが生成したものをみると、以下のようなディレクトリが作られている。

  • bin ・・・ Flex2のソースをコンパイルした結果(swfやswc)が入る。
  • html-template ・・・ Flex2コンパイル時に使用されるテンプレート
  • WEB-INF/src/main/flex
    • Flex2のソースが入っている。デフォで Main.mxml が作られている。ルートパッケージ(s2flex2.test)の下に、dto と web というパッケージが作られている。(中は空)
  • WEB-INF/src/main/java
    • Javaのソースを入っている。ルートパッケージの下にconverter,dao,dto,dxo,entity,helper,interceptor,logic,service,validator,web が作られている。
  • WEB-INF/src/main/resources
    • 設定ファイル(dicon)が入っている。

まずは、Main.mxmlを表示してみよう。
Tomcat を起動したら、H2のライブラリがないと怒られる。

org.seasar.framework.beans.IllegalPropertyRuntimeException: [ESSR0059]クラス(org.seasar.extension.dbcp.impl.XADataSourceImpl)のプロパティ(driverClassName)の設定に失敗しました。理由はorg.seasar.framework.exception.ClassNotFoundRuntimeException: [ESSR0044]クラスが見つかりませんでした。詳細はjava.lang.ClassNotFoundException: org.h2.Driver

ビルドパスを設定しなおしたときに、S2の最新版にH2が入っていると思って特に確認しなかったんだけど、違うのか。。あぅ。
落とすのがめんどくさいので、SuperAgile で Chura Project を作ったときに、生成された h2-2007-01-30.jar をコピーしてWEB-INF/libへ入れることにする。


再度Tomcatを起動。
今度はエラーがでないで起動した。今度は大丈夫そうだ。


http://localhost:8080/s2flex2/bin/Main.html
にアクセスすると、Hello World と表示された。