服务注册为类型,并实现接口,但不能用作类型



我在尝试用OSGI解决参考时遇到了特殊的行为。似乎存在分辨率的处理方式与字节码指示的内容之间似乎存在矛盾。

我想用felix,在框架的外部解决一个OBR存储库。

我感兴趣的是,即使我要求提供org.osgi.service.repository.Repository类型,的服务,我也能够获得该类型的服务参考,我是能够从该对象的.getClass().getInterfaces()方法返回org.osgi.service.repository.Repository(我认为这表明可以将对象施放为Repository(,Repository.class.isAssignableFrominstanceof返回此对象的false

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

import org.osgi.service.repository.Repository;
class OSGiRepositoryImpl implements Repository {
   // ...
}

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

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:tt" + serviceByName);
        System.out.println("Repository Service by Name Assignable from Repository:tt" + Repository.class.isAssignableFrom(serviceByName.getClass()));
        System.out.println("Repository Service by Name instanceof Repository:tt" + (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);
    }
}

有趣的是,如果我制作捆绑包,在框架中安装捆绑包,启动它,然后尝试在框架中解决Repository ,它可以正常工作。

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:tt" + Repository.class.isAssignableFrom(serviceByName.getClass()));
        System.out.println("Repository Service instanceof Repository:tt" + (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:    .bundlesorg.apache.felix.bundlerepository-2.0.10.jar
Starting bundle:    org.apache.felix.bundlerepository [1]
method one path:    .bundlesorg.osgi.service.obr-1.0.2.jar
Starting bundle:    org.osgi.service.obr [2]
method one path:    .bundleszlauncher.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,而不是在其外部?

在运行时,一个classloader唯一识别了一个类别的class Loader。因此,可以通过不同的类加载程序加载相同的确切类文件,而不是equals。因此,这里可能发生的事情是,您的代码正在从其他类加载程序中加载与实现类别的接口类。

更新您的测试代码,还显示接口类的类加载程序。

您可能会发现,接口类导出了多个捆绑包。您必须确保服务消费者和服务提供商使用相同的服务接口类(从同一类加载程序加载(。

相关内容