OSGi Web启动的classloader机制(基于equinox3.5.2)
来源:互联网 发布:ubuntu 查找组 编辑:程序博客网 时间:2024/06/04 17:52
1. 使用服务器加载BridgeServlet的Classloader加载FrameworkLauncher类。
在equinox官方的BridgeServlet中,org.eclipse.equinox.servletbridge.BridgeServlet.init()方法中,首先调用该Servlet的加载classloader来加载,相关代码片段为:
然后调用framework.init(), framework.start()方法。
2. FramworkLauncher中,启动EclipseStarter类,启动OSGi容器,同时获得EclipseStarter中切换后的ThreadContextClassloader并缓存。
在framework.start()方法中,使用ChildFirstURLClassLoader加载org.eclipse.osgi_3.5.2.R35x_v20100114.jar中的org.eclipse.core.runtime.adaptor.EclipseStarter,然后缓存OSGi容器中使用的ThreadContextClassloader,代码片段如下:
通过反射调用了org.eclipse.core.runtime.adaptor.EclipseStarter两个方法:setInitialProperties和startup。setInitialProperties方法用于初始化FrameworkProperties类中的OSGi框架属性,该方法传入的属性map参数initialPropertyMap, 即是从/eclipse/launch.ini文件中获得的。frameworkContextClassLoader即为OSGi容器中真实的上ThreadContextClassloader,其实际的类型为:org.eclipse.core.runtime.internal.adaptor.ContextFinder。然后,在BridgeSerlevet中,反射调用OSGi内部类的时候,都需要将当前线程上下文加载器换成frameworkContextClassLoader,使得内部类的加载可以在OSGi上下文中执行。
关于launch.ini和eclipse/configuration/config.ini配置文件的关系,可以这样理解,launch.ini的属性优先级高于config.ini,而且launch.ini是被启动EclipseStarter的类负责解析后,在EclipseStarter启动时设置到org.eclipse.osgi.framework.internal.core.FrameworkProperties中。在org.eclipse.core.runtime.adaptor.EclipseStarter.startup(String[], Runnable)方法中,会获取config.ini文件属性,如果某个属性在FrameworkProperties为定义,则set进去。
对于Web OSGi server中,launch.ini和config.ini相关属性为:osgi.contextClassLoaderParent属性
#Let org.eclipse.core.runtime.internal.adaptor.ContextFinder's parent class loader as Framwork's classloader: That is the original class loader from server.
#fwk(the class loader of Osgi class Framework and EclipseStarter), ext(sysytem ext class loader), app(system classloader)
osgi.contextClassLoaderParent=fwk
为了解决OSGi Web Bundle中可能通过System.getProperty()和ServletContext中的属性,可能要加载server中的类的,此时会出现加载类失败的问题。这里可以设置为osgi.contextClassLoaderParent=fwk,org.eclipse.osgi.framework.internal.core.Framework.initializeContextFinder()方法中,会在初始化org.eclipse.core.runtime.internal.adaptor.ContextFinder类的时候,将之前的ChildFirstURLClassLoader(之前加载FrameworkLauncher类的org.eclipse.equinox.servletbridge.FrameworkLauncher.ChildFirstURLClassLoader)设置为其parent.
在ChildFirstURLClassLoader)类中,首先尝试从jars url中加载类,如果失败会从其parent(即为加载BridgeServlet的Classloader)加载。这样,Web bundle中可以访问到server中的类了。
org.eclipse.core.runtime.internal.adaptor.ContextFinder中,会对每个Bundle生成一个BundleClassloader,并且将自己作为每个BundleClassloader的parent。 但是,加载bundle中的类的时候,却都是直接用ContextFinder,由ContextFinder在loadClass方法中根据BundleContext.loadClass()的调用者,选择不同的BundleClassloader进行加载类。
如果ClassA为特定bundle内的类。要想获得此bundle的加载器,只能ClassA.class.getClassLoader(), 这样获得到的类型是 org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader。这是因为Bundle中Activator类始终是第一个被加载的,框架会调用Bundle.loadClass(name)加载,Acativator类的加载器就为bundle上下文中的DefualtClassLoader。后续的类都一次被Activator加载,理所当然ClassA的加载器也是DefaultClassLoader。
这里我们看看Bundle.loadClass(name)怎么实现。此处Bundle的实现类为BundleHost,此方法会转到其loadClass方法:
protected Class loadClass(String paramString, boolean paramBoolean)/* */ throws ClassNotFoundException/* */ {
// 检查类加载的权限是否满足/* 220 */ if (paramBoolean) {/* */ try {/* 222 */ this.framework.checkAdminPermission(this, "class");/* */ } catch (SecurityException localSecurityException) {/* 224 */ throw new ClassNotFoundException();/* */ }/* */ }
/* 227 */ BundleLoader localBundleLoader = checkLoader();/* 228 */ if (localBundleLoader == null)/* 229 */ throw new ClassNotFoundException(NLS.bind(Msg.BUNDLE_CNFE_NOT_RESOLVED, paramString, getBundleData().getLocation()));/* */ try {/* 231 */ return localBundleLoader.loadClass(paramString);/* */ }/* */ catch (ClassNotFoundException localClassNotFoundException)/* */ {/* 235 */ if ((!(localClassNotFoundException instanceof StatusException)) && ((this.bundledata.getStatus() & 0x2) != 0) && (!(testStateChanging(Thread.currentThread()))))/* */ try/* */ {/* 238 */ this.framework.secureAction.start(this, 1);/* */ } catch (BundleException localBundleException) {/* 240 */ this.framework.adaptor.getFrameworkLog().log(new FrameworkLogEntry("org.eclipse.osgi", 2, 0, localBundleException.getMessage(), 0, localBundleException, null));/* */ }/* 242 */ throw localClassNotFoundException;/* */ }/* */ }
localBundleLoader来自org.eclipse.osgi.framework.internal.core.BundleLoaderProxy getBundleLoader()方法,我们可以看到,如果当前bundle id=0,则代表system bundle,其他的都返回org.eclipse.osgi.framework.internal.core.BundleLoader类型。
synchronized BundleLoader getBundleLoader() {/* 47 */ if (this.loader != null)/* 48 */ return this.loader;/* 49 */ if (this.bundle.isResolved()) {/* */ try {/* 51 */ if (this.bundle.getBundleId() == 0L) {/* 52 */ this.loader = new SystemBundleLoader(this.bundle, this); break label91:/* */ }/* 54 */ this.loader = new BundleLoader(this.bundle, this);/* */ } catch (BundleException localBundleException) {/* 56 */ this.bundle.framework.publishFrameworkEvent(2, this.bundle, localBundleException);/* 57 */ return null;/* */ }/* */ }/* 60 */ label91: return this.loader;/* */ }
然后,在BundleLoader中,可是调度Bundle类加载。OSGi类加载的精华就在BundleLoader.findClassInternal方法中。
private Class findClassInternal(String paramString, boolean paramBoolean, ClassLoader paramClassLoader) throws ClassNotFoundException {/* 407 */ if (Debug.DEBUG_LOADER)/* 408 */ Debug.println("BundleLoader[" + this + "].loadBundleClass(" + paramString + ")");/* 409 */ String str = getPackageName(paramString);/* 410 */ int i = 0;/* */ /* 412 */ if ((paramBoolean) && (paramClassLoader != null) && (isBootDelegationPackage(str)));/* */ try/* */ {/* 415 */ return paramClassLoader.loadClass(paramString);/* */ }/* */ catch (ClassNotFoundException localClassNotFoundException3) {/* 418 */ i = 1;/* */ /* 420 */ Class localClass = null;/* */ try {/* 422 */ localClass = (Class)searchHooks(paramString, 1);/* */ } catch (ClassNotFoundException localClassNotFoundException1) {/* 424 */ throw localClassNotFoundException1;/* */ }/* */ catch (FileNotFoundException localFileNotFoundException1) {/* */ }/* 428 */ if (localClass != null) {/* 429 */ return localClass;/* */ }/* 431 */ PackageSource localPackageSource = findImportedSource(str, null);/* 432 */ if (localPackageSource != null)/* */ {/* 434 */ localClass = localPackageSource.loadClass(paramString);/* 435 */ if (localClass != null)/* 436 */ return localClass;/* 437 */ throw new ClassNotFoundException(paramString);/* */ }/* */ /* 440 */ localPackageSource = findRequiredSource(str, null);/* 441 */ if (localPackageSource != null)/* */ {/* 443 */ localClass = localPackageSource.loadClass(paramString);/* */ }/* 445 */ if (localClass == null)/* 446 */ localClass = findLocalClass(paramString);/* 447 */ if (localClass != null) {/* 448 */ return localClass;/* */ }/* 450 */ if (localPackageSource == null) {/* 451 */ localPackageSource = findDynamicSource(str);/* 452 */ if (localPackageSource != null) {/* 453 */ localClass = localPackageSource.loadClass(paramString);/* 454 */ if (localClass != null) {/* 455 */ return localClass;/* */ }/* 457 */ throw new ClassNotFoundException(paramString);/* */ }/* */ }/* */ /* 461 */ if (localClass == null)/* */ try {/* 463 */ localClass = (Class)searchHooks(paramString, 2);/* */ } catch (ClassNotFoundException localClassNotFoundException2) {/* 465 */ throw localClassNotFoundException2;/* */ }/* */ catch (FileNotFoundException localFileNotFoundException2)/* */ {/* */ }/* 470 */ if ((localClass == null) && (this.policy != null))/* 471 */ localClass = this.policy.doBuddyClassLoading(paramString);/* 472 */ if (localClass != null) {/* 473 */ return localClass;/* */ }/* */ /* 476 */ if ((paramClassLoader != null) && (i == 0) && ((((paramBoolean) && (this.bundle.framework.compatibiltyBootDelegation)) || (isRequestFromVM()))));/* */ try/* */ {/* 479 */ return paramClassLoader.loadClass(paramString);/* */ }/* */ catch (ClassNotFoundException localClassNotFoundException4)/* */ {/* 483 */ throw new ClassNotFoundException(paramString); } }/* */ }
这就能解释systembundle的fragement bundle发布的package,所有bundle都能访问到的原因。比如IBM Domino8中,systembundle的fargment bundle将Domino notes的api返回出来,Domino中所有的bundle都能访问NotesThread Notes Session等。WebOSGi应用也应用了此方法,将外部BridgeServlet等类发布给OSGi内部使用。也可以判断,当某个bundle和外部依赖的bundle包重名的时候,优先尝试加载外部依赖bundle中的类。在bundle classpath上设置的类,优先级当然会和定义的顺序一致。
所以,对于bundle中的某个类,Class.getClassloader(),得到的是该bundle专属的DefaultClassLoader。某种意义上,只要知道了bundle中的某个类,或者其classloader,里应外合,就能游刃有余的突破OSGi严格的类加载机制和bundle的export和依赖规则。
- OSGi Web启动的classloader机制(基于equinox3.5.2)
- Java的反射机制, ClassLoader及OSGI
- 打造一个基于OSGi的Web Application——在WebApplication中启动OSGi
- 打造一个基于OSGi的Web Application
- 打造一个基于OSGi的Web Application
- 打造一个基于OSGi的Web Application
- 打造一个基于OSGi的Web Application
- 基于OSGi bundle的Web工程
- OSGI框架自身的ClassLoader
- OSGI之budnle的启动级别机制和使用
- 基于OSGi的Web应用开发系列一(转帖)
- 基于OSGi的Web应用开发系列二(转帖)
- 基于OSGi的Web模块开发基本流程
- 基于OSGi的Virgo Server最简单Spring web实例
- OSGi.基于Spring,Hibernate的Web应用快速开发指南
- 基于OSGi的Virgo Server最简单Spring web实例
- 基于OSGi的Virgo Server最简单Spring web实例
- 基于OSGi的JSF Web组件开发问题求解
- Dialog总结(一)
- 需求,想读懂你的心很难
- PHP上传原理及应用
- 菜鸟入门之浅谈函数重载和多态的区别
- Playing Video on the iPhone and iPad 播放器
- OSGi Web启动的classloader机制(基于equinox3.5.2)
- 导入
- MATLAB中取整函数
- 网页
- 从零开始学习jQuery (二) 万能的选择器
- iphone之MPMoviePlayer本地视频 与 远程视频
- souseinsight添加文件类型
- 解决替换线程刷新
- 特征选择--文本分类: 信息增益