具有组件XML文件的OSGI DeclarativeService Multiton

时间:2015-10-16 13:21:12

标签: java osgi

我需要给定OSGI组件的多个实例,即一些bundle将获得相同的实现实例,而其他bundle需要另一个实例。如果可能,我需要使用 XML文件而不是注释(例如 @Component )。我正在使用一个混合的OSGI 4.3平台,包括来自eclipse和amp;的捆绑包。费利克斯。

我们的服务界面如下所示:

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>

这些是消费者类,具有相同的语义:

public class Consumer1 {

    public void activate() {
        System.out.println("Consumer1 activated");
    }

    public void setSocketService(SocketService service) {
        System.out.println("Consumer1 got SocketService@" + System.identityHashCode(service));
    }
}

public class Consumer2 {

    public void activate() {
        System.out.println("Consumer2 activated");
    }

    public void setSocketService(SocketService service) {
        System.out.println("Consumer2 got socketservice@" + System.identityHashCode(service));
    }
}

他们的组件定义XML&#39>:

<?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>

Everthing按预期工作,两个组件都获得相同的实例:

  

套接字服务Impl已激活
  Consumer1得到了SocketService @ 1769618707
  Consumer1激活了   消费者2获得了socketservice @ 1769618707
  Consumer2已激活

我需要Component1和Component2来获取不同的 SocketService实例,而Component2和Component3(未显示)需要相同的 SocketService实例。< / p>

如果我将配置策略更改为&#34;要求&#34;没有消费者组件被激活:

<?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等的同时,我一直在拉我的头发。我找不到一个具体,简洁的解决方案。有一些相互矛盾的方法,例如此答案https://stackoverflow.com/a/4129464/330464表示不使用ManagedService,但Karaf教程http://sully6768.blogspot.com.tr/2012/09/declarative-services-with-karaf-part-4.html解决了它的使用问题。

4 个答案:

答案 0 :(得分:1)

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

您可以通过config admin使用DS组件的配置,而不是复制组件xml并修改其中的属性。 Peter Kriens对DS有一个很好的解释,包括配置: http://enroute.osgi.org/services/org.osgi.service.component.html。以下是一些步骤,包括使用配置更详细地设置参考:

  1. 将configuration-pid添加到组件xmls。让我们假设socketFactory的socketFactory和消费者的消费者。

  2. 安装配置管理员。我认为felix和eclipse的工作都很好,我只使用了felix。

  3. 在要安排套接字工厂开始的捆绑包中,执行一些获取ConfigAdmin实例的代码并调用类似

    的代码
    Configuration sfc = ca.createFactoryConfiguration("socketFactory", null);
    Hashtable<String, Object> props = new Hashtable<String, Object>();
    props.put("socketType", "MyType");
    sfc.update(props);
    
  4. 如果此代码在包含SocketFactory代码的包中,则可以省略null参数。 这将导致SocketFactory组件具有socketType = MyType服务属性,类似于您对组件属性所做的操作。

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

      Configuration cc = ca.createFactoryConfiguration("consumer", null);
      Hashtable<String, Object> props = new Hashtable<String, Object>();
      props.put("SocketService.target", "(socketType=MyType)");
      cc.update(props);
      
    2. 这将导致消费者组件具有指定的目标过滤器。

      你或许可以选择与... felix fileinstall从属性文件安装配置。这是非常具有说服力的,但我没有任何经验,我看到有些人难以将所有部分排成一列,所以它的工作原理。在工作中,我可以使用xml工作的系统:它从元类型生成模式,并将xml配置文档(符合生成的模式)与元类型组合以生成配置。它可以从例如生成目标滤波器元件。嵌套的xml。我经常希望有一个开源版本。

      顺便说一下,您可能想重新考虑避免使用spec ds注释。它们不是在运行时使用,而是在组装bundle时由bnd处理。 (你正在使用bnd,不是吗?)。 Bnd将生成与您的组件兼容的最低ds版本的component.xml,它将比手动执行更多验证(至少我可以手动完成)。

答案 1 :(得分:0)

您不会说出Component1,2和3所在的捆绑包。您可以使用servicefactory = true来让DS为每个使用捆绑包创建一个独特的SocketService实例。在DS 1.3(在Core R6上),您甚至可以使用新的PrototypeServiceFactory支持为所有使用的组件创建不同的实例。但是,如果您希望完全控制某些任意组件使用相同的组件而其他组件使用不同的SocketFactory实例,则您遇到的问题很难通过DS解决。

答案 2 :(得分:0)

复制服务的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>

然后我们将目标属性添加到消费组件:

<?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方法获取Map中的给定属性:

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;
    }

}

只是复制XML文件,而不是实现类,也没有暴露实现,我们可以向消费者提供单独的实例:

  

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

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

答案 3 :(得分:0)

首先,你的问题听起来有点可疑?如果不同的接收者可以指定不同的端口来监听,您如何共享服务? startListening方法听起来像早先尝试共享同一个实例并在其外观后面调度到不同的服务器?

我假设您要将服务连接到不同的捆绑包。正如大卫所说,最好通过配置完成。

方法是通过工厂配置创建SocketListener服务,并使用引用的目标过滤器来获得正确的:

@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() { ... }
}

只需转到Web控制台并在那里编辑配置;它将显示属性的GUI。 group属性未在代码中使用,但可作为服务属性使用,以便消费者可以对其进行目标选择。

请注意,目标过滤器也可以通过配置覆盖。 <{1}} PID上的target.listener=(group=3)等属性可以完成这项工作。

P.S。这个例子是当前的OSGi R6,但是R4.3中使用的bnd注释也可以实现每个功能。

P.S2。正如大卫所说的那样,看看http://enroute.osgi.org/services/org.osgi.service.coordinator.htmlhttps://github.com/osgi/osgi.enroute.examples/tree/master/osgi.enroute.examples.component.application如果你发现缺少PR,请不要犹豫。