DIVE INTO S2 .HACK : INCLUDE PART II+

Seasar2に慣れ親しむためにS2Containerをプチハックしてみる第2.5弾。 (^^;;;

  • カスタムPathResolverを使って、S2Container間の依存関係(include)をDEPENDENCY-LOOKUPからDEPENDENCY-INJECTIONにしてみる。
  • てゆうか、既存のdiconファイルの設定を変更することなく、一部のdiconファイルを差し替える。 (^^;;;

ResourceResolverを使用してやってみる予定だったのですが、PathResolverをいじらずに、前回とおなじようなことをするためには、XmlS2ContainerBuilderにも変更を加える必要があったので、前のPathResolver版に蛇の足を付けてお茶を濁す
今回は、既存のdiconファイルを、さらに上位のdiconファイルでインクルードすることで、diconツリーの一部のdiconを差し替えてみたり…なんか、視点が、DEPENDENCY-LOOKUPとかINJECTIONとかじゃなくなってきたような…orz

    • 想定する既存のdiconファイル群は、こんな感じです。*1
    • ごく普通のdiconファイルたち。

■org/seasar/framework/container/factory/PathAliasResolverTest2.Node0a.dicon





component0
component1
component2

"Here is node0a container."

■org/seasar/framework/container/factory/PathAliasResolverTest2.Node1a.dicon




"Here is node1a container."

■org/seasar/framework/container/factory/PathAliasResolverTest2.Node2a.dicon



"Here is node2a container."

■org/seasar/framework/container/factory/PathAliasResolverTest2.Node2b.dicon



"Here is node2b container."

    • で、これらの上位に位置させて、既存のdiconを差し替えるためのルートdiconファイルがこちら。
    • 1目のincludeで、Node2aをNode2bに差し替えている。

■org/seasar/framework/container/factory/PathAliasResolverTest2.dicon



<include path="org/seasar/framework/container/factory/PathAliasResolverTest2.Node2a.dicon
=org/seasar/framework/container/factory/PathAliasResolverTest2.Node2b.dicon"/>


    • テストコードは、こんな感じで。

■org/seasar/framework/container/factory/PathAliasResolverTest2.java


package org.seasar.framework.container.factory;

import java.util.List;

import junit.framework.TestCase;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.util.ClassUtil;

/**
* @author hidebsd
*/
public class PathAliasResolverTest2 extends TestCase {

private static final String BASE = ClassUtil.getResourcePath(PathAliasResolverTest2.class).replaceAll("\\.class$", "");

private static final String S2CFG = BASE + ".Configuration.dicon";

private static final String PATH0 = BASE + ".Node0a.dicon";

private static final String PATH1 = BASE + ".dicon";

public void setUp() {
S2ContainerFactory.configure(S2CFG);
}

public void testDefaultSettings() throws Exception {
S2Container container = S2ContainerFactory.create(PATH0);
container.init();
List list = (List) container.getComponent("collection");
for (int i = 0; i < list.size(); ++i) {
assertEquals("node" + i, "Here is node" + i + "a container.", list.get(i));
}
}

public void testOverrideSettings() throws Exception {
S2Container container = S2ContainerFactory.create(PATH1);
container.init();
List list = (List) container.getComponent("collection");
for (int i = 0; i < list.size(); ++i) {
String node = "node" + i + (i != 2 ? "a" : "b");
assertEquals("node" + i, "Here is " + node + " container.", list.get(i));
}
}

}

    • で、S2コンテナカスタマイズ用diconはこんな感じで。PathResolverインターフェースを実装しているクラスがあると差し替えられるので、これでOK。
    • ちなみに、s2container.dicon というファイル名でクラスパスのルートに置けば、明示的に S2ContainerFactory.configure(…) しなくても、自動的にそのファイルを読み込んで、S2コンテナがカスタマイズされます。

■org/seasar/framework/container/factory/PathAliasResolverTest2.Configuration.dicon





    • で、本命のPathResolverインターフェースを実装したPathAliasResolverなるクラスを作成。
    • delimiterは、正規表現パターンの一部となるので、指定する際には、場合によりエスケープが必要。

■org/seasar/framework/container/factory/PathAliasResolver.java


package org.seasar.framework.container.factory;

import java.util.HashMap;
import java.util.Map;

import org.seasar.framework.util.ClassUtil;
import org.seasar.framework.util.StringUtil;

/**
* @author hidebsd
*/
public class PathAliasResolver implements PathResolver {

private static final String TRIM = "[\\n\\r\\t ]*";

private static final String dummyConfigPath = ClassUtil.getResourcePath(
PathAliasResolver.class).replaceAll("\\.class$", "Dummy.dicon");

private final Map aliasDefinitions = new HashMap();

private String delimiter = "=";

public PathAliasResolver() {
}

public PathAliasResolver(String delimiter) {
this.delimiter = delimiter;
}

public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}

public String resolvePath(String context, String path) {
if (path == null) {
throw new IllegalStateException("Specified path was null.");
}
path = trimPath(path);
if (path.contains(delimiter)) {
path = registerPathAlias(path);
}
if (aliasDefinitions.containsKey(path)) {
path = resolvePath(path);
}
return path;
}

private String trimPath(String path) {
path = path.replaceAll("^" + TRIM, "");
path = path.replaceAll(TRIM + "$", "");
path = path.replaceAll(TRIM + delimiter + TRIM, delimiter);
return path;
}

private String registerPathAlias(String path) {
int pos = path.indexOf(delimiter);
String alias = path.substring(0, pos);
if (!aliasDefinitions.containsKey(alias)) {
path = path.substring(pos + delimiter.length());
path = (path.length() != 0 ? path : dummyConfigPath);
aliasDefinitions.put(alias, path);
}
return dummyConfigPath;
}

private String resolvePath(String path) {
String resolvedPath = (String) aliasDefinitions.get(path);
if (resolvedPath == null) {
return path;
}
return resolvedPath;
}

}

    • それから、diconファイルパスの別名を登録する場合、返すパスがないのですが、なにかしらdiconファイルのパスを返す必要があったので、ダミーのdiconファイルを作成。
    • これは、なんとかならないかなぁ…。やっぱりIncludeTagHandlerで対処するしかないかなぁ…、ま、実行に影響はないからいっか…。

■org/seasar/framework/container/factory/PathAliasResolverDummy.dicon



    • では、テスト開始〜!
    • RED RED RED RED RED RED GREEN!
    • Y-A-T-T-A-A !!!
    • さらなる深みを目指して、DIVE INTO S2 !

*1:xml宣言とDTDの部分は省略してあります