本文适合在部署OSGi系统【确切的是Equinox】时, 遇到问题的解决方法。 更基础理论和实践,请按照下面的链接进行索引,绝大部分案例都提供了源码。
【1】OSGI学习手册
【2】 服务端架构技术——基于OSGI服务端的架构设计和实现
【3】 OSGI 进阶学习——《OSGI In Practice》阅读总结
【4】OSGI项目持续集成(环境搭建, 编译和发布总结)
1.1运行时的ClassNotFoundException
编译时的问题都好解决, Eclipse会给足够提示
情形1: 间接引用带来的问题
原因:Plugin_1 没有直接使用Plugin_2的Java文件,但运行时使用了Plugin_2中的Jar包中的文件。
解决措施:将Plugin_2中的Jar包导出来, 同时在Plugin_1中导入这些包:
图一:导出, 使其可被访问
图二: 设置依赖, 使其可以访问
情形2: 利用String反射到类或对象的时候出现的问题
Plugin_1涉及到由String转化为类对象Plugin_2_ClassA(Plugin_2_ClassA表示Plugin_2中的Class A)的操作, 这个时候调用非常隐晦, 也需要按照情形1进行处理.
情形3: 加载顺序不对带来的问题Caused by: java.lang.ClassNotFoundException: cn.com.sany.isw.service.mixingstation.service.TruckService
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:506)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:422)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:410)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
... 16 more
Root exception:
java.lang.NoClassDefFoundError: cn/com/sany/isw/service/mixingstation/service/TruckService
at cn.com.sany.isw.service.bplpush.Activator.startAllRecievePushMsgThread(Activator.java:151)
at cn.com.sany.isw.service.bplpush.Activator.start(Activator.java:77)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl$1.run(BundleContextImpl.java:783)
at java.security.AccessController.doPrivileged(Native Method)
1. 如果是IDE环境下出现的问题, Run Configuration中配置一下启动顺序
2. 发布环境下, config.ini 需要做新的调整,先加载底层插件,再加载依赖插件,最后加载不被任何插件依赖的项目 (参见文章开始的参考资料4)
3. 发布环境下, 如果问题一再出现, 请删除\configuration目录下的org.eclipse.osgi 目录, 再进行启动
1.2运行时的NoClassDefFoundError
情形一: 发布环境下,某个需要的Jar包Class未定义
该问题在Eclipse环境下表现非常正常, 但发布出去后直接报错,这个都是由于Jar包包含引起的问题, 这里不做具体详述:
参考: 解决OSGI环境第三方包的NoClassDefFoundError
点评: 这个Error追踪了半天, 这里阐述了一个解决方案, 但原理性的解释, 还需要自己进一步探讨, 可以确定的是: 这个解决方案很凑效. 问题是这么解决的——1. 将第三方包作为Bundle 2. 在改bundle中的Manifest.MF文件中加入上文所说的.DynamicImport-Package
情形二: 发布环境下, 某个J2SE/Eclipse框架的Class文件未定义
加载org.w3c.dom, 发布的时候,出现classNotfoundErr问题, 解决方案: 在import package中,增加org.w3c.dom,版本号改为0.0.0
【本问题相当NB, 重申一下特征: 系统自带的jar包出现ClassNotFound Error, 修改版本号的位置, 从上面两个图中可以看出来】
1.3运行时报资源文件找不到
Plugin_2访问Plugin_1, 但其中Plugin_1加载了一个资源文件p1.xml,这个资源文件中p1.xml中配置了一个相对路径<relative_path>.
l 按照常理, 我们把Plugin_1中的资源文件放在Eclipse安装目录下, 这个没问题,完全可以访问。
l 那么<relative_path>中应该放在哪里? 注意,需要放在解析relative_path这个文件的类所在的classpath路径。比如,Plugin_1采用了第三方包jar_1,并将这个jar_1放置在Plugin_10中,那么,这个relative_path描述的资源文件就应该放在Plugin_10中。参考《OSGi轻量级数据库解决方案》中将描述Ibatis这个数据框架中配置问题,将有更直观认识。
1.4编译环境下,无法Import某个Class
Plugin_1中无法Import Plugin_2的jar包中的类Plugin_2_JAR_CLASS_A
l 按照1.1情形一的方式检查: Plugin_2的jar是否做了runtime导出设置,Plugin_1是否做了Dependency引入
l 是否在ClassPath中做了添加Jar包的操作,见下图的ClassPath
图三: 设置ClassPath, 将第三方包加入
【如果还是不行, 将上一步加入的Jar包删除再添加一次, 这个绝杀了大部分的问题, 另外一方面也觉得这个Bug是否太低级了一点点?】
1.5为避免死锁, 对象初始化不完全的问题
问题特征有两个错误提示:错误提示一: To avoid deadlock, is proceeding but may not be fully initialized
!MESSAGE While loading class "cn.com.sany.isw.service.m2m.M2MDB", thread "Thread[Thread-6,5,main]" timed out waiting (5000ms) for thread "Thread[Start Level Event Dispatcher,5,main]" to finish starting bundle "cn.com.sany.isw.service.m2m_1.0.0 [21]". To avoid deadlock, thread "Thread[Thread-6,5,main]" is proceeding but "cn.com.sany.isw.service.m2m.M2MDB" may not be fully initialized.
错误提示二: Unknow resource
org.osgi.framework.BundleException: State change in progress for bundle "initial@reference:file:E:/projects/osgi/ISW_2012_6/cn.com.sany.isw.service.m2m/" by thread "Start Level Event Dispatcher".
at org.eclipse.osgi.framework.internal.core.AbstractBundle.beginStateChange(AbstractBundle.java:1077)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:282)
at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:417)
at org.eclipse.osgi.internal.loader.BundleLoader.setLazyTrigger(BundleLoader.java:265)
at org.eclipse.core.runtime.internal.adaptor.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:106)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(ClasspathManager.java:453)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(DefaultClassLoader.java:216)
at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:393)
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:469)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:422)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:410)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
at java.lang.ClassLoader.loadClass(Unknown Source)
at cn.com.sany.isw.service.m2m.shareData.ThreadHashGpsDev.run(ThreadHashGpsDev.java:65)
Caused by: org.eclipse.osgi.framework.internal.core.AbstractBundle$BundleStatusException
... 14 more
Root exception:
org.eclipse.osgi.framework.internal.core.AbstractBundle$BundleStatusException
at org.eclipse.osgi.framework.internal.core.AbstractBundle.beginStateChange(AbstractBundle.java:1077)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:282)
at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:417)
at org.eclipse.osgi.internal.loader.BundleLoader.setLazyTrigger(BundleLoader.java:265)
at org.eclipse.core.runtime.internal.adaptor.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:106)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(ClasspathManager.java:453)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(DefaultClassLoader.java:216)
at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:393)
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:469)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:422)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:410)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
at java.lang.ClassLoader.loadClass(Unknown Source)
at cn.com.sany.isw.service.m2m.shareData.ThreadHashGpsDev.run(ThreadHashGpsDev.java:65)
该问题出现的根源是: 线程里面调用了一个需要花费大量时间初始化的类或者对象, 上述问题就是由Thread中的run方法需要调用一个类【】的静态方法, 但这个类的其他变量初始化实在是太消耗时间了, 这样非常容易导致deadlock. 于是系统为了避免deadlock, 就没有完全加载这个类从而导致问题的爆发. 这个是问题的根源, 在Thread的run方法中,有这样一个调用:
InstInfo info = M2MDB.getInstallInfoByDevid(devid);
我解决的方法是, 线程启动之前,就调用了一个什么都不干的类, 比如在这个Thread初始化的时候,进行一次调用M2MDB的空方法:
public static ThreadHashGpsDev getInstance()
{
M2MDB.xx();
if( null == instance)
{
synchronized(lockInstace)
{
if( null == instance)
{
instance = new ThreadHashGpsDev();
initFromDB_help();
}
}
}
return instance;
}
从而解决了整个问题。