Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend and override 'ConfigClientProperties' #724

Closed
derTobsch opened this issue Jun 18, 2017 · 8 comments
Closed

Extend and override 'ConfigClientProperties' #724

derTobsch opened this issue Jun 18, 2017 · 8 comments
Labels

Comments

@derTobsch
Copy link

Hey,
I want to provide my own 'ConfigClientProperties' with special default values integrated in my
own autoconfiguration. I can not provide my own property because there is no
@ConditionalOnMissingBean in the ConfigServiceBootstrapConfiguration class.
Also I would need something like @AutoConfigureBefore in the bootstrap phase, or?

Is there a way to provide my own bean of the ConfigClientProperties? I also want to expose the configuration on another conf. path and not under "spring.cloud.config.*"

@ryanjbaxter
Copy link
Contributor

I supposed you could try adding @Primary to your bean, did you try that? Why not just set the values you want in your properties|yaml files?

@derTobsch
Copy link
Author

I cannot use properties|yml files, because I do not have any properties|yml files in my autoconfigure. I want to build a separate starter/autoconfigure project with predefined values and also add the OAuth2 rest template with different values and want to wrap that all up.

I can try that with @primary. But maybe there will be a way, like in the basic spring boot autoconfigure, to provide an ordering of BootstrapConfigurations and to let other override the ConfigurationProperties of spring cloud or like in this case spring cloud config.

--

I tried @primary, but that does not work, because first the
ConfigServiceBootstrapConfiguration will create the ConfigClientProperties and than the
ConfigServicePropertySourceLocator with the ConfigClientProperties as dependency and then
PropertySourceBootstrapConfiguration#locate will be called from the PropertySourceBootstrapConfiguration.
After all that configuration my, with @primary marked, Properties will be created but they just laying there till the ends of their days :)

if there would be a mechanism like @AutoConfigureBefore I could provide my own property classes
and your mechanism will use mine.

@spencergibb
Copy link
Member

I think the section on providing a custom rest template should give you some idea http://cloud.spring.io/spring-cloud-static/Dalston.SR1/#custom-rest-template

@derTobsch
Copy link
Author

derTobsch commented Jun 26, 2017

OK I tried some configurations but nothing works.

First

If I expose the CoffeeNetConfigClientProperties as configClientProperties then your
ConfigClientProperties will be used and my one will be ignored event if
I annotate the bean with @Primary.

First your ConfigServiceBootstrapConfiguration#configClientProperties
bean will be created and then if fails with

Caused by: java.lang.IllegalStateException: @Bean method CoffeeNetConfigBootstrapConfiguration.configClientProperties called as a bean reference for type [coffee.synyx.autoconfigure.config.CoffeeNetConfigClientProperties] but overridden by non-compatible bean instance of type [org.springframework.cloud.config.client.ConfigClientProperties]. Overriding bean of same name declared in: org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration

and that makes sense. Here my configuration:

    @Bean
    @Primary
    CoffeeNetConfigClientProperties configClientProperties() {

        CoffeeNetConfigClientProperties clientProperties = new
CoffeeNetConfigClientProperties(environment);
        clientProperties.setEnabled(false);

        return clientProperties;
    }


    @Bean
    CoffeeNetClientCredentialsResourceDetailsProperties
coffeeNetClientCredentialsResourceDetailsProperties() {

        return new CoffeeNetClientCredentialsResourceDetailsProperties();
    }


    @Bean
    ConfigServicePropertySourceLocator configServicePropertySource() {

        final ConfigClientProperties properties =
coffeeNetConfigClientProperties();
        final CoffeeNetClientCredentialsResourceDetailsProperties
resourceDetailsProperties =
            coffeeNetClientCredentialsResourceDetailsProperties();

        ConfigServicePropertySourceLocator locator = new
ConfigServicePropertySourceLocator(properties);
        locator.setRestTemplate(new
OAuth2RestTemplate(resourceDetailsProperties));

        return locator;
    }

Next try

So I tried to prefix the ConfigClientProperties with coffeeNet of course my
coffeeNetConfigClientProperties bean will be injected, because there is only
this one with that class. But then it crashes in the 'auto configure' phase because

***************************
APPLICATION FAILED TO START
***************************

Description:

Method configClientProperties in org.springframework.cloud.config.client.ConfigClientAutoConfiguration required a single bean, but 2 were found:
	- coffeeNetConfigClientProperties: defined by method 'coffeeNetConfigClientProperties' in coffee.synyx.autoconfigure.config.CoffeeNetConfigBootstrapConfiguration
	- configClientProperties: defined by method 'configClientProperties' in class path resource [org/springframework/cloud/config/client/ConfigClientAutoConfiguration.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

Here is my code:

    @Bean
    @Primary
    CoffeeNetConfigClientProperties coffeeNetConfigClientProperties() {

        CoffeeNetConfigClientProperties clientProperties = new CoffeeNetConfigClientProperties(environment);
        clientProperties.setEnabled(false);

        return clientProperties;
    }


    @Bean
    CoffeeNetClientCredentialsResourceDetailsProperties coffeeNetClientCredentialsResourceDetailsProperties() {

        return new CoffeeNetClientCredentialsResourceDetailsProperties();
    }


    @Bean
    ConfigServicePropertySourceLocator configServicePropertySource() {

        final ConfigClientProperties properties = coffeeNetConfigClientProperties();
        final CoffeeNetClientCredentialsResourceDetailsProperties resourceDetailsProperties =
            coffeeNetClientCredentialsResourceDetailsProperties();

        ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(properties);
        locator.setRestTemplate(new OAuth2RestTemplate(resourceDetailsProperties));

        return locator;
    }

Next try

Then I thought maybe I have to completely override all ConfigClientProperties
so the last attempt was:

    @Bean
    @Primary
    ConfigClientProperties configClientProperties() {

        CoffeeNetConfigClientProperties clientProperties = new CoffeeNetConfigClientProperties(environment);
        clientProperties.setEnabled(false);

        return clientProperties;
    }

to expose the bean with the class ConfigClientProperties. But then again not my
CoffeeNetConfigClientProperties will be used in my provided ConfigServicePropertySourceLocator

    @Bean
    ConfigServicePropertySourceLocator configServicePropertySource() {

        final ConfigClientProperties properties = configClientProperties();
        final CoffeeNetClientCredentialsResourceDetailsProperties resourceDetailsProperties =
            coffeeNetClientCredentialsResourceDetailsProperties();

        ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(properties);
        locator.setRestTemplate(new OAuth2RestTemplate(resourceDetailsProperties));

        return locator;
    }

and now maybe you can tell me what I am doing wrong :) Without any a @ConditionalOnMissingBean' it is very hard to inject my own ConfigClientProperties.class`.

Fyi the injection of the OAuth2RestTemplate is working well via

locator.setRestTemplate(new OAuth2RestTemplate(resourceDetailsProperties));

The code is located there:
https://github.com/coffeenet/coffeenet-starter/tree/config-starter/coffeenet-autoconfigure/src/main/java/coffee/synyx/autoconfigure/config

@derTobsch
Copy link
Author

and? :) do you have some information for me?

@spencergibb
Copy link
Member

After some thought, I dont' think we want to support users trying to subclass and replace our configuration properties.

@bluejaguar
Copy link

bluejaguar commented Sep 6, 2017

Hmm, what if someone wants their own custom behavior to be part of the PropertySourceBootstrapConfiguration. Not everyone wants to use .properties or .yml. This is a similar issue that has been discussed around people wanting to load .json files in the Spring Cloud Config as in here: spring-projects/spring-boot#4027 (comment)
As far as I can tell, that proposed theoretical solution and class in there (JsonPropertySourceLocator) does not work because it is never called even with META-INF/spring.factories defining it via org.spring.framework.boot.env.PropertySourceLoader=
I'm trying to understand if this really is a blindspot in SpringCloudConfig. It does seem like reading in data from custom sources and utilizing the mechanism of SCC is a win-win for everyone. ??

@spencergibb
Copy link
Member

PropertySourceBootstrapConfiguration is general and allows any PropertySourceLocator to be used. They are not sourced via spring.factories but as spring beans.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants