@ConstructorBinding
@ConfigurationProperties("auth")
data class AuthProperties(
    val client: Client,
    ...
)

data class Client(
    // several secretive properties
) {
    private lateinit var uuids: ConcurrentLinkedQueue<Pair<String, Instant>>
    val uuid: String
      get() {
         // return the top item from the queue
      }
      set(value) {
          // parse string and insert into queue
      }
}

Problems:

  1. Property setter doesn't get called. The traditional way would be to write a Converter, but that also has issues as listed below; Kotlin way is simply calling the setter. It could be that the @ConstructorBinding is interfering with the setter, but that's on the top level class, not on nested ones. I suggest that the seemingly useless @NestedConfigurationProperty class could be allowed on the Client, and if a corresponding @ConstructorBinding is not specified, setters are called instead. Doing so would allow each nested class to override the parents instantiation strategy.
  2. Changing the definition to inner class Client doesn't work. What if the inner class wants access to properties of the outer? To make things worse, it shows a completely misleading error message.
Failed to bind properties under 'auth.client' to mycompany.AuthProperties$Client:

    Property: auth.client.port
    Value: 80
    Origin: class path resource [application.yml]:5:11
    Reason: Failed to bind properties under 'auth.client' to mycompany.AuthProperties$Client
  1. To workaround the problem in 1, I create a first-class binding candidate val uuids: Queue<Pair<String, Instant>> in Client, then reference it in uuid.get. Except now I get an error saying Queue is not an allowed type. Why not, since I'm providing a Converter for it?
@ConfigurationPropertiesBinding
Property: auth.client.uuids
Value: 6196c660a289
Origin: class path resource [application.yml]:11:14
Reason: Unsupported Collection interface: java.util.Queue
  1. So I create a more specific type, val uuids: ConcurrentLinkedQueue<Pair<String, Instant>> and create a Converter<String, Queue<Pair<String, Instant>>; however, it fails.
Reason: No converter found capable of converting from type [java.lang.String] to type [java.util.List<kotlin.Pair<java.lang.String, java.time.Instant>>]

It appears the binder is picking the first of all superinterfaces and trying to find a matching converter. A ConcurrentLinkedQueue has several superinterfaces, all should be tried one by one. 5. I can't find a way to write a Converter that works in Kotlin, so I write one in Java as Converter<String, List<Pair<String, Instant>>>. This is problematic because I don't want a List, and I should not have to switch to Java. 6. Having an @Autowired in the Client class doesn't get set. 7. If the application.yml doesn't exist (this class was in a library module that didn't have one during testing), the binding doesn't tell me that, instead fails with a failed-to-invoke-constructor error (I don't have the stacktrace for it).

Comment From: wilkinsona

There's quite a lot going on here and the code snippet as provided doesn't compile. Filling in the blanks, changing the uuid property to var, etc, is unlikely to result in the same code as you have so we may end up diagnosing a different problem.

It looks like you're trying to mix constructor binding and JavaBean binding. This isn't supported with Java or Kotlin. If I've misinterpreted the intent of the code snippet and you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.