OSGI 中的声明式服务
Declarative services in OSGI
我创建了一个(非常)简单的测试来确定如何使用 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 创建它,然后我将这个 jar 文件安装到我的 apache felix 容器中并启动它。但是,没有任何反应。什么都没有打印出来。
提前致谢!
您似乎已经完成大部分工作了!正如您所确定的,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);
}
这不是编写激活方法的正确方法(因此它可能不会被调用),您也不应在此处将您的组件注册为服务。当您将 class 设为 @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")
如果愿意,您可以完全摆脱 activate 方法。
最后:
Event Admin 不是消息队列,您的发布者是一次性发送。因此,如果您的发布者在处理程序完全注册之前发送事件,那么它将永远不会收到该事件。考虑让发布者发送周期性事件,或者确保接收者在发布者之前启动,以便您看到消息。
P.S.
这在技术上不是问题,但我看到您使用的是 maven-bundle-plugin
的 2.4 版。这是 非常 旧的,当前发布的 bnd 版本是 3.5.0。 Bnd 团队也开始提供他们自己的 Maven 插件(例如 bnd-maven-plugin
),您可能想看看。
我创建了一个(非常)简单的测试来确定如何使用 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 创建它,然后我将这个 jar 文件安装到我的 apache felix 容器中并启动它。但是,没有任何反应。什么都没有打印出来。
提前致谢!
您似乎已经完成大部分工作了!正如您所确定的,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);
}
这不是编写激活方法的正确方法(因此它可能不会被调用),您也不应在此处将您的组件注册为服务。当您将 class 设为 @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")
如果愿意,您可以完全摆脱 activate 方法。
最后:
Event Admin 不是消息队列,您的发布者是一次性发送。因此,如果您的发布者在处理程序完全注册之前发送事件,那么它将永远不会收到该事件。考虑让发布者发送周期性事件,或者确保接收者在发布者之前启动,以便您看到消息。
P.S.
这在技术上不是问题,但我看到您使用的是 maven-bundle-plugin
的 2.4 版。这是 非常 旧的,当前发布的 bnd 版本是 3.5.0。 Bnd 团队也开始提供他们自己的 Maven 插件(例如 bnd-maven-plugin
),您可能想看看。