ZooKeeper latest value is not reloaded without restarting server - spring-boot

I am using ZooKeeper with spring boot. And In application.properties file I am using below properties as shown below.
minio.url=${minio.connection-string}
minio.access.key=${minio.accesskey}
where minio.connection-string and minio.accesskey value will be came from ZooKeeper znode data. I am using minio.url and minio.access.key in other Spring boot bean as shown below.
#Configuration
#RefreshScope
public class MinioClientConf
{
#Value("${minio.url}")
private String minioUrl;
#Value("${minio.access.key}")
private String minioKey;
.
.
When I start my spring boot application then all stuff works but when I change ZooKeeper node value then it is not reflecting in bean value without re-starting server.
My problem is that I want to reload latest zookeeper value without re-starting server. I have also tried with refresh scope annotation but it didn't work.

Instead of that use #ConfigurationProperties
#ConfigurationProperties("minio")
public class MinioClientConf
{
private String minioUrl;
private String minioKey;
.
.
For more details click here

Related

Spring Cloud Config client doesn't get values from server

I have been following this tutorial from Baeldung in setting up a Spring Cloud Config server and client. I believe I have set up the server correctly since going to http://localhost:8888/service-config/development/master will seemingly correctly show the json with the user.role defined:
{"name":"service-config","profiles":["development"],"label":"master","version":"d30a6015a6b8cb1925f6e61cee22721f331e5783","state":null,"propertySources":[{"name":"file:///C:.../git/service-config-data/service-config-development.properties","source":{"user.role":"Developer"}},{"name":"file:///C:.../git/service-config-data/service-config.properties","source":{"user.role":"Developer"}}]}
However, I cannot seem to correctly connect the Spring Cloud Config client to the server - the client fails to run with cannot resolve placeholder errors on the #Value("${user.role}") annotation. I've tried adding a default value to the annotation and then manually refreshing the values (using the #refreshscope annotation), but to no avail.
I've looked at the debug and trace logs by running the tools with --debug --trace flags but I cannot find when/if the client is making the call to the server and which config-data .properties file the server is actually looking for. I am a bit lost on how to proceed.
Here is my server application.properties:
spring.application.name=service-config
server.port=8888
spring.cloud.config.server.git.uri=file:///${user.home}/git/service-config-data
spring.cloud.config.server.git.clone-on-start=false
spring.security.user.name=root
spring.security.user.password=toor
And inside the service-config-data folder, there are files with this inside:
user.role=Developer
I have tried files called service-config.properties, service-config-client.properties, service-config-development.properties, and service-config-client-development.properties, and none of them seem to pull through the user.role.
Here is my client:
#SpringBootApplication
#RestController
#RefreshScope
public class ServiceConfigClient {
#Value("${user.role:unknown}")
private String role;
#RequestMapping(
value = "/whoami/{username}",
method = RequestMethod.GET,
produces = MediaType.TEXT_PLAIN_VALUE)
public String whoami(#PathVariable("username") String username) {
return String.format("Hello! You're %s and you'll become a(n) %s...\n", username, role);
}
}
And my client bootstrap.properties:
spring.application.name=service-config-client
spring.profiles.active=development
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.username=root
spring.cloud.config.password=toor
management.endpoints.web.exposure.include=*
How do I correctly connect the client to the server so that it can successfully pull the config data? Thank you.
My problem was fixed by adding spring.cloud.config.name=service-config to the bootstrap.properties of the service-config-client as well as adding spring-cloud-config-client to and removing spring-cloud-config-server from my pom.xml
There is confusion about client service name and corresponding property sources in config server.
You need to do one of the following:
Change client service name:
//client bootstrap.properties
spring.application.name=service-config
Change folder name in git repo from service-config to service-config-client

Embedded Kafka Broker IP Not Resolving in Property File

I'm facing an issue where my Kafka ProducerConfig is getting an invalid bootstrap.servers value because my unit test #PropertySource isn't resolving the spring.embedded.kafka.brokers property. When I dump my producer config to the logs, I get the following:
acks = 0
batch.size = 10000
bootstrap.servers = [${spring.embedded.kafka.brokers}]
...
Clearly, the property isn't getting resolved. Suppose I have the following embedded Kafka test.
#EmbeddedKafka
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
#RunWith(SpringRunner.class)
public class EmbeddedKafkaTest {
#Value("${spring.embedded.kafka.brokers}")
private String embeddedKafkaBrokers;
#Test
public void test(){}
#SpringBootConfiguration
#PropertySource("classpath:kafkaTestProps.properties")
#EnableAutoConfiguration
class EmbeddedKafkaTestConfiguration {
}
}
and my kafkaTestProperties.properties file is as follows:
embedded-kafka-brokers=${spring.embedded.kafka.brokers}
...
embedded-kafka-brokers will eventually be provided to Kafka's ProducerConfig through some underlying autoconfiguration.
Interestingly, the embeddedKafkaBrokers instance field in the test class does contain the broker IPs set by embedded kafka.
I've concluded there's an issue with the property source loading order, where #EmbeddedKafka isn't setting the broker IP system property in time for kafkaTestProperties.properties to resolve it. This problem arose after porting our code base to Spring Boot 2 and Spring Cloud Finchley SR2, where Spring Kafka APIs have upgraded.
I've tried removing #SpringBootTest and wrapping the test() code in a SpringApplicationBuilder to no avail.
Any advice as to how I can fix this? Perhaps I can leverage #AutoConfigurationAfter or #Order? Is there a way to order the loading of property sources?
It looks that your placeholder is not valid.
Try to replace:
embedded-kafka-brokers=${"spring.embedded.kafka.brokers"}
with:
embedded-kafka-brokers=${spring.embedded.kafka.brokers}
and provide it to the producer using class with #ConfigurationProperties or using #Value("${embedded-kafka-brokers}").
documentation link

Unable to read values from external property file in Spring Boot

I have a running Spring Boot project. I want to read some environment specific properties from an external properties file.
I mentioned config files names and locations while starting the server as follows:
java -jar AllergiesConditions.jar --spring.config.name=application,metadata --spring.config.location=classpath:/,/APPS/SpringBoot/
The property files loads successfully(because i tried to log one of the external key values inside datasource bean and It printed successfully) But when i try to access a value using #Value annotation - It returns null.
My test Class is as follows:
#Component
public class testclass {
private Logger logger = LogManager.getLogger(testcla.class);
#Value("${sso.server}")
public String sso;
public void test(){
logger.info("sso url is: "+sso); //This sso is logged as null
otherStuff();
}
}
This test() function is called when a particular API is hit after server is running.
The external config file - metadata.properties contains this variable:
sso.server=1234test
Edit: As suggested in this apparently duplicate question I also tried adding #PropertySource(name = "general-properties", value = { "classpath:path to your app.properties"}) in main Application configuration class and It loaded the files, but still I get null value itself.
Can someone please help in what's going wrong here?? Does the testclass need some specific annotation OR it needs to be a bean or something??
Thanks in Advance :)
Thanks to M.Deinum for great input and saving my time
Just posting his comment as answer
Factually ${sso.server} cannot be null. If ${sso.server} couldn't be resolved, my application will break at startup itself.
So the obvious problem was that I was creating a new instance of testclass in my controller using
testclass obj = new testclass(); obj.test();
Rather I should be using spring managed instance by autowiring testclass in my controller.

springboot + JDBTemplate (No datasource specified error even though specified in application.properties

Application.properties
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=
i placed this application.properties in resources folder.
Java Class
#Component
public class data{
#Autowired
private JdbcTemplate jdbcTemplate;
public void queryData(){
String sql = "select * from DEPOSIT";
jdbcTemplate = new JdbcTemplate();
jdbcTemplate.execute(sql);
}
}
I am getting
java.lang.Illegal Argument Exception:No Data Source Specified
I am getting this error message even though i specified data source in application.properties
I am using Spring Boot for this task. I Have added almost all the dependencies required in POM.
Not sure why i am not able to access data source. basically trying to access data from DB using Spring boot, MySQL, jdbcTemplate.
Not sure whats wrong here.
Do i have to add anything in the code so that data source can be specified in java class?
Add below properties to your application.properties file. This specifies the data source for your application. Do check if mysql is running on your machine before starting your application.
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=yourusername
spring.datasource.password=yourpassword
For additional information refer to below link:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html

Reload property value when external property file changes ,spring boot

I am using spring boot, and I have two external properties files, so that I can easily change its value.
But I hope spring app will reload the changed value when it is updated, just like reading from files. Since property file is easy enough to meet my need, I hope I don' nessarily need a db or file.
I use two different ways to load property value, code sample will like:
#RestController
public class Prop1Controller{
#Value("${prop1}")
private String prop1;
#RequestMapping(value="/prop1",method = RequestMethod.GET)
public String getProp() {
return prop1;
}
}
#RestController
public class Prop2Controller{
#Autowired
private Environment env;
#RequestMapping(value="/prop2/{sysId}",method = RequestMethod.GET)
public String prop2(#PathVariable String sysId) {
return env.getProperty("prop2."+sysId);
}
}
I will boot my application with
-Dspring.config.location=conf/my.properties
I'm afraid you will need to restart Spring context.
I think the only way to achieve your need is to enable spring-cloud. There is a refresh endpoint /refresh which refreshes the context and beans.
I'm not quite sure if you need a spring-cloud-config-server (its a microservice and very easy to build) where your config is stored(Git or svn). Or if its also useable just by the application.properties file in the application.
Here you can find the doc to the refresh scope and spring cloud.
You should be able to use Spring Cloud for that
Add this as a dependency
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter', version: '1.1.2.RELEASE'
And then use #RefreshScope annotation
A Spring #Bean that is marked as #RefreshScope will get special treatment when there is a configuration change. This addresses the problem of stateful beans that only get their configuration injected when they are initialized. For instance if a DataSource has open connections when the database URL is changed via the Environment, we probably want the holders of those connections to be able to complete what they are doing. Then the next time someone borrows a connection from the pool he gets one with the new URL.
Also relevant if you have Spring Actuator
For a Spring Boot Actuator application there are some additional management endpoints:
POST to
/env to update the Environment and rebind #ConfigurationProperties and log levels
/refresh for re-loading the boot strap context and refreshing the #RefreshScope beans
Spring Cloud Doc
(1) Spring Cloud's RestartEndPoint
You may use the RestartEndPoint: Programatically restart Spring Boot application / Refresh Spring Context
RestartEndPoint is an Actuator EndPoint, bundled with spring-cloud-context.
However, RestartEndPoint will not monitor for file changes, you'll have to handle that yourself.
(2) devtools
I don't know if this is for a production application or not. You may hack devtools a little to do what you want.
Take a look at this other answer I wrote for another question: Force enable spring-boot DevTools when running Jar
Devtools monitors for file changes:
Applications that use spring-boot-devtools will automatically restart
whenever files on the classpath change.
Technically, devtools is built to only work within an IDE. With the hack, it also works when launched from a jar. However, I may not do that for a real production application, you decide if it fits your needs.
I know this is a old thread, but it will help someone in future.
You can use a scheduler to periodically refresh properties.
//MyApplication.java
#EnableScheduling
//application.properties
management.endpoint.refresh.enabled = true
//ContextRefreshConfig.java
#Autowired
private RefreshEndpoint refreshEndpoint;
#Scheduled(fixedDelay = 60000, initialDelay = 10000)
public Collection<String> refreshContext() {
final Collection<String> properties = refreshEndpoint.refresh();
LOGGER.log(Level.INFO, "Refreshed Properties {0}", properties);
return properties;
}
//add spring-cloud-starter to the pom file.
Attribues annotated with #Value is refreshed if the bean is annotated with #RefreshScope.
Configurations annotated with #ConfigurationProperties is refreshed without #RefreshScope.
Hope this will help.
You can follow the ContextRefresher.refresh() code implements.
public synchronized Set<String> refresh() {
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
this.scope.refreshAll();
return keys;
}

Resources