How to load properties in Spring Boot Library Project with environments? - spring-boot

I have a Spring Boot Security Library project as package/dependency. Now I want to add properties for different environments [local,dev,stage,prod], so I added file security-properties.yml in resources directory as shown below
security:
jwt:
secret: xxxxxxxxxxxxxxxxxxxx
---
spring:
profiles: local
security:
# Proxy
proxy:
authUrl: http://localhost:8080
# JWT
jwt:
expiryMs: 14400000
---
spring:
profiles: dev
security:
# Proxy
proxy:
authUrl: https://example-dev.com
# JWT
jwt:
expiryMs: 43200000
---
spring:
profiles: stage
security:
# Proxy
proxy:
authUrl: https://example-stage.com
# JWT
jwt:
expiryMs: 43200000
---
spring:
profiles: prod
security:
# Proxy
proxy:
authUrl: https://example.com
# JWT
jwt:
expiryMs: 43200000
Now to load the properties I created SecurityProperties.class
#Getter
#Setter
#Configuration
#PropertySource(value = "classpath:security-properties.yml", factory = YamlPropertySourceFactory.class)
#ConfigurationProperties(prefix = "security")
public class SecurityProperties {
private Jwt jwt = new Jwt();
private Proxy proxy = new Proxy();
#Getter
#Setter
public static class Jwt {
private String tokenHeader = "Authorization";
private String tokenHead = "Bearer ";
private String secret;
private Long expiryMs = 43200000L;
}
#Getter
#Setter
public static class Proxy {
private String authUrl;
}
#Getter
#Setter
public static class IgnoreUrls {
private String[] get = {};
private String[] post = {};
private String[] patch = {};
private String[] delete = {};
}
}
and YamlPropertySourceFactory.class to load yaml file
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
yamlFactory.setResources(resource.getResource());
yamlFactory.afterPropertiesSet();
Properties properties = yamlFactory.getObject();
assert properties != null;
return new PropertiesPropertySource(Objects.requireNonNull(resource.getResource().getFilename()), properties);
}
}
Now after publishing the artifact for the library project and injecting the dependency in main project, I am unable to load security properties for environments from the library project.
Note: I can add security properties in application-{profile}.yml file in main project , it works
How to add properties in Spring Boot Library projects [dependency] ?

Related

How to inject Bean of property within my context Bean?

I'm trying to inject Bean of properties within context Bean.
(Spring-boot 2.7.3 / Java 11)
My application.yml is like below:
spring:
config:
active: dev
---
spring:
config:
activate:
on-profile: dev
keycloak:
username: "local"
password: "local"
---
spring:
config:
activate:
on-profile: stg
keycloak:
username: "stg"
password: "stg"
---
spring:
config:
activate:
on-profile: prod
keycloak:
username: "prod"
password: "prod"
and my KafkaProducerConfig.java code like below:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.stereotype.Component;
import lombok.Getter;
#Getter
#Component
#ConstructorBinding
#ConfigurationProperties("keycloak")
public class KafkaProducerConfig {
private final String username;
private final String password;
public KafkaProducerConfig(String username, String password) {
this.username = username;
this.password = password;
}
}
and finally I failed to inject within another class.
Actually, UserDataProducer class extended in a context bean class which means UserDataProducer class also instanciated by spring IoC Container as I know.
I also tried #DependsOn which doesn't work.
#Slf4j
#DependsOn(value = {"KafkaProducerConfig"})
public class UserDataProducer {
#Autowired
KafkaProducerConfig kafkaProducerConfig;
private final String topicName;
public UserDataProducer() {
log.info("===========================================================");
log.info("Initializing UserDataProducer ...");
System.out.println(kafkaProducerConfig.getPassword());
log.info("===========================================================");
// additional properties for transactional producing
topicName = ProducerConfig.PRODUCER_PROPS.getProperty("default.topic");
}
#Slf4j
#Component
public class UserDataProducer {
// use static initializer block to initialize your static fields
// private static final Producer<String, Object> producer;
// initialzer order : static{} -> instance block {} -> constructor
private final String topicName;
public UserDataProducer(KafkaProducerConfig kafkaProducerConfig) {
log.info("===========================================================");
log.info("Initializing UserDataProducer ...");
System.out.println(kafkaProducerConfig.getPassword());
log.info("===========================================================");
// additional properties for transactional producing
topicName = ProducerConfig.PRODUCER_PROPS.getProperty("default.topic");
}

null map when reading nested properties from yaml with configuration properties annotation

My application.yml looks like so
service:
cloud:
piglet:
published-host: http://localhost:29191
webhook:
headers:
gitlab: X-Gitlab-Token
The Config class
#Component
#ConfigurationProperties(prefix = "service.cloud.piglet.webhook")
public class WebhooksConsumerTokenHeadersProperties {
private final Map<String, String> headers;
public WebhooksConsumerTokenHeadersProperties(Map<String, String> headers) {
this.headers = headers;
}
public String getTokenHeaderName(String app) {
return headers.get(app);
}
}
I ran on debug and noticed that the headers map is null when initialising it in the constructor.

actuator/refresh path not updating #value variables when use spring cloud vault

When I update a property in the vault and call actuator / refresh, it still shows an older value.
I use spring boot and spring cloud.
<spring-cloud-dependencies.version>2020.0.3</spring-cloud-dependencies.version>
<spring-boot-dependencies.version>2.5.3</spring-boot-dependencies.version>
#SpringBootApplication
#EnableEurekaClient
#VaultPropertySource(value = "secret/services/${spring.application.name}/config", propertyNamePrefix = "", renewal = VaultPropertySource.Renewal.RENEW)
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
#RestController
public class ErrorControllerImpl implements ErrorController {
#Value("${gateway.prefix}")
private String prefixPath;
#Override
public Mono<String> errorPost(String serviceName) {
return errorResponse(serviceName);
}
#Override
public Mono<String> errorGet(String serviceName) {
System.out.println(prefixPath);
return errorResponse(serviceName);
}
Yml
spring:
application:
name: ${APP_NAME}
profiles:
active: ${PROFILE:dev}
cloud:
config:
enabled: ${CONFIG_SERVER_ENABLED:false}
vault:
uri: https://localhost:8200
authentication: APPROLE
app-role:
role-id: ${VAULT_SERVICE_ROLE_ID}
secret-id: ${VAULT_SERVICE_SECRET_ID}
scheme: http
fail-fast: true
kv:
enabled: false
The prefixPath in the controller does not change after refresh.
What would be the reason ?
Thanks for your help.

Spring Boot - MongoDB integration test database config change

MongoDBConfig
#Configuration
#EnableMongoRepositories(basePackages="......persistence.repositories")
public class MongoDBConfig extends AbstractMongoClientConfiguration {
#Value("${spring.data.mongodb.host}")
private String host;
#Value("${spring.data.mongodb.port}")
private String port;
#Value("${spring.data.mongodb.username}")
private String username;
#Value("${spring.data.mongodb.password}")
private String password;
#Value("${spring.data.mongodb.database}")
private String database;
#Override
public MongoClient mongoClient() {
return MongoClients.create("mongodb://" + host + ":" + port);
}
#Override
protected String getDatabaseName() {
return database;
}
}
application.yml
spring:
data:
mongodb:
host: localhost
port: 27017
username:
password:
database: spring-mongodb-demo
With the above configurations, spring app using mongo database as per defined in yml - spring-mongodb-demo.
One of my integration test beings like this:
#RunWith(SpringRunner.class)
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class UserRepositoryIntegrationTest {
#Autowired
private MongoTemplate mongoTemplate;
#Autowired
private UserRepository userRepo;
When I run an integration tests it uses the database named - test.
I want the integration tests to use the database - spring-mongodb-demo as per defined in yml file or setting a different database as my preference.
I have tried by making a new profile named application-test.yml and set #ActiveProfiles(profiles = "test") in test class.
I have found lots of thread in this regard, nothing worked.

Spring Boot Configuration Not Working

I'm using Spring boot.
I have an application.yml in src/main/resources. I then have a Configuration class that I am trying to get to load the application.yml. However, when I try to use the configuration class in another bean, the values are null. See the ApiHelper.java as to where the values are null.
I'm attempting to run the jar as so:
java -jar build/libs/app.jar
Am I doing something wrong? I've also tried using a properties file instead. When I unzip the jar file the configuration files are in the root.
src/main/resources/application.yml
spring:
profiles.active: default
---
spring:
profiles: default
api:
path: http://some-path
---
spring:
profiles: qa
api:
path: http://some-path2
src/main/java/AppConfig.java
#Configuration
#EnableConfigurationProperties(ApiConfig.class)
public class AppConfig {
#Autowired
private ApiConfig apiConfig;
#ConfigurationProperties(value = "api", exceptionIfInvalid=true)
public static class ApiConfig {
private String path;
public ApiConfig() {
System.out.println("Am I getting called?"); // yes it is
}
public String getPath() {
return path;
}
}
#Bean
public ApiHelper getApiHelper() {
return new ApiHelper();
}
}
src/main/java/ApiHelper.java
public class ApiHelper {
#Autowired
private ApiConfig apiConfig;
#PostConstruct
private void init() {
System.out.println(apiConfig); // prints ApiConfig#168498d6
System.out.println(apiConfig.getPath()); // prints null
}
}
It turns out that you need a setter to make it work:
#ConfigurationProperties(value = "api", exceptionIfInvalid=true)
public static class ApiConfig {
...
public void setPath(String path) {
this.path = path;
}
}

Resources