Equinox加载Bundle Class的实现

对于想使用Equinox来构建OSGi应用的同学们而言,掌握Equinox是如何加载Bundle中的Class无疑是相当重要的,这样在碰到各类ClassNotFoundException的时候也就有底了,否则可能出现的ClassNotFoundException会多的让你非常的头疼,本文提取自《OSGi原理与最佳实践》,介绍下equinox是如何来加载Bundle中的class的。

Equinox在创建BundleClassLoader时,首先获取bundleclasspath,然后执行createBCLPrevileged方法,此方法最后转交由BaseData来创建ClassLoader

BaseDate创建ClassLoader的关键代码片段为:

    ClassLoadingHook[] hooks = adaptor.getHookRegistry().getClassLoadingHooks();

    ClassLoader parent = adaptor.getBundleClassLoaderParent();

    BaseClassLoader cl = null;

    for (int i = 0; i < hooks.length && cl == null; i++)

       cl = hooks[i].createClassLoader(parent, delegate, domain, this, bundleclasspath);

    if (cl == null)

       cl = new DefaultClassLoader(parent, delegate, domain, this, bundleclasspath);

    return cl;

Equinox中,默认的情况下adaptor.getBundleClassLoaderParent返回的为bootstrap  classloader,可通过修改启动的osgi.parentClassLoader来改变这个parent classloader,默认值采用的boot,可选的其他值有:appextfwkapp对应的为SystemClassLoaderext对应的为SystemClassLoaderparentfwk对应的为启动EquinoxClassLoaderClassLoadingHookcreateClassLoader的时候都没有做动作,因此最后ClassLoader都是通过创建DefaultClassLoader对象来构建的,其中parent参数为nulldelegate参数为BundleLoader实例,bundleclasspath参数为bundleclasspath

经过以上步骤后,完成了ClassLoader的创建,可以开始加载class了,根据上面上述,BundleClass就由DefaultClassLoader来完成了。

查看DefaultClassLoaderloadClass代码,发现真正的加载class的过程是转为调用了delegatefindClass来完成的,delegate参数对应的为BundleLoader实例,转为跟踪BundleLoaderfindClass方法。

BundleLoaderfindClass方法的代码片段:

    if (checkParent && parentCL != null && name.startsWith(JAVA_PACKAGE))

       return parentCL.loadClass(name);

从以上这个代码片段,可以看到,Equinoxjava.开头的类转交给了parent classloader去加载,这也意味着没必要在系统中提供对外export java.开头的package

如果不是java.开头的类,则交由findClassInternal方法来完成加载。

findClassInternal方法遵循的为OSGi规范中定义的Class的加载顺序,不过仍然稍有改动:

1)         判断是否交由parent classloader去完成加载

在启动Equinox时,Equinox会读取org.osgi.framework.bootdelegation属性,该属性对应配置的为需要从parent classloader中加载的package,如值配置的为*,说明所有的都从parent classloader中加载,如值配置的为具体的package,那么则放入bootDelegation集合;如配置的为带通配符的package,那么则放入bootDelegationStems集合。

判断时Equinox首先判断是否所有的都从parent classloader中加载,如是则从parent classloader中加载;

如需要加载的类的package位于bootDelegationbootDelegationStems集合中,那么同样从parent classloader中加载。

如不从parent classloader中加载,则进入下面的步骤。

2)         尝试调用Equinox提供的ClassLoaderDelegateHook的扩展来加载

Equinox对外提供了ClassLoaderDelegateHook的接口扩展,可编写ClassLoaderDelegateHook的实现,注册到Framework中,那么当有Class需要加载等动作时都会得到通知。

在默认情况下,Equinox中没有ClassLoaderDelegateHook的实现,因此继续下面的步骤。

3)         判断是否在import-package中,如在则交由相应的PackageSource去加载

根据Bundle配置的import-package,判断目前需要加载的类是否在import-package中,如在则交由对应的PackageSource进行加载,PackageSource在加载时即直接交由对应的Bundleclassloader去加载,如加载的类的packageimport-package中,但加载后仍然没有找到Class,则直接抛出ClassNotFoundException,如加载到,则直接返回。

如所需要加载的类不的package不在import-package中,则继续下面的步骤。

4)         尝试从require-bundle中加载

尝试使用require-bundle来加载,如加载到,则直接返回,如加载不到,则继续下面的步骤。

5)         尝试从当前Bundle中加载

直到经过以上步骤的尝试,才尝试由当前Bundle中加载,当前Bundle加载的方法即从Bundle-Classpath或当前BundleFragment中查找相应名称的class文件,并读取该文件进行加载,如class文件已加载,则进行缓存,再次加载时则不需要查找和解析class文件。

如从当前Bundle中仍然未找到所需的类,则继续下面的步骤。

6)         尝试从DynamicImport-Package中加载

判断需要找的类的package是否在DynamicImport-Package中,如果在,则交由相应的PackageSource进行加载,如PackageSource中加载不到,则抛出ClassNotFoundException;如不在DynamicImport-Package中,则继续下面的步骤。

7)         再次尝试调用Equinox提供的ClassLoaderDelegateHook的扩展来加载

这步和第2)步相同,因此在默认情况下继续下面的步骤。

8)         尝试使用eclipsebuddy机制来加载

Buddy机制是Eclipse的扩展,并不符合OSGi规范,因此在此不做深入分析。

9)         判断一定的条件,如符合则从parent classloader中加载

判断的条件为:parent classloader不为null、不从parent classloader中加载、Equinox的向后兼容属性(osgi.compatibility.bootdelegation)为true以及jvmbug class,如满足以上条件,则尝试从parent classloader中加载。

如经过以上所有步骤后,仍然未找到需要加载的class,则抛出ClassNotFoundException

从上面的代码分析中,在Equinox中可以通过osgi.parentClassLoaderorg.osgi.framework.bootdelegation来控制从Bundle ClassLoader外来加载Class,这对于集成Equinox其他容器而言,非常有用,另外,还可以通过实现ClassLoaderDelegateHook来改变Class的加载。

posted on 2009-05-10 17:25 BlueDavy 阅读(6674) 评论(12)  编辑  收藏 所属分类: OSGi、SOA、SCA

评论

# re: Equinox加载Bundle Class的实现 2009-05-10 20:15 galaxystar

顶一下毕老大  回复  更多评论   

# re: Equinox加载Bundle Class的实现 2009-05-16 22:31 Kane

应该附带把Equniox的findLibrary一起讲了
:)  回复  更多评论   

# re: Equinox加载Bundle Class的实现[未登录] 2009-05-20 16:57 jame

今天刚好遇到一个Bundle中调用javax.xml的问题,一至报ClassNotFoundException:javax.xml.parsers.SAXParserFactory错误.

http://docs.codehaus.org/display/JETTY/OSGi+Tips
中是这么说的:
All of these, except for javax.mail, org.apache.tools.ant. and org.eclipse.jdt. can all be found in the jvm's rt.jar. In Equinox, by default, these system packages from the system classloader are not exposed inside the OSGi framework. So, we need to expose them by using this setup in the config.ini:

org.osgi.framework.system.packages=javax.naming, javax.naming.directory, javax.naming.spi, javax.management, javax.management.loading, javax.management.modelmbean, javax.net, javax.net.ssl, javax.crypto, javax.crypto.interfaces, javax.crypto.spec, javax.security.auth, javax.security.auth.spi, javax.security.auth.callback, javax.security.auth.login, javax.security.cert, javax.xml.parsers, javax.xml.xpath, javax.xml.transform.sax, javax.xml.transform.dom, javax.xml.namespace, javax.xml.transform, javax.xml.transform.stream, javax.xml.validation, org.xml.sax, org.xml.sax.helpers, org.xml.sax.ext, com.sun.org.apache.xalan.internal, com.sun.org.apache.xalan.internal.res, com.sun.org.apache.xml.internal.utils, com.sun.org.apache.xpath.internal, com.sun.org.apache.xpath.internal.jaxp, com.sun.org.apache.xpath.internal.objects, com.sun.org.apache.xml.internal, org.w3c.dom, org.w3c.dom.traversal, org.w3c.dom.ls, javax.sql, javax.transaction, sun.misc

但加上这些package后还是没有解决....最后发现要加
org.osgi.framework.bootdelegation=*这句.

找了一个下午,汗.......

BlueDavy什么时候能详细说一下config.ini中主要配置的作用吗?
如:http://tieba.baidu.com/f?kz=76392347这里列出来的这些...
期待....
  回复  更多评论   

# re: Equinox加载Bundle Class的实现 2009-05-20 17:52 Kane

import javax.* in Import-package header  回复  更多评论   

# re: Equinox加载Bundle Class的实现[未登录] 2009-05-21 09:42 jame

@Kane
Import-Package: javax.xml;version="1.3.0",
javax.xml.datatype;version="1.3.0",
javax.xml.namespace;version="1.3.0",
javax.xml.parsers;version="1.3.0",
org.osgi.framework

启动时报:
org.osgi.framework.BundleException: The bundle could not be resolved. Reason: Missing Constraint: Import-Package: javax.xml; version="1.3.0"

正常的java文件中是能import javax.xml.parsers.*;
应该是OSGI ClassLoader的特殊性.

之前在Eclipse3.4中测试是会没这个问题的,当通过命令行方式来启动equinox时才遇到这个问题.
按此BLOG中讲的在configuration\config.ini中添加org.osgi.framework.bootdelegation=*解决问题  回复  更多评论   

# re: Equinox加载Bundle Class的实现 2009-05-21 10:49 BlueDavy

@jame
...配置成*的话,所有类都会从bootstrap classloader里装载的,应该只配javax.*
  回复  更多评论   

# re: Equinox加载Bundle Class的实现 2009-05-21 11:00 Kane

@jame
org.osgi.framework.bootdelegation=*并不是好的解决办法,它让OSGi的classloader的模块化特性丢失了。
我觉得通过指定org.osgi.framework.system.packages是更好的办法,比如
-Dorg.osgi.framework.system.packages=javax.accessibility,javax.activation,javax.activity,javax.annotation, javax.annotation.processing,javax.crypto,javax.crypto.interfaces,javax.crypto.spec,javax.imageio,javax.i mageio.event,javax.imageio.metadata...
  回复  更多评论   

# re: Equinox加载Bundle Class的实现[未登录] 2009-05-21 13:19 jame

@Kane
这种方式与在config.ini配置有区别吗?
我配置在config.ini中的时候好象不想作用?

@BlueDavy
如果一个正在运行OSGI的项目中新增一个Bundle,但这个新增的Bundle需要用到java以外package中的类,有没有其他更好的方法?如果改配置那得重启整个项目了....(就如修改Eclipse中某些配置后需要重启才能生效?)
  回复  更多评论   

# re: Equinox加载Bundle Class的实现[未登录] 2009-05-21 13:28 jame

@Kane
这是我run.bat中的内容,刚才测试这种方法好象不能解决这个问题.是不是还是设置其他项?(config.ini org.osgi.framework.bootdelegation=javax.*,org.*除外)

@echo off
java -Xms128m -Xmx512m -Dorg.osgi.service.http.port=8111 -Dorg.osgi.framework.system.packages=javax.naming,javax.naming.directory,javax.naming.spi... -jar org.eclipse.osgi_3.4.3.R34x_v20081215-1030.jar -configuration ../configuration -console  回复  更多评论   

# re: Equinox加载Bundle Class的实现 2009-05-21 15:15 BlueDavy

@jame
如果新增的bundle需要用到java以外package的类,通常做法是把依赖的类也打到一个bundle,然后对外export..
  回复  更多评论   

# re: Equinox加载Bundle Class的实现 2009-05-21 15:16 BlueDavy

@jame
在Equinox中,另外一种方法就是自己实现ClassLoadingHook,就可以自己控制了。  回复  更多评论   

# re: Equinox加载Bundle Class的实现[未登录] 2009-05-21 16:55 jame

@BlueDavy
谢谢指点...

新书什么时候出版,期待....  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

公告

 









feedsky
抓虾
google reader
鲜果

导航

<2009年5月>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

统计

随笔分类

随笔档案

文章档案

Blogger's

搜索

最新评论

阅读排行榜

评论排行榜