Spring CommandLineRunner with actuator [duplicate] - java

This is not a web project, but I still add spring-boot-starter-actuator in pom, and specify management.port=8081, then in last of run method of CommandLineRunner , have below code,
System.in.read();
when started this project, it will hold on. Then I will to access http://localhost:8081/configprops, but show below message:
This webpage is not available
So how could access actuator endpoints in non-web application?
BTW why I want to do so, because when started my project it can't load yml data successfully and caused NPE. So I want to access /configprops to check it.
My yml like this:
spring:
profiles.active: default
---
spring:
profiles: default
user:
foo:
aaa: aaa#foo.com
---
spring:
profiles: test
user:
foo:
aaa: aaa#foo.com
bbb: bbb#foo.com
#ConfigurationProperties(prefix="user", locations="user.yml")
public class UserConfig {
private HashMap<String, String> foo;
}

In a non web application you can access the Actuator endpoints with JMX. Use JConsole or JVisualVM to read the MBeans. See the Spring Boot documentation for more details: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-jmx

Thanks #dunni in jconsole MBeans tab you could see
when click getData I got a list , one line told me
userConfig={prefix=user, properties={foo=null}}

Related

Use Spring Boot Cloud Config to change application.properties in classpath

I have to change my custom defined spring properties (defined via #ConfigurationProperties beans) during runtime of my Spring Boot application.
Is there any elegant way of doing this using Spring Cloud Config?
I don't want to use an external application.properties in a git repository (as the spring boot application gets shipped to customers and I dont' want to create a git repository for everyone of them).
I just want to access and change the local application.properties (the one in the classpath, located in src/main/resources) file in my Spring container or (if thats not possible) in the Spring Cloud Config Server, which I could embed into my Spring Boot app. Is that possible somehow?
BTW: The goal is to create a visual editor for the customers, so that they can change the application.properties during runtime in their spring boot app.
Spring Boot supports profile based application configuration. Just add application-<profile>.properties file. Then just when running the application select a profile depending on the environment making use of spring.profiles.active.
-Dspring.profiles.active=dev
This will run the application with application-dev.properties file (overriding the default application.properties, i.e you can just leave the common stuff in the default file and change the rest depending on the env)
On a side note, having a repo for configuration is not a must. You could just place them in the class path and give a search-location.
spring:
application:
name: config-server
profiles:
active: native
cloud:
config:
server:
native:
search-locations: classpath:configs/
It actually is possible and in the end quite easy to achieve. It just took me a whole day to get all the information together. Maybe this helps someone:
You basically just need Spring Actuator, but for a certain endpoint, you also need the spring cloud dependency. (to make Post requests to the /env endpoint of Spring Actuator)
To alter your config at runtime, just add the following to your application.properties:
management.endpoints.web.exposure.include: env,refresh
management.endpoint.env.post.enabled: true //this property is only available when spring cloud is added as dependency to your project
If you (like me) don't need the feature of an externalized config, then you also have to add the following (otherwise, your Spring app will not start and throw an error that some config is missing)
spring.cloud.config.enabled: false
Now, if you send a POST request to /actuator/env endpoint with an object in the HTTP body in the form of {"name":"...", "value":"..."} (name is the name of a config property), then your config gets changed. To check that, you can do a GET request to /actuator/env/[name_of_config_property] and see that your config property has changed. No need to restart your app.
Don't forget to secure the /actuator endpoint in your SecurityConfig if you use a custom one.
It seems to me that you neither need the #RefreshScope annotation at your config classes nor the /actuator/refresh endpoint to "apply" the config changes.
Maybe what your looking for could be achieved with Spring cloud config and spring cloud bus. It's explained here: https://cloud.spring.io/spring-cloud-config/reference/html/#_push_notifications_and_spring_cloud_bus
In summary, any change on configuration sent an event to the spring cloud bus and you can then reload app context or configuration with new properties.

Is there a way to see the property artifacts for a particular profile?

Is it possible to see the final property artifacts without launching spring boot app?
For example, suppose the following application.yml exists:
hoge: hoge-value
---
spring.config.activate.on-profile: local
bar: bar-local-value
---
spring.config.activate.on-profile: production
hoge: hoge-production-value
bar: bar-production-value
When using local/production profile, the following property artifacts can be created.
# local:
hoge: hoge-value
bar: var-local-value
# production
hoge: hoge-production-value
bar: var-production-value
I know that I can check it by launching the spring boot app in each profile and calling /actuator/env endpoint.
But I don't want to launch spring boot App with production profile on local PC.
(To be exact, the spring boot app cannot be started because the bean initialization fails due to Network ACL etc.)
I'd like to make sure that each profile is correct when I make major changes to application.yml. Is there a good way?

Adding a conditional external PropertySource in a spring boot application

I'm currently using an EnvironmentPostProcessor to add my external PropertySource, the code looks like this :
public class ExternalPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor
{
private static final String EXTERNAL_PROPERTY_SOURCE_NAME = "ExternalPropertySource";
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application)
{
if (environment.acceptsProfiles(Profiles.EXTERNAL_PROPERTY_SOURCE_ENABLED_PROFILE)) {
environment.getPropertySources()
.addLast(new ExternalPropertySource(EXTERNAL_PROPERTY_SOURCE_NAME, new ExternalSource()));
}
}
}
A spring.factories is also used to register this EnvironmentPostProcessor.
This code actually works if the profile is set in the vm environment variables, but if it is added in src/main/resources/application.yml, the profile doesn't seem to be injected yet in the environment and is not returned by the environment.getActiveProfiles(). I've tried using the interface Ordered with the lowest precedence, but it doesn't help.
To add a bit of context around what I'm trying to achieve, this class is in a small library that adds an external property source like a database. Then we can use it in some other spring boot applications. Something like Spring Cloud Config does.
I'd like a clean way to enable or disable this property source depending on the environment where the code runs. I don't mind using something else then profiles or even another way to inject the property source, I just want something clean that doesn't depend on several factors to work.
The main problem in my code right now is that I'm using spring boot's property sources to make my own property source configurable.
UPDATE : I used a Spring Cloud app to debug this, and was confusing the bootstrap context with the normal spring boot context. See my answer below.
Further investigation made me figure out the problem appeared only with a Spring Cloud application.
In fact the breakpoint I had in this code was triggered twice, once after the bootstrap context initialization and once after the spring boot context initialization. I was only debugging the profiles in the first occurence. At that point, only the bootstrap.yml properties are loaded and not the ones from the application.yml file. The second occurence obviously had the profiles from my application.yml file.
My code worked as expected with a vanilla Spring Boot application. As the documentation states :
The Environment has already been prepared with all the usual property
sources that Spring Boot loads by default.
I was confused by the behaviour of my app which seemed to be different from that statement, but it was Spring Cloud's bootstrap that was messing with my debugging.
Since I need a PropertySource that has the highest precedence, I need to add it in the post bootstrap initialization for Spring Cloud apps. I used an init flag on my EnvironmentPostProcessor so it doesn't get executed twice and used the bootstrap.yml on Spring Cloud apps to set the profile.
TL;DR :
With Spring Cloud, an EnvironmentPostProcessor gets called twice: once after the bootstrap init and once after the normal Spring Boot context init. If you need injected properties and are targeting the Spring Cloud's post bootstrap initialization, use the bootstrap.yml instead of application.yml.

Correct way to load values/properties from spring cloud consul

I have a spring boot application that has a lot of properties defined in a .yml file. I am moving away from having the properties in a file to having them in a cloud, so for this I am using the key/value feature in spring cloud consul.
I am able to retrieve a value for a given key from consul, but the only way I have seen documented and only way I have been able to do it is like so:
#Autowired
private Environment env;
#RequestMapping("/test")
String home() {
return env.getProperty("test.property");
}
This is fine and dandy, but I have some existing classes that are still set to load properties the old way:
#Value("${test.property}")
String testProperty;
After adding the dependency for spring cloud consul to my project, I get new errors saying that all these properties cannot be resolved. I assume it is because spring boot sees the consul dependency and is either ignoring the yml file or parts of it now.
Is there a way to get these properties to load from consul without having to change any code in the classes? Or will I need to change these classes to follow the example that uses the env object?
Also, any further insight into how spring cloud consul works with spring boot for this specific purpose would be greatly appreciated. There isn't a whole lot of documentation on it.
I was able to get this to work, but it was a little tricky. You need to have the spring cloud consul starter dependency added. I had just the spring cloud consul config dependency.
I had to add consul config properties to both my application.yml and bootstrap.yml, the properties in my boostrap just set the format and data-key:
spring:
cloud:
consul:
config:
format: YAML
data-key: data
This tells spring boot to load a yaml blob that is the value of key data in my application config cloud directory.
The configuration in my application.yml is the default they provide in the example. Once I added those configs and the #autoconfiguration tag to my service main application class, everything started working.

Settings in application.yml for spring.cloud.config aren't used when app is executing

I have a problem with spring cloud: my settings in application.yml for spring.cloud.config aren't used when app is executing. let me put more detail here.
I'd like to my services could get settings from a remote ConfigServer. I've created the ConfigServer as a spring boot app with annotation #EnableConfigServer.
After that i've created client app with next config file:
application:
name: mw
cloud:
config:
enabled: true
uri: http://172.17.42.1:8888
fail-fast: true
main class:
#EnableEurekaClient
#SpringBootApplication
public class MwApplication
and extra configuration into app:
#Configuration
#EnableJpaRepositories(basePackages = {"com.sample.repository"})
#EnableTransactionManagement
#EnableScheduling
public class AppConfiguration
also i have next dependencies:
spring-cloud-starter-eureka
spring-cloud-config-client
spring-boot-configuration-processor
spring-boot-starter-data-jpa
When i execute my client app, i've got this message: ConfigServicePropertySourceLocator : Could not locate PropertySource: I/O error on GET request for "http://localhost:8888/mw/default"
The app try to get data from default uri(localhost) instead of to use uri from my setting. I've looked at app in debug mode and saw org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration was creating ConfigClientProperties with default property and my settings from application.yml weren't used.
What am i doing wrong?
thanks.
You need to add the following to your application.yml file:
spring:
cloud:
config:
enabled: true
Per comment chain, you also need to add the properties to bootstrap.yml instead of application.yml . The reason is that the former is loaded before the latter in the spring startup cycle. Here is another SO post answered by user Michael Isvy explaining why, and copied below for posterity: What is the diference between putting a property on application.yml or bootstrap.yml in spring boot?
I have just asked the Spring Cloud guys and thought I should share the info I have here.
bootstrap.yml is loaded before application.yml.
It is typically used for the following:
when using Spring Cloud Config Server, you should specify spring.application.name and spring.cloud.config.server.git.uri inside bootstrap.yml
some encryption/decryption information
Technically, bootstrap.yml is loaded by a parent Spring ApplicationContext. That parent ApplicationContext is loaded before the one that uses application.yml.

Categories

Resources