尝试使用OSGi解析引用时遇到了奇怪的行为。在如何处理分辨率和字节码表示的内容之间似乎存在矛盾。
我想用Felix解决一个OBR存储库,在框架之外。
引起我兴趣的是,即使我要求提供org.osgi.service.repository.Repository
,和类型的服务,我也可以获得该类型的服务参考,和我能够从该服务引用中获取一个对象,和该对象的.getClass().getInterfaces()
方法返回org.osgi.service.repository.Repository
(我认为这表明该对象可以转换为{ {1}}),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);
// ...
}
和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:\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);
}
}
,它就可以正常工作。
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:\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 {
}
}
注意:我知道获得服务的首选方式是使用OSGi DS。但是因为我正在嵌入框架,并提供一个易于使用的启动器,我希望能够在框架的上下文之外使用服务对象。
为什么我能够在框架内解析服务,但不能在框架之外解析,即使服务引用返回相同的对象?为什么在框架内可以将同一个对象强制转换为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
,而不是在它之外?
答案 0 :(得分:2)
在运行时,Class由ClassLoader唯一标识,并加载它。因此,相同的类文件可以由不同的类加载器加载,而不是equals
。所以这里可能发生的是你的代码是从不同的类加载器加载接口类而不是实现类。
更新测试代码以显示接口类的类加载器。
您可能会发现有多个捆绑包导出接口类。您必须确保服务使用者和服务提供使用相同的服务接口类(从同一个类加载器加载)。