骆驼蓝图测试 - 我如何动态地 replace/proxy 蓝图中引用的远程服务以防止使用真实服务?

Camel Blueprint Testing - how can I dynamically replace/proxy a remote service referenced in Blueprint in order to prevent use of the real service?

我有以下场景:

我有一个 OSGI 包,它有一个在蓝图 XML 中定义的服务引用,它引用远程包中的一个接口,以及一个使用 impl 的方法之一来填充 Properties 对象的 bean。

Bundle #1 的相关片段 XML(消费者):

...
<!-- other bean definitions, namespace stuff, etc -->
<!-- reference to the fetching service -->
<reference id="fetchingService"    interface="company.path.to.fetching.bundle.FetchingService" />
<!-- bean to hold the actual Properties object: the getConfigProperties     method is one of the overridden interface methods -->
<bean id="fetchedProperties" class="java.util.Properties" factory-ref="fetchingService" factory-method="getProperties" />

<camelContext id="contextThatNeedsProperties" xmlns="http://camel.apache.org/schema/blueprint">

<propertyPlaceholder id="properties" location="ref:fetchedProperties" />

...
<!-- the rest of the context stuff - routes and so on -->

</camelContext>

远程捆绑包的 blueprint.xml:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/blueprint"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">


<cm:property-placeholder id="config-properties" persistent-id="company.path.configfetcher" />

<bean id="fetchingService" class="company.path.to.fetching.bundle.impl.FetchingServiceImpl" scope="singleton" init-method="createLoader" depends-on="config-properties">
    <property name="environment" value="${environment}" />
    <property name="pathToRestService" value="${restPath}" />
</bean>

<service ref="fetchingService" interface="company.path.to.fetching.bundle.FetchingService" />
<!-- END TESTING -->
</blueprint>

来自 Impl Class:

public synchronized Properties getProperties() {
    if(!IS_RUNNING) {
// timer task that regularly calls the REST api to check for updates
        timer.schedule(updateTimerTask, 0, pollInterval);
        IS_RUNNING = true;
    }
//Map<String, Properties> to return matching object if it's there 
    if(PROPERTIES_BY_KEY.containsKey(environment)) {
        return PROPERTIES_BY_KEY.get(environment);
    }
    /* if nothing, return an empty Properties object - if this is the case, then whatever bundle is relying on these
    *  properties is going to fail and we'll see it in the logs
    */
    return new Properties();
}

问题:

我有一个测试 class(扩展 CamelBlueprintTestSupport)并且有很多移动部件,所以我无法真正改变事物的顺序。不幸的是,属性 bean 方法在 CamelContext 启动之前被调用。没什么大不了的,因为在测试环境中没有配置文件可以从中读取必要的属性,因此检索失败,我们得到一个空的属性对象 [注意:我们正在用假货覆盖属性组件,因为它不是 class 正在测试],但在一个完美的世界中,我希望能够做两件事:

1) 用新的 Impl() 替换服务

2) 拦截对 getProperties 方法的调用或将 bean 绑定到新服务,以便调用 return 来自假 impl

的属性

想法?

编辑#1:

这是我现在正在做的一种解决方法:

try {
ServiceReference sr =      this.getBundleContext().getServiceReference(FetchingService.class);

        if(sr != null) {
            ((FetchingServiceImpl)this.getBundleContext().getService(sr)).setEnvironment(env);
            ((FetchingServiceImpl)this.getBundleContext().getService(sr)).setPath(path);
        }

    } catch(Exception e) {
        log.error("Error getting Fetching service: {}", e.getMessage());
    }

这里最大的问题是我必须等到调用 createCamelContext 才能使 BundleContext 存在;因此,getProperties 调用已经发生了一次。正如我所说,由于在测试环境中不存在 FetchingService class 的配置来提供环境和路径字符串,因此第一次调用将失败(导致属性对象为空)。第二次,上面的代码在 impl class 中设置了属性,我们开始比赛了。这不是关于无法正常工作的问题。相反,它是一种更好、更优雅的解决方案,可以应用于其他场景。

哦,在有人问之前澄清一下,这个服务的重点是我们不必为部署到我们的 Servicemix 实例的每个 OSGI 包都有一个 .cfg 文件 - 这个中央服务将去获取其他包需要的配置和唯一需要存在的 .cfg 文件是用于 Fetcher。

其他相关细节:

Camel 2.13.2 - 希望它是 2.14,因为他们在该版本中添加了更多 属性 占位符工具,这可能会使这更容易

Servicemix - 5.3.1

您是否尝试在测试中覆盖 CamelBlueprintTestSupportaddServicesOnStartup(参见 "Adding services on startup" http://camel.apache.org/blueprint-testing.html)?

在你的情况下是这样的:

@Override
protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
    services.put(FetchingService.class.getName(), asService(new FetchServiceImpl(), null));
}