實現ClassLoaderDelegateHook控制Equinox的類加載
Equinox的設計非常經典,其在擴展方面提供了很多的支持,同樣包括類加載方面的控制,除了通過標準的org.osgi.framework.bootdelegation以及equinox提供的osgi.parentClassLoader這兩個屬性來簡單的控制類加載之外,還可通過實現ClassLoaderDelegateHook來更為靈活的控制類加載。
對Equinox代碼進行分析,想讓擴展的ClassLoaderDelegateHook起作用,首先要能夠讓這個ClassLoaderDelegateHook注冊到Equinox中,Equinox在進行類加載的過程中會調用searchHooks來尋找ClassLoaderDelegateHook的實現,尋找的方法為獲取bundle.framework.delegateHooks屬性,而framework中的delegateHooks屬性是在initialize時通過adaptor的getHookRegistry()的getClassLoaderDelegateHooks()方法來獲取的,并且framework沒有提供setDelegateHooks或addDelegateHook這樣的方法,所以沒辦法直接設置,從這來看,是否意味著只需要在framework initialize之前通過adaptor的getHookRegistry來注冊ClassLoaderDelegateHook就可以呢,繼續看BaseAdaptor類,恩,確實通過adaptor的getHookRegistry可以來增加ClassLoaderDelegateHook,但由于啟動過程是在EclipseStarter里面完成的,BaseAdaptor是在啟動的代碼里創建的,這樣就沒辦法保證在framework initialize之前增加自己實現的ClassLoaderDelegateHook了,查看EclipseStarter代碼,幸運的是可以通過設置osgi.adaptor屬性來指定Adaptor類的名稱,于是決定繼承BaseAdaptor,在構造器中創建通過獲取HookRegistry來增加自行實現的ClassLoaderDelegateHook類,這里還有個小問題,BaseAdaptor在構造器中創建HookRegistry后調用了其initiliaze方法,而HookRegistry在initialize方法中將其私有的一個readonly屬性設置為了true,這導致的后果是無法在這之后增加ClassLoaderDelegateHook,如增加,則會拋出:IllegalStateException,錯誤信息為:Cannot add hooks dynamically,因此,在調用addClassLoaderDelegateHook之前必須通過反射先將HookRegistry中的readonly屬性設置為false,按照上面的方法,實現的關鍵代碼如下:
// 設置自行實現的Adaptor類名
FrameworkProperties.setProperty(EclipseStarter.PROP_ADAPTOR, CustomAdaptor.class.getName());
ClassLoaderDelegateHook類則可根據自己的需求進行實現,在這個類中可靈活控制類加載的過程,接口中控制類加載的方法說明如下:
preFindClass
在Equinox判斷是否需要從boot classloader中加載后,就會嘗試調用ClassLoaderDelegateHook的這個方法來加載類,如此方法拋出ClassNotFoundException,則終止類查找,因此,如果希望Hook中加載不到時繼續執行Equinox的類加載的話,在Hook中就不要拋出ClassNotFoundException。
postFindClass
在Equinox已經嘗試過從Bundle的import-package、require-bundle、bundle-classpath以及dynamicimport-package中加載后,會嘗試調用ClassLoaderDelegateHook的這個方法來加載類。
上面的方法并不優雅,從Equinox設計了Hook來看,不可能要這么復雜才能增加一個Hook,于是繼續回頭看代碼,不負期望,在HookRegistry的initialize方法中,會去加載配置的osgi.hook.configurators、osgi.hook.configurators.include和osgi.hook.configurators.exclude三個屬性值或指定的hookconfigurators.properties文件,最后合并形成需要加載執行的HookConfigurator類,從上面的代碼來看,只用在自行實現的ClassLoaderDelegateHook類上再增加HookConfigurator接口的實現,并將其注冊到HookRegistry中,最后在osgi.hook.configurators中配置這個類即可,由于equinox內部已經有配置了一些HookConfigurator的,因此此處需要把equinox jar中的hookconfigurators.properties里面配置的hook.configurators也增加進去,要么就是把自己需要增加的HookConfigurator加到equinox jar的hookconfigurators.properties中,這種方法就優雅很多了,按照這種方法實現后的關鍵代碼如下:
// 設置需要裝載的HookConfigurator的實現類
FrameworkProperties.setProperty("osgi.hook.configurators", CustomClassLoaderDelegate.class.getName());
ps: 在《OSGi原理與最佳實踐》中沒來得及寫上這部分,就在這篇blog上寫了。
對Equinox代碼進行分析,想讓擴展的ClassLoaderDelegateHook起作用,首先要能夠讓這個ClassLoaderDelegateHook注冊到Equinox中,Equinox在進行類加載的過程中會調用searchHooks來尋找ClassLoaderDelegateHook的實現,尋找的方法為獲取bundle.framework.delegateHooks屬性,而framework中的delegateHooks屬性是在initialize時通過adaptor的getHookRegistry()的getClassLoaderDelegateHooks()方法來獲取的,并且framework沒有提供setDelegateHooks或addDelegateHook這樣的方法,所以沒辦法直接設置,從這來看,是否意味著只需要在framework initialize之前通過adaptor的getHookRegistry來注冊ClassLoaderDelegateHook就可以呢,繼續看BaseAdaptor類,恩,確實通過adaptor的getHookRegistry可以來增加ClassLoaderDelegateHook,但由于啟動過程是在EclipseStarter里面完成的,BaseAdaptor是在啟動的代碼里創建的,這樣就沒辦法保證在framework initialize之前增加自己實現的ClassLoaderDelegateHook了,查看EclipseStarter代碼,幸運的是可以通過設置osgi.adaptor屬性來指定Adaptor類的名稱,于是決定繼承BaseAdaptor,在構造器中創建通過獲取HookRegistry來增加自行實現的ClassLoaderDelegateHook類,這里還有個小問題,BaseAdaptor在構造器中創建HookRegistry后調用了其initiliaze方法,而HookRegistry在initialize方法中將其私有的一個readonly屬性設置為了true,這導致的后果是無法在這之后增加ClassLoaderDelegateHook,如增加,則會拋出:IllegalStateException,錯誤信息為:Cannot add hooks dynamically,因此,在調用addClassLoaderDelegateHook之前必須通過反射先將HookRegistry中的readonly屬性設置為false,按照上面的方法,實現的關鍵代碼如下:
// 設置自行實現的Adaptor類名
FrameworkProperties.setProperty(EclipseStarter.PROP_ADAPTOR, CustomAdaptor.class.getName());
public class CustomAdaptor extends BaseAdaptor {
public CustomAdaptor(String[] args) {
super(args);
HookRegistry hookRegistry=getHookRegistry();
try{
Field readOnlyField=HookRegistry.class.getDeclaredField("readonly");
readOnlyField.setAccessible(true);
readOnlyField.setBoolean(hookRegistry, false);
hookRegistry.addClassLoaderDelegateHook(自行實現的ClassLoaderDelegateHook類);
}
catch(Exception e){
e.printStackTrace();
}
}
}
public CustomAdaptor(String[] args) {
super(args);
HookRegistry hookRegistry=getHookRegistry();
try{
Field readOnlyField=HookRegistry.class.getDeclaredField("readonly");
readOnlyField.setAccessible(true);
readOnlyField.setBoolean(hookRegistry, false);
hookRegistry.addClassLoaderDelegateHook(自行實現的ClassLoaderDelegateHook類);
}
catch(Exception e){
e.printStackTrace();
}
}
}
ClassLoaderDelegateHook類則可根據自己的需求進行實現,在這個類中可靈活控制類加載的過程,接口中控制類加載的方法說明如下:
preFindClass
在Equinox判斷是否需要從boot classloader中加載后,就會嘗試調用ClassLoaderDelegateHook的這個方法來加載類,如此方法拋出ClassNotFoundException,則終止類查找,因此,如果希望Hook中加載不到時繼續執行Equinox的類加載的話,在Hook中就不要拋出ClassNotFoundException。
postFindClass
在Equinox已經嘗試過從Bundle的import-package、require-bundle、bundle-classpath以及dynamicimport-package中加載后,會嘗試調用ClassLoaderDelegateHook的這個方法來加載類。
上面的方法并不優雅,從Equinox設計了Hook來看,不可能要這么復雜才能增加一個Hook,于是繼續回頭看代碼,不負期望,在HookRegistry的initialize方法中,會去加載配置的osgi.hook.configurators、osgi.hook.configurators.include和osgi.hook.configurators.exclude三個屬性值或指定的hookconfigurators.properties文件,最后合并形成需要加載執行的HookConfigurator類,從上面的代碼來看,只用在自行實現的ClassLoaderDelegateHook類上再增加HookConfigurator接口的實現,并將其注冊到HookRegistry中,最后在osgi.hook.configurators中配置這個類即可,由于equinox內部已經有配置了一些HookConfigurator的,因此此處需要把equinox jar中的hookconfigurators.properties里面配置的hook.configurators也增加進去,要么就是把自己需要增加的HookConfigurator加到equinox jar的hookconfigurators.properties中,這種方法就優雅很多了,按照這種方法實現后的關鍵代碼如下:
// 設置需要裝載的HookConfigurator的實現類
FrameworkProperties.setProperty("osgi.hook.configurators", CustomClassLoaderDelegate.class.getName());
public class CustomClassLoaderDelegate implements ClassLoaderDelegateHook,HookConfigurator {
public Class postFindClass(String name, BundleClassLoader classloader,
BundleData data) throws ClassNotFoundException {
// 自行實現
}
public URL preFindResource(String name, BundleClassLoader classloader,
BundleData data) throws FileNotFoundException {
// 自行實現
}
public Enumeration preFindResources(String name, BundleClassLoader classloader,
BundleData data) throws FileNotFoundException {
// 自行實現
}
public String postFindLibrary(String arg0, BundleClassLoader arg1,
BundleData arg2) {
// 自行實現
}
public URL postFindResource(String arg0, BundleClassLoader arg1,
BundleData arg2) throws FileNotFoundException {
// 自行實現
}
public Enumeration postFindResources(String arg0, BundleClassLoader arg1,
BundleData arg2) throws FileNotFoundException {
// 自行實現
}
public Class preFindClass(String arg0, BundleClassLoader arg1,
BundleData arg2) throws ClassNotFoundException {
// 自行實現
}
public String preFindLibrary(String arg0, BundleClassLoader arg1,
BundleData arg2) throws FileNotFoundException {
// 自行實現
}
public void addHooks(HookRegistry registry) {
registry.addClassLoaderDelegateHook(this);
}
}
public Class postFindClass(String name, BundleClassLoader classloader,
BundleData data) throws ClassNotFoundException {
// 自行實現
}
public URL preFindResource(String name, BundleClassLoader classloader,
BundleData data) throws FileNotFoundException {
// 自行實現
}
public Enumeration preFindResources(String name, BundleClassLoader classloader,
BundleData data) throws FileNotFoundException {
// 自行實現
}
public String postFindLibrary(String arg0, BundleClassLoader arg1,
BundleData arg2) {
// 自行實現
}
public URL postFindResource(String arg0, BundleClassLoader arg1,
BundleData arg2) throws FileNotFoundException {
// 自行實現
}
public Enumeration postFindResources(String arg0, BundleClassLoader arg1,
BundleData arg2) throws FileNotFoundException {
// 自行實現
}
public Class preFindClass(String arg0, BundleClassLoader arg1,
BundleData arg2) throws ClassNotFoundException {
// 自行實現
}
public String preFindLibrary(String arg0, BundleClassLoader arg1,
BundleData arg2) throws FileNotFoundException {
// 自行實現
}
public void addHooks(HookRegistry registry) {
registry.addClassLoaderDelegateHook(this);
}
}
ps: 在《OSGi原理與最佳實踐》中沒來得及寫上這部分,就在這篇blog上寫了。
posted on 2009-07-14 11:04 BlueDavy 閱讀(6154) 評論(1) 編輯 收藏 所屬分類: OSGi、SOA、SCA