doptimusprime
doptimusprime

Reputation: 9395

Spring boot native configuration

I am trying to migrate existing spring boot application to spring native. In this case, I have application.properties and application-dev.properties.

package com.test;

import java.util.ArrayList;
import java.util.List;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import com.test.RestTemplateHeaderModifierInterceptor;

@SpringBootApplication(proxyBeanMethods = false)
public class TestApplication {

    @Value("${rest.template.connect.timeout}")
    private Integer CONNECT_TIMEOUT;

    @Value("${rest.template.connect.request.timeout}")
    private Integer CONNECT_REQUEST_TIMEOUT;

    @Value("${rest.template.read.timeout}")
    private Integer READ_TIMEOUT;

    @Value("${rest.template.max.conn.per.route}")
    private Integer MAX_CONN_PER_ROUTE;

    @Value("${rest.template.max.conn}")
    private Integer MAX_CONN;

    @Value("${slow.rest.template.read.timeout}")
    private Integer SLOW_READ_TIMEOUT;

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

    @Bean
    @Primary
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return getRestTemplateWithTimeout(READ_TIMEOUT);
    }

    @Bean("slowRestTemplate")
    public RestTemplate slowRestTemplate(RestTemplateBuilder builder) {
        return getRestTemplateWithTimeout(SLOW_READ_TIMEOUT);
    }

    private RestTemplate restTemplate(HttpComponentsClientHttpRequestFactory requestFactory) {
        RestTemplate restTemplate = new RestTemplate(requestFactory);

        List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
        if (CollectionUtils.isEmpty(interceptors)) {
            interceptors = new ArrayList<>();
        }
        interceptors.add(new RestTemplateHeaderModifierInterceptor());
        restTemplate.setInterceptors(interceptors);
        return restTemplate;
    }


    private RestTemplate getRestTemplateWithTimeout(Integer readTimeout) {
        HttpComponentsClientHttpRequestFactory requestFactory = getRequestFactory(readTimeout);
        return this.restTemplate(requestFactory);
    }

    private HttpComponentsClientHttpRequestFactory getRequestFactory(Integer readTimeout) {
        HttpComponentsClientHttpRequestFactory requestFactory = getConnectionSettings();
        requestFactory.setReadTimeout(readTimeout);
        return requestFactory;
    }

    private HttpComponentsClientHttpRequestFactory getConnectionSettings() {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setDefaultMaxPerRoute(MAX_CONN_PER_ROUTE);
        connectionManager.setMaxTotal(MAX_CONN);
        HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory(
                        HttpClientBuilder.create().setConnectionManager(connectionManager).build());
        requestFactory.setConnectionRequestTimeout(CONNECT_REQUEST_TIMEOUT);
        requestFactory.setConnectTimeout(CONNECT_TIMEOUT);
        return requestFactory;
    }
}

AppConfig.java

@Configuration(proxyBeanMethods = false)
@Component
@EnableAspectJAutoProxy
@EnableAutoConfiguration
@EnableRetry
public class AppConfig {
}

Config.java

@Configuration(proxyBeanMethods = false)
@PropertySource("classpath:application-${environment_region}.properties")
public class Config {

  private Map<String, String> map = new HashMap<String, String>();

  public String get(String key) {
    AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext(Config.class);
    ConfigurableEnvironment env = context.getEnvironment();
    if (!map.containsKey(key) || map.get(key) == null) {
      map.put(key, env.getProperty(key));
    }
    String propertyValue = map.get(key);
    context.close();
    return propertyValue;
  }
}

After building image using

./mvnw package -Pnative -DskipTests

when I am running the application, I am getting following error:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'rest.template.connect.timeout' in value "${rest.template.connect.timeout}"
        at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[na:na]
        at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[na:na]
        at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239) ~[na:na]
        at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210) ~[na:na]
        at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:191) ~[na:na]
        at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:936) ~[na:na]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1330) ~[na:na]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1309) ~[na:na]
        at org.springframework.aot.beans.factory.InjectedFieldResolver.resolve(InjectedFieldResolver.java:43) ~[na:na]
        at org.springframework.aot.beans.factory.InjectedElementResolver.resolve(InjectedElementResolver.java:35) ~[na:na]
        at org.springframework.aot.beans.factory.InjectedElementResolver.invoke(InjectedElementResolver.java:53) ~[na:na]
        at com.oyo.boltkeeper.ContextBootstrapInitializer.lambda$registerBoltKeeperApplication$6(ContextBootstrapInitializer.java:15) ~[na:na]
        at org.springframework.aot.beans.factory.BeanDefinitionRegistrar$ThrowableFunction.apply(BeanDefinitionRegistrar.java:294) ~[na:na]
        at org.springframework.aot.beans.factory.BeanDefinitionRegistrar.lambda$instanceSupplier$0(BeanDefinitionRegistrar.java:115) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1249) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[na:na]
        ... 13 common frames omitted

What is the way to pass the configuration in spring boot native application? Or there is different way? Is something wrong being done here?

Upvotes: 4

Views: 2196

Answers (1)

BigMouse
BigMouse

Reputation: 83

I don't have enough reputation to post a simple comment: my answer is not complete...

First I think you should drop the notion of profile, it is recommended in the official documentation. Also, the concept of profile is contrary to the Ahead Of Time compilation. If you still want to, the documentation suggests to explicitly specify your active profiles in a configuration file. I guess with spring.profiles.active=default,dev for example.

On the other hand, I think you should specify on the command line the path of the configuration file with this option: --spring.config.location=classpath:/default.properties,classpath:/override.properties. See this other documentation.

In an "enterprise" context, we don't include the configuration file in the .jar executable: the configuration is externalized. So I think that with a native image it's always the case.

Don't hesitate to share your solution if you find one

Upvotes: 1

Related Questions