OSGI不难
我已经多次遇到OSGI困难的问题。 而且,他本人曾经有过这样的看法。 确切地说,是2009年。 当时,我们使用Maven Tycho收集了项目,并将其部署到Equinox。 这确实比为JavaEE开发和组装项目要困难得多(那时EJB 3的版本刚刚出现,我们切换到了该版本)。 例如,Equinox不如Weblogic方便,并且OSGI的好处对我而言并不明显。
但是后来,多年后,我不得不开始一项新工作,该项目是在Apache Camel和Apache Karaf的基础上构思的。 这不是我的主意,那时我很早就认识骆驼,并决定即使没有要约也要阅读有关卡拉夫的文章。 我读了一个晚上,便意识到-在这里,它是简单的现成的,几乎可以解决典型JavaEE的某些问题,类似于我曾经使用Weblogic WLST,Jython和Maven Aether跪下的解决方案。
因此,假设您决定在Karaf平台上尝试OSGI。 我们从哪里开始?
如果您想更深入的了解
您当然可以先阅读文档。 哈布雷(Habré)有可能-这里有很多很好的文章,比如说很久以前。 但是总的来说,到目前为止,karaf很少受到关注。
这个或
这个还有更多的评论。 最好不要提到卡拉夫。 正如他们所说,晚上不要阅读苏联报纸……因为他们会告诉您karaf是OSGI框架-因此您不相信它。 OSGI框架是Apache Felix或Eclipse Equinox,在此基础上karaf才可以使用。 您可以选择任何一个。
应该注意的是,当提到Jboss Fuse或Apache ServiceMix时,应将其读作“带有预装组件的Karaf”,即 实际上-同一件事,仅由供应商收集。 在实践中,我不建议从此开始,但是例如,很有可能阅读有关ServiceMix的评论文章。
首先,我将在这里尝试简要地确定什么是OSGI,以及它可以用于什么用途。
总的来说,OSGI是一种用于从模块创建Java应用程序的工具。 可以考虑使用类似的JavaEE,并且在某种程度上OSGI容器可以执行JavaEE模块(例如War形式的Web应用程序),另一方面,许多JavaEE容器在内部包含OSGI作为“自行实现模块化”的手段”。 也就是说,JavaEE和OSGI类似于兼容性,并且可以成功地互补。
任何模块化系统的重要部分是模块本身的定义。 就OSGI而言,该模块称为捆绑包,它是所有开发人员都熟知的jar存档,带有一些附加功能(也就是说,此处与war或ear非常相似)。 与JavaEE类似,捆绑包可以导出和导入服务,这些服务本质上是类方法(即,服务是接口或类的所有公共方法)。
包元数据对于每个META-INF / MANIFEST.MF都是熟悉的。 OSGI清单清单的标头分别与JRE的标头不相交,因为OSGI容器捆绑包是常规jar。 重要的是,在元数据中始终存在:
Bundle-SymbolicName: com.example.myosgi Bundle-Version: 1.0.0
这些是捆绑软件的“坐标”,并且我们可以在一个容器中同时安装两个或多个同一个捆绑软件且可以正常工作的版本这一事实非常重要。
与JavaEE类似,捆绑软件的生命周期如下所示:

除服务外,捆绑软件还可以导入和导出软件包(在Java术语的通常意义上为软件包)。 导出的软件包在捆绑包中定义,并且当捆绑包安装在系统上时可供其他组件使用。 导入的文件是从外部定义的,必须由某人导出,并由容器提供给捆绑软件,然后容器才能开始工作。
包导入以及服务导入可以声明为可选。 导入和导出包含版本(或版本范围)的指示非常重要。
与JavaEE的区别
好吧,他们是一样的-我们了解。 它们又有何不同?
在我看来,主要的区别在于OSGI给了我们更大的灵活性。 捆绑包处于“开始”状态后,可能性仅受您的想象力限制。 假设您可以轻松创建线程(是的,是的,我知道ManagedExecutorService),数据库的连接池等。 容器不能像JavaEE一样控制所有资源。
您可以在此过程中导出新服务。 尝试说在JavaEE中动态创建新的servlet? 而且,在这里很有可能,基于码头创建的karaf servlet容器将被创建的servlet立即检测到,并且可以通过特定URL供客户端使用。
尽管这是一个略微的简化,但是如果JavaEE应用程序以其经典形式主要由以下组件组成:
- 被动,等待客户端的呼叫
- 静态定义,即在部署应用程序时定义。
另一方面,基于OSGI的应用程序可能包含:
- 主动和被动计划的组件,执行轮询,正常运行,侦听套接字等。
- 可以动态定义和发布服务
- 您可以订阅框架事件,例如,侦听服务,捆绑软件等的注册,接收到其他捆绑软件和服务的链接以及执行更多操作。
是的,在JavaEE上,其中很多也是部分可能的(例如,通过JNDI),但是在OSGI的情况下,实际上更容易实现。 尽管这里可能还会有更多风险。
karaf和纯OSGI之间的区别
除了karaf框架,还有很多有用的东西。 本质上,karaf是一种用于方便地管理OSGI框架的工具-在其中安装捆绑软件(包括组),对其进行配置,监视,描述角色模型并确保安全性等。
并且已经练习了吗?
好吧,让我们立即开始安装。 这里没有什么要写的-转到karaf.apache.org,下载分发包,然后解压缩。 karaf的版本在支持不同的OSGI规范(4、5或6)和Java版本方面有所不同。 我不建议使用2.x系列,但是这里有3个(如果您有Java 8,例如我的Java),并且可以使用4个,尽管今天只有4.x系列正在开发(当前版本4.2.2,它支持OSGI 6)和Java最多10个)。
Karaf在Windows和Linux下运行良好,创建服务和自动运行所需的一切都可用。 还声明了对MacOS和许多其他类型的Unix的支持。
如果您正在上网,通常可以立即开始karaf。 如果没有,那么通常值得修复配置文件,以指出您在哪里拥有Maven存储库。 通常,喜欢的人将是公司Nexus,或者说是Artifactory。 karaf配置位于发行版的etc文件夹中。 配置文件的名称不是很明显,但是在这种情况下,您需要org.ops4j.pax.url.mvn.cfg文件。 该文件的格式是java属性。
您可以在配置文件本身中指定存储库,可以列出设置中的URL列表,也可以仅显示settings.xml的位置。 karaf将在其中找到您的代理服务器的位置,通常需要在Intranet上知道该位置。
Kafar需要几个端口,这些端口是HTTP,HTTPS(如果配置了Web,默认情况下没有配置),SSH,RMI,JMX。 如果他们忙于您,或者您想在同一主机上运行多个副本,则也必须更改它们。 这些端口大约有五个。
诸如jmx和rmi之类的端口-此处:org.apache.karaf.management.cfg,ssh-org.apache.karaf.shell.cfg,要更改http / https端口,您将需要创建(很可能不是)etc /文件org.ops4j.pax.web.cfg,并在其中输入值org.osgi.service.http.port =端口。
然后,您可以肯定地启动它,并且一切都会开始。 显然,对于工业用途,您将必须对bin / setenv或bin / setenv.bat文件进行更改,例如,以分配所需的内存量,但是首先要看到的是不必要的。
您可以使用控制台,karaf命令立即启动Karaf,也可以使用start server命令在后台运行它,然后通过SSH连接到它。 这是完全标准的SSH,支持SCP和SFTP。 您可以执行命令,以及来回复制文件。 可以与任何客户端连接,例如,我最喜欢的工具是Far NetBox。 可以通过登录名和密码以及按键来登录。 在gschitz中,它暗示了一切。
我建议立即有一个附加的控制台窗口来查看位于data / log / karaf.log中的日志(尽管可以自定义,但通常还有其他文件)。 从控制台中的短消息来看,日志对您很有用,并不是所有事情都一目了然。
我建议立即安装Web和hawtio Web控制台。 这两件事将使您更轻松地导航容器中发生的事情并在很大程度上指导容器的过程(此外,您还将获得jolokia以及通过http进行监视的功能)。 通过来自karaf控制台的两个命令(
如此处所述 )完成hawtio的安装,而且,今天,不再支持karaf 3.x版本(您将不得不寻找较旧的hawtio版本)。
开箱即用,https不会立即生效,为此,您需要做出一些努力,例如生成证书等。实现基于码头,因此所有这些努力大部分都是以相同的方式完成的。
好,它开始了,下一步是什么?

实际上,您期望什么? 我说会是ssh。 Tab可以,如果可以的话。
现在该安装一些应用程序了。 OSGI的应用程序可以是一个捆绑软件,也可以由多个捆绑软件组成。 Karaf可以采用多种格式部署应用程序:
- 一个带有或不带有OSGI清单的jar包
- 包含Spring DM或Blueprint的xml
- 包含所谓功能的xml,它是捆绑软件,其他功能和资源(配置文件)的集合
- .kar归档文件,包含几个功能和一个具有依赖关系的Maven存储库
- JavaEE应用程序(在某些附加条件下),例如.war
有几种方法可以做到这一点:
- 将应用程序放在deploy文件夹中
- 使用install命令从控制台安装
- 使用功能部件中的命令安装功能部件:安装控制台
- kar:安装
嗯,总的来说,这与典型的JavaEE容器可以完成的工作非常相似,但是更加方便(我会说它更加方便)。
简单的罐子
最简单的选择是安装常规jar。 如果您在maven存储库中拥有它,那么该命令足以安装:
install mvn:groupId/artifactId/version
同时,Karaf意识到自己面前有一个普通的罐子,并对其进行处理,从而在运行中创建了一个捆扎包装纸,即所谓的 包装器,生成默认清单,以及包的导入和导出。
仅安装jar的感觉通常不多,因为此捆绑包是被动的-它仅导出可用于其他捆绑包的类。
此方法用于安装诸如Apache Commons Lang的组件,例如:
install mvn:org.apache.commons.lang3/commons-lang/3.8.1
但这不起作用:)这是正确的坐标:
install mvn:org.apache.commons/commons-lang3/3.8.1
让我们看看发生了什么:list -u将向我们显示捆绑包及其来源:
karaf@root()> list -u START LEVEL 100 , List Threshold: 50 ID | State | Lvl | Version | Name | Update location ------------------------------------------------------------------------------------------------- 87 | Installed | 80 | 3.8.1 | Apache Commons Lang | mvn:org.apache.commons/commons-lang3/3.8.1 88 | Installed | 80 | 3.6.0 | Apache Commons Lang | mvn:org.apache.commons/commons-lang3/3.6
如您所见,很有可能安装一个组件的两个版本。 更新位置-这是我们获得捆绑软件的位置,并且可以在需要时进行更新。
Jar和Spring上下文
如果您的jar内有一个Spring Context,事情将会变得更加有趣。 Karaf Deployer会自动在META-INF / spring文件夹中搜索xml上下文,并在成功找到该包所需的所有外部包后创建它们。
因此,上下文中的所有服务都将启动。 例如,如果您在那里有骆驼泉,骆驼路线也将开始。 这意味着我们说您已经可以启动REST服务或侦听TCP端口的服务。 当然,在一个端口上启动多个服务侦听将无法实现。
Just Spring XML上下文
例如,如果您在Spring Context中具有JDBC DataSources定义,则可以将它们分别安装在Karaf中。 即 取一个仅包含<bean>形式的数据源或任何其他组件集的xml文件,您可以将其放在deploy文件夹中。 上下文将以标准方式启动。 唯一的问题是,以这种方式创建的数据源将对其他捆绑包不可见。 它们需要作为服务导出到OSGI。 关于这个-稍后。
弹簧dm
Spring DM(支持OSGI的版本)与经典Spring有什么区别? 因此,在经典情况下,上下文中的所有bean都是在上下文的初始化阶段创建的。 新的不会出现,旧的不会走到任何地方。 对于OSGI,可以安装新的捆绑软件,而删除旧的捆绑软件。 环境变得越来越动态,您需要以某种方式做出响应。
响应方法称为服务。 服务通常是某个接口,具有自己的方法,由某个捆绑发布。 服务具有元数据,该元数据允许对其进行搜索并将其与实现类似接口的另一服务(显然是与另一数据源)区别开来。 元数据是一组简单的键值属性。
由于服务可以显示和消失,因此需要它们的人可以在启动时订阅服务,也可以收听事件以了解服务的出现或消失。 在XML的Spring DM级别上,这被实现为两个元素,即service和reference,其基本目的非常简单:将上下文中的现有bean作为服务发布,并通过将其发布到当前spring上下文来订阅外部服务。
因此,当初始化这样的捆绑包时,容器将找到它所需的外部服务,并发布在其内部实现的捆绑包,从而使它们可以从外部访问。 仅在解决服务链接后,捆绑软件才会启动。
实际上,一切都有些复杂,因为捆绑包可以使用类似服务的列表,并立即订阅该列表。 即 通常,服务具有诸如基数的属性,其值为0..N。 在这种情况下,表示为0..1的预订描述了一项可选服务,在这种情况下,即使系统中没有这样的服务,捆绑包也会成功启动(并且将获得一个存根,而不是指向其的链接)。
我注意到服务只是任何接口(或者您可以只发布类),因此您可以将数据作为服务很好地发布java.util.Map。
其中,服务允许您指定元数据,而引用允许您通过此元数据搜索服务。
蓝图
蓝图是较新的Spring DM化身,这有点简单。 即,如果在Spring中有自定义XML元素,那么它们就不在这里,这是不必要的。 有时这仍然会带来不便,但坦率地说-很少。 如果您不打算从Spring迁移项目,则可以立即从Blueprint开始。
这里的本质是相同的-它是XML,它描述了组合上下文所组成的组件。 对于那些了解Spring的人来说,没有什么陌生的地方。
这是一个如何描述数据源并将其导出为服务的示例:
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource"> <property name="URL" value="URL"/> <property name="user" value="USER"/> <property name="password" value="PASSWORD"/> </bean> <service interface="javax.sql.DataSource" ref="dataSource" id="ds"> <service-properties> <entry key="osgi.jndi.service.name" value="jdbc/ds"/> </service-properties> </service> </blueprint>
好了,我们将该文件部署到了部署文件夹,并查看了list命令的结果。 他们看到捆绑包没有启动-处于Indtalled状态。 我们尝试启动,并且收到错误消息。
现在,在捆绑软件列表中,状态为“失败”。 怎么了 显然,他还需要依赖项,在这种情况下,需要具有Oracle JDBC类的Jar,或更准确地说,是oracle.jdbc.pool包。
我们可以在存储库中找到必要的jar,或者从Oracle网站下载并安装,如前所述。 我们的数据源已经启动。
如何使用所有这些? 该服务链接在“蓝图”参考中(在另一个捆绑软件的上下文中的某个地方)被调用:
<reference id="dataSource" interface="javax.sql.DataSource"/>
然后,此bean像往常一样成为其他bean的依赖项(在camel-sql示例中):
<bean id="sql" class="org.apache.camel.component.sql.SqlComponent"> <property name="dataSource" ref="dataSource"/> </bean>
罐子和活化剂
初始化包的规范方法是使用实现Activator接口的类。 这是一个典型的生命周期接口,其中包含通过
context的 start和stop方法。 在它们内部,捆绑软件通常启动其线程,如有必要,开始监听端口,使用OSGI API订阅外部服务,等等。 这也许是最复杂,最基本,最灵活的方式。 三年来,我从不需要它。
设置和配置
显然,如示例所示,DataSource的这种配置很少有人需要。 登录名,密码等,所有内容都硬编码在XML中。 必须删除这些参数。
<property name="url" value="${oracle.ds.url}"/> <property name="user" value="${oracle.ds.user}"/> <property name="password" value="${oracle.ds.password}"/>
该解决方案非常简单,并且与经典Spring中使用的解决方案类似:在上下文生命周期的某个时刻,可以从各种来源替换属性值。
至此,我们将结束第一部分。 如果对此主题感兴趣,则将继续。 我们将考虑如何从捆绑中组装应用程序,配置,监视和自动在此平台上部署系统。