S2.3RC1の自動登録での怪しい挙動

別のパッケージに同じ名前のクラス名があるときに、org.seasar.framework.container.autoregister.DefaultAutoNamingでコンポーネント名を自動解決すると、バッティングしてコンポーネント登録に失敗するようです。
登録時にエラーにならず、コンポーネント取得時に始めて気がつくという状態。

一応、原因はわかりました。
↓初、続きを読む。

sample.autoregist
    Greeting
    GreetingClient
    GreetingClientImpl
    GreetingImpl
    GreetingMain
    sample.dicon
sample.autoregist.sub
    GreetingClient
    GreetingClientImpl

というパッケージ構成で、(それぞれ何をやっているかはクラス名からご想像ください(^^;)

<component class="sample.autoregist.GreetingImpl" ></component>
<component class="sample.autoregist.sub.GreetingClientImpl" ></component>
<component class="sample.autoregist.GreetingClientImpl" ></component>

というdiconファイルを書いて、明示的にコンポーネント登録すると、

public class GreetingMain {
	private static final String PATH = "sample/autoregist/sample.dicon";
    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        container.init();
        System.out.println(container.getComponent(sample.autoregist.GreetingClient.class));
        System.out.println(container.getComponent(sample.autoregist.sub.GreetingClient.class));
    }
}

の結果は、

sample.autoregist.GreetingClientImpl@3eca90
sample.autoregist.sub.GreetingClientImpl@64dc11

と期待通り。


ここで、diconファイルをコンポーネント自動登録用に書き換えてみる。

<component
  class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister">
    <property name="fileNameOfRoot">"app.dicon"</property>
    <property name="autoNaming">
        <component class="org.seasar.framework.container.autoregister.DefaultAutoNaming"/>
    </property>
    <initMethod name="addClassPattern">
        <arg>"sample.autoregist"</arg>
        <arg>".*Impl"</arg>
    </initMethod>
    <initMethod name="registAll"/>
</component>

そうすると…、

sample.autoregist.GreetingClientImpl@d70d7a
Exception in thread "main" org.seasar.framework.container.ComponentNotFoundRuntimeException: [ESSR0046]コンポーネント(interface sample.autoregist.sub.GreetingClient)が見つかりません
	at org.seasar.framework.container.impl.S2ContainerBehavior$DefaultProvider.acquireFromGetComponentDef(S2ContainerBehavior.java:83)
	at org.seasar.framework.container.impl.S2ContainerBehavior$DefaultProvider.acquireFromGetComponent(S2ContainerBehavior.java:77)
	at org.seasar.framework.container.impl.S2ContainerBehavior.acquireFromGetComponent(S2ContainerBehavior.java:41)
	at org.seasar.framework.container.impl.S2ContainerImpl.getComponent(S2ContainerImpl.java:100)
	at sample.autoregist.GreetingMain.main(GreetingMain.java:18)

と、sample.autoregist.subパッケージの方のGreetingClientImplがコンポーネント登録されていないといわれてしまう!


interaface名のGreetingClientがかぶっているが原因かと思い、subの方のinterafaceだけSubGreetingClientとしても…、

sample.autoregist.GreetingClientImpl@d70d7a
Exception in thread "main" org.seasar.framework.container.ComponentNotFoundRuntimeException: [ESSR0046]コンポーネント(interface sample.autoregist.sub.SubGreetingClient)が見つかりません
	at org.seasar.framework.container.impl.S2ContainerBehavior$DefaultProvider.acquireFromGetComponentDef(S2ContainerBehavior.java:83)
	at org.seasar.framework.container.impl.S2ContainerBehavior$DefaultProvider.acquireFromGetComponent(S2ContainerBehavior.java:77)
	at org.seasar.framework.container.impl.S2ContainerBehavior.acquireFromGetComponent(S2ContainerBehavior.java:41)
	at org.seasar.framework.container.impl.S2ContainerImpl.getComponent(S2ContainerImpl.java:100)
	at sample.autoregist.GreetingMain.main(GreetingMain.java:18)

となりエラー。
ふむ。見つからないといっているinterface名はSubGreetingClientになってますね。

今度はinterface名は元に戻して、subの方の実装クラス名だけをSubGreetingClientImplにしてみると…、

sample.autoregist.GreetingClientImpl@d70d7a
sample.autoregist.sub.SubGreetingClientImpl@b5f53a

んぉ?成功か。

というわけで、実装クラス名が問題のようだ。
コンポーネント名の自動解決辺りが怪しい?

DefaultAutoNamingではクラス名の先頭を小文字にして末尾がImplならそれを除去した文字列をコンポーネント名として返している。
これ自体は別にまあ標準ですよね。

で、普通ならコンポーネント名がバッティングしてはいるけど、正常に登録されるんじゃないかと思います。コンポーネント名greetingClientで取得しようとしたら、TooMany〜Exceptionが出る、っていう例の動きですね。それなら納得できるんです。


ためしに、diconファイルを

<component class="sample.autoregist.GreetingImpl" ></component>
<component name="greetingClient"  class="sample.autoregist.sub.GreetingClientImpl" ></component>
<component name="greetingClient"  class="sample.autoregist.GreetingClientImpl" ></component>

としてみましょう。kijimunaが怒りますが、まあそれは無視して実行。

sample.autoregist.GreetingClientImpl@3eca90
sample.autoregist.sub.GreetingClientImpl@64dc11

やっぱり動きますよね。
で、greetingClientというコンポーネント名で取得する行をmainに追加してみると

Exception in thread "main" org.seasar.framework.container.TooManyRegistrationRuntimeException: [ESSR0045]greetingClientに複数のコンポーネント(sample.autoregist.sub.GreetingClientImpl, sample.autoregist.GreetingClientImpl)が登録されています
	at org.seasar.framework.container.impl.TooManyRegistrationComponentDefImpl.getComponent(TooManyRegistrationComponentDefImpl.java:43)
	at org.seasar.framework.container.impl.S2ContainerImpl.getComponent(S2ContainerImpl.java:104)
	at sample.autoregist.GreetingMain.main(GreetingMain.java:19)

ですよねぇ。これなら予想通りなんで納得です。


でも、今回は

Exception in thread "main" org.seasar.framework.container.ComponentNotFoundRuntimeException: [ESSR0046]コンポーネント(interface sample.autoregist.sub.SubGreetingClient)が見つかりません

なんですよねぇ。そもそも登録されていない。


ってことで、FileSystemComponentAutoRegisterのソースを見てみましょう。

    protected void regist(ClassPattern classPattern, File packageDir, String packageName) {
        // 〜(省略)〜
            if (classPattern.isApplied(shortClassName)) {
                String className = packageName == null ? shortClassName : packageName + "." + shortClassName; 
                ComponentDef cd = annoHandler.createComponentDef(className);
                if (cd.getComponentName() == null && autoNaming != null) {
                    cd.setComponentName(autoNaming.defineName(packageName, shortClassName));
                }
                if (hasComponentDef(cd.getComponentName())) { // ---★
                    continue;
                }
                annoHandler.appendDI(cd);
                getContainer().register(cd);
            }
        }
    }

ありましたね。★の部分ですね。同じコンポーネント名だったら登録しないと。

これって、明示的指定の場合と振る舞いが違いませんか?


ここのif文3行を削除すると、元の構成で

sample.autoregist.GreetingClientImpl@d70d7a
sample.autoregist.sub.GreetingClientImpl@b5f53a

となりました。期待通り。
で、コンポーネント名greetingClientで取得すると、

Exception in thread "main" org.seasar.framework.container.TooManyRegistrationRuntimeException: [ESSR0045]greetingClientに複数のコンポーネント(sample.autoregist.GreetingClientImpl, sample.autoregist.sub.GreetingClientImpl)が登録されています
	at org.seasar.framework.container.impl.TooManyRegistrationComponentDefImpl.getComponent(TooManyRegistrationComponentDefImpl.java:43)
	at org.seasar.framework.container.impl.S2ContainerImpl.getComponent(S2ContainerImpl.java:104)
	at sample.autoregist.GreetingMain.main(GreetingMain.java:19)

ふむ。期待通り。

コンポーネント名がnullならOKなんで、diconファイルでDefaultAutoNamingを指定しなければ、それはそれで正常に通るんですけどね。

[追記]
RC2で修正されました。