OSGI声明性服务(DS):使用服务组件实例的好方法是什么

发布于 2024-07-27 02:38:19 字数 2837 浏览 9 评论 0原文

我刚刚开始使用 Equinox 和 Eclipse PDE 来使用 OSGI 和声明性服务 (DS)。

我有 2 个捆绑包,A 和 B。 捆绑包 A 公开了由捆绑包 B 使用的组件。这两个捆绑包还再次向 OSGI 服务注册表公开此服务。

到目前为止,一切工作正常,Equinox 正在将组件连接在一起,这意味着 Bundle A 和 Bundle B 由 Equinox 实例化(通过调用默认构造函数),然后使用绑定/取消绑定方法进行连接。

现在,当 Equinox 正在创建这些组件/服务的实例时,我想知道获取此实例的最佳方法是什么?

因此,假设有 第三类 类未由 OSGI 实例化:

Class WantsToUseComponentB{
public void doSomethingWithComponentB(){
 // how do I get componentB??? Something like this maybe?
 ComponentB component = (ComponentB)someComponentRegistry.getComponent(ComponentB.class.getName());
}

我现在看到以下选项:



1. 在 Activator 中使用 ServiceTracker 来获取 ComponentBundleA.class.getName() 的服务(我已经尝试过了,它可以工作,但对我来说似乎开销很大)并通过静态工厂方法使其可用

 
public class Activator{

   private static ServiceTracker componentBServiceTracker;   

   public void start(BundleContext context){

     componentBServiceTracker = new ServiceTracker(context, ComponentB.class.getName(),null);
   }

   public static ComponentB getComponentB(){
     return (ComponentB)componentBServiceTracker.getService(); 
   };

}

2. 创建某种注册表,一旦调用 activate() 方法,每个组件就会在其中注册。

public ComponentB{

public void bind(ComponentA componentA){
   someRegistry.registerComponent(this);
}

public ComponentB{

   public void activate(ComponentContext context){
      someRegistry.registerComponent(this);
   }

}

}

3. 使用 osgi/equinox 内现有的注册表,其中包含这些实例? 我的意思是 OSGI 已经在创建实例并将它们连接在一起,因此它已经在某个地方拥有了对象。 但是哪里? 我怎样才能得到它们?

结论 WantsToUseComponentB 类(不是组件,也不是由 OSGI 实例化)从哪里获取 ComponentB 的实例? 有什么模式或最佳实践吗? 正如我所说,我设法在 Activator 中使用 ServiceTracker,但我认为没有它也是可能的。

我要找的实际上是Springframework的BeanContainer之类的东西,我可以直接说Container.getBean(ComponentA.BEAN_NAME)之类的东西。 但我不想使用Spring DS。

我希望这已经足够清楚了。 否则我也可以发布一些源代码来更详细地解释。

谢谢 克里斯托夫


更新: 回答尼尔的评论:

感谢您针对原始版本澄清了这个问题,但我认为您仍然需要说明为什么不能通过 DS 等方式创建第三个类。

嗯不知道。 也许有办法,但我需要重构我的整个框架以基于 DS,这样就不再有“new MyThirdClass(arg1, arg2)”语句了。 我真的不知道该怎么做,但我读了一些关于 DS 中的 ComponentFactories 的内容。 做 a,而是

MyThirdClass object = new MyThirdClass(arg1, arg2);

因此,我可能不会

ComponentFactory myThirdClassFactory = myThirdClassServiceTracker.getService(); // returns a 

if (myThirdClassFactory != null){
  MyThirdClass object = objectFactory.newInstance();

   object.setArg1("arg1");
  object.setArg2("arg2");
}
else{
 // here I can assume that some service of ComponentA or B went away so MyThirdClass Componenent cannot be created as there are missing dependencies?

}

做 a在撰写本文时,我不知道如何使用 ComponentFactories,但这应该是某种伪代码:)

谢谢 克里斯托夫

I am just getting started with OSGI and Declarative Services (DS) using Equinox and Eclipse PDE.

I have 2 Bundles, A and B.
Bundle A exposes a component which is consumed by Bundle B. Both bundles also expose this service to the OSGI Service registry again.

Everything works fine so far and Equinox is wireing the components together, which means the Bundle A and Bundle B are instanciated by Equinox (by calling the default constructor) and then the wireing happens using the bind / unbind methods.

Now, as Equinox is creating the instances of those components / services I would like to know what is the best way of getting this instance?

So assume there is third class class which is NOT instantiated by OSGI:

Class WantsToUseComponentB{
public void doSomethingWithComponentB(){
 // how do I get componentB??? Something like this maybe?
 ComponentB component = (ComponentB)someComponentRegistry.getComponent(ComponentB.class.getName());
}

I see the following options right now:

1. Use a ServiceTracker in the Activator to get the Service of ComponentBundleA.class.getName() (I have tried that already and it works, but it seems to much overhead to me) and make it available via a static factory methods

 
public class Activator{

   private static ServiceTracker componentBServiceTracker;   

   public void start(BundleContext context){

     componentBServiceTracker = new ServiceTracker(context, ComponentB.class.getName(),null);
   }

   public static ComponentB getComponentB(){
     return (ComponentB)componentBServiceTracker.getService(); 
   };

}

2. Create some kind of Registry where each component registers as soon as the activate() method is called.

public ComponentB{

public void bind(ComponentA componentA){
   someRegistry.registerComponent(this);
}

or

public ComponentB{

   public void activate(ComponentContext context){
      someRegistry.registerComponent(this);
   }

}

}

3. Use an existing registry inside osgi / equinox which has those instances? I mean OSGI is already creating instances and wires them together, so it has the objects already somewhere. But where? How can I get them?

Conclusion
Where does the class WantsToUseComponentB (which is NOT a Component and NOT instantiated by OSGI) get an instance of ComponentB from? Are there any patterns or best practises? As I said I managed to use a ServiceTracker in the Activator, but I thought that would be possible without it.

What I am looking for is actually something like the BeanContainer of Springframework, where I can just say something like Container.getBean(ComponentA.BEAN_NAME). But I don't want to use Spring DS.

I hope that was clear enough. Otherwise I can also post some source code to explain in more detail.

Thanks
Christoph


UPDATED:
Answer to Neil's comment:

Thanks for clarifying this question against the original version, but I think you still need to state why the third class cannot be created via something like DS.

Hmm don't know. Maybe there is a way but I would need to refactor my whole framework to be based on DS, so that there are no "new MyThirdClass(arg1, arg2)" statements anymore.
Don't really know how to do that, but I read something about ComponentFactories in DS. So instead of doing a

MyThirdClass object = new MyThirdClass(arg1, arg2);

I might do a

ComponentFactory myThirdClassFactory = myThirdClassServiceTracker.getService(); // returns a 

if (myThirdClassFactory != null){
  MyThirdClass object = objectFactory.newInstance();

   object.setArg1("arg1");
  object.setArg2("arg2");
}
else{
 // here I can assume that some service of ComponentA or B went away so MyThirdClass Componenent cannot be created as there are missing dependencies?

}

At the time of writing I don't know exactly how to use the ComponentFactories but this is supposed to be some kind of pseudo code :)

Thanks
Christoph

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

司马昭之心 2024-08-03 02:38:20

Christoph,

感谢您针对原始版本澄清了这个问题,但我认为您仍然需要说明为什么不能通过 DS 之类的东西创建第三个类。

DS 使组件作为服务发布,因此从 DS“获取”任何组件的唯一方法是通过服务注册表访问它。 不幸的是,服务注册表可能很难使用较低级别的 API 正确使用,因为它是动态的,因此您必须应对服务消失或在您希望它们可用时不可用的可能性,等等。 这就是 DS 存在的原因:它为您提供了依赖服务的抽象,并根据组件引用的服务的可用性来管理组件的生命周期。

如果您确实需要在不使用 DS 或类似的东西的情况下访问服务(并且“类似的东西”有很多选择,例如 Spring-DM、iPOJO、Guice/Peaberry 等),那么您应该使用 ServiceTracker。 我同意这会带来很多开销——这也是 DS 存在的原因。

要回答您的建议,否(2),不,您不应该创建自己的服务注册表,因为服务注册表已经存在。 如果您创建了一个单独的并行注册表,那么您仍然需要处理所有动态,但您必须在两个地方而不是一个地方处理它。 这同样适用于建议(3)。

我希望这有帮助。

问候,
Neil

更新:顺便说一句,虽然 Spring 有 Container.getBean() 后门,但您会注意到,在所有 Spring 文档中,强烈建议不要使用该后门:要获取 Spring bean,只需创建另一个引用它的 bean。 这同样适用于 DS,即获得 DS 组件的最佳方式是创建另一个 DS 组件。

另请注意,在 OSGi 世界中,即使您使用 Spring-DM,也没有简单的方法来调用 getBean(),因为您需要首先获取 Spring ApplicationContext。 这本身就是一个 OSGi 服务,那么如何获得该服务呢?

Christoph,

Thanks for clarifying this question against the original version, but I think you still need to state why the third class cannot be created via something like DS.

DS causes components to be published as services, therefore the only way to "get" any component from DS is to access it via the service registry. Unfortunately the service registry can be hard to use correctly using the lower level APIs because it is dynamic, so you have to cope with the possibility of services going away or not being available at precisely the moment you want them to be available, and so on. This is why DS exists: it gives you an abstraction for depending on services and managing the lifecycle of your components based on the availability of services that they reference.

If you really need to access a service without using DS or something like it (and there is quite a choice of "things like it" e.g. Spring-DM, iPOJO, Guice/Peaberry, etc) then you should use ServiceTracker. I agree there is a lot of overhead -- again, this is why DS exists instead.

To answer your suggestion no (2), no you should not create your own registry of services because the service registry already exists. If you created a separate parallel registry then you would still have to handle all the dynamics, but you would have to handle it in two places instead of one. The same applies to suggestion (3).

I hope this helps.

Regards,
Neil

UPDATED: Incidentally, although Spring has the Container.getBean() backdoor, you notice that in all Spring documentation it is strongly recommended not to use that backdoor: to get hold of a Spring bean, just create another bean that references it. The same applies to DS, i.e. the best way to get hold of a DS component is to create another DS component.

Also note that in the OSGi world, even if you're using Spring-DM there is no easy way to just call getBean() because you need to get hold of the Spring ApplicationContext first. That is itself an OSGi service, so how to you get that service?

蹲在坟头点根烟 2024-08-03 02:38:20

克里斯托夫,
不知道我是否真正理解你的问题。
每前。
Bundle A 正在使用 DS 组件提供服务:

<service>
  <provide interface="org.redview.lnf.services.IRedviewLnfSelectedService"/>

捆绑包 B 需要使用 DS 组件提供此服务:

<implementation class="ekke.xyz.rcp.application.internal.XyzApplicationLnfComponent"/>

一旦 Bundle A 提供了 Service,Bundle B 就会通过实现类的 bind() 方法“获取”它:

public class XyzApplicationLnfComponent {
public void bind(IRedviewLnfSelectedService lnfSelectedService) {
    // here it is
}

希望这会有所帮助
埃克

christoph,
dont know if I really understand your problem.
per ex.
Bundle A is providing a service using DS component:

<service>
  <provide interface="org.redview.lnf.services.IRedviewLnfSelectedService"/>

Bundle B requires this service using DS component:

<implementation class="ekke.xyz.rcp.application.internal.XyzApplicationLnfComponent"/>

as soon as Bundle A provides the Service, Bundle B "gets" it through the bind() methode of the implementation class:

public class XyzApplicationLnfComponent {
public void bind(IRedviewLnfSelectedService lnfSelectedService) {
    // here it is
}

hope this helps
ekke

执手闯天涯 2024-08-03 02:38:20

简单的方法:使用 Riena 将 DS 组件注入到您的 Activator 类中:
http://wiki.eclipse.org/Riena_Getting_Started_with_injecting_services_and_extensions

然后你可以从任何地方调用它:Activator.getDefault ().getWhateverService()

Easy way: Inject the DS component into your Activator class with Riena:
http://wiki.eclipse.org/Riena_Getting_Started_with_injecting_services_and_extensions

Then you can call it from everywhere: Activator.getDefault().getWhateverService()

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文