Spring auto configuration tries to set some bean values from application.properties while I just want to use that informations somewhere else? - spring-boot

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

Related

Spring #Value not working in Spring Boot 2.5.5, getting null values

I am trying to inject some property values into variables by means of Spring #Value annotation but I get null values. I tried different configurations and triks but it doesn't work. Think is that before today everythink was working properly. I do not know what I changed in order to get things broken.
Here is my java class:
#Component
#ConditionalOnProperty(prefix = "studioghibli", name = "get")
public class StudioGhibliRestService {
#Value("${studioghibli.basepath}")
private static String BASE_PATH;
#Value("${studioghibli.path}")
private static String PATH;
#Value("${studioghibli.protocol:http}")
private static String PROTOCOL;
#Value("${studioghibli.host}")
private static String HOST;
private static String BASE_URI = PROTOCOL.concat("://").concat(HOST).concat(BASE_PATH).concat(PATH);
#Autowired
StudioGhibliRestConnector connector;
public List<StudioGhibliFilmDTO> findAllFilms() throws SipadContenziosoInternalException {
var response = connector.doGet(BASE_URI, null, null);
if (!response.getStatusCode().is2xxSuccessful() || !response.hasBody()) {
throw new SipadContenziosoInternalException(Errore.INTERNAL_REST_ERROR, "FindAll(), microservizio ".concat(BASE_URI), null);
}
return (List<StudioGhibliFilmDTO>) response.getBody();
}
}
As you can see, the class is annotated with #Component, that because I will need to use it as #Service layer in order to make a rest call in my business logic.
The class is also annotaded with conditional on property...
Here is a screenshot of the debug window at startup:
Since the PROTOCOL value is null, i get a null pointer exception immediately at start up.
Here is part of the application-dev.properties file:
studioghibli.get
studioghibli.protocol=https
studioghibli.host=ghibliapi.herokuapp.com
studioghibli.basepath=/
studioghibli.path=/films
First of all, #Value annotation does not work with static fields.
Secondly, fields with #Value annotation is processed when the instance of the class (a bean) is created by Spring, but static fields exist for a class (for any instance), so when the compiler is trying to define your static BASE_URI field other fields are not defined yet, so you get the NPE on startup.
So you might need a refactoring, try to inject values with the constructor like this:
#Component
#ConditionalOnProperty(prefix = "studioghibli", name = "get")
public class StudioGhibliRestService {
private final StudioGhibliRestConnector connector;
private final String baseUri;
public StudioGhibliRestService(StudioGhibliRestConnector connector,
#Value("${studioghibli.basepath}") String basePath,
#Value("${studioghibli.path}") String path,
#Value("${studioghibli.protocol:http}") String protocol,
#Value("${studioghibli.host}") String host) {
this.connector = connector;
this.baseUri = protocol.concat("://").concat(host).concat(basePath).concat(path);
}
// other code
}
Thanks, It works for me, I have to add some codes to my project. Then I check the spring core document in "#Value" section. Besides
When configuring a PropertySourcesPlaceholderConfigurer using
JavaConfig, the #Bean method must be static.
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}

Changes in Application properties doesn't impact - Springboot

I am using Spring boot 2.5.4. I have written web application. Now i am facing issue with application.properties file variables. If i am changing existing value, In code old value is been read, newly defined object is not coming.
Find the below application.properties file
spring.datasource.url=jdbc:mysql://XXXXXXXXXXX:3306/test
spring.datasource.username=user
spring.datasource.password=XXXXXXXXXXXXXXXX
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
# Config Variables
ml.url=https://google.com/ml/entities
main.url=https://xxxx.com/staging/mainfile
and application config java file
#Component
public class ApplicationConfig {
#Value("${ml.url}")
public String mlurl;
#Value("${main.url}")
public String mainurl;
#PostConstruct
public void initThat(){
that = this;
}
}
reading variable in code as
#RequestMapping("/readfile")
#RestController
public class AppointmentResource {
private static Logger S_LOGGER = LogManager.getLogger( AppointmentResource.class );
#Autowired
private ApplicationConfig applicationConfig;
#GetMapping(value = "/websiteUrl",produces = MediaType.APPLICATION_JSON_VALUE)
public String getProduct() {
String websiteUrl = applicationConfig.mlurl;
S_LOGGER.info("website url is " + websiteUrl);
return websiteUrl;
}
}
After compiling for few times. Then i changes ml.url to https://google.com/prod/entities/test
but in code still i am getting as https://google.com/ml/entities.
Can anyone help in getting latest changes from application.properties
I am struck here. Help
I fixed it. It was picking from the folder config. which i created for different environments

This annotation is not applicable to target 'local variable

I want to get value from application.yml, but I got "This annotation is not applicable to target 'local variable" for this part,how to solve this problem?
#Value("\${aws.secretsManager.secretName}")
val secretName: String? = ""
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties
fun getSecret() {
#Value("\${aws.secretsManager.secretName}")
val secretName: String? = ""
val region = "us-west-2"
val logger: Logger = LoggerFactory.getLogger(GetSecretConfig::class.java)
// Create a Secrets Manager client
val client = AWSSecretsManagerClientBuilder.standard().withRegion(region).build()
val getSecretValueRequest = GetSecretValueRequest().withSecretId(secretName)
var getSecretValueResult: GetSecretValueResult? = try {
client.getSecretValue(getSecretValueRequest)
}
}
application.yml
aws:
secretsManager:
secretName: "test-mvp"
region: "us-west-2"
user: "root"
password: "root"
From the #Value javadoc:
Annotation used at the field or method/constructor parameter level that indicates a default value expression for the annotated element.
The #Value annotation is defined as follow:
#Target(value = {FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
#Retention(value = RUNTIME)
#Documented
public #interface Value
As you see by the #Target, the #Value annotation it's not intended to be used in a LOCAL_VARIABLE.
The solution is to define the secretName variable outside of the function - as a field of the class.
Workaround logic here:
(Using Java 8 though - shouldn't matter anyways)
Create a configuration class annotated with #Configuration like so:
#Configuration
public class ApplicationSecretsConfig {
public ApplicationSecretsConfig(){}
#Value("${aws.secretsManager.secretName}")
private String secretName;
public String getSecretName(){
return secretName;
}
}
Then in your class, autowire the SecretsConfig dependency and get the value of secretName using its getter.
// class initialization done here
...
#Autowired
ApplicationSecretsConfig applicationSecretsConfig
public String getSecret() {
String secret = applicationSecretsConfig.getSecretName();
// continue your logic
...
}
Hopefully that helps someone.
there is no need to do a custom implementation for fetch secrets. Spring provides it, using spring-cloud-starter-aws-secrets-manager-config dependency, just need to do an small config:
spring.config.import=aws-secretsmanager:my-secret
there is working sample on documentation:
https://github.com/awspring/spring-cloud-aws/tree/main/spring-cloud-aws-samples/spring-cloud-aws-parameter-store-sample
and here you could find a db working too:
https://github.com/nekperu15739/aws-secrets-manager

#Value In Spring MVC is not getting populated

I am trying to populate an attribute using the #Value annotation in Spring MVC, and it is not getting populated.
I am trying to access the attribute using Struts2 JSP property. my use case looks like that:
public class TransferCreditsAction extends StudentAwareAction {
protected Log logger = LogFactory.getLog(this.getClass());
#Value( "${transfer.credit.url}" )
private String transferCreditUrl;
public void setStates( List<TranslatedValue> states ) {
this.states = states;
}
#Value( "${transfer.credit.url}" )
public String getTransferCreditUrl() {
return transferCreditUrl;
}
}
My property file looks like:
transfer.credit.url
I am accessing this attribute using JSP which looks like:
<s:property value='transferCreditUrl'/>"
I know for a fact that my JSP can access this field, because I tested it when I have this field set for a default value.
However, this field is not getting populated from my property file. I am using Spring 4.1.6
Any help is really appreciated.
Spring can only inject values in its own managed spring beans. That means your TransferCreditsAction should be a spring bean.
There are various ways to declare your TransferCreditsAction class as a spring bean, already answered elsewhere.
You haven't added whats on top of TransferCreditsAction class.
Values will be injected in a Bean Env.
There are many ways of Doing it
Assuming my property file contains
username=Ashish
app.name=Hello
1.
#Service
#PropertySource(value = { "classpath:sample.properties" })
public class PaloAltoSbiClientImpl implements PaloAltoSbiClient {
public static String username;
#Value("${username}")
public void setUrl(String data) {
username = data;
}
...
2.
#Service
public class PaloAltoSbiClientImpl implements PaloAltoSbiClient {
#Value("${username}")
public static String username;
...
3.
#Component
public class TokenHelper {
#Value("${app.name}")
private String APP_NAME;
Just give the properties file reference on top of the class in which you are trying to get.
#PropertySource(value = { "classpath:sample.properties" })
This issue was happening because I was missing <context:annotation-config/> in my applicationContext. Once I added it, it start working with no issues.

how to load property file in to spring boot project with annotations?

I have written queries in property file. I want to read the property file in to one class with annotations in spring boot. How can i read it? And is there any better approach for writing queries in spring boot project?
If you add your properties in application.properties file, you can read them inside the spring boot classes like:
#Service
public class TwitterService {
private final String consumerKey;
private final String consumerKeySecret;
#Autowired
public TwitterService(#Value("${spring.social.twitter.appId}") String consumerKey, #Value("${spring.social.twitter.appSecret}") String consumerKeySecret) {
this.consumerKey = consumerKey;
this.consumerKeySecret = consumerKeySecret;
} ...
You can annotate fields in your components by #Value("${property.name}")
Else, you can use Properties Object in java.util package.
For example, i have a mode property, which values are dev or prod, i can use it in my beans as follow :
#Value("${mode:dev}")
private String mode;
The other approach is by using :
Properties pro = new Properties();
pro.load(this.getClass().getClassLoader().getResourceAsStream());
You can use #PropertySource to read the properties from a file and then pass them to a bean. If you have a file called "queries.properties" that has a property like:
query1: select 1 from foo
Then your config might look like:
#PropertySource("classpath:queries.properties")
#Configuration
public class MyConfig {
#Bean
public DbBean dbBean(#Value("${queries.query1}") String query) {
return new DbBean(query);
}
}

Resources