Java模块化

1. OSGi

1.1 简介

1.1.1 什么是OSGi

1.1.1.1 定义

开放服务网关倡议(OSGi,Open Service Gateway Initiative)有双重含义。一方面它指OSGi Alliance组织;另一方面指该组织制定的一个基于Java语言的服务(业务)的动态模块化规范——OSGi服务平台(Service Platform)。

该规范和核心部分是一个框架,其中定义了应用程序的生命周期模式和服务注册。基于这个框架定义了大量的OSGi服务:日志、配置管理、偏好,HTTP(运行servlet)、XML分析、设备访问、软件包管理、许可管理、星级、用户管理、IO连接、连线管理、Jini和UPnP。

这个框架实现了一个优雅、完整和动态的组件模型。应用程序(称为bundle)无需重新引导可以被远程安装、启动、升级和卸载(其中Java包/类的管理被详细定义)。API中还定义了运行远程下载管理政策的生命周期管理。服务注册允许bundles去检测新服务和取消的服务,然后相应配合。

今天,OSGi的已经不再是原来Open Service Gateway Initiative的字面意义能涵盖的了,OSGi联盟给出的最新OSGi定义是The Dynamic Module System for Java,即面向Java的动态模块化系统。

1.1.1.2 演进

OSGi在R4版之前都处于初级阶段,OSGi规范在初级阶段一共发布了三个版本:

  • OSGi Release 1(R1):2000年5月发布。

  • OSGi Release 2(R2):2001年10月发布。

  • OSGi Release 3(R3):2003年3月发布。

2005年8月,OSGi R4版本发布,R4总共有5个版本,分别是R4 Version 4.0、4.0.1、4.1、4.2、4.3, 从OSGi R4版开始,OSGi的目标就从“在移动和嵌入式设备上的Java模块化应用”发展为“Java模块化应用”,去掉了“在移动和嵌入式设备上的”这个限定语,这意味着OSGi开始脱离Java ME的约束,向Java其他领域进军。

2012年7月,OSGi R5发布,R5的一个主要目标是建立一套基于OSGi的模块仓库系统。

目前最新版本为R9版本,正在开发中。各个版本具体内容参见OSGi specification

1.1.1.3 架构

OSGi框架被划分为下面几个层次:

  • Security Layer

  • Module Layer

  • Life Cycle Layer

  • Service Layer

  • Actual Services

整体架构设计如下:

安全层贯穿了框架整体逻辑,基于Java security,新增若干限制并填补了Java security的一些空白,安全层定义了一个安全的包的格式以及运行时同Java sercurity层交互的方式。

模块层定义了Java模块化模型,解决了Java部署模型的一些不足,模块层在Bundle间package的共享或者隐藏方面有严格的规则,模块层能够单独使用,生命周期层提供了管理模块层Bundle的API,服务层则提供了Bundle间通信的方式

生命周期层为Bundle提供了运行时的生命周期模型,定义了Bundle如何安装、更新、启动、停止、卸载,此外,生命周期层还提供了全面的事件API帮助Bundle控制OSGi框架的控制操作,生命周期层依赖于模块层

服务层为Bundle开发者提供了动态的,简单的持续的编程模型,简化了服务接口和实现解耦的Bundle的开发和部署,服务层允许开发者使用接口来绑定服务

各个层之间交互如下:

模块层、生命周期层以及服务层是框架的重点,在后续会详细进行讲解,此处先提一下有个印象

1.1.2 OSGi 和 jigsaw 的恩怨纠葛

Java 9 在2017年发布,其中一个重要的特性就是新的模块化系统,被称作Java平台模块系统(Java Platform Module System,JPMS)。即我们常说的jigsaw(拼图)。

“早在2007年,Sun公司就提出过JSR-277:Java模块系统(Java Module System),试图建立Java平台的模块化标准,但受挫于以IBM公司为主导提交的JSR-291:Java SE动态组件支持(Dynamic ComponentSupport for Java SE,这实际就是OSGi R4.1)。由于模块化规范主导权的重要性,Sun公司不能接受一个无法由它控制的规范,在整个Java SE 6期间都拒绝把任何模块化技术内置到JDK之中。在Java SE 7发展初期,Sun公司再次提交了一个新的规范请求文档JSR-294:Java编程语言中的改进模块性支持(Improved Modularity Support in the Java Programming Language),尽管这个JSR仍然没有通过,但是Sun公司已经独立于JCP专家组在OpenJDK里建立了一个名为Jigsaw(拼图)的子项目来推动这个规范在Java平台中转变为具体的实现。Java的模块化之争目前还没有结束,OSGi已经发布到R5.0版本,而Jigsaw从Java 7延迟至Java 8,在2012年7月又不得不宣布推迟到Java 9中发布,从这点看来,Sun在这场战争中处于劣势,但无论胜利者是哪一方,Java模块化已经成为一项无法阻挡的变革潮流。”                            ————《深入理解Java虚拟机(第二版)》

“早在2008年,在Java社区关于模块化规范的第一场战役里,由Sun/Oracle公司所提出的JSR-294、JSR-277规范提案就曾败给以IBM公司主导的JSR-291(即OSGi R4.2)提案。尽管Sun/Oracle并不甘心就此失去Java模块化的主导权,随即又再拿出Jigsaw项目迎战,但此时OSGi已经站稳脚跟,成为业界“事实上”的Java模块化标准。曾经在很长一段时间内,IBM凭借着OSGi广泛应用基础让Jigsaw吃尽苦头,其影响一直持续到Jigsaw随JDK 9面世才算告一段落。而且即使Jigsaw现在已经是Java的标准功能了,它仍需小心翼翼地避开OSGi运行期动态热部署上的优势,仅局限于静态地解决模块间封装隔离和访问控制的问题

Java模块化系统目前不支持在模块定义中加入版本号来管理和约束依赖,本身也不支持多版本号的概念和版本选择功能,尽管我们不论是在Java命令、Java类库的API抑或是《Java虚拟机规范》定义的Class文件格式里都能轻易地找到证据,表明模块版本应是编译、加载、运行期间都可以使用的,并且我们不论是在Java命令、Java类库的API抑或是《Java虚拟机规范》定义的Class文件格式里都能轻易地找到证据,表明模块版本应是编译、加载、运行期间都可以使用的,这一切迹象都证明了Java模块化系统对版本号的支持本可以不局限在编译期。而官方却在Jigsaw的规范文件、JavaOne大会的宣讲和与专家的讨论列表中,都反复强调“JPMS的目的不是代替OSGi”,“JPMS不支持模块版本”这样的话语

Oracle给出的理由是希望维持一个足够简单的模块化系统,避免技术过于复杂。但结合JCP执行委员会关于的Jigsaw投票中Oracle与IBM、RedHat的激烈冲突,实在很难让人信服这种设计只是单纯地基于技术原因,而不是厂家之间互相博弈妥协的结果。Jigsaw仿佛在刻意地给OSGi让出一块生存空间,以换取IBM支持或者说不去反对Jigsaw,其代价就是几乎宣告Java模块化系统不可能拥有像OSGi那样支持多版本模块并存、支持运行时热替换、热部署模块的能力,可这却往往是一个应用进行模块化的最大驱动力所在。如果要在JDK 9之后实现这种目的,就只能将OSGi和JPMS混合使用,如图7-4所示,这无疑带来了更高的复杂度。模块的运行时部署、替换能力没有内置在Java模块化系统和Java虚拟机之中,仍然必须通过类加载器去实现,实在不得不说是一个缺憾。”

                           ————《深入理解Java虚拟机(第三版)》

1.2 模块层

1.2.1 Bundle

OSGi是基于Java的模块化系统,那么Bundle就是模块,其实Bundle并不神秘,他就是我们常用的jar包,只不过在MANIFEST.MF文件中添加了大量的扩展定义,Bundle使用jar来实现,有个非常大的好处是兼容性,不支持OSGi的项目也能直接使用OSGi的Bundle,只不过没有Bundle的各种功能而已。

Bundle有一种特殊的Bundle,Fragment Bundle,他不能单独进行使用,必须依附于普通的Bundle来使用,经常用他来提供某种可选项,以及隔离Bundle中经常变动的部分,比如配置文件等,如果将变动部分置于Fragment Bundle中,那么通过切换不同的Fragment Bundle,我们可以进行配置的快速切换。

静态角度(开发期)来看,Fragment Bundle与普通Bundle没有太大区别,它们都以JAR文件格式为基础,具备相同的元数据信息标记,标记的含义与设置方式也一样。区别仅仅是Fragment Bundle的元数据中会使用Fragment-Host标记说明它的宿主Bundle。

动态角度(运行期)来看,Fragment Bundle与普通Bundle在运行时的处理差别却非常大,最重要的一点差异是Fragment Bundle不具备自己独立的类加载器。OSGi利用每个Bundle独立的类加载器互相协作来维护Bundle间导入、导出的依赖关系。没有类加载器,就无法直接与其他Bundle交互,必须依附于宿主,使用宿主Bundle的类加载器完成。

1.2.2 描述元数据

描述元数据就是OSGi框架可以识别的,我们在MANIFEST.MF文件中添加的自定义扩展,扩展数量很大,最新R9版本有30个扩展项(随着版本的更新,扩展数量也会发生变动),详情请参考Bundle Manifest Headers

标记作用
Bundle-ActivationPolicy设置Bundle的加载策略,该参数目前只有一个值:lazy,设置该参数后,Bundle将延迟激活,延迟至有其他的Bundle请求加载该Bundle中的类或资源时它才会被激活,如果不设置这个参数,那么Bundle启动时就会被激活。
Bundle-Activator指明一个Activator类,在Bundle启动和停止时会分别调用该类的start()和stop()方法,以便执行程序员所希望的动作
Bundle-Category指明该Bundle的功能类别,可使用逗号分隔多个类别名称。这个功能类别仅供人工分类和阅读,OSGi框架并不会使用它。
Bundle-Classpath指明该Bundle所引用的类路径,该路径应为Bundle包内部的一个合法路径,如果有多个Classpath,使用逗号分隔。
Bundle-ContactAddress描述Bundle发行者的联系信息,仅供人工阅读,OSGi框架并不会使用它。
Bundle-Copyright描述Bundle的版权信息,仅供人工阅读,OSGi框架并不会使用它。
Bundle-Description给出关于该Bundle的简短描述信息,仅供人工阅读,OSGi框架并不会使用它。
Bundle-DocURL给出该Bundle文档的链接地址,仅供人工阅读,OSGi框架并不会使用它。
Bundle-Icon给出该Bundle的显示图标,图标应为一张正方形的图片,并通过参数size指出图标的宽度。OSGi规范要求实现框架至少要支持PNG图片格式。
Bundle-License给出该Bundle的授权协议信息。
Bundle-Localization给出该Bundle在不同语言系统下的本地化信息,如果不设置此标记,它的默认值为OSGI-INF/l10n/bundle。
Bundle-ManifestVersion指出该Bundle应遵循哪个版本的OSGi规范,默认值为1。对于OSGi R3规范,该值为1;对于OSGi R4/R5规范,该值为2。
Bundle-Name定义该Bundle的名称。注意该名称只供人工阅读,在Bundle-SymbolicName标记中定义的名称才会作为程序使用的Bundle的唯一标识来使用
Bundle-NativeCode如果Bundle中需要使用JNI加载其他语言实现的本地代码,那么必须使用Bundle-NativeCode标记进行说明。
Bundle-RequiredExecutionEnvironment定义该Bundle所需的执行环境,支持多种执行环境的Bundle使用逗号分隔
Bundle-SymbolicName给出该Bundle在OSGi容器中的全局唯一标识符。与其他可选标记不同,这个标记没有默认值,并且是Bundle元数据信息之中唯一一个必须设置的标记程序将基于此标记和版本号在OSGi容器中定位到一个独一无二的Bundle
Bundle-UpdateLocation给出Bundle的网络更新地址。如果Bundle需要更新版本,将使用这个地址。
Bundle-Vendor给出该Bundle的发行者信息。
Bundle-Version出该Bundle的版本信息,默认值为“0.0.0”。注意,这项信息并不是仅供人工阅读的,“版本”在OSGi中是一项受系统管理的信息,版本号是有序的,完整的版本号会由“主版本号(Major)”+“副版本号(Minor)”+“微版本号(Micro)”+“限定字符串(Qualifier)”构成。
DynamicImport-Package描述运行时动态导入的Package
Export-Package描述被导出的Package
Export-Service描述被导出的服务,这个标记在OSGi规范中已经被声明为Deprecated了,不推荐继续使用此标记。
Fragment-Host当该Bundle是一个Fragment Bundle时,标记Fragment-Host指明它的宿主Bundle。
Import-Package描述该Bundle需要导入的Package
Import-Service描述导入的服务。这个标记在OSGi规范中已经被声明为Deprecated了,不推荐继续使用此标记。
Provided-Capability描述该Bundle提供的服务特性,在R4.3规范中加入了Provided-Capability和Require-Capability来声明Bundle所需要和能够提供的特性。
Require-Capability描述该Bundle所需要的服务特性
Require-Bundle描述该Bundle所依赖的其他Bundle,一旦声明了依赖某个Bundle,就意味着可以直接使用所有从这个Bundle中导出的Package

1.2.3 OSGi 类加载模型

1.2.3.1 模型

模块化的关键目标是实现可配置的封装隔离机制,OSGi通过class loader实现隔离,我们知道,对于任意一个类,都需要由加载它的类加载器和这个类本身一起共同确定其在Java虚拟机中的唯一性,每一个类加载器都拥有一个独立的类名称空间。

传统的Java的类加载模型是双亲委派模型,如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。

OSGi的类加载架构并未遵循Java所推荐的双亲委派模型,它的类加载器通过严谨定义的规则从Bundle的一个子集中加载类。除了Fragment Bundle外,每一个被正确解析的Bundle都有一个独立的类加载器支持,这些类加载器之间互相协作形成了一个类加载的代理网络架构,因此OSGi中采用的是网状的类加载架构,而不是Java传统的树状类加载架构,如下所示:

1.2.3.2 类加载器类型

在OSGi中,类加载器可以划分为3类:父类加载器Bundle类加载器其它类加载器

父类加载器:由Java平台直接提供,最典型的场景包括启动类加载器(BootstrapClassLoader)、扩展类加载器(Extension ClassLoader)和应用程序类加载器(Application ClassLoader)。它们用于加载以“java.*”开头的类以及在父类委派清单中声明为要委派给父类加载器加载的类。

Bundle类加载器:每个Bundle都有自己独立的类加载器,用于加载本Bundle中的类和资源

其它类加载器:譬如线程上下文类加载器、框架类加载器等。它们并非OSGi规范中专门定义的,但是为了实现方便,在许多OSGi框架中都会使用。例如框架类加载器,OSGi框架实现一般会将这个独立的框架类加载器用于加载框架实现的类和关键的服务接口类

1.2.3.3 类加载顺序

Bundle中加载类的顺序如下所示:

1.3 生命周期层

OSGi规范把模块化“静态”的一面,比如如何描述元数据、如何加载模块中的类和资源等内容定义于模块层规范之中;而把模块化“动态”的一面,比如模块从安装到解析、启动、停止、更新、卸载的过程,以及在这些过程中的事件监听和上下文支持环境等定义于生命周期层(Life Cycle Layer)之中。

1.3.1 Bundle状态及转换

生命周期层规范定义了Bundle生命周期过程之中的6种状态,分别是:UNINSTALLED(未安装)、INSTALLED(已安装)、RESOLVED(已解析)、STARTING(启动中)、STOPPING(停止中)、ACTIVE(已激活),状态间的转换如图所示:

1.3.2 事件监听

事件监听在OSGi中是一种很常见的设计模式,在Bundle生命周期的不同状态相互转换时,OSGi框架会发布出各种不同的事件供事先注册好的事件监听器处理,这些事件被称为“生命周期层事件”。OSGi框架支持的生命周期层事件包括继承于BundleEvent类的,用于报告Bundle的生命周期改变的Bundle事件,以及继承于FrameworkEvent类的,用于报告框架的启动、启动级别的改变、包的更新或捕获错误的框架事件。BundleEvent和Framework Event如下所示:

 

 

 事件类型的整型值使用位掩码来描述,这便于一个事件监听器同时处理多种事件类型

1.3.3 上下文

OSGi容器中运行的各个Bundle共同构成了一个微型的生态系统,Bundle的许多行为都无法孤立进行,必须在特定的上下文环境中才有意义,因为要与上下文的其他Bundle或OSGi框架进行互动。在代码中使用BundleContext对象来代表上下文环境,当Bundle启动的时候,OSGi框架就创建这个Bundle的BundleContext对象,直到Bundle停止运行从OSGi容器卸载为止。上下文可以进行以下操作:

  • 安装一个新的Bundle到当前OSGi容器之中(BundleContext.installBundle()方法)。

  • 从当前OSGi容器中获取已有的Bundle(BundleContext.getBundle()方法)。

  • 在OSGi容器中注册服务(BundleContext.registerService()方法)。

  • 从当前OSGi容器中获取已有的服务(BundleContext.getService()方法)。

  • 在OSGi容器中注册事件监听器(BundleContext.addBundleListener()、BundleContext.addFrameworkListener()方法)。

  • 从Bundle的持久储存区中获取文件(BundleContext.getDataFile()方法)。

  • 获取Bundle所处环境的环境属性(BundleContext.getProperty()方法)。

每个Bundle的BundleContext对象都是在调用Bundle.start()方法时由OSGi框架创建并以方法参数的形式传入到Bundle中,实现Bundle时一般会把这个对象的引用保留起来,以便在Bundle其他代码中使用。上下文对象涉及Bundle的数据安全和资源分配,它应当是Bundle私有的,不应当传递给其他Bundle使用。在Bundle的stop()方法执行完之后,Bundle对象就会随之失效,如果在Bundle停止后继续使用BundleContext对象,那么OSGi框架就会抛出IllegalStateException异常。

1.4 服务层

服务层规范描述了OSGi框架下用户自定义服务和框架标准服务的注册、查找和使用等操作。“服务”在基于OSGi架构实现的系统中非常常见,并不神秘,本质上一个服务就是一个普通的Java对象实例,一般来说,这个Java对象还实现了某个或某些接口。OSGi服务层的目的在于支持模块间对象级别的交互操作,而前面介绍的模块层和生命周期层则支持模块间包(类)级别的交互

1.4.1 服务

在OSGi系统中,服务是不能孤立存在的,每个服务都从属并运行在提供服务的Bundle上。Bundle要提供服务供其他模块使用,首先要把服务注册到一个由OSGi框架提供、被所有Bundle共享的服务注册表(Service Registry)中。其他Bundle使用服务时只需从注册表中查找所需的服务而不与提供服务的Bundle直接交互,因此并不需要关心该服务来源于哪个Bundle,更不需要导入提供服务的Bundle或它发布的Package。

1.4.2 服务属性

1.4.2.1 定义

在注册服务的BundleContext.registerService()方法中,最后一个参数用来为服务提供者定义这个服务的相关属性,这些属性以Key-Value值对的方式保存,可以由开发人员自主确定,唯一的要求是Key值不能重复。

OSGi规范预定义了几个服务的标准属性,其中“service.id”和“service.ranking”两个属性定义了在多个可选服务同时满足条件时,按照服务的优先级别选择。

1.4.2.2 属性过滤器

为了便于用户根据属性选择最佳的服务,OSGi框架提供了基于RFC 1960语法[1]的属性过滤器支持。其语法定义定义如下:

<filter> ::= '('<filtercomp>')'
<filtercomp> ::= <and>|<or>|<not>|<item>
<and> ::= '&'<filterlist>
<or> ::= '|'<filterlist>
<not> ::= '!'<filter>
<filterlist> ::= <filter>|<filter><filterlist>
<item> ::= <simple>|<present>|<substring>
<simple> ::= <attr><filtertype><value>
<filtertype> ::= <equal>|<approx>|<greater>|<less>
<equal> ::= '='
<approx> ::= '~='
<greater> ::= '>='
<less> ::= '<='
<present> ::= <attr>'=*'
<substring> ::= <attr>'='<initial><any><final>
<initial> ::= NULL|<value>
<any> ::= '*'<starval>
<starval> ::= NULL|<value>'*'<starval>
<final> ::= NULL|<value>

下面这条语句为过滤出实现了HTTP Service服务接口并且开放了80端口的服务: (&(objectClass=org.osgi.service.http.HttpService)(port=80))

1.4.3 服务工厂

OSGi中默认的服务就是单例服务,我们可以通过服务工厂来支持多例服务以及服务的延迟创建,在其他Bundle请求该服务时,服务工厂的实现类将接管该请求,就可以在ServiceFactory中为每个Bundle新建一个服务对象(取决于它的getService()是如何实现的),并可以将真实服务的创建时间延迟到有Bundle真正使用该服务的时候。

需要注意的是,服务工厂返回的“多例”是相对于多个Bundle调用而言的,即多个Bundle多个实例。在同一个Bundle中,OSGi容器对服务工厂的返回结果做了缓存,即使多次调用返回的仍然是同一个实例

1.4.4 服务跟踪器

OSGi是天然的动态化执行环境,一个服务在目前可能是可用的,下一刻就会随着Bundle停止而中断,再过几分钟又可能随着Bundle更新或启用而产生一个新版本的服务实例。为了不让服务使用者感到迷惑和复杂,需要一个能够持续追踪服务可用性的解决方案来代替使用者去处理服务可用性的问题。OSGi框架提供的服务跟踪器(Service Tracker)就是基于这种需求背景而引入的。

服务跟踪器由org.osgi.util.tracker.ServiceTracker类实现,它的作用是监视服务何时被添加、何时被移除(在添加和移除服务时框架会自动调用addingService()和removedService()方法,用户可继承ServiceTracker类覆盖这两个方法以实现服务变动时的处理逻辑)以及在服务可用的时候获取服务实例(getService()、getServices()和waitForService()方法用于返回服务实例)。

1.4.5 引用服务

OSGi框架推荐的服务使用方式是在每次使用时,动态的从BundleContext、ServiceReference和ServiceTracker中获取服务实例,将其存放在方法的局部变量中,在方法结束的时候对服务实例的引用会随着方法栈帧一起销毁。因为OSGi自身的动态执行环境,服务在OSGi环境中随时都可能会被停用。

如果长期持有服务实例可能导致什么问题呢?

从一开始我们就知道所谓服务并不神秘,它只是一个普通的Java对象,存在引用的Java对象当然不可能无缘无故地自动销毁掉[1]。持有服务对象不一定会导致程序报错,但是肯定会为系统的内存回收带来压力,因为只要有一个对象实例还存在引用,它的接口、实现类、实现类中引用的其他类,以及加载这些类的类加载器都无法被垃圾收集器回收。

并且当Bundle重新安装后,OSGi会为Bundle构造一个新的类加载器,这样一个类在内存中便同时存在两份副本,当Bundle多次更新后,很可能形成内存泄露。除此之外,当服务更新后,如果逻辑中没有更新操作,那么会导致服务实例得不到更新。

因此OSGi推荐每次使用服务时,都动态获取,尽管可能造成一定的性能损失,但这样做是值得的。

2. Equinox 实战

equinox 是 OSGI 的规范的一个实践,是实现了所有 OSGI 定义的规范的一个框架,可以使用 equinox 来开发以及运行 osgi 项目

2.1 equinox 环境搭建

2.1.1 版本选择

环境搭建使用我们最常使用的 IntelliJ IDEA + Equinox进行搭建,其中,Equinox版本为4.11,因为在搭建过程中遇到许多问题,最新版本的 Equinox 至今仍未搭建成功,因此建议使用我指定版本进行搭建,不然会遇到启动控制台失败的问题,问题异常如下:

!SESSION 2021-11-2 14:49:08.594 ----------------------------------------------- eclipse.buildId=unknown java.version=1.8.0_231 java.vendor=Oracle Corporation BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=zh_CN Command-line arguments: -console

!ENTRY org.eclipse.equinox.console 4 0 2021-10-22 14:49:09.437 !MESSAGE FrameworkEvent ERROR !STACK 0 org.osgi.framework.BundleException: Could not resolve module: org.eclipse.equinox.console [1] Unresolved requirement: Import-Package: org.osgi.service.cm; version="[1.4.0,2.0.0)"; resolution:="optional" Unresolved requirement: Require-Capability: org.apache.felix.gogo; filter:="(org.apache.felix.gogo=runtime.implementation)" -> Provide-Capability: org.apache.felix.gogo; org.apache.felix.gogo="runtime.implementation"; version:Version="1.0.0" org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event Unresolved requirement: Import-Package: org.apache.felix.service.command; version="[1.0.0,2.0.0)" -> Export-Package: org.apache.felix.service.command; bundle-symbolic-name="org.apache.felix.gogo.runtime"; bundle-version="1.1.4.v20210111-1007"; version="1.0.0"

at org.eclipse.osgi.container.Module.start(Module.java:463)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.lambda$1(ModuleContainer.java:1834)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1829)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1777)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1739)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1661)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1)
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:228)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:339)

!ENTRY org.apache.felix.gogo.runtime 4 0 2021-11-2 14:49:09.439 !MESSAGE FrameworkEvent ERROR !STACK 0 org.osgi.framework.BundleException: Could not resolve module: org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event

at org.eclipse.osgi.container.Module.start(Module.java:463)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.lambda$1(ModuleContainer.java:1834)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1829)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1777)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1739)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1661)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1)
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:228)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:339)

!ENTRY org.apache.felix.gogo.shell 4 0 2021-11-2 14:49:09.440 !MESSAGE FrameworkEvent ERROR !STACK 0 org.osgi.framework.BundleException: Could not resolve module: org.apache.felix.gogo.shell [3] Unresolved requirement: Import-Package: org.apache.felix.service.command; version="[1.0.0,2.0.0)" -> Export-Package: org.apache.felix.service.command; bundle-symbolic-name="org.apache.felix.gogo.runtime"; bundle-version="1.1.4.v20210111-1007"; version="1.0.0" org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event

at org.eclipse.osgi.container.Module.start(Module.java:463)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.lambda$1(ModuleContainer.java:1834)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1829)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1777)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1739)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1661)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1)
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:228)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:339)

!ENTRY org.apache.felix.gogo.command 4 0 2021-11-2 14:49:09.441 !MESSAGE FrameworkEvent ERROR !STACK 0 org.osgi.framework.BundleException: Could not resolve module: org.apache.felix.gogo.command [4] Unresolved requirement: Import-Package: org.apache.felix.service.command; version="[1.0.0,2.0.0)" -> Export-Package: org.apache.felix.service.command; bundle-symbolic-name="org.apache.felix.gogo.runtime"; bundle-version="1.1.4.v20210111-1007"; version="1.0.0" org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event

at org.eclipse.osgi.container.Module.start(Module.java:463)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.lambda$1(ModuleContainer.java:1834)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1829)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1777)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1739)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1661)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1)
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:228)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:339)

!ENTRY org.eclipse.osgi 4 0 2021-11-2 14:49:09.442 !MESSAGE Bundle initial@file:/E:/equinox-SDK-4.22M1/plugins/org.eclipse.equinox.console_1.4.400.v20210602-1312.jar was not resolved.

!ENTRY org.eclipse.osgi 4 0 2021-11-2 14:49:09.443 !MESSAGE Bundle initial@file:/E:/equinox-SDK-4.22M1/plugins/org.apache.felix.gogo.runtime_1.1.4.v20210111-1007.jar was not resolved.

!ENTRY org.eclipse.osgi 4 0 2021-11-2 14:49:09.444 !MESSAGE Bundle initial@file:/E:/equinox-SDK-4.22M1/plugins/org.apache.felix.gogo.shell_1.1.4.v20210111-1007.jar was not resolved.

!ENTRY org.eclipse.osgi 4 0 2021-11-2 14:49:09.445 !MESSAGE Bundle initial@file:/E:/equinox-SDK-4.22M1/plugins/org.apache.felix.gogo.command_1.1.2.v20210111-1007.jar was not resolved.

!ENTRY org.eclipse.osgi 4 0 2021-11-2 14:49:09.447 !MESSAGE Could not start bundle: org.eclipse.equinox.console !STACK 0 org.osgi.framework.BundleException: Could not start bundle: org.eclipse.equinox.console at org.eclipse.core.runtime.internal.adaptor.ConsoleManager.checkForConsoleBundle(ConsoleManager.java:67) at org.eclipse.core.runtime.adaptor.EclipseStarter.startup(EclipseStarter.java:351) at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:251) at org.eclipse.core.runtime.adaptor.EclipseStarter.main(EclipseStarter.java:228) Caused by: org.osgi.framework.BundleException: Could not resolve module: org.eclipse.equinox.console [1] Unresolved requirement: Import-Package: org.osgi.service.cm; version="[1.4.0,2.0.0)"; resolution:="optional" Unresolved requirement: Require-Capability: org.apache.felix.gogo; filter:="(org.apache.felix.gogo=runtime.implementation)" -> Provide-Capability: org.apache.felix.gogo; org.apache.felix.gogo="runtime.implementation"; version:Version="1.0.0" org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event Unresolved requirement: Import-Package: org.apache.felix.service.command; version="[1.0.0,2.0.0)" -> Export-Package: org.apache.felix.service.command; bundle-symbolic-name="org.apache.felix.gogo.runtime"; bundle-version="1.1.4.v20210111-1007"; version="1.0.0"

at org.eclipse.osgi.container.Module.start(Module.java:463)
at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:439)
at org.eclipse.core.runtime.internal.adaptor.ConsoleManager.checkForConsoleBundle(ConsoleManager.java:65)
... 3 more

Root exception: org.osgi.framework.BundleException: Could not resolve module: org.eclipse.equinox.console [1] Unresolved requirement: Import-Package: org.osgi.service.cm; version="[1.4.0,2.0.0)"; resolution:="optional" Unresolved requirement: Require-Capability: org.apache.felix.gogo; filter:="(org.apache.felix.gogo=runtime.implementation)" -> Provide-Capability: org.apache.felix.gogo; org.apache.felix.gogo="runtime.implementation"; version:Version="1.0.0" org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event Unresolved requirement: Import-Package: org.apache.felix.service.command; version="[1.0.0,2.0.0)" -> Export-Package: org.apache.felix.service.command; bundle-symbolic-name="org.apache.felix.gogo.runtime"; bundle-version="1.1.4.v20210111-1007"; version="1.0.0"

at org.eclipse.osgi.container.Module.start(Module.java:463)
at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:439)
at org.eclipse.core.runtime.internal.adaptor.ConsoleManager.checkForConsoleBundle(ConsoleManager.java:65)
at org.eclipse.core.runtime.adaptor.EclipseStarter.startup(EclipseStarter.java:351)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:251)
at org.eclipse.core.runtime.adaptor.EclipseStarter.main(EclipseStarter.java:228)

!ENTRY org.apache.felix.gogo.command 2 0 2021-11-2 14:49:09.451 !MESSAGE Could not resolve module: org.apache.felix.gogo.command [4] Unresolved requirement: Import-Package: org.apache.felix.service.command; version="[1.0.0,2.0.0)" -> Export-Package: org.apache.felix.service.command; bundle-symbolic-name="org.apache.felix.gogo.runtime"; bundle-version="1.1.4.v20210111-1007"; version="1.0.0" org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event

!ENTRY org.apache.felix.gogo.runtime 2 0 2021-11-2 14:49:09.452 !MESSAGE Could not resolve module: org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event

!ENTRY org.eclipse.equinox.console 2 0 2021-11-2 14:49:09.453 !MESSAGE Could not resolve module: org.eclipse.equinox.console [1] Unresolved requirement: Import-Package: org.osgi.service.cm; version="[1.4.0,2.0.0)"; resolution:="optional" Unresolved requirement: Require-Capability: org.apache.felix.gogo; filter:="(org.apache.felix.gogo=runtime.implementation)" -> Provide-Capability: org.apache.felix.gogo; org.apache.felix.gogo="runtime.implementation"; version:Version="1.0.0" org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event Unresolved requirement: Import-Package: org.apache.felix.service.command; version="[1.0.0,2.0.0)" -> Export-Package: org.apache.felix.service.command; bundle-symbolic-name="org.apache.felix.gogo.runtime"; bundle-version="1.1.4.v20210111-1007"; version="1.0.0"

!ENTRY org.apache.felix.gogo.shell 2 0 2021-11-2 14:49:09.455 !MESSAGE Could not resolve module: org.apache.felix.gogo.shell [3] Unresolved requirement: Import-Package: org.apache.felix.service.command; version="[1.0.0,2.0.0)" -> Export-Package: org.apache.felix.service.command; bundle-symbolic-name="org.apache.felix.gogo.runtime"; bundle-version="1.1.4.v20210111-1007"; version="1.0.0" org.apache.felix.gogo.runtime [2] Unresolved requirement: Import-Package: org.osgi.service.event

!ENTRY org.eclipse.osgi 4 0 2021-11-2 14:49:09.456 !MESSAGE Application error

2.1.2 Equinox控制台环境搭建流程

  1. 官网下载 Equinox 4.11 版本,下载地址为:4.11版本下载地址,最新版本下载地址为:最新版本下载地址

  2. Equinox 下载好以后,解压,然后新增文件 equinox-SDK-4.11\plugins\configuration\config.ini,文件内容如下:

    osgi.bundles=file\:org.eclipse.equinox.console_1.4.400.v20210602-1312.jar@start,file:\org.apache.felix.gogo.runtime_1.1.4.v20210111-1007.jar@start,file:\org.apache.felix.gogo.shell_1.1.4.v20210111-1007.jar@start,file:\org.apache.felix.gogo.command_1.1.2.v20210111-1007.jar@start

  3. 尝试使用 java -jar org.eclipse.equinox.console_1.3.200.v20181115-0906.jar -console 指令启动 Equinox,成功启动,结果如下:

    至此,Equinox 控制台环境搭建成功

如果不需要使用控制台的话,直接将下载的文件解压即可,解压后,equinox 环境其实已经有了,可以直接运行 osgi 项目了,只是没有控制台,没法对 bundle 进行各种操作

2.1.3 IDEA 环境搭建

如果用户只是想运行 osgi 项目,那么只需要下载后解压即可,如果用户想进行 osgi bundle 开发,那么还需要 IDE 的支持(直接手撸也行,但不推荐),其实 eclipse 是配合开发 Equinox 最好的 IDE,因为 Eclipse本身就是基于 Equinox 开发的,对其支持非常充分,但是由于对 Eclipse 不熟悉,并且实际开发中,我们更为常用的是 IDEA,因此下面介绍如何在 IDEA 中搭建开发 OSGI 项目的环境

  1. 添加框架实例 IDEA 中选择 settings\Language & Frameworks\OSGi,点击 + 添加 OSGi 环境,如下所示:

  2. 设置 OSGi 项目默认参数 设置中选择 settings\Language & Frameworks\OSGi Project Defaults ,设置 OSGi 框架,如下:

至此环境就搭建完毕,下面创建OSGi项目

2.2 示例演示

2.2.1 创建OSGi项目

 

 项目设置:

模块设置:

 

facets设置:

该部分是设置osgi属性的部分,相当于Eclipse中manifast的设置。IDEA中osgi的manifast文件,不像Eclipse在编译阶段便生成,而是在打包后才生成,因此所有的设置内容均只能在该选项卡中设置。

 

Additional Contents这个功能,有点类似于Eclipse的osgi中的Build。添加需要一起打包成为最终jar包的jar包。

而在IDEA中,没有了Runtime选项卡,本来Runtime选项卡中的Exported与ClassPath,被拆分。Exported已在前述的Dependencies选项卡中有所介绍。在Eclipse中的ClassPath添加jar包,可自动在manifast文件中生成Bundle-ClassPath。而在IDEA中,必须在Manifest Generration选项卡中的Additional properties中,手动添加,如下图所示。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值