如何在 OSGi 中获取调用 Bundle 的 BundleWiring?

How can I obtain the BundleWiring of the calling Bundle in OSGi?

我有一个名为 bundleA 的活动包,它使用另一个包中名为 doSomething() 的方法实现了一个名为 Example 的 class。 ExampleImpl 实现 Example 并通过 ServiceLoader (thanks to SPI Fly 加载)。在 bundleAExampleImpl.doSomething() 中,我需要获取调用 doSomething() 的包的 BundleWiring(不传递 BundleBundleContext,因为我无法更改 API)。我可以得到 bundleABundleWiring,但不能得到调用包的 BundleWiring:

Bundle bundleA = FrameworkUtil.getBundle(ExampleClass.class);
BundleWiring bundleWiringOfBundleA = bundle.adapt(BundleWiring.class);

如何获取调用包的BundleWiring

在我的特定情况下,调用包将始终是一个 WAB,恰好是 Liferay Portal 7.0 中的 运行。因此,如果有特定于 Liferay Portal 的解决方案,我会接受,但更通用的 OSGi 解决方案也可以。

请注意,我想要调用包的捆绑布线而不是依赖于当前捆绑布线的每个捆绑的捆绑布线。我知道我可以获得依赖于当前捆绑布线的捆绑布线,但这不会帮助我具体获取调用捆绑:

Bundle bundleA = FrameworkUtil.getBundle(ExampleClass.class);
List<BundleWires> bundleWires =
    bundleWiring.getProvidedWires(BundleRevision.PACKAGE_NAMESPACE);
bundleWires.get(0).getRequirerWiring();

你不能这样做。 OSGi 根本不干预跨束方法调用,因此您唯一可以用来尝试获取此信息的是 Java 调用堆栈,可通过 Thread.currentThread().getStackTrace() 访问。然而,这个 returns 只给你 class 名称的 StackTraceElement 对象而不是 java.lang.Class 对象,所以没有办法将它们关联回包。

除了不可能之外,我非常怀疑这是否是个好主意。您根本不应该导出实现 classes,更不用说尝试使它们对调用者敏感。

如果您需要实现对调用包敏感的行为,那么实现该目标的规范且正确的方法是使用 ServiceFactory 接口注册服务。

您可以使用 BundleTracker 并对正在安装的所有 WAB 做出反应。然后您可以内省每个 WAB,例如扫描 类 以获取注释。

声明式服务和现有的 WAB 支持以这种方式工作。不过编写这样的扩展器并不容易。

你说你不能改变 API。但是你能改变调用包吗?如果是这样,那么你可以有一个 "proxy" 服务,调用包使用而不是原来的服务。然后您可以将 Bundle and/or BundleContext 传递给代理服务并将您的依赖逻辑放在那里。

想到的另一个选择是使用 ThreadLocal 变量将此信息传递给调用的包,但我不确定该方法可能有什么副作用。

在 Liferay 中,调用 WAB 的 BundleContextServletContext 中存储为 "osgi-bundlecontext":

BundleContext bundleContext = servletContext.getAttribute("osgi-bundlecontext");
Bundle bundle = bundleContext.getBundle();
BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);

所以只要你能访问 Liferay 中的 ServletContext 你就可以获得调用包的 BundleBundleWiring.