如何在 OSGi 中使用 Apache POI

How to use Apache POI in OSGi

我想在 OSGi 中使用 Apache POI 编写一个 Excel 工作簿与流,OOXML API (SXSSF)。流式传输 API 从 POI 3.9 开始可用。

由于最新的 Apache POI 3.11 jar 不是捆绑包:让 POI 在 OSGi 中工作的最佳方法是什么?

我尝试了两种方法:

  1. 将 jars 直接嵌入到将使用它们的唯一包中
  2. 使用预先包装好的 POI 罐子

我对将所有依赖项放在一起感到绝望。

首先关于将 POI jar 嵌入我的包中:我的 bndtools 文件包含

-buildpath:  \
...
libs/dom4j-1.6.1.jar;version=file,\
libs/poi-3.11.jar;version=file,\
libs/poi-ooxml-3.11.jar;version=file,\
libs/poi-ooxml-schemas-3.11.jar;version=file

Private-Package:  \
...
org.openxmlformats.schemas.*,\
org.apache.poi.*,\
org.dom4j.*,\
com.microsoft.schemas.office.x2006.*,\
schemaorg_apache_xmlbeans.*,\
schemasMicrosoftComOfficeExcel.*,\
schemasMicrosoftComOfficeOffice.*,\
schemasMicrosoftComVml.*

这会生成一个包,其中导入了很多很多东西,例如 org.bouncycastle.asn1.x509org.junit。我不打算在我的应用程序中加密或测试 - 所以这两个可能以某种方式 "optional"。我怎样才能指定这个?有什么好的方法可以收集所有这些依赖项吗?

注意:至少还需要 org.apache.commons.codeccom.sun.msv.datatype.xsd.lib,但它们已经是捆绑包了。


使用预包装的罐子,我尝试使用 org.apache.servicemix.bundles.poi 3.9_2。这也需要 dom4j 所以我使用了预包装的 org.apache.servicemix.bundles.dom4j 但这至少需要 javax.xml.stream 的 1.0 版本,我的 JVM/Felix OSGi 将其宣传为 "only" 版本 0.0.0.1_007_JavaSE.我手动修复了这个(丑陋),但后来陷入了另一个依赖。

有什么好的方法吗?

我们使用 Gradle 和 bnd-platform 来基于 Maven 依赖项为我们的应用程序构建 OSGi 包。 不确定这是否是 "the good way",但这就是我们为基于 OSGi 的应用程序构建目标平台的方式,Apache POI 是其中的一部分。在您必须对捆绑包进行调整(例如,使 JUnit 可选)或合并 JAR(例如,由于 OSGi 中的类加载问题)以使它们工作时,它特别有用。

我建立了一个 example build with an Apache POI bundle (and implicitly, its POM-defined dependencies) on GitHub. You can clone it (sample-poi 分支)并尝试 运行 ./gradlew clean bundles。创建的包将在 build/plugins.

请注意,默认情况下不会包含任何可选的 Maven 依赖项,如果您需要它们,则必须手动添加到构建中(由于 limitations in Gradle)。

我没有此解决方法的工作示例,但这些文档可能对您有所帮助。

Can POI be used with OSGI?

Starting with POI 3.16 there's a workaround for OSGIs context classloader handling, i.e. it replaces the threads current context classloader with an implementation of limited class view. This will lead to IllegalStateExceptions, as xmlbeans can't find the xml schema definitions in this reduced view. The workaround is to initialize the classloader delegate of POIXMLTypeLoader , which defaults to the current thread context classloader. The initialization should take place before any other OOXML related calls. The class in the example could be any class, which is part of the poi-ooxml-schema or ooxml-schema: POIXMLTypeLoader.setClassLoader(CTTable.class.getClassLoader());