【问题标题】:Require a service to be active before a bundle is started在启动捆绑包之前要求服务处于活动状态
【发布时间】:2019-02-05 04:05:49
【问题描述】:

我编写了一个 BundleActivator,它应该在它的包启动之前更新某些配置。我需要 ConfigurationAdmin 服务,但我在 BundleActivator 的启动方法中从 BundleContext 获得了一个空的 ServiceReference。

BundleActivator扩展了以下抽象类,只实现了具体的更新逻辑:

public abstract class AbstractConfigUpdater implements BundleActivator {

    private ServiceReference<ConfigurationAdmin> configurationAdminServiceReference;

    @Override
    public void start(final BundleContext context) throws Exception {
        configurationAdminServiceReference = context.getServiceReference(ConfigurationAdmin.class);
        final ConfigurationAdmin configurationAdmin = context.getService(configurationAdminServiceReference);
        final Configuration[] configurations =
                                         configurationAdmin.listConfigurations(getFilter());
        if (configurations != null) {
            for (final Configuration configuration : configurations) {
                final Dictionary<String, Object> properties = configuration.getProperties();
                    if (updateProperties(properties)) {
                    configuration.update(properties);
                }
            }
        }
    }

    protected abstract String getFilter();

    /**
     * Updates the properties if needed.
     *
     * @param properties
     *            the configuration properties
     * @return if any modifications to the Dictionary were made
     */
    protected abstract boolean updateProperties(final Dictionary<String, Object> properties);

    @Override
    public void stop(final BundleContext context) throws Exception {
        context.ungetService(configurationAdminServiceReference);
    }

}

我已经在具体的 BundleActivator 中添加了一个注释来生成一个清单头,以要求 ConfigurationAdmin 服务对包可用:

@RequireCapability(filter = "(objectClass=org.osgi.service.cm.ConfigurationAdmin)",
               ns = "osgi.service",
               resolution = Resolution.mandatory)

清单标头已生成,但我仍然得到一个空的 ServiceReference。我应该如何解决这个问题?或者是否有其他方法可以在启动组件之前更新配置?

【问题讨论】:

  • 有什么理由不使用声明式服务 (DS)?您要解决的问题在 Activator 中非常困难,但在 DS 中微不足道。
  • 您还将静态依赖项(Require-Capability)与动态依赖项(此捆绑包应该处于活动状态)混淆了。它们是两个不同的东西,不相关。 Require Capability 确保有一个捆绑包已安装,但不能保证该服务已注册,因为这可能取决于其他东西。
  • 感谢您对 Require-Capability 的澄清。我选择 BundleActivator 而不是 DS 组件的原因是我想在 bundle 启动时更新配置,因为配置的 ObjectClassDefinition 已更改,我想在激活相应的 DS 组件之前运行配置更新。
  • 您查看过 R7 中的 OSGi 配置器吗? (或其前身在 v2archive.osgi.enroute/osgi.enroute.configurator.simple.provider)。这可能会以更简单的方式解决您的问题。你现在的路是一个兔子洞。

标签: osgi


【解决方案1】:

我不知道这是否有帮助,但是你可以开发一个org.osgi.service.cm.ConfigurationPlugin 来拦截所有在运行时注入的属性并修改它们:

public class MyConfigurationPlugin implements BundleActivator, ConfigurationPlugin {
    ServiceRegistration<ConfigurationPlugin> configPluginRef;

    @Override
    public void start(BundleContext context) throws Exception {
        //... init the config plugin
        Map<String,String> properties = new HashMap<>();            
        configPluginRef = context.registerService(
            ConfigurationPlugin.class, 
            this, 
            new Hashtable<>(properties));
    }

    @Override
    public void modifyConfiguration(ServiceReference<?> reference,
            Dictionary<String, Object> properties) {
        /*
         * View and possibly modify a set of configuration properties 
         * before they are sent to the Managed Service or the Managed Service Factory.  
         */
    }

}

当然,声明式服务方法是一种更简单的选择:

@Component ( 
    service= {}, 
    configurationPid={
        configPid1,
        configPid2,
        ...
    })
public class MyComponent {

    @Activate
    public void activate(BundleContext context, Map<String, String> properties) {

    }

    @Modified
    public void updated(BundleContext context, Map<String, String> properties) {
        // Called when properties change
    }
}

但在这种情况下,您无法更改属性值:您只能对属性更改做出反应。

【讨论】:

    【解决方案2】:

    您可以使用 OSGi ServiceTracker 等待并从服务注册表中检索服务。

    例如,

    import org.osgi.framework.Constants
    import org.osgi.framework.Filter;
    import org.osgi.util.tracker.ServiceTracker;
    import org.osgi.service.cm.ConfigurationAdmin;
    
    ...
    
    private static final long TIMEOUT_MILLIS = 10000;
    
    @Override
    public void start(final BundleContext context) throws Exception {
        Filter filter = context.createFilter("(" + Constants.OBJECTCLASS + "=org.osgi.service.cm.ConfigurationAdmin)");
        ServiceTracker<?, ?> configurationAdminTracker = new ServiceTracker<>(context, filter, null);
    
        configurationAdminTracker.open();
        ConfigurationAdmin configurationAdmin = (ConfigurationAdmin) configurationAdminTracker.waitForService(TIMEOUT_MILLIS);
        configurationAdminTracker.close();
    
        if (configurationAdmin == null) {
            // Not found
        }
        ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-26
      • 2019-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多