Spring Cloud config refreshed values are not updating with #value annotation - java

I have a spring boot project with cloud config server.I have enabled cloud bus with kafka and added required actuator endpoints to live reload properties on both server and client.
cloud:
bus:
enabled: true
management:
endpoints:
web:
exposure:
include: refresh, bus-refresh, beans, env
Issue is when I am using #value annotation to read property value. I am not getting updating value. (#value only get updated when I apply #RefreshScope,But I cannot use it because I have ton of client services and will to add #RefreshScope on all beans where properties are being used)
#Value("${message.testValue}")
private String info;
private String readValue() {
return info;
}
But when I read using Environment, I am getting updated value after refresh
private String readValue() {
return this.environment.getProperty("message.testValue");
}
What changes I need to implement to get updated value using #Value annotation withou #RefreshScope? Or there is something which I am missing in implementation.

Related

Spring Boot Actuator not showing any information

I'm using Spring Boot version 2.2.5.RELEASE and have trouble to receive any information from the actuator endpoint. What I'm receiving is only the empty structure of Actuator as following:
This is my configuration:
implementation "org.springframework.boot:spring-boot-starter-actuator"
#Bean
public HttpTraceRepository httpTraceRepository() {
return new InMemoryHttpTraceRepository();
}
management:
endpoints:
web:
exposure:
include: "*"
What do I miss?
Hallelujah, finally found the issue of my above described problem!
The problem was that i configured my spring boot application to use Gson instead of the default Jackson json parser. Not completely clear why, but with the following lines of code it results in issues to show the data at the actuator endpoints.
implementation ("org.springframework.boot:spring-boot-starter-web") {
exclude group: "org.springframework.boot", module: "spring-boot-starter-json"
}
implementation "com.google.code.gson:gson:2.8.6"
The following above described Bean is also unnecessary!
#Bean
public HttpTraceRepository httpTraceRepository() {
return new InMemoryHttpTraceRepository();
}
The following configuration was hence as well a problem:
spring:
http:
converters:
preferred-json-mapper: gson
gson:
exclude-fields-without-expose-annotation: true
And finally with removing of all of my #Expose annotations it started to work perfectly fine again.
#Expose
private String content;
If anyone can explain to me why this happened, or how to configure Gson to work properly with Spring Boot Actuator in combination, I'm here to listen and understand.
I had the same problem and I fixed by removing the following line
gsonBuilder.excludeFieldsWithoutExposeAnnotation()

Spring okta oauth2 properties using spring cloud vault

We are using okta for authentication in our Spring boot project. We have successfully done authentication using java application as (refer - https://developer.okta.com/blog/2017/03/21/spring-boot-oauth)
Now what I am trying to do is move the okta clientId and secret property to vault.
Bootstrap for vault
spring.cloud.vault:
host: localhost
port: 8200
scheme: http
token: 00000000-0000-0000-0000-000000000000
Now please have a look at the below 2 cases for application properties
Case 1 : properties used using #Value works
am.clientId=${account.clientId}
am.issuer=${account.issuer}
used as
#Value("${am.clientId}")
private String clientId;
#Value("${am.issuer}")
private String clientSecret;
Case 2 : used as spring properties does not work
I use the same properties for spring oAuth and it fails
okta.oauth2.clientId=${account.clientId}
okta.oauth2.issuer=${account.issuer}
Exception log
java.lang.IllegalArgumentException: Could not resolve placeholder 'account.clientId' in value "${account.clientId}"
account-web_1 | at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:172) ~[spring-core-5.0.5.RELEASE.jar!/:5.0.5.RELEASE]
account-web_1 | at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124) ~[spring-core-5.0.5.RELEASE.jar!/:5.0.5.RELEASE]
account-web_1 | at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:237) ~[spring-core-5.0.5.RELEASE.jar!/:5.0.5.RELEASE]
account-web_1 | at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:211) ~[spring-core-5.0.5.RELEASE.jar!/:5.0.5.RELEASE]
[Update]
So debugging the spring code I realised that the problem is only with property
okta.oauth2.issuer
Github has one issue regarding same, but the okta spring boot satarter 0.6.0 version still has issue.
Have you tried setting the okta.oauth2.* properties in your cloud config provider? I usually test Cloud Config using a file repo, but from the client's perspective it should be the same. (I know this doesn't fully answer your question, but just want to make sure that works before continuing)
So I have got one sollution to this one. Created a bean "oktaOAuth2Properties" to overrid the spring boot default bean and get the value from vault using the #Value annotation. the code looks like below. This works for me
#Value("${okta.clientId}")
private String clientId;
#Value("${okta.issuer}")
private String issuer;
#Bean
public OktaOAuth2Properties oktaOAuth2Properties() {
OktaOAuth2Properties properties = new OktaOAuth2Properties();
properties.setClientId(clientId);
properties.setIssuer(issuer);
return properties;
}

How can I change a value in consul K/V Store from Spring Boot app

I use Consul's Key/Value Store as a PropertySource in a Spring Boot 2 app. (org.springframework.cloud:spring-cloud-starter-consul-config)
I can read the properties from the K/V Store with #ConfigurationProperties and even update them with #RefreshScope when I change the value via the Consul web interface.
But I do have some dynamic properties which can change in the application.
How do I propagate these changes to Consul, so that the values are actually changed.
I tried to use the Setter for the property but that did not change the value in Consul.
Use this code to set KV values.
Create private variable.
#Autowired
private ConsulClient consulClient;
change KVs using setKVValue() method.
consulClient.setKVValue("key", "value")

SpEL not supported in Spring annotation #Entry.base

I use Spring Data LDAP and Spring Boot provides out of the box support for an embedded UnboundID server. However, when I use Spring Data LDAP's #Entry annotation, I need to specify a different base in the annotation based on whether I'm using the embedded UnboundID LDAP server, or a remote Active Directory server.
I was attempting to do this with SpEL and profile-based properties by specifying:
#Entry(base = "${ldap.person.base}", ...)
Then I have an application.propreties with ldap.person.base=OU=AD Person Base and an application-embedded.properties with ldap.person.base=OU=Embedded Person Base.
However, the #Entry annotation does not seem to support SpEL evaluation:
javax.naming.InvalidNameException: Invalid name: ${ldap.person.base}
There is an open issue in Spring LDAP to add support for this, but is there any workaround or some other way I can accomplish this until it is supported in Spring LDAP?
I'm not sure I'm following here, but assuming you're using the LDAP auto-configuration in Spring Boot, is it not enough to set the property spring.ldap.base to one or the other (OU=AD Person Base or OU=Embedded Person Base) based on the profile you're using?
Both EmbeddedLdapAutoConfiguration and LdapAutoConfiguration use an LdapProperties object to set various attributes on the LdapContextSource during bean creation, including its base. As far as I can tell, you won't have to define it for each #Entry in your codebase if LdapContextSource.base is set.
If you're not using the auto-configuration, and if I'm correct in my assumptions, you should still be able to create your own LdapContextSource bean and set its base to the desired value based on a Spring property.
Turns out the reason I needed a different base in the first place is because Spring was not setting the base on the ContextSource.
When you let Spring Boot autoconfigure the embedded LDAP server, it creates a ContextSource as such in EmbeddedLdapAutoConfiguration:
#Bean
#DependsOn("directoryServer")
#ConditionalOnMissingBean
public ContextSource ldapContextSource() {
LdapContextSource source = new LdapContextSource();
if (hasCredentials(this.embeddedProperties.getCredential())) {
source.setUserDn(this.embeddedProperties.getCredential().getUsername());
source.setPassword(this.embeddedProperties.getCredential().getPassword());
}
source.setUrls(this.properties.determineUrls(this.environment));
return source;
}
As you can see, nowhere in there does it call source.setBase(). So to solve this, I added a configuration file with #Profile("embedded") and manually created a ContextSource where I set the base myself (I leave off the credentials part because I don't use credentials for the embedded server):
#Configuration
#Profile("embedded")
#EnableConfigurationProperties({ LdapProperties.class })
public class EmbeddedLdapConfig {
private final Environment environment;
private final LdapProperties properties;
public EmbeddedLdapConfig(final Environment environment, final LdapProperties properties) {
this.environment = environment;
this.properties = properties;
}
#Bean
#DependsOn("directoryServer")
public ContextSource ldapContextSource() {
final LdapContextSource source = new LdapContextSource();
source.setUrls(this.properties.determineUrls(this.environment));
source.setBase(this.properties.getBase());
return source;
}
}
Now, I can leave the value of the base attribute in my #Entry the same for both the Active Directory server and the embedded UnboundID server and it works properly.

Spring Cloud Config Server lookup through Eureka using Java instead of bootstrap.yml

I'm attempting to build code into our base pom which autoconfigures Spring Cloud Config server lookup through Eureka. We're doing this to avoid templating .yml properties for developers building microservices. For example, we want to java config all behavior triggered from these properties:
spring:
application:
name: MyMicroservice
cloud:
config:
enabled: true
server:
prefix: /diagnostics/admin/config
failFast: true
discovery:
enabled: true
serviceId: echo
management:
context-path: /diagnostics/admin
eureka:
password: password
client:
serviceUrl:
defaultZone: http://user:${eureka.password}#localhost:8761/eureka/
instance:
leaseRenewalIntervalInSeconds: 10
statusPageUrlPath: /diagnostics/admin/info
healthCheckUrlPath: /diagnostics/admin/health
After much experimenting, the following approach mostly works except for the Eureka-discovered config server (resulting in no overridden config properties):
#Order(-1)
public class AdditionalBootstrapPropertySourceLocator implements PropertySourceLocator {
#Override
public PropertySource<?> locate(Environment environment) {
Map<String, Object> theBootstrapYmlConfig = new HashMap<>();
theBootstrapYmlConfig.put("spring.cloud.config.enabled", new Boolean(true));
theBootstrapYmlConfig.put("spring.cloud.config.server.prefix", "/diagnostics/admin/config");
theBootstrapYmlConfig.put("spring.cloud.config.failFast", new Boolean(true));
theBootstrapYmlConfig.put("spring.cloud.config.discovery.enabled", new Boolean(true));
theBootstrapYmlConfig.put("spring.cloud.config.discovery.serviceId", "echo");
theBootstrapYmlConfig.put("management.context-path", "/diagnostics/admin");
theBootstrapYmlConfig.put("eureka.client.serviceUrl.defaultZone", "http://user:password#localhost:8761/eureka/");
theBootstrapYmlConfig.put("eureka.instance.leaseRenewalIntervalInSeconds", new Integer(10));
theBootstrapYmlConfig.put("eureka.instance.statusPageUrlPath", "/diagnostics/admin/info");
theBootstrapYmlConfig.put("eureka.instance.healthCheckUrlPath", "/diagnostics/admin/health");
return new MapPropertySource("myExtraBootstrap", theBootstrapYmlConfig);
}
}
And I seem to need this Bean as well:
#ConditionalOnWebApplication
#Configuration
#Import(EurekaClientAutoConfiguration.class)
public class WorkfrontDiscoveryClientConfigServiceBootstrapConfiguration {
#Bean
#ConditionalOnClass({ DiscoveryClient.class, ConfigServicePropertySourceLocator.class })
#ConditionalOnMissingBean
DiscoveryClientConfigServiceBootstrapConfiguration discoveryClientConfigServiceBootstrapConfiguration() {
DiscoveryClientConfigServiceBootstrapConfiguration discoveryClientConfigServiceBootstrapConfiguration =
new DiscoveryClientConfigServiceBootstrapConfiguration();
return discoveryClientConfigServiceBootstrapConfiguration;
}
}
Finally, I put both into spring.factories to ensure they are constructed. The problem is that the PropertySourceLocator is never used to construct the call within ConfigServicePropertySourceLocator to retrieve the properties. No matter what I do, I cant seem to match the behaviors that specifying the properties within bootstrap.yml would produce.
Edit 4 days later
The key factor (and restriction) here is the ability to look up the config server through Eureka. In the current spring cloud release (1.0.2), the property source is retrieved and constructed too early in the spring initialization cycle for the config-lookup-through-eureka java property source config I have above. Plus if the Eureka server is slow or not available at bootstrap startup time, the Config server property source is never reconstructed when Eureka finally comes up. This in my mind is a bug.
I solved this all by eliminating the concept of looking up the config server through Eureka, and requiring this minimum config in bootstrap.yml:
spring:
application:
name: MyMicroservice
cloud:
config:
uri: http://localhost:8888/diagnostics/admin/config
eureka:
client:
serviceUrl:
defaultZone: http://user:password#localhost:8761/eureka/
and then setting the rest in the java AdditionalBootstrapPropertySourceLocator
Edit 30 days later
Java-configing bootstrap properties continues to be a challenge. I'm doing this because I'm developing a framework without templating or code generation (the premise of spring boot). I've added spring-retry to the mix and client-to-config gets retried but re-registration to Eureka does not. This is why Eureka-first had to be abandoned for me. I'd put my vote in for integrating spring-retry into the Eureka registration process so I can go back to Eureka-first for my framework. Still on Spring Cloud 1.0.2.
Edit 100 days later
Update for where we ended up. Continuing along our mantra of avoiding property templating, enforcing policies and practices within code .. and continuing without a Eureka-first concept, we abandoned PropertySourceLocator and simply used a SpringApplicationRunListener as follows:
public class OurFrameworkProperties implements SpringApplicationRunListener {
:
public void started() {
if (TestCaseUtils.isRunningFromTestCase()) {
System.setProperty("spring.cloud.config.failFast", "false");
System.setProperty("spring.cloud.config.enabled", "false");
System.setProperty("eureka.client.enabled", "false");
} else {
// set production values same way
}
}
}
A caution that this started() actually gets called twice inside the spring code (once not passing any program arguments btw) everytime your Spring application runs or gets an Actuator refresh().
If your PropertySourceLocator is listed inspring.factories (I assume as a BootstrapConfiguration) then it needs to be a #Component (or maybe even a #Configuration).
you have to set this properties in the boostrap.properties
eureka.instance.metadataMap.configPath: /your-app-name
and comment this one
#spring.cloud.config.uri=http://localhost:8888/
and obviously it must be also this
eureka.client.serviceUrl.defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
eureka.client.instance.preferIpAddress: true
according with the documentation
https://cloud.spring.io/spring-cloud-config/multi/multi__spring_cloud_config_client.html#discovery-first-bootstrap

Categories

Resources