使用STATIC策略绑定OSGi声明式服务的顺序

时间:2019-04-18 13:26:54

标签: osgi declarative-services

假设我具有以下OSGi组件,该组件应在运行时每次注册新的SomeInterface实现时发送一个事件。

为此,我将EventAdmin绑定到eventAdmin变量中,然后在bindSomeInterface方法中使用它。

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;

@Component
public class Sender {

    private EventAdmin eventAdmin;

    @Reference
    public void bindEventAdmin(EventAdmin eventAdmin) {
        this.eventAdmin = eventAdmin;
    }

    public void unbindEventAdmin(EventAdmin eventAdmin) {
        this.eventAdmin = null;
    }

    @Reference(cardinality = ReferenceCardinality.MULTIPLE)
    public void bindSomeInterface(SomeInterface instance) {

       // var prop create here... (non relevant code)
        Event event = new Event("topic", prop);

       // it is NULL!
        eventAdmin.sendEvent(event);
    }

    public void unbindSomeInterface(SomeInterface instance) {

    }

}

生成的xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.package.Sender">
   <reference bind="bindSomeInterface" cardinality="0..n" interface="com.package.bindSomeInterface" name="SomeInterface" policy="static" unbind="unbindSomeInterface"/>
   <reference bind="bindEventAdmin" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static" unbind="unbindEventAdmin"/>
   <implementation class="com.package.Sender"/>
</scr:component>

问题

bindSomeInterface首先被调用(获得“通知”,表明运行时已注册SomeInterface的新实例),然后调用bindEventAdmin。这不是理想的效果。

预期行为

我想先绑定EventAdmin实例,然后绑定SomeInterface实例。

我该怎么做?


非常接近的问题(但不相同):Bind order of OSGi declarative services


PS:我正在尝试避免使用ServiceTraker之类的东西。

3 个答案:

答案 0 :(得分:2)

如果您使用bnd生成XML,则顺序基于引用名称的词法顺序。即这些引用在写入XML之前按名称进行排序。

但是,第二个引用是动态的。我不确定您是否可以依赖该命令。我可以设想,如果在SCR试图注入之前调用public static void Write<T>(string filepath, System.Collections.Generic.IEnumerable<T> records, string schema, string delimiter = "\t") { var properties = typeof(T).GetProperties(); var classMap = new DefaultClassMap<T>(); var schemaList = schema.Split(';'); foreach (var name in schemaList) { if (!properties.Any(p => p.Name.ToUpper() == name.ToUpper())) { throw new Exception($"The given class is missing the property '{name}'"); } var property = properties.FirstOrDefault(p => p.Name.ToUpper() == name.ToUpper()); classMap.Map(typeof(T), property).Name(property.Name); } var config = new CsvHelper.Configuration.Configuration { Delimiter = delimiter }; using (var writer = new System.IO.StreamWriter(filepath)) using (var csv = new CsvHelper.CsvWriter(writer, config)) { csv.Configuration.RegisterClassMap(classMap); csv.WriteRecords(records); } } 的事件管理员之前有新服务出现。 1

这就是说,声明式服务规范中对此进行了详细介绍。

1 在OSGi标准化它们之前在bnd中开发的原始注释中,我将其设置为MULTIDY DYNAMIC的默认值,因为当您拥有 not 时不动态是很危险的多。在标准化过程中,我似乎无法使其他成员相信MULTISTAT STATIC是一个非常糟糕的组合,因为结果不确定,每次启动都可能改变。

答案 1 :(得分:2)

虽然您 可以使用词法排序(如Peter所建议的)或通过手动排序XML元素来控制注入的顺序,但我还是建议不要依赖于此。

在您的示例中,当绑定EventAdminEventAdmin服务时,您想向SomeInterface发送事件。进行此操作以及其他所需的初始化的理想场所是组件的Activate方法。确保在绑定 all 静态引用之后调用activate方法。在您的情况下,SomeInterface的基数为0..n,因此可能多次被称为零。您可以将所有实例累积到一个列表中,然后通过Activate方法对该列表进行迭代。

您甚至不必担心使List成为线程安全的或使用同步,因为SCR确保在最后一个服务绑定与Activate方法的开始之间存在“先于后预”的关系。

答案 2 :(得分:0)

只需更改标签顺序即可完成“技巧”。 xml文件现在看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.package.Sender">
   <reference bind="bindEventAdmin" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static" unbind="unbindEventAdmin"/>
   <reference bind="bindSomeInterface" cardinality="0..n" interface="com.package.bindSomeInterface" name="SomeInterface" policy="static" unbind="unbindSomeInterface"/>
   <implementation class="com.package.Sender"/>
</scr:component>

很明显,该框架尝试按照在xml文件中声明的顺序来解析所有引用

有趣的是,当您使用声明式服务实现您的类并且不要过多看IDE(在本例中为Eclipse)生成的xml文件时,这可能是一场噩梦,因为您希望在Windows中解决这些服务。声明 bind unbind 方法的顺序相同(.java文件中的代码行顺序)。