我在尝试用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 {
// ...
}
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);
// ...
}
所以我能看到的一切似乎都表明这应该起作用。
我使用Comparable
和Integer
尝试了类似的代码,并得到了我期望的结果(可以安全地铸造(。
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
。因此,这里可能发生的事情是,您的代码正在从其他类加载程序中加载与实现类别的接口类。
更新您的测试代码,还显示接口类的类加载程序。
您可能会发现,接口类导出了多个捆绑包。您必须确保服务消费者和服务提供商使用相同的服务接口类(从同一类加载程序加载(。