java - 服务注册为类型,并实现接口(interface),但不能用作类型

标签 java osgi

在尝试使用 OSGi 解析引用时,我遇到了奇怪的行为。解析的处理方式和字节码表示的内容之间似乎存在矛盾。

我想与 Felix 一起解析 OBR 存储库,在框架之外

引起我兴趣的是,即使我请求 org.osgi.service.repository.Repository 类型的服务,并且我也能够获得服务该类型的引用,我能够从该服务引用获取一个对象,该对象的 .getClass().getInterfaces()方法返回org.osgi.service.repository.Repository(我认为这表明该对象可以转换为Repository),两者都是Repository.class.isAssignableFrom instanceof 对此对象返回 false

我查看了 OSGiRepositoryImpl 的源代码,发现它实际上是 Repository 的子类型

import org.osgi.service.repository.Repository;

class OSGiRepositoryImpl implements Repository {
   // ...
}

然后我查看了类型是如何在激活器中注册的,发现它实际上(大概)正确地注册了它

import org.osgi.service.repository.Repository;
// ...

public void start(BundleContext context) {
    context = context;
    logger = new Logger(context);
    this.m_repoAdmin = new RepositoryAdminImpl(context, logger);
    context.registerService(RepositoryAdmin.class.getName(), this.m_repoAdmin, (Dictionary)null);
    context.registerService(Repository.class.getName(), new OSGiRepositoryImpl(this.m_repoAdmin), (Dictionary)null);
    // ...
}

所以我能看到的一切似乎都表明这应该有效。

我使用ComparableInteger尝试了类似的代码,并得到了我预期的结果(可以安全地通过强制转换)。

package net.zephyrion.pokemod.launcher;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
import org.osgi.service.repository.Repository;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

public final class Launcher {

    public static void main(String[] args) throws BundleException {
        Framework framework = null;
        try {
            Map<String, String> frameworkConfig = new HashMap<>();
            frameworkConfig.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);

            FrameworkFactory frameworkFactory = ServiceLoader.load(FrameworkFactory.class).iterator().next();
            framework = frameworkFactory.newFramework(frameworkConfig);

            framework.start();

            BundleContext frameworkContext = framework.getBundleContext();

            System.out.println("Starting/Installing bundles...");
            Files.list(Paths.get("./", "bundles")).forEach(path -> {
                try {
                    System.out.println("method one path:\t" + path);
                    String absolutePath = path.toAbsolutePath().toString();
                    Bundle bundle = frameworkContext.installBundle("file:///" + absolutePath);
                    System.out.println("Starting bundle:\t" + bundle);
                    bundle.start();
                } catch (BundleException e) {
                    e.printStackTrace();
                }
            });
            System.out.println();

            printNumberExample();
            printInformation(frameworkContext);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            if (framework != null) {
                framework.stop();
                System.exit(0);
            }
        }
    }

    private static void printInformation(BundleContext frameworkContext) {
        ServiceReference<?> repositoryReferenceByName = frameworkContext.getServiceReference("org.osgi.service.repository.Repository");
        System.out.println("Repository Reference by Name:\t" + repositoryReferenceByName);

        Object serviceByName = frameworkContext.getService(repositoryReferenceByName);
        Class<?> serviceClass = serviceByName.getClass();

        System.out.println("interfaces implemented by " + serviceClass + ": {");
        Arrays.stream(serviceClass.getInterfaces()).forEach(interfaceClass -> System.out.println("\t" + interfaceClass));
        System.out.println("}");
        System.out.println();

        System.out.println("Repository Service by Name:\t\t" + serviceByName);
        System.out.println("Repository Service by Name Assignable from Repository:\t\t" + Repository.class.isAssignableFrom(serviceByName.getClass()));
        System.out.println("Repository Service by Name instanceof Repository:\t\t" + (serviceByName instanceof Repository));

        Repository repository = Repository.class.cast(serviceByName);
        System.out.println("Repository:\t" + repository);
    }

    private static void printNumberExample() {
        Integer number = 42;
        boolean comparableIsAssignableFromInteger = Comparable.class.isAssignableFrom(number.getClass());

        System.out.println("Comparable is assignable from Integer:\t" + comparableIsAssignableFromInteger);

        System.out.println("interfaces implemented by " + Integer.class + ": {");
        Arrays.stream(Integer.class.getInterfaces()).forEach(interfaceClass -> System.out.println("\t" + interfaceClass));
        System.out.println("}");

        Comparable comparableNumber = (Comparable) number;
        System.out.println("Comparable number:\t" + comparableNumber);
    }
}

有趣的是,如果我制作一个 bundle ,在框架中安装该 bundle ,启动它,然后尝试解析框架内的存储库,它就可以工作就好了。

package net.zephyrion.pokemod.launcher;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.repository.Repository;

import java.util.Arrays;

public class TestActivator implements BundleActivator {

    @Override
    public void start(BundleContext bundleContext) throws Exception {
        System.out.println("=======================================");
        System.out.println("Bundle context:\t" + bundleContext);
        ServiceReference<Repository> repositoryReference = bundleContext.getServiceReference(Repository.class);
        System.out.println("Repository reference in activator:\t" + repositoryReference);

        Object serviceByName = bundleContext.getService(repositoryReference);
        Class<?> serviceClass = serviceByName.getClass();

        System.out.println("interfaces implemented by " + serviceClass + ": {");
        Arrays.stream(serviceClass.getInterfaces()).forEach(interfaceClass -> System.out.println("\t" + interfaceClass));
        System.out.println("}");
        System.out.println();

        Repository repository = (Repository) serviceByName;
        System.out.println("Repository Service:\t" + repository);

        System.out.println("Repository Service Assignable from Repository:\t\t" + Repository.class.isAssignableFrom(serviceByName.getClass()));
        System.out.println("Repository Service instanceof Repository:\t\t" + (serviceByName instanceof Repository));
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {

    }
}

下面是输出

Framework context:  org.apache.felix.framework.BundleContextImpl@5ba23b66
Starting/Installing bundles...
method one path:    .\bundles\org.apache.felix.bundlerepository-2.0.10.jar
Starting bundle:    org.apache.felix.bundlerepository [1]
method one path:    .\bundles\org.osgi.service.obr-1.0.2.jar
Starting bundle:    org.osgi.service.obr [2]
method one path:    .\bundles\zlauncher.jar
Starting bundle:    launcher [3]
=======================================
Bundle context: org.apache.felix.framework.BundleContextImpl@482f8f11
Repository reference in activator:  [org.osgi.service.repository.Repository]
interfaces implemented by class org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl: {
    interface org.osgi.service.repository.Repository
}

Repository Service: org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl@6767c1fc
Repository Service Assignable from Repository:      true
Repository Service instanceof Repository:       true

Comparable is assignable from Integer:  true
interfaces implemented by class java.lang.Integer: {
    interface java.lang.Comparable
}
Comparable number:  42
Repository Reference by Name:   [org.osgi.service.repository.Repository]
interfaces implemented by class org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl: {
    interface org.osgi.service.repository.Repository
}

Repository Service by Name:     org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl@6767c1fc
Repository Service by Name Assignable from Repository:      false
Repository Service by Name instanceof Repository:       false
java.lang.ClassCastException: Cannot cast org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl to org.osgi.service.repository.Repository
    at java.lang.Class.cast(Class.java:3369)
    at net.zephyrion.pokemod.launcher.Launcher.printInformation(Launcher.java:79)
    at net.zephyrion.pokemod.launcher.Launcher.main(Launcher.java:50)

Process finished with exit code 0

注意:我知道获取服务的首选方式是使用 OSGi DS。但因为我嵌入了框架,并提供了一个易于使用的启动器,所以我希望能够在框架上下文之外使用服务对象。

为什么我能够解析框架内的服务,但不能解析框架外的服务,即使服务引用返回相同的对象?为什么同一个对象可以转换到框架内的Repository,但不能转换到框架之外?

最佳答案

在运行时,类由加载它的类加载器唯一标识。因此,同一个类文件可以由不同的类加载器加载,但不能等于。因此,这里可能发生的情况是,您的代码从与实现类不同的类加载器加载接口(interface)类。

更新您的测试代码以显示接口(interface)类的类加载器。

您可能会发现有多个导出接口(interface)类的包。您必须确保服务使用者和服务提供者使用相同的服务接口(interface)类(从同一个类加载器加载)。

关于java - 服务注册为类型,并实现接口(interface),但不能用作类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48836415/

相关文章:

java - 创建需要用户参数的 OSGI 服务

java - OSGI 的 MVC Web 容器?

java - 由于空指针异常导致运行时错误

java - 无法使用 javac 和 lombok 在控制台中编译 .h 文件。错误 : package lombok does not exist

java - 如何过滤 OSGi 服务可见性?

eclipse - 如何在Eclipse平台上自动启动/热启动OSGi服务

java - 使用类构造函数和另一个数组初始化一个数组

java - Jdk 7 中的 FileChannel.open() 与 RandomAccessFile

java jar错误: Unable to access from command line

java - 在 OSGi 包之间共享对象