如何使用Configuration Admin为同一个类创建多个配置?

时间:2018-06-16 23:40:36

标签: java osgi

我正在开发一个利用OSGi框架的实体组件系统游戏引擎。 我希望用户/开发人员能够以模块化方式创建自己的组件类型,类似于Bethesda Creation Kit。

Creation Kit Object Window

我考虑接近这个的方法是创建一个代表组件类型的类,然后使用Configuration Admin创建配置,但我不确定我的理解是否正确。

我有一个我想用作组件类型的类

@Component(
    configurationPid = "Species",
    configurationPolicy = ConfigurationPolicy.REQUIRE,
    service = Species.class
)
public final class Species {
    // ...
}

为了测试这个,我为Apache Gogo创建了一个命令来创建Species。我的想法是我应该能够用这个命令创建多个物种。

@Component(
    property = {
        CommandProcessor.COMMAND_SCOPE + "=species",
        CommandProcessor.COMMAND_FUNCTION + "=create"
    },
    service = CreateSpeciesCommand.class
)
public class CreateSpeciesCommand {

    /* L1 */

    @Reference(bind = "bindConfigurationAdmin")
    private ConfigurationAdmin configurationAdmin;

    @Descriptor("creates a species")
    public void create(@Descriptor("id of the species") final String speciesId) throws IOException, InvalidSyntaxException {
        final String filter = String.format("(%s=%s)", Constants.OBJECTCLASS, Species.class.getSimpleName());
        final Configuration[] existingConfigurations = configurationAdmin.listConfigurations(filter);
        System.out.println(Arrays.toString(existingConfigurations));

        final Configuration speciesConfiguration = configurationAdmin.getConfiguration(Species.class.getSimpleName(), "?");
        Dictionary<String, Object> configProperties = new Hashtable<>();
        configProperties.put(Constants.SERVICE_PID, "Species");

        speciesConfiguration.update(configProperties);
    }
}

但所有发生的事情都是它修改了配置,而不是创建一个新配置。

使用Configuration Admin为同一个类创建多个配置需要做什么?

2018-06-19编辑:

进行Peter Kriens' answer指定的更改:

  • @Designate注释添加到Species
  • @Component#name设为唯一
  • @Component#configurationPolicy设为ConfigurationPolicy.REQUIRE
  • @ObjectClassDefinition添加到Species.Config
  • 使用ConfigurationAdmin#getConfigurationcreateConfiguration不存在,只有createFactoryConfiguration@Component#name作为pid

只会创建一个配置,后续调用会更新。

2 个答案:

答案 0 :(得分:2)

OSGi Configuration Admin有两种不同类型的配置:

  • Singleton - 有一个PID
  • 工厂 - 具有PID(用于工厂实例)和实例“组”的工厂PID。

在OSGi&gt; = 6中,您可以这样做:

 @Designate( ocd= Species.Config.class, factory=true )
 @Component( name = "species.pid", configurationPolicy=ConfigurationPolicy.REQUIRE )
 public class Species {
      @ObjectClassDefinition
      @interface Config {
          String id();
      }

      @Activate
      void activate( Config config) {
          System.out.println( config.id() );
      }
}

现在命令(用list + delete函数扩展):

 @Component(
   property = {
     CommandProcessor.COMMAND_SCOPE + "=species",
     CommandProcessor.COMMAND_FUNCTION + "=create",
     CommandProcessor.COMMAND_FUNCTION + "=list",
     CommandProcessor.COMMAND_FUNCTION + "=delete"
   },
   service = CreateSpeciesCommand.class
 )
 public class CreateSpeciesCommand {
   @Reference
   ConfigurationAdmin configurationAdmin;

   public Configuration create(String speciesId) throws Exception {
     Configuration c = configurationAdmin.createFactoryConfiguration( "species.pid", "?");
     Hashtable<String,Object> d = new Hashtable();
     d.put("id", speciesId);
     c.update( d );
     return c;         
   }

   public Configuration[] list() throws Exception {
     return configurationAdmin.
        listConfigurations( "(service.factoryPid=species.pid)");
   }

   public boolean delete(String id) throws Exception {
     Configuration[] list = configurationAdmin.
        listConfigurations( "(&(service.factoryPid=species.pid)(id="+id+"))");
     if ( list == null) {
        return false;
     }
     for ( Configuration c : list ) {
        c.delete();
     }
     return true;
   }
}

一些注意事项:

  • 编写时不编译,因此可能存在编译错误
  • WebConsole可以更好地创建和删除Species
  • 跳过Gogo注释以提高可读性
  • 显然无法防范“过滤器”注入攻击

答案 1 :(得分:0)

为了获得每个呼叫的新配置,我需要更改以下内容:

  • Species类的ConfigurationAdmin#createFactoryConfiguration值设置为唯一值
  • 使用getConfiguration方法代替@Designate方法

我尝试应用Peter Kriens' answer指定的更改:

  • Species类添加@Component#name注释
  • @Component#configurationPolicy设置为唯一的内容
  • ConfigurationPolicy.REQUIRE设置为@ObjectClassDefinition
  • Species.Config添加到ConfigurationAdmin#getConfiguration
  • 使用createConfiguration作为pid使用createFactoryConfiguration@Component#name不存在,只有createFactoryConfiguration); @Component#factory使用来自package net.zephyrion.hummingbird.module.species; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; import java.util.Objects; @Component( factory = "Species", configurationPolicy = ConfigurationPolicy.REQUIRE, service = Species.class ) public final class Species { @interface Config { String id() default ""; } private Config config; @Activate public void configure(final Config config) { this.config = Objects.requireNonNull(config); } private String getId() { return config.id(); } } 的值

不仅可以进行新的配置,还可以同时激活物种组分。不知道为什么会这样,但我正在调查中。

更新代码

Species.java

package net.zephyrion.hummingbird.module.species;

import org.apache.felix.service.command.CommandProcessor;
import org.apache.felix.service.command.Descriptor;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

import java.io.IOException;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;

@Component(
    property = {
        CommandProcessor.COMMAND_SCOPE + "=species",
        CommandProcessor.COMMAND_FUNCTION + "=create"
    },
    service = CreateSpeciesCommand.class
)
public class CreateSpeciesCommand {

    /* L1 */

    @Reference(bind = "bindConfigurationAdmin")
    private ConfigurationAdmin configurationAdmin;

    @Descriptor("creates a species")
    public void create(@Descriptor("id of the species") final String speciesId) throws IOException {
        try {
            final String factoryPid = Species.class.getSimpleName();
            final String filter = String.format("(&(id=%s)(service.factoryPid=%s))", speciesId, factoryPid);
            final boolean configurationExists = configurationAdmin.listConfigurations(filter) != null;
            if (!configurationExists) {
                final Configuration speciesConfiguration = configurationAdmin.createFactoryConfiguration(factoryPid, "?");
                Dictionary<String, Object> configProperties = new Hashtable<>();
                configProperties.put("id", speciesId);

                speciesConfiguration.update(configProperties);
            }
        }
        catch (InvalidSyntaxException e) {
            e.printStackTrace();
        }
    }

    /* L2 */

    private void bindConfigurationAdmin(final ConfigurationAdmin configurationAdmin) {
        // TODO Obj.rnn
        this.configurationAdmin = configurationAdmin;
    }
}

CreateSpeciesCommand.java

  control_conversion  test_conversion 
day1  100                 101         
day3  140                 200
day5  200                 320
day7  400                 800