I have following block in application.yaml file:
foo:
bar: bazz
I want to map that configuration to a configuration class using #ConfigurationProperties.
#Validated
#Getter #Setter
#ConfigurationProperties(prefix = "foo")
public class FooProperties {
#NotNull
private String bar;
}
And here is configuration class
#Configuration
#EnableConfigurationProperties(FooProperties.class)
public class FooConfiguration {
#Bean
public Foo getFoo(FooProperties properties) {
///
}
}
However when I try to start application I get following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'foo' to a.b.c.FooProperties failed:
Property: foo.bar
Value: null
Reason: must not be null
Action:
Update your application's configuration
Did I miss anything else? I cant figure out why such a trivial thing fails.
Try putting the value in single quotes like:
foo:
bar: 'bazz'
Since the value being read is mapped to a string variable.
Related
In Spring Boot project, the configuration in the YML file can be automatically converted to an #ConfigurationProperties annotated bean. But I need to override behavior to make non standard conversion because I inject value from environmental variable (which is a sting) but it should be AS map.
There is error
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'app.push.firebase.application-keys' to java.util.Map<com.example.services.push.service.api.model.kafka.Application, java.lang.String>:
Property: app.push.firebase.application-keys
Value: "{"applicationOne": "api=key-one","applicationTwo": "api=key-two"}"
Origin: class path resource [application-local.yml] - 47:25
Reason: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.util.Map<com.example.services.push.service.api.model.kafka.Application, java.lang.String>]
My application.yml
app:
use-dev-apns: true
kafka.consumer.group: 'local'
push:
errorCallbackUrl: 'callback-url'
firebase:
applicationKeys: '{"applicationOne": "api=key-one","applicationTwo": "api=key-two"}'
defaultKey: 'api-key'
My property class
#ConstructorBinding
#ConfigurationProperties("app.push")
data class PushProperties(
val errorCallbackUrl: String,
val firebase: FirebaseProperties
)
data class FirebaseProperties(
val applicationKeys: Map<Application,String>,
val defaultKey: String
)
And custom converter
#ConfigurationPropertiesBinding
#Component
class StringToMapConverter: Converter<String, Map<Application, String>> {
override fun convert(source: String): Map<Application, String> {
try {
val map = BasicJsonParser().parseMap(source) as Map<String, String>
return map.mapKeys { Application.valueOf(it.key.uppercase()) }
} catch (e: JsonParseException) {
throw Exception("app.callback-mappings property is invalid. Must be a JSON object string")
}
}
}
What could be the problem?
Custom converter bind data from string to Map<Application, String>
I have solved the problem.
The problem was that Kotlin Map class does not match the java.util.Map class.
When I change Kotlin Map class to MutableMap class everything works correctly
So I use keycloak for my application and I have some values in application.properties like:
keycloak.auth-server-url = http://10.10.10.10:1010/auth
keycloak.resource = test-client
keycloak.credentials.secret = <very-big-secret>
keycloak.realm = test-realm
Spring configure the keycloak connection using these data, but I also use them in my code so I have a config like this:
#Data
#Configuration
#ConfigurationProperties(prefix = "keycloak")
public class KeycloakConfig {
private String authServerUrl;
private String realm;
private String resource;
private Credentials credentials;
}
I have an admin user in keycloak and I want it's credentials in the application.properties like this:
keycloak.admin.username=admin.admin
keycloak.admin.password=changeit
So I tried to change my config class to this:
#Data
#Configuration
#ConfigurationProperties(prefix = "keycloak")
public class KeycloakConfig {
private String authServerUrl;
private String realm;
private String resource;
private Credentials credentials;
private Admin admin;
}
#Data
public class Admin {
private String username;
private String password;
}
But when I try to run the application like this, I think the spring tries to set the values for keycloak (the .admin part) and it does not start:
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target [Bindable#1cd5e41 type = org.keycloak.adapters.springboot.KeycloakSpringBootProperties, value = 'provided', annotations = array<Annotation>[#org.springframework.boot.context.properties.ConfigurationProperties(ignoreInvalidFields=false, ignoreUnknownFields=false, prefix=keycloak, value=keycloak)]] failed:
Property: keycloak.admin.password
Value: changeit
Origin: "keycloak.admin.password" from property source "applicationConfig: [classpath:/application.properties]"
Reason: The elements [keycloak.admin.password,keycloak.admin.username] were left unbound.
Property: keycloak.admin.username
Value: admin.admin
Origin: "keycloak.admin.username" from property source "applicationConfig: [classpath:/application.properties]"
Reason: The elements [keycloak.admin.password,keycloak.admin.username] were left unbound.
Action:
Update your application's configuration
Is it possible to have the .admin part under keycloak or I have to make a new class for example:
#Data
#Configuration
#ConfigurationProperties(prefix = "my-keycloak")
public class MyKeycloakConfig {
private Admin admin;
}
And:
my-keycloak.admin.username=admin.admin
my-keycloak.admin.password=changeit
I am not familiar with KeyCloak, but you can inject the bean that initialized by KeyCloak that reads the properties.
Keycloak reads values from application properties using KeycloakSpringBootProperties. Looks like there are no such values as username or password. Probably Keycloak doesn't require those values to work properly.
So you need to specify the properties seperately from keycloak.
No, you cannot customize keycloak.* "domain" in spring-boot (loaded) properties!
Proof: KeycloakSpringBootProperties, which says:
#ConfigurationProperties(prefix = "keycloak", ignoreUnknownFields = false)
So it is definitely the second approach!
By defining (in application.properties):
my-keycloak.admin.username=admin.admin
my-keycloak.admin.password=changeit
a) ... You can just go for:
#Value("${my-keycloak.admin.xxx}")
private String myKeacloakXXX;
b) Or as described by Typesafe Configuration Properties (and implemented by [1] for prefix="keycloak"):
You (just) have to introduce a "pojo" like (depicting your properties structure(type safe)):
#ConfigurationProperties("my-keycloak.admin")
public class MyKeykloakProperties {
private String username, password; // getter, setter/lombok
}
You can have also more structure with "my-keykloak" (prefix, and nesting classes/properties, see exmaple/doc)
To enable them:
#Configuration
// Or:
#EnableConfigurationProperties(MyKeykloakProperties.class)
// OR:
//#ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyKeycloakConfig { ...
see also Enabling.
Then you can "wire" them as you see fit (also in the above config class):
#Autowired
private MyKeykloakProperties properties;
As a decision help, please refer to: #Value vs type safe.
Cheers
I need to use variables declared in my applications.yaml file, as an example all it is:
num_error:
value: "error"
result: 1
And I have a class trying to call it like the following:
#ConfigurationProperties(prefix = "num_error")
#Component
class NumError {
companion object {
lateinit var value: String
lateinit var result: Number
}
}
However, when I try and call this class using NumError.value I get an the following error
lateinit property value has not been initialized
kotlin.UninitializedPropertyAccessException: lateinit property value has not been initialized
What have I done wrong, why is this error happening?
You do not need to have companion object, and since Spring boot 2.2 you can have ConstructorBinding to make it work.
#ConstructorBinding
#ConfigurationProperties(prefix = "num_error")
data class NumError(
val value: String, val result: Number
)
Make sure you include following dependency
dependencies {
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
}
EDIT
For older versions, define the variables directly in the class instead of companion object.
#Configuration
#ConfigurationProperties(prefix = "num_error")
class NumError {
var value: String = "some default value",
var result: Number? = null
}
Problem
I think that I havn't understood something properly because my #Value is always loading the default calue.
Java Code
So I have the following:
#Value("${disableQuerySecurityDebug:false}")
private boolean disableQuerySecurityDebug;
And this is set to false always.
Property file: application-disableQuerySecurityDebug.properties
I have a properties file called application-disableQuerySecurityDebug.properties.
And I have the following entry inside the file:
disableQuerySecurityDebugMne=true
And I run the application with the following profile: disableQuerySecurityDebugMne
I was expecting the value to be set to true, but it is always false.
Update
Based on deadpool's answer, I ended up with the following:
#Profile("disableQuerySecurityDebug") #Data
#Configuration
public class DisableSecurityConfig implements DisableQuerySecurityDebug {
#Value("${disableQuerySecurityDebug:true}")
private boolean securityDisabled;
}
#Profile("!disableQuerySecurityDebug") #Data
#Configuration
public class EnableSecurityConfig implements DisableQuerySecurityDebug{
#Value("${disableQuerySecurityDebug:false}")
private boolean securityDisabled;
}
public interface DisableQuerySecurityDebug{
public boolean isSecurityDisabled();
}
#Value annotation is only used to inject properties values into spring Beans from yml or properties file
This annotation can be used for injecting values into fields in Spring-managed beans and it can be applied at the field or constructor/method parameter level.
If you want to inject values based on profile specific then use #Profile on class
#Profile("disableQuerySecurityDebug")
#Configuration
public class Config {
#Value("${disableQuerySecurityDebug:false}")
private boolean disableQuerySecurityDebug;
}
You could also specify it on the command line by using the following switch:
java -jar demo.jar --spring.profiles.active=disableQuerySecurityDebug
I want to load a #Configuration class based on an enum in properties file, so I have the following class:
#Configuration
#ConditionalOnExpression("#{ ${demo.spel.demo-enum} eq T(demo.spel.DemoEnum).VALUE }")
public class DemoConfig {}
And I have: demo.spel.demo-enum=value in application.properties
This does not work, and throws the exception:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'value' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?
The odd thing is, that if I add single quotes to the property part, and a toString() to the enum part of the expression, there is no exception, the condition is true, and the bean is created (verified by checking console output in debug logging):
#ConditionalOnExpression("#{ '${demo.spel.demo-enum}' eq T(demo.spel.DemoEnum).VALUE.toString() }")
Questions:
Why is comparing an enum like this fails ? How come Spring can successfully convert the values and compare as string but not as their types ?
This is on Spring Boot 2.0.4
It should be pretty obvious, really.
Consider the following Java code:
foo.equals(DemoEnum.VALUE)
It would require an object foo, perhaps a field on this:
this.foo.equals(DemoEnum.VALUE)
If your property placeholder evaluates to 'foo', your first SpEL expression is the equivalent of
#this.foo eq T(DemoEnum).VALUE
So SpEL looks for a property foo on #this
EDIT
If you create a Class:
public class Foo {
#Value("${some.property}")
private DemoEnum enum;
public getEnum() {
return this.enum;
}
}
And add a bean to the context called "foo" you could then use
foo.enum eq ...
Since #this is a BeanExpressionContext allowing you to reference other beans.
I had a similar problem.
I had a feature, which was enabled by default. To disable it, application config file, should have it disabled explicitly. demo.feature.disable:true
I had a spring bean conditional on this property (enabled by default).
#ConditionalOnExpression("#{ ${demo.feature.disable} != true }")
#Component
public class FeatureModule {
}
The problem was, when demo.spel.value was not defined in the config file - application.yml, initialization of this component will fail with
Caused by: org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'lcurly({)'
To solve it, I provided default value.
#ConditionalOnExpression("#{ ${demo.feature.disable:false} != true }")
#Component
public class FeatureModule {
}
Now, when I test it.
By default this component is initialized.
If config file does not have demo.feature.disable, this component will be initialized.
If config file has demo.feature.disable:true, this component will not be initialized.
If config file has demo.feature.disable:false this component will be initialized.