Currently, randomized configuration properties produce new randomized values each time they are referenced within config.
For example, with the following yaml configuration:
my:
random: ${random.value}
another:
property: ${my.random}
my.random and another.property will be two different randomized values when pulled in via @Value, for example.
Even using the same property twice will result in two different values:
@Value("${my.random}")
String random1;
@Value("${my.random}")
String random2;
In the above case, random1 will not equal random2.
It seems like it would be useful and more consistent for the my.random property to be randomized but reflect the same random value no matter where, how, or how many times it is referenced.
Comment From: whboyd
One possible strategy that I thought of would be to store the values generated by RandomValuePropertySource (perhaps in a map within RandomValuePropertySource itself). However, this would require a change to the expressions used to reference random values, perhaps adding some way to arbitrarily "name" them such that the named value would remain consistent, for example:
my:
random:
one: ${random.value.one}
two: ${random.value.one}
three: ${random.value.another}
In this case, my.random.one would be equal to my.random.two but not my.random.three
I'm curious as to whether anyone has any ideas on a more elegant solution. I'm willing to put together a pull request if we come up with a good idea to solve this.
Comment From: wilkinsona
I'm not keen on making the property source stateful. I'd rather bind a single random value into a @ConfigurationProperties annotated bean and then use that bean to configure anything else that needed the same value.
Comment From: wilkinsona
We've discussed this today and the Boot team are in agreement that the property source should not be stateful.
Comment From: rLitto
I think that the right way to make it work is that to do two passes of converting properties, the first one replacing the random values and passing it to the next level. It is not much different probably what happens in the ${} value replacement already
If someone can point me the logic where the property resolution is done I can have a look
Comment From: rLitto
The proposed solution does not work if you have no control on where the random value is accessed. My error scenario was something like that: Spring Boot application with Discovery Client, and
server.port: ${random.int[10000,65000]}
Eureka Client would be receiving a random port that was not the real port I was starting.
Since
server.port: 0
is already not working, I was testing this solution
Comment From: loganmzz
Passing by an intermediate doesn't solved problem like:
- property indirection: I add a custom property and make standard one referencing the custome one. Ex:
spring.activemq.brokerUrl=${super.custom.property.for.software.configuration.team}
super.custom.property.for.software.configuration.team=vm://${random.uuid}
- provider/consumer consistency: I define a service and have local clients that must consume them. Ex:
my.super.service.server.id=${random.uuid}
my.super.clients.ref=${my.super.service.server.id}
Comment From: mrozlukasz
The key thing here is the @Autowired and ConfigurationProperties. Each time the configuration object is instantiated it's reading and evaluateing the set of properties that are stored in the .properties file (Stateless). Hence, using ConfigurationProperties in two places like in Application and a Test Context, evaluates two different random values. I'm writing Spock Integration test that generates some random folder, runs a an app against that random folders, then asserts that those folders are in desired state. My problem is, that two randoms are different. Looking for a solution that won't force me to alter the Application because of the Test problem.
Comment From: wilkinsona
@mrozlukasz GitHub issues, particularly closed ones, aren't a very good place to ask questions. Stack Overflow or Gitter are both better places for this sort of thing.
Comment From: roded
What's the current recommended solution for creating a single reusable ramdom.uuid in the application.properties?
Comment From: wilkinsona
@roded Please see this comment above.
Comment From: roded
@wilkinsona Thanks for the reference. Though, I'm not sure I understand your suggestion.
Are you suggesting to bind a random value once to a @ConfigurationProperties object and then inject that object in Java code where needed?
Thing is, using properties via application.properties is useful for allowing default with the possibility of overrides. This is a very useful pattern which breaks with random values. I suspect that accomplishing the same in Java code will require some proprietary code to check and define the properties' dependence. I expect this to be taken care of by Spring.
Also, the defaults in application.properties are useful for documentation (e.g., with spring-boot-configuration-processor).
Or am I misunderstanding your suggestion?
TY
Comment From: wilkinsona
You have understood the suggestion. Without making the property source stateful (which we do not wish to do), I believe it's your best option.
Comment From: DennisBecker
And how can you generate a random groupId for an @KafkaListener annotation? When I use something like
my.conumser.groupId=${random.uuid}
And then I need my.consumer.groupIdmultiple times, and also have @KafkaListener configured like
@KafkaListener(id = ${my.consumer.groupId})
which does not work as pointed out here, how would you fix that?
I tried a configuration component with a static field defining the random groupId but at application startup the field is not available ...
Comment From: loganmzz
And how can you generate a random groupId for an @KafkaListener annotation? When I use something like
my.conumser.groupId=${random.uuid}And then I need
my.consumer.groupIdmultiple times, and also have @KafkaListener configured like
@KafkaListener(id = ${my.consumer.groupId})which does not work as pointed out here, how would you fix that?
I tried a configuration component with a static field defining the random groupId but at application startup the field is not available ...
Not tested (and I'm far from Spring now) but maybe injecting the random value in a bean of type property source (or something like that) and use properties "emitted" from this property source ?
Comment From: wilkinsona
@DennisBecker For that to work, I think you'd have to write your own PropertySource implementation with the desired caching behavior. You could register it using an EnvironmentPostProcessor and a corresponding entry in META-INF/spring.factories.