With the following configuration:

spring:
  kafka:
    producer:
      client-id: some-id
      ssl:
        key-password: secret
        key-store-location: classpath:keystore.jks # line 18
        key-store-password: secret
        trust-store-location: classpath:truststore.jks
        trust-store-password: secret

I get the following message:

[WARN] org.springframework.boot.context.properties.migrator.PropertiesMigrationListener - 
The use of configuration keys that have been renamed was found in the environment:

Property source 'migrate-Config resource 'class path resource [application-integration.yml]' via location 'optional:classpath:/'':
    Key: spring.kafka.producer.ssl.keystore-location
        Line: 18
        Replacement: spring.kafka.producer.ssl.key-store-location
    Key: spring.kafka.producer.ssl.keystore-password
        Line: 19
        Replacement: spring.kafka.producer.ssl.key-store-password
    Key: spring.kafka.producer.ssl.truststore-location
        Line: 20
        Replacement: spring.kafka.producer.ssl.trust-store-location
    Key: spring.kafka.producer.ssl.truststore-password
        Line: 21
        Replacement: spring.kafka.producer.ssl.trust-store-password

Property source 'Config resource 'class path resource [application-integration.yml]' via location 'optional:classpath:/'':
    Key: spring.kafka.producer.ssl.keystore-location
        Line: 18
        Replacement: spring.kafka.producer.ssl.key-store-location
    Key: spring.kafka.producer.ssl.keystore-password
        Line: 19
        Replacement: spring.kafka.producer.ssl.key-store-password
    Key: spring.kafka.producer.ssl.truststore-location
        Line: 20
        Replacement: spring.kafka.producer.ssl.trust-store-location
    Key: spring.kafka.producer.ssl.truststore-password
        Line: 21
        Replacement: spring.kafka.producer.ssl.trust-store-password


Each configuration key has been temporarily mapped to its replacement for your convenience. To silence this warning, please update your configuration to use the new keys.

But I already changed them to the new one. It's just a warning, but it seems to be incorrect. Also interesting it shows it twice with different property source names.

Comment From: mhalbritter

This indeed looks like a bug. The org.springframework.boot.context.properties.migrator.PropertiesMigrationReporter#getMatchingProperties returns it as deprecated properties, because this line:

ConfigurationProperty match = propertySource.getConfigurationProperty(metadataName);

returns a non-null match when invoked with e.g. "spring.kafka.producer.ssl.keystore-location". I assume this is because of the relaxed mapping support in SpringIterableConfigurationPropertySource and "keystore" and "key-store" map to the same relaxed value.

However, I can't reproduce that you get those messages two times. Could you maybe provide a small sample which reproduces this double printing issue?

Comment From: martinvisser

I see you added 2.7.x milestone, but forgot to mention this (also) happened using Spring Boot 3.1.0

Comment From: mhalbritter

I suspect the underlying cause is in Boot 2.7.x too. When we fix that bug there, we forward merge to 3.0.x and 3.1.x.

Comment From: p-palanisami

I am a new contributor. Shall I take this bug?

Comment From: mhalbritter

Hey, thanks for the offer. If you want to help, keep an eye out on first timer issues and ideal for contribution issues.

I think this issue could get quite complicated and is not suitable for getting started.

Comment From: Wzy19930507

Hi, may i pick it up


i'll chang code at https://github.com/spring-projects/spring-boot/blob/0ef87f51020939fef8c06d922dce87eab0f15190/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java#L96-L112

after change

class SpringIterableConfigurationPropertySource extends SpringConfigurationPropertySource
        implements IterableConfigurationPropertySource, CachingConfigurationPropertySource {

    @Override
    public ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name) {
        for (String candidate : getMappings().getMapped(name)) {
            if(!isFieldDashNameMatch(candidate, name)) {
                return null;
            }
            Object value = getPropertySource().getProperty(candidate);
            if (value != null) {
                Origin origin = PropertySourceOrigin.get(getPropertySource(), candidate);
                return ConfigurationProperty.of(this, name, value, origin);
            }
        }
    }

    private static class Mappings {
        public static boolean isFieldDashNameMatch(String propertyName, ConfigurationPropertyName fieldName) {
            ConfigurationPropertyName name = this.reverseMappings.get(propertyName);
            if (name == null || fieldName == null) {
                return false;
            }
            if (name.getNumberOfElements() != fieldName.getNumberOfElements()) {
                return false;
            }
            for (int i = 0; i < name.getNumberOfElements(); i++) {
                String element = name.getElement(i, Form.Dash);
                String fieldElement = fieldName.getElement(i, Form.Dash);
                if (!ObjectUtils.nullSafeEquals(element, fieldElement)) {
                    return false;
                }
            }
            return true;
        }
    }

}

Comment From: philwebb

@Wzy19930507 You're welcome to submit a PR but I'm afraid with our 3.2 release so imminent we won't be able to provide a lot of guidance at this time. In the future, it's better to start a draft PR rather than describing the code changes you intend to make as a comment in the issue. It's very hard for us to discuss changes when they are issue comments and it adds a lot of noise for folks watching the issue.

Comment From: arkinmodi

Hi. I am running Spring Boot 3.3.3 and encountering this issue (with same configurations as in the original report). Could this issue be reopen?

Here is a Dockerfile to reproduce this warning log.

FROM eclipse-temurin:21

WORKDIR /tmp/demo

RUN apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        curl \
        unzip

RUN curl \
        -G https://start.spring.io/starter.zip \
        -d type=gradle-project \
        -d javaVersion=21 \
        -d bootVersion=3.3.3 \
        -o /tmp/demo.zip

RUN unzip /tmp/demo.zip -d /tmp/demo

RUN ./gradlew dependencies

RUN sed -i '/dependencies {/a runtimeOnly("org.springframework.boot:spring-boot-properties-migrator")' build.gradle

RUN echo 'spring.kafka.ssl.trust-store-password=secret' >> ./src/main/resources/application.properties

CMD ["./gradlew", "bootRun"]

Output:

Starting a Gradle Daemon, 1 incompatible and 1 stopped Daemons could not be reused, use --status for details

> Task :bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.3)

2024-08-30T20:34:39.745Z  INFO 142 --- [demo] [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 21.0.4 with PID 142 (/tmp/demo/build/classes/java/main started by root in /tmp/demo)
2024-08-30T20:34:39.746Z  INFO 142 --- [demo] [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2024-08-30T20:34:39.982Z  INFO 142 --- [demo] [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.368 seconds (process running for 0.484)
2024-08-30T20:34:39.986Z  WARN 142 --- [demo] [           main] o.s.b.c.p.m.PropertiesMigrationListener  :
The use of configuration keys that have been renamed was found in the environment:

Property source 'Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'':
        Key: spring.kafka.ssl.truststore-password
                Line: 2
                Replacement: spring.kafka.ssl.trust-store-password


Each configuration key has been temporarily mapped to its replacement for your convenience. To silence this warning, please update your configuration to use the new keys.


BUILD SUCCESSFUL in 9s
4 actionable tasks: 4 executed

Comment From: philwebb

@arkinmodi Thanks for the reproducer. I've opened #42068 to investigate.