Spring value populated with maven command line argument - spring

I have the following configuration.
#Configuration
public class JasyptConfiguration {
#Getter
#Setter
#Value("${jasypt.encryptor.password}")
private String jasyptEncryptorPassword;
#Bean(name = "encryptorBean")
public StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(jasyptEncryptorPassword);
config.setAlgorithm("PBEWithMD5AndDES");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor;
}
}
And I would like to run maven project with following command line arguments.
-Dspring.profiles.active=dev
-Djasypt.encryptor.password=xxx
However spring cannot inject the second argument what is the proper structure ?

You can make some changes to your java code to achieve this.
Instead of using #Value use #ConfigurationProperties like below
#ConfigurationProperties("jasypt.encryptor")
#Configuration
public class JasyptConfiguration {
#Getter
#Setter
private String password;
...
}
Now in your application.properties or application.yml make below changes
jasypt.encryptor.password=${ENV_PASSWORD}
and when starting your app use
-Dspring.profiles.active=dev -DENV_PASSWORD=xxx

Related

Why setters are mandatory for fields in a class which reads properties from application.yml file in springboot?

Following is my code:
Why setter is mandatory. Without it, the class does not
read the property from the
application.yml file
correctly.
Thank you.
#Getter
#Setter
#NoArgsConstructor
#Configuration
#ConfigurationProperties(prefix = "test")
#EnableConfigurationProperties
public class KafkaTopicConfig {
private String bootstrapAddress;
#Value(value = "${test.bootstrapAddress}")
private String bootstrapAddressFromVariable;
should only use #Value in encapsulated components/services (we can call them configuration services).
This way, we will have all our configurations in one place, and that component will only have the responsibility of loading and providing them to other components.
https://stackabuse.com/the-value-annotation-in-spring
From baeldung.com... The Spring framework uses standard Java bean setters, so we must declare setters for each of the properties.
So it looks like you're using Lombok so I would make my class look more like this:
#ConfigurationProperties(prefix = "test")
#Data
public class KafkaTopicConfig {
private String bootstrapAddress;
}
Then in the main spring boot application class or a #Configuration class I would do:
#Configuration
#EnableConfigurationProperties({KafkaTopicConfig.class})
public MyApplicationConfig{
}
Then to use my configuration properties I would autowire it into the #Component where I wished to use it.
e.g.
#Component
public MyComponent{
private final KafkaTopicConfig config;
public MyComponent(KafkaTopicConfig config) {
this.config = config;
}
public void doStuff() {
if ("some address".equals(config.getBootstrapAddress())) {
blah();
}
}
}
Using the #Value inside the configuration properties feels confusing to me, and defeats the point of using configuration properties in the first place.

SpringBoot Failed to bind properties under app

I have a SpringBoot 2.1.7.RELEASE project with gradle. I'm getting an error when I try to use #ConfigurationProperties
The property that I'm trying to bind is existing in my application-default.properties and if I run the project using Itellij I can see that the property is ingested in my component.
If I enable #EnableConfigurationProperties I got an error.
My application-default.properties
app.forwarding-endpoint=localhost:8080
My AppProperties.java
#ConfigurationProperties(prefix = "app", ignoreUnknownFields = false)
#Validated
#Data
public class AppProperties {
#NotBlank
#Pattern(regexp = "^(.+):\\d+$")
private String forwardingEndpoint;
}
My Application.java
#SpringBootApplication
#EnableConfigurationProperties(AppProperties.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application .class, args);
}
}
My component that is using the property:
public MyComponent(#Value("${app.forwarding-endpoint}") String forwardingEndpoint) {
log.info("Forwarding endpoint {}", forwardingEndpoint);
}
The error that I get is:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'app' to com.config.AppProperties failed:
Property: app.forwardingEndpoint
Value: null
Reason: must not be blank
What am I missing?
The cause is in the order of initialization.
You did not fill AppProperties but start to use it in components. You need to annotate this class also as a component but it's not a good approach from point of view of an architecture.
The concept of #ConfigurationProperties is quite raw for Spring and without some manipulations, you will quite difficult to force it to work correctly. I propose a simple 'trick' (or 'another approach'):
#Data
public class AppProperties {
#NotBlank
#Pattern(regexp = "^(.+):\\d+$")
private String forwardingEndpoint;
}
(I think the place of #validated is not in the entitity/DO).
And place in your #Configuration next code:
#Bean
#ConfigurationProperties(prefix = "app", ignoreUnknownFields = false)
public AppProperties setAppProperties() {
return new AppProperties();
}
And next, you can inject AppProperties bean in any component.

Spring bind different properties files to different beans

It's my MySQL properties file
mysql.properties
dialect=org.hibernate.dialect.MySQL57Dialect
hbm2ddl_method=validate
show_sql=true
format_sql=false
pool_name=testpool
jdbc_url=jdbc:mysql://localhost:3306/testdb
minimum_idle=2
uname=root
password=testpsw
cache_prep_stmts=true
prep_stmt_cache_size=256
prep_stmt_cache_sql_limit=2048
use_server_prep_stmts=true
maximum_pool_size=30
driver_class_name=com.mysql.jdbc.Driver
and Oracle properties file for datasource configuration.
dialect=org.hibernate.dialect.Oracle10gDialect
hbm2ddl_method=validate
show_sql=true
format_sql=false
pool_name=testpool
jdbc_url=jdbc:oracle:thin:#localhost:1521:testdb
minimum_idle=2
uname=barn_act
password=testpsw
cache_prep_stmts=true
prep_stmt_cache_size=256
prep_stmt_cache_sql_limit=2048
use_server_prep_stmts=true
maximum_pool_size=30
driver_class_name=oracle.jdbc.OracleDriver
I created two classes like this to bind properties into fields.
#Component("mysql_props")
#PropertySource(value = "classpath:/mysql.properties")
#ConfigurationProperties
#Getter
#Setter
public class HibernateMySQLProperties {
private String dialect;
//other props
}
#Component("oracle_props")
#PropertySource(value = "classpath:/oracle.properties")
#ConfigurationProperties
#Getter
#Setter
public class HibernateOracleProperties {
//same fileds as mysql
}
When I inject these two beans to PersistenceConfiguration class same propety fields are injected.
#Configuration
#EnableConfigurationProperties({ HibernateOracleProperties.class, HibernateMySQLProperties.class })
public class PersistenceConfig {
#Autowired
#Qualifier("oracle_props")
private HibernateOracleProperties oracleProps;
#Autowired
#Qualifier("mysql_props")
private HibernateMySQLProperties mysqlProps;
}
How to solve this problem ?
This is a known problem/behavior in spring prior to spring-boot. In spring property placeholder can understand unique key. Your both properties file has same name of keys.
So the solution would be like below with fewer changes.
Change the property file like below.
mysql.properties
mysql.dialect=org.hibernate.dialect.MySQL57Dialect
****** all otheres same start with mysql.
Oracle properties file
oracle.dialect=org.hibernate.dialect.Oracle10gDialect
****** all otheres same start with oracle.
Now change your Hibernate*Properties.java #ConfigurationProperties annotation.
#Component("oracle_props")
#PropertySource(value = "classpath:/oracle.properties")
#ConfigurationProperties(prefix = "oracle")
#Component("mysql_props")
#PropertySource(value = "classpath:/mysql.properties")
#ConfigurationProperties(prefix = "mysql")
No need of any change in PersistenceConfig.java file.

Set Spring Session Redis on production only

Is it possible to set the Spring session data redis module on production profile only?
I have tried using #Profile but the AbstractHttpSessionApplicationInitializer was initialized before setting the profile/environment and thus will give me an NoSuchBean Exception
NoSuchBeanDefinitionException: No bean named 'springSessionRepositoryFilter' available
And when I try to inject the profile using #Values it returns null value.
#Value("${spring.profiles.active:local}")
private String activeProfile; // result is null
I have solved the problem using this article.
The production session config will be like this.
#Configuration
#ConditionalOnProperty(name = "spring.profiles.active", havingValue = "production")
#EnableRedisHttpSession
#ConfigurationProperties(prefix = "redis.db")
public class ProductionSessionConfig {
#Setter
private String server;
#Setter
private Integer port;
#Bean
public JedisConnectionFactory connectionFactory() {
//creates your own connection
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(server, port);
return new JedisConnectionFactory(config);
}
}
And if you are not going to use the spring-session-data-redis in other environment. You can just proceed. Just keep in mind the #ConditionalOnProperty value.
And do not forget to exclude the Auto Configuration of Session.
#SpringBootApplication(exclude = {SessionAutoConfiguration.class})

Kotlin spring-boot #ConfigurationProperties

I'm trying to create the following bean AmazonDynamoDBAsyncClientProvider. I've application.properties that defines endpoint and tablePrefix which I'm trying to inject using #ConfigurationProperties
Following is the code snippet for the same. When I run my spring-boot app it doesn't work.
I've tried doing the same ConfigurationProperties class using a regular java class which does set those properties but when it comes to AmazonDynamoDBAsyncClientProvider, the properties are empty. What am I missing here?
#Component
open class AmazonDynamoDBAsyncClientProvider #Autowired constructor(val dynamoDBConfiguration: DynamoDBConfig){
#Bean open fun getAmazonDBAsync() = AmazonDynamoDBAsyncClientBuilder.standard()
.withEndpointConfiguration(
AwsClientBuilder.EndpointConfiguration(dynamoDBConfiguration.endpoint, dynamoDBConfiguration.prefix))
.build()
}
here is the kotlin bean that I'm trying to autowire with configuration
#Component
#ConfigurationProperties(value = "dynamo")
open class DynamoDBConfig(var endpoint: String="", var prefix: String="")
finally heres the regular java bean that does get populated with ConfigurationProperties but when it gets Autowired I see those properties being empty/null
#Component
#ConfigurationProperties("dynamo")
public class DynamoDBConfiguration {
private String endpoint;
private String tablePrefix;
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public String getTablePrefix() {
return tablePrefix;
}
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
}
Have you tried getting rid of the #Component annotation on your ConfigurationProperties class?
Here is what I have done with Kotlin and Spring, hope it helps.
I am trying to leverage the kotlin-spring and kotlin-allopen gradle plugin
dependencies {
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlinVersion"
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion"
}
apply plugin: 'kotlin-spring'
apply plugin: 'kotlin-noarg'
noArg {
annotation("your.noarg.annotation.package.NoArg")
}
They do make spring development with kotlin a lot easier.
#ConfigurationProperties("dynamo")
#NoArg
data class DynamoDBConfiguration(var endpoint: String, var prefix: String)
I tried your configuration class and it gets populated. I think your mistake is in the way you are trying to create the bean, the function needs to be in a class annotated with #Configuration, this should work:
#Configuration
class Beans {
#Bean
fun getAmazonDBAsync(config: DynamoDBConfiguration) =
AmazonDynamoDBAsyncClientBuilder.standard()
.withEndpointConfiguration(
AwsClientBuilder.EndpointConfiguration(config.endpoint, config.prefix)
)
.build()
}
Spring will inject the config for you, as long as you annotate the config with #Component, like you did above.
I had a similar problem and fixed it this way:
I defined the configuration properties class with lateinit vars:
#ConfigurationProperties(prefix = "app")
open class ApplicationConfigProperties {
lateinit var publicUrl: String
}
Then configured a bean in my spring boot application:
#SpringBootApplication
open class Application {
#Bean open fun appConfigProperties() = ApplicationConfigProperties()
}

Resources