発生したバージョンは以下の通り:
(別のバージョンでも発生するかもですが試してない)
SAStruts: 1.0.4-sp9
Seasar(S2Container): 2.4.46
S2ClassBuilder: 0.0.11
-> diconファイルの扱い(S2ClassBuilder) | Ymir
-> S2ClassBuilder | DBFlute
例えば、jta+UserTransaction.diconを作成して、
UserTransactionをオーバーライドするとします。
そのUserTransactionが、とあるdicon(foo.dicon)の
コンポーネント(fooManager)をDI対象にしてるとします。
なので、jta+UserTransaction.diconで、
foo.diconをincludeします。
HotDeployでは全く正常に動作します。
UserTransactionの拡張も、DIされたfooManagerを
使って元気良く動きます。
しかし、CoolDeployにすると、初回画面アクセス時に、
以下の例外が発生:
[ESSR0007]externalContextはnullあるいは空であってはいけません
at ...deployer.RequestComponentDeployer.deploy(RequestComponentDeployer.java:54)
at ...impl.ComponentDefImpl.getComponent(ComponentDefImpl.java:111)
at ...config.S2ActionMapping.getAction(S2ActionMapping.java:320)
at ....action.ActionWrapper.(ActionWrapper.java:76)
ExternalContext extCtx = cd.getContainer().getRoot()
.getExternalContext();
if (extCtx == null) { // ★ここが null
RuntimeException re = new EmptyRuntimeException("externalContext");
logger.log(re);
throw re;
}
なんで、ExternalContextがnullなのー!?
ちゃんと、S2ContainerFilterで設定してるけどぅ。
実際に、SingletonS2ContainerFactoryからベタに
取得しても、しっかりとExternalContextは取得できます。
なので、そのActionのComponentDefのContainerを取得して、
ContainerのPathやNameなどをログに出してみました。
すると path が "jta+UserTransaction.dicon" !?
すぐさまこのファイルを消して試してみると動作しました。
もちろん、拡張が無くなっちゃうのでその分は動かない。
で、こんどはそのdiconは設置するけど、
そのdiconの中のfoo.diconへのincludeを削除。
すると正常に動作しました(するとDIはされないけど)。
正常に動作するときのActionの属するContainerは、
app.diconになります。これが恐らく正しいのかな。
なので、とりあえず回避(いや恒久対応か!?)で、
拡張したUserTransactionの中では、
fooManagerをDIせずにgetComponent()するように。
これでHotでもCoolでも無事元気良く動くように。
これ、"ぷらなんとかdicon" じゃなくて、
"ぷらぷらdicon" (e.g. foo++.dicon) でも、
発生しました。
実際にDIしてなくても、includeするだけで発生。
でも、発生しないケースもありました。
aop++.diconに意味もなくjta.diconをincludeしてもOK。
この辺のケースの厳密な切り分けはできていません。
とりあえずトリガーが分かったので回避。
まあ、仕組みの奥底なので、getComponent()でもいいやと。
多分、根本の原因は S2ClassBuilder と Seasarの
すれ違いのような気もするのだけど、どちらも枯れてて、
今後解決されるかどうかが不明なので、
バッドノウハウとしてしっかり覚えておこうと。
HotDeployは便利でありながらも、
"Coolだと動かないよ問題" にわりと対峙するものなので、
こうやってしっかりブログに書いておきます。
(同じ問題に遭遇した他の人 or 将来の自分のため)