同一OSGI捆绑包中的多个类加载器会导致类强制转换异常

时间:2018-09-11 08:59:31

标签: java osgi aem apache-felix

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

这是伪代码:

捆绑 X

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

...

/* ... */

@Reference
private InMemoryUserTokenStore inMemoryUserTokenStore;

/* ... */

protected UsersTokenStore getTokenStore() {
    return inMemoryUserTokenStore; // <- reference to service from another bundle where tokens are stored
}

/* The token is created and obtained in the same bundle */

A token = new A(...);
getTokenStore().addToken(token)

/* ... */

B token = getTokenStore().getToken(id)
((A) token).doSomething(); // <- this is when class cast exception is thrown*/

使用调试器,我发现这里的token的类名是A,这两个类的类加载器都返回相同的包名和ID(包 X ,相同的ID),但不相等。

捆绑 Y

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**
    }
/* ... */
}

我不确定这是OSGI的问题还是我们设计方面的问题?

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

如果有问题的两个类实例具有不同的类加载器,则会抛出

ClassCastException。

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

Sling的(是AEM的基础框架)捆绑安装程序的实现在安装,更新或删除后调用刷新,因此该问题根本不会出现。您应该调查在某些情况下刷新失败的原因-起点是为以下类启用跟踪日志记录并查看发生的情况-

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