How to consume spring cloud config server Jdbc backend configs from Spring cloud Client server? - spring

I went through lots of tutorials regarding this but could not get this done.
Here is my table structure for this.
Application Profile Label prop_key value
masking dev latest test-property message
I have a cloud config server which should integrate with JDBC backend. Here is my application.properties in config server
server.port=8082
spring.application.name=masking
management.endpoints.web.exposure.include=*
spring.datasource.url=jdbc:postgresql://localhost:8000/finos?currentSchema=xlabs
spring.datasource.username=mufgdev
spring.datasource.password=XXX
spring.profiles.active=XXX
spring.cloud.config.server.jdbc.sql=SELECT prop_key,value from xlabs.properties where application=? and profile=? and label=?
spring.cloud.config.server.jdbc.order=1
With this configs if I enter http://localhost:8082/masking/dev/latest response will show the results as I want.
I want to consume properties in client side with the following configs in bootstrap.properties
spring.application.name=masking
spring.cloud.config.uri=http://localhost:8082
spring.cloud.config.label=latest
spring.cloud.config.profile=dev
And in my client side
#RestController
#RefreshScope
public class TestController {
#Value("${test-property}")
private String aConf;
#GetMapping("/message")
String message() {
String name =aConf ;
return name;
}
}
This gives java.lang.IllegalArgumentException: Could not resolve placeholder 'test-property' in value "${test-property}"
Can anyone comment on this?
Thanks.

This issue comes with the latest Spring boot release, All the above code segments steps all okay, But by default Spring has disabled bootstrap. So you have to enable them by adding
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
No need add for older versions of the Spring boot projects.

Related

How to use and config caching in Spring MVC

I want to cache the following getMessagesList method. I want to call one time When user log into the system. Therefor I think caching is the best solution for that. And I need to remove when user log out. How I can do this.
public List<String> getMessagesList(String username)
{ // return messages list in DB by username}
My project was create using Maven 4.0 and Spring MVC. spring version 5.3
Assuming you use Spring Security as part of your app, it should be managing your session, and every time you log out, it will create a new session. Unless you had posted this code, I'm not going to be able to help you there. However, assuming you can log in/out, this should be covered already.
As for the cacheing, in general, this sounds like a Database Caching need, which is something that you would use Spring Boot Caching on.
To use this in Spring Boot, you would add the following dependency to maven (or the equivalent in Gradle, etc):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
Adjust your application to allow using Cacheing, which can be done by adding the annotation #EnableCaching to your Spring Boot application
#SpringBootApplication
#EnableCaching
public class MyApplication {
...
}
Create a Java Service Object, called something like MessagesService.class:
#CacheConfig(cacheNames={"Messages"})
public class MessagesService {
#Cacheable(value="cacheMessages")
List<String> getMessages() {
//access the database to load data here
...
}
...
}

I am not able to connect to Google Cloud Memory Store from Spring Boot

I am developing a module with spring boot in my backend where i need to use Redis through GCP Memory Store. I have been searching in forum and even the "oficial documentation" about memory store but i cannot understand how to connect to memory store with my spring boot app.
I found a google code lab but they use a Compute Engine VM to install spring boot and then save and retrieve information from memory store. So i tried to do it like that in my local spring boot but it didnt work because throws an error saying:
Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to 10.1.3.4
the codelab i mentioned earlier says that you only have to add this line to your application.properties:
spring.redis.host=10.1.3.4
as well as the annotation #EnableCaching in the main class and #Cachable annotation in the controller method where you try to do something with redis.
the method looks like this:
#RequestMapping("/hello/{name}")
#Cacheable("hello")
public String hello(#PathVariable String name) throws InterruptedException {
Thread.sleep(5000);
return "Hello " + name;
}
i dont know what else to do. Notice that i am new on this topic of redis and memory store.
Anyone can give me some guidance on this please?
thanks in advance
codelab url: https://codelabs.developers.google.com/codelabs/cloud-spring-cache-memorystore#0
See this documentation on how to setup Memorystore Redis instance.
Included in the documentation is how you can connect and test your Memorystore instance from different computing environments.
There's also a step by step guide on how SpringBoot can use Redis to cache with annonations.
Add the Spring Data Redis starter in your pom.xml if you're using Maven for your project setup.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Add this configuration in your application.properties file:
spring.redis.host=<MEMORYSTORE_REDIS_IP>
# Configure default TTL, e.g., 10 minutes
spring.cache.redis.time-to-live=600000
Turn on caching capability explicitly with the #EnableCaching annotation:
#SpringBootApplication
#EnableCaching
class DemoApplication {
...
}
Once you configured the Spring Boot with Redis and enabled caching, you can use the #Cacheable annotation to cache return values.
#Service
class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
#Cacheable("order")
public Order getOrder(Long id) {
orderRepository.findById(id);
}
}

Spring cloud bus with AWS Kinesis stream #refreshscope

I read everywhere #RefreshScope for cloud bus applications work with RabbitMQ and Kafka. But in my case, I am using AWS Parameter store. I want all my client instances to be refreshed automatically without rebuilding servers on AWS Console.
I created AWS Eventbridge from Paramstore to notify Kinesis Stream but I am not able to figure out how can it notify all my client nodes instead of load balancer refresh to only one node(instance).
Thank you for responding in advance.
I've never worked with AWS Eventbridge / Kinesis, however:
#RefreshScope is something that belongs to spring cloud and not not AWS.
More precisely, beans defined with this scope will be re-loaded by spring without reloading the whole application context "dynamically" when configuration changes in spring boot cloud configuration service. Usually this means that you don't have to restart the application.
Now, spring boot microservice should be deployed with actuator that exposes refresh endpoint. Calling this endpoint manually will cause all the #RefreshScope beans to reload.
Here is the source code of the RefreshEndpoint:
#Endpoint(id = "refresh")
public class RefreshEndpoint {
private ContextRefresher contextRefresher;
public RefreshEndpoint(ContextRefresher contextRefresher) {
this.contextRefresher = contextRefresher;
}
#WriteOperation
public Collection<String> refresh() {
Set<String> keys = this.contextRefresher.refresh();
return keys;
}
}
As you see, its merely invokes contextRefresher.refresh(), ContextRefresher is a bean that you can inject in your custom code that will listen to the changes coming from AWS Parameter store (it should somehow invoke it directly or maybe send some message that you could consume or something).
If you're using spring-cloud-bus (disclaimer, I've never worked with it) it exposes the bus-refresh endpoint as well (pretty similar mechanism to what I've described), read spring-cloud-bus documentation for more details.
Thank you Team for sharing info.
Here is what I did to make it work. Added these two libraries to my project
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kinesis</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
And added these two entries into bootstrap.properties
cloud.aws.region.static=us-east-1
cloud.aws.stack.auto = false
And refreshing using this endpoint (/bus-refresh)

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

How to pass additional parameters to MariaDB connect string to fix timezone issue (e.g. useLegacyDatetimeCode)

I'm deploying some spring-boot app to Swisscom AppCloud which uses a MariaDB service. The service gets automatically configured in my app using the CloudFoundry connectors and the connection works fine.
However: since I heavily use ZonedDateTime-Objects in my Java-Code, I also included in the pom.xml...
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-java8</artifactId>
</dependency>
...to properly preserve the ZonedDateTimes in the database.
This works fine on my local MariaDB, when I add...
...?useLegacyDatetimeCode=false
...to the connect string (as described here: https://moelholm.com/2016/11/09/spring-boot-controlling-timezones-with-hibernate/ -> "BONUS TIP: Getting the Hibernate configuration to work with MariaDB / MySQL").
How can I add this flag (and maybe others, too) to the connection to the MariaDB service on Swisscom AppCloud?
If you use Spring on CloudFoundry in conjunction with a MariaDB binding, the datasource is automatically configured by this mechanism: https://docs.cloudfoundry.org/buildpacks/java/spring-service-bindings.html
This is powered by the Spring cloud connectors project, which you can customize to your needs.
I did not test it, but you should be able to set the driver properties as follows:
#Bean
public DataSource dataSource() {
PoolConfig poolConfig = new PoolConfig(5, 30, 3000);
ConnectionConfig connConfig = new ConnectionConfig("useLegacyDatetimeCode=false");
DataSourceConfig dbConfig = new DataSourceConfig(poolConfig, connConfig);
return connectionFactory().dataSource(dbConfig);
}

Resources