Java 带有组件XML文件的OSGI DeclarativeService Multiton

Java 带有组件XML文件的OSGI DeclarativeService Multiton,java,osgi,Java,Osgi,我需要一个给定OSGI组件的multiton实例,即一些捆绑包将获得相同的实现实例,而其他捆绑包需要另一个实例。如果可能的话,我需要使用XML文件而不是注释(如@Component)。我使用的是一个混合OSGi4.3平台,由eclipse和felix的捆绑包组成 比方说,我的服务界面如下所示: public interface SocketService { // Does nothing if already listening on given port public vo

我需要一个给定OSGI组件的multiton实例,即一些捆绑包将获得相同的实现实例,而其他捆绑包需要另一个实例。如果可能的话,我需要使用XML文件而不是注释(如@Component)。我使用的是一个混合OSGi4.3平台,由eclipse和felix的捆绑包组成

比方说,我的服务界面如下所示:

public interface SocketService {

    // Does nothing if already listening on given port
    public void startListening(int port);

    public String getNextMessage();
}
声明性XML文件如下所示,工作正常:

<?xml version="1.0" encoding="UTF-8"?>
  <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="SocketService">
    <implementation class="declarativemultiton.service.impl.SocketServiceImpl"/>
    <service>
      <provide interface="declarativemultiton.service.SocketService"/>
    </service>
</scr:component>
以及它们的组件定义XML:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Consumer1" immediate="true">
   <implementation class="declarativemultiton.consumer1.Consumer1"/>
   <reference bind="setSocketService" cardinality="1..1" interface="declarativemultiton.service.SocketService" name="SocketService" policy="static"/>
</scr:component>

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Consumer2" immediate="true">
   <implementation class="declarativemultiton.consumer2.Consumer2"/>
   <reference bind="setSocketService" cardinality="1..1" interface="declarativemultiton.service.SocketService" name="SocketService" policy="static"/>
</scr:component>

一切正常,两个组件得到相同的实例:

套接字服务Impl已激活
消费者得到SocketService@1769618707
消费者1已激活
消费者2得到socketservice@1769618707
消费者2已激活

我需要Component1和Component2获取不同的SocketService实例,Component2和Component3(未显示)具有相同的SocketService实例

如果我将配置策略更改为“需要”,则不会激活任何使用者组件:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" configuration-policy="require" name="SocketService">
   <implementation class="declarativemultiton.service.impl.SocketServiceImpl"/>
   <service>
      <provide interface="declarativemultiton.service.SocketService"/>
   </service>
</scr:component>    


这就是我迷路的地方,我不知道如何将配置动态地传递给SocketService。我在读有关ConfigurationAdmin、ManagedService、ManagedServiceFactory、ComponentFactory等方面的文章时,一直很紧张。我找不到一个具体、简洁的解决方案。有一些相互矛盾的方法,比如这个答案说不要使用ManagedService,但是Karaf教程介绍了它的使用。

您没有说组件1、组件2和组件3在哪个捆绑包中。您可以使用servicefactory=true让DS为每个消费捆绑包创建一个不同的SocketService实例。在DS 1.3(在Core R6上)中,您甚至可以使用新的PrototypeServiceFactory支持为所有使用组件创建不同的实例。但是,如果您想完全控制某些任意组件使用相同的SocketFactory实例,而其他组件使用不同的SocketFactory实例,那么您就遇到了一个使用DS很难解决的问题。

复制服务的XML文件解决了我的问题,但可能不是真正的OSGI方式

首先,我们复制服务的组件描述符,但添加一个属性以将其与其他实现区分开来:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="SocketService2">
   <implementation class="declarativemultiton.service.impl.SocketServiceImpl"/>
   <service >
      <provide interface="declarativemultiton.service.SocketService" />
   </service>
   <property name="socketType" type="String" value="server"/>
</scr:component>

然后我们向消费组件添加target属性:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Consumer2" immediate="true">
   <implementation class="declarativemultiton.consumer2.Consumer2"/>
   <reference bind="setSocketService" cardinality="1..1" interface="declarativemultiton.service.SocketService" 
        name="SocketService2" policy="static" target="(socketType=server)"/>
</scr:component>

服务实现类使用activate方法获取映射中的给定属性:

public class SocketServiceImpl implements SocketService {

    public void activate(Map<String, Object> props) {
        System.out.println("SocketServiceImpl@" + System.identityHashCode(this) + " activated with props: "
                + Joiner.on(';').withKeyValueSeparator("=>").join(props.entrySet()));
    }

    @Override
    public void startListening(int port) {
        //
    }

    @Override
    public String getNextMessage() {
        //
        return null;
    }

}
公共类SocketServiceImpl实现SocketService{
公共空间激活(地图道具){
System.out.println(“SocketServiceImpl@”+System.identityHashCode(this)+“使用道具激活”:
+Joiner.on(“;”).withKeyValueSeparator(=>”).join(props.entrySet());
}
@凌驾
公共监听(int端口){
//
}
@凌驾
公共字符串getNextMessage(){
//
返回null;
}
}
只需复制XML文件,而不复制实现类,也不公开实现,我们就可以向使用者提供单独的实例:

SocketServiceImpl@936816937用props激活:component.name=>SocketService;id=>0;objectClass=>[Ljava.lang.String;@5062e9b4
消费者得到SocketService@936816937
消费者1已激活
SocketServiceImpl@2029093081用props激活:objectClass=>[Ljava.lang.String;@37426497;socketType=>server;component.name=>SocketService2;component.id=>1
消费者2得到socketservice@2029093081
消费者2已激活
消费者3得到SocketService@2029093081
消费者3已激活


如上所述,我怀疑有更好的方法来实现这一点,但我厌倦了现有的各种文档。

我认为您使用服务属性来识别或区分SocketFactory实例和消费组件上的目标属性,以确定使用哪一种是正确的方法,但我建议使用配置,而不是每个实现类使用多个组件xml。我不太清楚SocketFactory或consumer有多少个实现类。为了说明这一点,我将假设每个实现类都有一个

您可以通过config admin使用DS组件的配置,而不是复制组件xml并修改其中的属性。Peter Kriens对DS有很好的解释,包括此处的配置: 。以下是一些步骤,更详细地介绍了使用配置设置引用:

  • 将配置pid添加到组件xmls中。假设socketFactory为socketFactory,consumer为consumer

  • 安装config admin。我认为felix和eclipse都可以正常工作,我只使用过felix

  • 在希望安排套接字工厂启动的捆绑包中,执行一些获取ConfigAdmin实例的代码并调用

    Configuration sfc = ca.createFactoryConfiguration("socketFactory", null);
    Hashtable<String, Object> props = new Hashtable<String, Object>();
    props.put("socketType", "MyType");
    sfc.update(props);
    
    Configuration sfc=ca.createFactoryConfiguration(“socketFactory”,null);
    Hashtable props=新的Hashtable();
    道具放置(“socketType”、“MyType”);
    证监会更新(道具);
    
  • 如果此代码与SocketFactory代码在捆绑包中,则可以省略null参数。 这将导致SocketFactory组件具有socketType=MyType服务属性,类似于您对组件属性所做的操作

  • 在要安排消费者设置的捆绑包中,使用目标过滤器执行类似操作:

    Configuration cc = ca.createFactoryConfiguration("consumer", null);
    Hashtable<String, Object> props = new Hashtable<String, Object>();
    props.put("SocketService.target", "(socketType=MyType)");
    cc.update(props);
    
    Configuration cc=ca.createFactoryConfiguration(“消费者”,null);
    Hashtable props=新的Hashtable();
    props.put(“SocketService.target”,“(socketType=MyType)”;
    抄送更新(道具);
    
  • 这将生成具有指定目标筛选器的使用者组件

    你或许可以选择与之斗争
    Configuration cc = ca.createFactoryConfiguration("consumer", null);
    Hashtable<String, Object> props = new Hashtable<String, Object>();
    props.put("SocketService.target", "(socketType=MyType)");
    cc.update(props);
    
    @Component
    public class Consumer1 {
      @Reference( target="(group=1)")
      SocketListener listener;
    
    }
    
    @Component
    public class Consumer2 {
      @Reference( target="(group=2)")
      SocketListener listener;
    
    }
    
    @Designate( Config.class, factory=true )
    @Component
    public class SocketListenerImpl extends Thread 
      implements SocketListener {
      @interface Config {
        int port();
        String group();
      }
      private ServerSocket server;
    
      @Activate void activate(Config config ) {
        server = new ServerSocket( config.port());
        super.start();
      }
    
      public void run() { ... }
    
      @Override public String getNextMessage() { ... }
    }