【问题标题】:Imported Jackson Annotations Ignored for POJOs in OSGi Dependency at Runtime运行时 OSGi 依赖项中的 POJO 忽略了导入的 Jackson 注释
【发布时间】: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-annotationsjackson-corejackson-databind。如果您的两个捆绑包都同意使用哪些杰克逊类,我认为它应该可以工作。
  • 我认为@gjoranv 正在做某事。 OP 可能无法控制容器(最终用户通常只能将新的 OSGi 包添加到 Jira,但不能触及主容器本身中可用的内容)。但是,如果 OP 分别在两个插件中的每一个中捆绑 Jackson 2,那么每个插件中的注释类都会不同,我想它会按照问题中的描述运行。在这种情况下,如果 OP 调整其代码以使用 Jira 中已包含的 Jackson 1 实现,则两个插件将共享相同的类并且它应该可以工作。
  • 最简单和最好的方法是将 jackson2 捆绑包与您自己的捆绑包一起部署,并确保您的两个捆绑包都有Import-Package(使用提供的范围)。从其中一个包中导出 jackson2 包,然后将其导入另一个包应该以相同的方式工作,但会给您额外的工作来确定要导出哪些包。正确的包集已经从 jackson2 包中导出。为什么这有关系?我怀疑(根据经验)问题在于您的两个捆绑包对相同的杰克逊注释使用不同的类。
  • 当杰克逊框架扫描类的注解时,它会寻找它使用的特定注解类实例。如果一个包使用它自己的同一个类的实例,注释将不会被识别。在 OSGi 中,每个包都有一个单独的类加载器,每个类加载器可以包含给定类的自己的实例(但只有一个)。

标签: java jackson osgi jira apache-felix


【解决方案1】:

根据我的 cmets 和您的反馈:

问题在于您的两个捆绑包具有不同的 Jackson 注释类实例。当 Jackson 框架扫描类以查找注释时,它会查找它使用的特定注释类实例。如果一个bundle有它自己的同一个类的实例,这个注解将不会被识别。 (在 OSGi 中,每个包都有一个单独的类加载器,每个类加载器都可以包含给定类的自己的实例。)

您可以通过两种方式解决此问题:

  1. 将 jackson2 作为单独的包部署到您的 OSGi 容器中,并确保您的导入和导出包在相关的 jackson 包上都有 Import-Package。最核心的捆绑包是:jackson-annotationsjackson-corejackson-databind。在这种情况下,请使用范围 provided 作为您的两个包中的 jackson2 依赖项。
  2. 从您的一个包中导出 jackson2 包,然后将其导入另一个包中。这将为您提供额外的工作来确定和维护要导出的包。在这种情况下,在导出包中为 jackson2 使用范围 compile,在导入包中使用范围 provided

【讨论】:

  • 看准了!我为我的具体情况添加了解决方案.. :-)
猜你喜欢
  • 1970-01-01
  • 2015-10-19
  • 1970-01-01
  • 1970-01-01
  • 2018-04-24
  • 2016-08-07
  • 1970-01-01
  • 2016-10-02
  • 1970-01-01
相关资源
最近更新 更多