読者です 読者をやめる 読者になる 読者になる

この日記は私的なものであり、所属会社の見解ではありません。 GitHub: takahashikzn

OGNL-3.1で、(!!"1") == false になって困った

OGNL-3.1がひっそりとリリースされているのですが、かなり困った仕様変更が入ってます。

foo || barが動かない

例えば、barが文字列型の値だとして、

foo.aMethod(bar || "default string")

のようにして、barnullなら"default string"を渡すようなコードが動かなくなります。 これはJavascript等では頻出するイディオムです。

どう変わったのか

ここに詳細があります。 http://github.com/jkuhnert/ognl/issues/8

要するに、文字列型なら"true"の時のみtrue、それ以外は常にfalseです。 だからfooが"1"とかそういう値であってもfalseになります。

流石にメンテナー*1もこの非互換性は気になっているようで、この仕様変更を含まない3.0.12も同時リリースされています。

Javassistで無かったことにする

件のPullRequestの作者*2は、どうもJavassistでこの動作を無理矢理ねじ込んでいたようなのですが、とうとう我慢できなくなってPullRequestを送ったようです。

For me, it would allow me to remove this hugely ugly, javassist-based hack from thymeleaf: https://github.com/thymeleaf/thymeleaf/blob/159f963907e9d5e3943843031845df28934adafc/src/main/java/org/thymeleaf/standard/expression/OgnlVariableExpressionEvaluator.java#L201-L261

一方の僕はこの仕様変更を受け入れられないので、こちらもJavassistで無理やり打ち消そうと思います。

3.0.12を使えばいいのでは?という意見もありますが、以後の主流になる3.1系を使いたいのです。

final ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(new ClassClassPath(this.getClass()));

final CtClass ctClass = classPool.get(OgnlOps.class.getName());
final CtClass objectType = ctClass.getClassPool().get(Object.class.getName());

final String code = "if ($1 instanceof CharSequence)" +
    "{ return ((CharSequence) $1).length() > 0; }";

ctClass.getDeclaredMethod("booleanValue", new CtClass[] { objectType })
    .insertBefore(code);

この処理をJava Agent経由でねじ込むと元通りになります。

*1: Struts2のメンテナーと同一人物

*2: thymeleafの作者