Java 同一OSGI包中的多个类装入器导致类强制转换异常

Java 同一OSGI包中的多个类装入器导致类强制转换异常,java,osgi,aem,apache-felix,Java,Osgi,Aem,Apache Felix,有时,在部署应用程序并卸载和安装捆绑包之后,我们会遇到类强制转换异常,其中类A无法强制转换为类A。问题是,已在内存中一段时间的实例的类装入器与类本身的类装入器不同。部署不会影响存储实例的捆绑包(在本例中为捆绑包Y) 以下是伪代码: 捆绑X public class A extends B { /* ... */ } 我不确定这是OSGI的问题还是我们这边的设计错误?您正在存储由旧版本的捆绑包创建的类型的对象,并希望它们可以转换为由新版本的捆绑包创建的类型。当捆绑包停止时,缓存(令牌存储)应该

有时,在部署应用程序并卸载和安装捆绑包之后,我们会遇到类强制转换异常,其中类A无法强制转换为类A。问题是,已在内存中一段时间的实例的类装入器与类本身的类装入器不同。部署不会影响存储实例的捆绑包(在本例中为捆绑包Y)

以下是伪代码:

捆绑X

public class A extends B {
/* ... */
}


我不确定这是OSGI的问题还是我们这边的设计错误?

您正在存储由旧版本的捆绑包创建的类型的对象,并希望它们可以转换为由新版本的捆绑包创建的类型。当捆绑包停止时,缓存(令牌存储)应该使捆绑包存储的任何对象无效

如果所讨论的两个类实例具有不同的类加载器,则会引发ClassCastException

更新或卸载捆绑包并导出软件包时,这些软件包不会被删除,在刷新软件包之前,针对旧的已卸载捆绑包解析的捆绑包仍将使用旧的/过时的代码。这是OSGi规范定义的正确行为

Sling(它是AEM的底层框架)实现的bundle安装程序在安装、更新或删除后调用刷新,所以这个问题根本不应该出现。您应该调查刷新在某些情况下失败的原因—一个起点是为以下类启用跟踪日志记录,并查看发生了什么-

org/apache/sling/installer/core/impl/tasks/BundleUpdateTask
org/apache/sling/installer/core/impl/tasks/RefreshBundlesTask

我认为有趣的部分是“引用存储令牌的另一个捆绑包中的服务”这一行。您能否发布更多信息,说明您是如何获得该服务的,以及您是否/如何确保正确取消设置该服务的?@isnot2bad此服务由
@Reference
SCR注释引用-在
getTokenStore()
的最终hereDo调用方上没有自定义逻辑存储此引用,以便在重新启动捆绑包后仍然存在?或者您是否存储任何其他类型的bundle Y的实例(例如,
令牌
-实例),如果这是我们在每次部署中都会遇到此类异常的根本原因。正如我所说,有时会发生这种情况(可能10%的部署受到影响,其中令牌类根本没有更改)。某种原因导致了OSGI的非确定性行为,即在一个bundle只有一个(?)的情况下为一个bundle保留两个活动类加载程序。您确定每次部署时都会重新加载有问题的bundle,而不仅仅是在受影响的部署中吗?是,我完全肯定-这个捆绑包是我们的核心捆绑包之一,它总是被部署和重新编码。word版本在这里可能有点误导。当捆绑包停止并重新启动时,捆绑包类加载器将更改-无需实际更新。但是,不同的类装入器意味着从“旧”类装入器进行的类强制转换将失败。这是预期的行为,但由于捆绑包的任何导入发生更改时,捆绑包将自动重新启动,因此可能看起来不确定(重新启动可能会在您没有注意到的情况下发生,但是您在调试器中看到了两个不同的类加载器)。查看停止和重新启动bundle X是否会复制异常。我编辑了我的答案,将“版本”更改为“修订版”,这是OSGi规范中使用的更精确的术语。
public class InMemoryUserTokenStore implements UsersTokenStore {
/* ... */
    private ConcurrentMap<String, B> tokens = new ConcurrentHashMap();
    /* ... */
    public B getToken(String id) {
        /* ... */
        return tokens.get(id); // <- instance returned here sometimes has different class loader than class A from bundle **X**
    }
/* ... */
}
org/apache/sling/installer/core/impl/tasks/BundleUpdateTask
org/apache/sling/installer/core/impl/tasks/RefreshBundlesTask