ClassLoader#setPackageAssertionStatusがウゴゲ。
こんなクラスを用意しるます。
public class Hoge { public void fuga(String s) { assert s != null : "s must not be null"; System.out.println(s); } }
ポイントは、全く同じクラスを、二つのパッケージに配置する事。
- aaa.bbb.ccc
- SystemClassLoaderでロードする。
- zzz.zzz
- 俺様URLClassLoaderでロードする。
の二つ。
つまり、こんな感じのコード。
import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import aa.bb.cc.Hoge; public class Main { public static void main(String[] args) throws Exception { Hoge h = new Hoge(); h.fuga("s"); h.fuga(null); URLClassLoader cl = new URLClassLoader(new URL[] { new File("classes") .toURI().toURL() }); cl.setPackageAssertionStatus("zzz", true); Class<?> clazz = cl.loadClass("zzz.zzz.Hoge"); Object o = clazz.newInstance(); Method m = o.getClass().getMethod("fuga", String.class); m.invoke(o, "aaa"); m.invoke(o, new Object[] { null }); } }
これを、-ea でassert構文を有効化せずにJVMを起動しる。
s null aaa Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at Main.main(Main.java:26) Caused by: java.lang.AssertionError: s must not be null at zzz.zzz.Hoge.fuga(Hoge.java:6) ... 5 more
おおう!ファンタスティック!
JVMの-eaオプションは、まぁ、何というかビックリする位扱い辛い*1ので、
オレオレClassLoaderを提供する皆様におかれましては、
その実行を制御する為の設定ファイル内において、
assertモードをハンドリング出来る様にしてはうかがか?
例えば、こんな感じ?
<config assert="enable"> <component assert="enable" class="aaa.bbb.ccc.Hoge" /> </config>
アプリケーションコードのアサートと、
フレームワークのアサートのスイッチを、
JVMの起動オプションじゃ無しに、
設定ファイル的な何かでスイッチ出来たりすると、何か幸せかもぞ。
ちなむと、java.lang.ClassLoader には、こんな感じでメソッドが並んでいる。
- clearAssertionStatus()
- setClassAssertionStatus(String className, boolean enabled)
- setDefaultAssertionStatus(boolean enabled)
- setPackageAssertionStatus(String packageName, boolean enabled)
尚、このassertionStatusは、Classのロードじスタティックイニシャライザで、
Class#desiredAssertionStatus() を呼出してstaticメンバに抱えてしまうので、
一度ロードされたクラスの状態を変更する事は出来ませぬ。