【发布时间】:2018-06-12 14:24:30
【问题描述】:
总结
当 POJO 在运行时在导入包中序列化时,应用于通过 OSGi 依赖项导出的 POJO 的杰克逊注释不起作用。如果 POJO 直接放在 using 包中,或在单元测试中进行测试(在任一包中),一切都会按预期工作。
有谁知道什么可以使运行时序列化在导入 OSGi 捆绑包中忽略运行时的 Jackson 注释?
这是一个很长的问题。我试图创建一个尽可能简单的示例。如果有什么不清楚的地方,请告诉我,我会尽量详细说明。
内容
-
导出包内
- POJO
- 单元测试(有效)
- 运行时测试(有效)
-
导入包内
- 单元测试(有效)
- 运行时测试(失败)
运行时环境详细信息
- 捆绑清单(简化)
简化示例:
假设我们想要序列化一个通过 OSGi 导出和导入的简单 POJO。 JSON 注释应该在导入和导出包中,运行时和单元测试期间都有效(导入时运行时失败)。
内部导出包:
Jackson 序列化的运行时和单元测试都可以在声明 POJO 本身的包中正常工作。
POJO 序列化
@JsonProperty-annotation 应该使这个 POJO 的任何序列化版本看起来像 {"correctSerializedName":"someName"} 而不是 {"javaName":"someName"}:
package exporting.osgi.bundle.models;
import com.fasterxml.jackson.annotation.JsonProperty;
public class DependencyModel {
private String javaName;
@JsonProperty("correctSerializedName")
public String getJavaName() {
return javaName;
}
public DependencyModel(String javaName) {
this.javaName = javaName;
}
}
正确行为:单元测试
import com.fasterxml.jackson.*;
import org.junit.jupiter.*;
class ExportingBundleTests {
@Test
void serialize_inDepdendencyProject_getsCorrectJsonName() {
DependencyModel dependencyModel = new DependencyModel("name");
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(dependencyModel);
// Asserts True -> serialization works as expected
assertEquals(jsonString, "{\"correctSerializedName\":\"name\"}");
}
}
正确的行为:运行时
public void runtimeFromDepdendency() {
DependencyModel dependencyModel = new DependencyModel("name");
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(dependencyModel);
// jsonString = {"correctSerializedName":"name"}
}
在导入的 OSGi 包中
正确行为:单元测试
import exporting.osgi.bundle.models.DependencyModel;
import com.fasterxml.jackson.*;
import org.junit.jupiter.api.*;
class ImportingBundleTests {
@Test
void serialize_inUsingProject_getsCorrectJsonName() {
DependencyModel dependencyModel = new DependencyModel("name");
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(dependencyModel);
// Asserts True -> serialization works as expected
assertEquals(jsonString, "{\"correctSerializedName\":\"name\"}");
}
}
失败:运行时
import exporting.osgi.bundle.models.DependencyModel;
import com.fasterxml.jackson.*;
public Response runsFromUsingProject() throws JsonProcessingException {
DependencyModel dependencyModel = new DependencyModel("name");
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(dependencyModel);
// jsonString = {"javaName":"name"} <---- WHICH IS WRONG
}
运行时环境
- Jira 7.2.2 OSGi(基于 Apache Felix)
- Maven 3.2.1
- Java 1.8
- 杰克逊 2.9.3
导出插件 Bundle Manifest(简化)
Created-By: Apache Maven Bundle Plugin
Manifest-Version: 1.0
Build-Jdk: 1.8.0_111
Bundle-ManifestVersion: 2
Export-Package:
...
exporting.osgi.bundle;version="1.0.0";
uses:="exporting.osgi.bundle.models",
...
Originally-Created-By: Apache Maven Bundle Plugin
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Spring-Context: *
Tool: Bnd-2.4.1.201501161923
导入插件 Bundle Manifest(简化)
Created-By: Apache Maven Bundle Plugin
Manifest-Version: 1.0
Build-Jdk: 1.8.0_111
Bundle-ManifestVersion: 2
Archiver-Version: Plexus Archiver
Bundle-ClassPath: .,META-INF/lib/gt-epsg-hsql-18.1.jar,META-INF/lib/sq
lite-jdbc-3.8.11.1.jar
Import-Package:
...
exporting.osgi.bundle.models
...
com.fasterxml.jackson.dataformat.xml;resolution:=optional,
com.fasterxml.jackson.dataformat.xml.deser;resolution:=optional,
com.fasterxml.jackson.dataformat.xml.ser;resolution:=optional,
org.codehaus.jackson;resolution:=optional,
org.codehaus.jackson.annotate;resolution:=optional,
org.codehaus.jackson.map;resolution:=optional,
org.codehaus.jackson.map.annotate;resolution:=optional,
...
Originally-Created-By: Apache Maven Bundle Plugin
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Spring-Context: *
Tool: Bnd-2.4.1.201501161923
如上所示,@JsonProperty-annotation 在运行时在导入依赖项中被忽略,但其他一切都按预期工作。同样,我不认为这是杰克逊版本的问题,尽管我可能忽略了一些东西。
我是否错过了一些基本的 OSGi 行为?
【问题讨论】:
-
听起来您在 Jira 中使用了两个动态加载的 P2 插件。您的依赖插件是否有可能使用不存在该注释的同一类的早期版本(之前加载在第一个插件的早期版本中)?加载两个插件后重新启动 Jira 可以帮助您确定这个理论是否成立。
-
当您使用 Jackson 2 时,为什么您的导入包会从旧的 Jackson 1 (org.codehaus) 导入类?另外,您是否将杰克逊捆绑包部署到您的 OSGi 容器中?如果没有,我建议您尝试一下,并确保您的导入和导出包在相关的杰克逊包上都有
Import-Package。三个最重要的捆绑包是:jackson-annotations、jackson-core和jackson-databind。如果您的两个捆绑包都同意使用哪些杰克逊类,我认为它应该可以工作。 -
我认为@gjoranv 正在做某事。 OP 可能无法控制容器(最终用户通常只能将新的 OSGi 包添加到 Jira,但不能触及主容器本身中可用的内容)。但是,如果 OP 分别在两个插件中的每一个中捆绑 Jackson 2,那么每个插件中的注释类都会不同,我想它会按照问题中的描述运行。在这种情况下,如果 OP 调整其代码以使用 Jira 中已包含的 Jackson 1 实现,则两个插件将共享相同的类并且它应该可以工作。
-
最简单和最好的方法是将 jackson2 捆绑包与您自己的捆绑包一起部署,并确保您的两个捆绑包都有
Import-Package(使用提供的范围)。从其中一个包中导出 jackson2 包,然后将其导入另一个包应该以相同的方式工作,但会给您额外的工作来确定要导出哪些包。正确的包集已经从 jackson2 包中导出。为什么这有关系?我怀疑(根据经验)问题在于您的两个捆绑包对相同的杰克逊注释使用不同的类。 -
当杰克逊框架扫描类的注解时,它会寻找它使用的特定注解类实例。如果一个包使用它自己的同一个类的实例,注释将不会被识别。在 OSGi 中,每个包都有一个单独的类加载器,每个类加载器可以包含给定类的自己的实例(但只有一个)。
标签: java jackson osgi jira apache-felix