java.util.ServiceConfigurationError 使用 OSGi 包时提供程序不是子类型

java.util.ServiceConfigurationError Provider not a subtype while using OSGi bundle

我正在创建一个 Liferay 7.1 OSGi 包,其中有一些外部依赖项。考虑到时间,我们选择将外部 JAR 嵌入到我们的 OSGi Bundle 中。我设法创建了一个包含所有 ElasticSearch 依赖项的 bnd 文件,并将它们放在包 class 路径中。我使用了 github (https://github.com/liferay/liferay-portal/blob/master/modules/apps/portal-search-elasticsearch6/portal-search-elasticsearch6-impl/build.gradle) 和 bnd.bnd 文件中的源代码来检查导入的内容。

激活包时,抛出异常:

The activate method has thrown an exception 
java.util.ServiceConfigurationError: org.elasticsearch.common.xcontent.XContentBuilderExtension: Provider org.elasticsearch.common.xcontent.XContentElasticsearchExtension not a subtype
    at java.util.ServiceLoader.fail(ServiceLoader.java:239)
    at java.util.ServiceLoader.access0(ServiceLoader.java:185)
    at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:376)
    at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
    at java.util.ServiceLoader.next(ServiceLoader.java:480)
    at org.elasticsearch.common.xcontent.XContentBuilder.<clinit>(XContentBuilder.java:118)
    at org.elasticsearch.common.settings.Setting.arrayToParsableString(Setting.java:1257)

XContentBuilderExtension 来自 elasticsearch-x-content-6.5.0.jar, XContentElasticsearchExtension class 包含在 elasticsearch-6.5.0.jar 中。两者都是包含资源,并已放在 class 路径上。

Activate 方法在我的另一个 jar 中初始化了一个 TransportClient,因此它发生在激活时 ;)。

编辑:

我注意到第一次安装时或门户重新启动时不会出现此错误。所以它只发生在我卸载并重新安装捆绑包时。 (这是我真正喜欢的功能!)。也许是个愚蠢的想法.. 但会不会有一些 'hanging thread'? bundle 没有正确安装,或者 TransportClient 仍然存在?我正在检查这个。欢迎任何提示!

编辑 2:

我担心这是 SPI 和 OSGi 之间的不兼容?我检查过:高级休息客户端有同样的问题。 (但随后又进行了另一个扩展)。我将尝试使用低级 Rest 客户端。这应该可行,因为我猜它的依赖性很小。我仍然很好奇为什么会存在不兼容性。我当然不是 OSGi 方面的专家,也不是 SPI 方面的专家。 (是时候学习新东西了!)

您可以在一个 Java 应用程序中有 2 个弹性搜索连接,而 Liferay 默认情况下不会公开它所拥有的连接。

一种解决方法是重建 Liferay ES 连接器。这没什么大不了的,因为您不需要更改代码,只需更改 OSGi 描述符即可公开更多服务。

我在一个 POC 项目中做过,效果很好。棘手的事情是重建 Liferay jar,但 Pettry 在他的 google 之类的搜索博客文章中解释了这一点。 https://community.liferay.com/blogs/-/blogs/creating-a-google-like-search (it is a series but it's kind of hard to navigate in the new Liferay blogs but Google will probably help) Either way it is all nicely documented here https://github.com/peerkar/liferay-gsearch

那么唯一需要做的就是在导出部分的 bnd.bnd 文件中添加 org.elasticsearch.*。然后,您将能够使用原生弹性 API.

似乎是 OSGi 使用您的 bundle 来解决另一个 bundle 的依赖关系的情况,可能是系统启动时使用您的 bundle 来解决包的问题。

从现象看:开机、重启都没有。它也不是子类型。

当 OSGi 使用该包来解决依赖关系时,它会保留一个副本,即使您删除它也是如此。当 bundle 返回时,之前被另一个 bundle 使用的包可能仍然存在,你可能会遇到这样的情况,其中使用的 class 有两个版本,来自不同的 classloader,这意味着它们是不一样 class,因此不是子类型。

只公开必要的内容以尽量减少这种影响。仅在需要导入时导入。如果您正在使用 Liferay Gradle 配置将捆绑包包含在其中,请停止 - 这是一种糟糕的包含方式,因为它暴露了很多东西。如果使用 bnd 文件包含资源并为附加的 class 路径位置创建条目,则如无必要请不要公开。如果您有多个包使用一个作为依赖项,请确保它们使用的版本以及交换对象是否来自有问题的 class,如果它们这样做,则需要格外小心。

PS: 您可以在导出时包含属性 and/or 导入以便更具体并避免使用来自错误来源的包。