【问题标题】:Declarative services in OSGIOSGI 中的声明式服务
【发布时间】:2018-05-20 10:31:49
【问题描述】:

我创建了一个(非常)简单的测试来确定如何使用 Apache Felix 发送和接收事件。

这是我的发件人:

package be.pxl;

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

import java.util.HashMap;

@Component(name = "be.pxl.Publisher", immediate = true)
public class Publisher {

    EventAdmin admin;

    @Activate
    public void run(Object object) {
        System.out.println("IN PUBLISHER");
        Event event = new Event("event", new HashMap<String, Object>());
        System.out.println("\tEVENT: " + event);
        admin.postEvent(event);
        System.out.println("\tADMIN: " + admin);
    }

    @Reference(name="be.pxl.admin", service = EventAdmin.class)
    protected void setEventAdmin(EventAdmin admin) {
        this.admin = admin;
    }

}

这是我的接收器:

package be.pxl;

import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import java.util.Dictionary;
import java.util.Hashtable;

@Component(name = "be.pxl.Subscriber", immediate = true)
public class Subscriber implements EventHandler {

    private BundleContext context;

    @Activate
    public void run(Object object) {
        System.out.println("IN SUBSCRIBER");
        System.out.println("\tIN RUN METHOD");
        String[] topics = new String[]{"event"};
        Dictionary props = new Hashtable();
        props.put(EventConstants.EVENT_TOPIC, topics);
        System.out.println("\t\tCONTEXT: " + context);
        context.registerService(EventHandler.class.getName(), this, props);
        System.out.println("\t\tCONTEXT AFTER REGISTERSERVICE: " + context);
    }

    public void handleEvent(Event event) {
        System.out.println("IN SUBSCRIBER");
        String text = event.getProperty("text").toString();
        System.out.println("\tEVENT CALLED: " + text);
    }

    @Reference(name="be.pxl.context", service=BundleContext.class)
    protected void setBundleContex(BundleContext context) {
        this.context = context;
    }

}

这是我发件人的 pom:

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>be.pxl</groupId>
    <artifactId>EventSender</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.service.event</artifactId>
            <version>1.3.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.service.component.annotations</artifactId>
            <version>1.3.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.osgi</groupId>
            <artifactId>org.eclipse.osgi.services</artifactId>
            <version>3.2.100.v20100503</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>2.4.0</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-Vendor>SmartCampus</Bundle-Vendor>
                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
                        <Export-Package>
                            be.pxl.*;version="1.0.0"
                        </Export-Package>
                        <Import-Package>
                            org.osgi.service.component.annotations
                            org.eclipse.osgi.service
                            org.osgi.core
                            org.osgi.service.event
                        </Import-Package>
                        <_dsannotations>*</_dsannotations>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

一切都编译得很好。我使用 mvn clean package 创建它,然后在我的 apache felix 容器中安装这个 jar 文件并启动它。然而,什么也没有发生。没有任何东西可以打印出来。

提前致谢!

【问题讨论】:

    标签: maven annotations osgi apache-felix declarative-services


    【解决方案1】:

    您似乎已经完成了大部分任务!如您所见,Event Admin 使用白板模型来接收事件。重要的是你需要告诉白板你想听哪些主题,你做什么。

    %%% 更新 %%%

    事件管理主题名称使用由/ 字符分隔的令牌层次结构。将事件发布到特定主题时,例如 foo/bar/baz。接收事件时,将为匹配其注册兴趣的主题调用 EventHandler。这些兴趣可以针对特定主题,也可以以* 结尾以表示通配符匹配。例如foo/bar/* 将接收发送到foo/bar/baz 的事件和发送到foo/bar/fizzbuzz 的事件。

    %%% 回到原来的%%%

    不过,您的代码存在一些问题:

    首先:

    @Reference(name="be.pxl.context", service=BundleContext.class)
    protected void setBundleContex(BundleContext context) {
        this.context = context;
    }
    

    这不是您访问捆绑包的BundleContext 的方式。如果您确实需要BundleContext,那么它应该作为参数注入到您的@Activate 注释方法中。 BundleContext 永远不应该注册为服务(它代表你的包对 OSGi 框架的私有访问),我不会惊讶地发现这个引用在你的示例中不满意。但是,您实际上并不需要 BundleContext,因为...

    其次:

    @Activate
    public void run(Object object) {
        System.out.println("IN SUBSCRIBER");
        System.out.println("\tIN RUN METHOD");
        String[] topics = new String[]{"event"};
        Dictionary props = new Hashtable();
        props.put(EventConstants.EVENT_TOPIC, topics);
        System.out.println("\t\tCONTEXT: " + context);
        context.registerService(EventHandler.class.getName(), this, props);
        System.out.println("\t\tCONTEXT AFTER REGISTERSERVICE: " + context);
    }
    

    这不是编写激活方法的正确方法(因此它可能不会被调用),您也不应该在此处将组件注册为服务。当您将您的类设为@Component 时,它将使用每个直接实现的接口自动注册为服务。这意味着:

    @Component(name = "be.pxl.Subscriber", immediate = true)
    public class Subscriber implements EventHandler {
        ...
    }
    

    已经是一个 OSGi EventHandler 服务了!

    您可以使用@Component 注释将服务属性添加到您的组件,或者使用组件属性注释从OSGi R7 版本(几个月后到期)添加服务属性。在这种情况下,您希望像这样设置 event.topics 属性:

    @Component(property="event.topics=event")
    

    如果你愿意,你可以完全摆脱激活方法。

    最后:

    Event Admin 不是消息队列,您的发布者是一次性发送。因此,如果您的发布者在处理程序完全注册之前发送事件,那么它将永远不会收到该事件。考虑让发布者发送周期性事件,或者确保接收者在发布者之前开始以便您看到消息。

    附言

    这在技术上不是问题,但我看到您使用的是 maven-bundle-plugin 的 2.4 版。这是非常旧的版本,当前发布的 bnd 版本是 3.5.0。 Bnd 团队也开始提供他们自己的 Maven 插件(例如 bnd-maven-plugin),您可能想看看。

    【讨论】:

    • 感谢您提供非常有帮助的解释!但是如何使用我在 Component 注释中声明的属性呢?另外,我需要一个 BundleActivator 来启动我的组件吗?这个演示的目的是立即启动订阅者,然后在 Apache Felix 容器中启动发布者,并打印出发布者发送的文本。另外,我如何确定接收者在发布者之前开始?提前致谢!
    • 如何使用我用@Component 声明的属性? 答案已经说明了如何声明它,所以我假设您的问题是关于事件管理主题的。我为此添加了一个部分。 我需要 BundleActivator 吗? 绝对不需要。我建议人们不要编写 Bundle Activators,因为在现代 OSGi 应用程序中使用低级 API 的理由很少。 如何确定接收器在发布者之前启动?如果您安装了 gogo shell,您可以使用 scr:listscr:info 命令检查 DS 组件
    猜你喜欢
    • 2012-04-27
    • 1970-01-01
    • 2013-10-09
    • 1970-01-01
    • 2010-12-31
    • 2011-09-03
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    相关资源
    最近更新 更多