I'm having trouble setting up a health indicator for my Spring project. Since version 1.4.0 Spring comes with its own CouchbaseHealthIndicator (see http://docs.spring.io/spring-boot/docs/1.4.1.RELEASE/api/org/springframework/boot/actuate/health/CouchbaseHealthIndicator.html) but I cannot make it work. Other health indicators are working fine (disk space in this project, mail and db in other projects).
#ConditionalOnClass({ CouchbaseOperations.class, Bucket.class })
#ConditionalOnBean(CouchbaseOperations.class)
#ConditionalOnEnabledHealthIndicator("couchbase")
This are the conditions for the health indicator being initialized. The mentioned classes are on the classpath.
After the project is started, I let it print the beans it has in its application context - there is a bean 'CouchbaseTemplate', which implements the required CouchbaseOperations.
I also manually enabled the couchbase health check like this
management.health.couchbase.enabled = true
Still, it keeps checking disc space, no check for couchbase.
You can find my project on GitHub: https://github.com/Age15990/CouchbaseHI
Please feel free to download and try. If you have come across this problem before or you have an idea how to solve it I would be happy to read your answer.
Thanks in advance!
There seems to be a defect in spring-boot-starter-actuator. I filed a ticket for that here.
A work-around could be to provide the configuration yourself:
#Configuration
#ConditionalOnClass({ CouchbaseOperations.class, Bucket.class })
//#ConditionalOnBean(CouchbaseOperations.class)
#ConditionalOnEnabledHealthIndicator("couchbase")
public class CouchbaseHealthConfig extends CompositeHealthIndicatorConfiguration<CouchbaseHealthIndicator, CouchbaseOperations>
{
private final Map<String, CouchbaseOperations> couchbaseOperations;
public CouchbaseHealthConfig(Map<String, CouchbaseOperations> couchbaseOperations)
{
this.couchbaseOperations = couchbaseOperations;
}
#Bean
#ConditionalOnMissingBean(name = "couchbaseHealthIndicator")
public HealthIndicator couchbaseHealthIndicator()
{
return createHealthIndicator(this.couchbaseOperations);
}
}
Explanation:
The auto-configuration of the couchbase health indicator seems to be broken. It works for me when I comment-out #ConditionalOnBean(CouchbaseOperations.class) from the original configuration in HealthIndicatorAutoConfiguration.class.
This means CouchbaseOperations.class was not initialized properly before the auto-configuration took place.
Related
I have a Spring project, and I need to configure Flyway.
Using the default FlywayAutoConfiguration, Flyway migration is immediatly executed before everything else (caching, PostConstruct annotations, services). This is the behavior I expected (in term or startup workflow)
Unfortunatly, I need to override the default FlywayAutoConfiguration because I use a custom Flyway implementation, but that's not the my main problem here, my issue is really related to Spring Priority for configuration and initialization sequence.
So do use my own flyway, I first copied FlywayAutoConfiguration to my maven module, name it CustomFlywayAutoConfiguration and just adapt imports. I also change the property "spring.flyway.enabled" to false and create another one "spring.flywaycustom.enabled" to be able to activate mine and not the default one.
Doing that fully change the sequence of startup. Now, flyway is executed at the end of the startup sequence (after caching and other #PostConstruct that are in my project)
The following bean defined in CustomFlywayAutoConfiguration is now created onyl at the end of the startup sequence. With the default FlywayAutoConfiguration , was well created on the beginning.
#Bean
#ConditionalOnMissingBean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway,
ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
return new FlywayMigrationInitializer(flyway, migrationStrategy.getIfAvailable());
}
I tryied to play a lot with ordering (HIGHEST_PRECEDENCE, and LOWEST_PRECEDENCE)
#AutoConfigureOrder on configuration class
#Order on components
Try to Autowire FlywayMigrationInitializer to force the bean initialisation earlier
It's doesn't change anything, looks like Spring ignore #Order and #AutoConfigureOrder
Any idea why when Configuration is inside spring-boot-autoconfigure dependencies, it started as first priority, and when the same Configuration code is inside my project, I don't have the same ordering?
Thank you so much for your help.
Thanks to your answer, Focusin my attention FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor help me to solve the issue.
#Configuration(proxyBeanMethods = false)
#AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
#EnableConfigurationProperties({ DataSourceProperties.class, FlywayProperties.class })
#Import({ FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor.class }) // Very important to force flyway before
public class CustomFlywayConfiguration {
#Bean
public Flyway flyway(FlywayProperties properties, DataSourceProperties dataSourceProperties,
ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
#FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
ObjectProvider<JavaMigration> javaMigrations, ObjectProvider<Callback> callbacks) {
FluentConfiguration configuration = new FluentConfiguration(resourceLoader.getClassLoader());
DataSource dataSourceToMigrate = configureDataSource(configuration, properties, dataSourceProperties, flywayDataSource.getIfAvailable(), dataSource.getIfUnique());
checkLocationExists(dataSourceToMigrate, properties, resourceLoader);
configureProperties(configuration, properties);
List<Callback> orderedCallbacks = callbacks.orderedStream().collect(Collectors.toList());
configureCallbacks(configuration, orderedCallbacks);
fluentConfigurationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(configuration));
configureFlywayCallbacks(configuration, orderedCallbacks);
List<JavaMigration> migrations = javaMigrations.stream().collect(Collectors.toList());
configureJavaMigrations(configuration, migrations);
return new CustomFlyway(configuration);
}
/**
* FlywayAutoConfiguration.FlywayConfiguration is conditioned to the missing flyway bean. #Import annotation are not executed in this case.
*
* So if we declare our own Flyway bean, we also need to create this bean to trigger the flyway migration.
*
* The main issue is now that bean is create after the #PostConstruct init method in MyInitComponent.
*
*/
#Bean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway,
ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
return new FlywayMigrationInitializer(flyway, migrationStrategy.getIfAvailable());
}
/**
* Thanks to that, it's now working, because make sure it's required before to create EntityManager
*/
// #ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
// #ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
static class FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor() {
super(FlywayMigrationInitializer.class);
}
}
...
So that confirm we can't easily override the Flyway bean without copy some logic from FlywayAutoConfiguration.
I created a small project to reproduce the bug, with the solution I found.
https://github.com/w3blogfr/flyway-issue-demo
I don't know if it's necessary to fix it or not in spring auto-configuration project?
I checked the history to find back the commit
https://github.com/spring-projects/spring-boot/commit/795303d6676c163af899e87364846d9763055cf8
And the ticket was this one.
https://github.com/spring-projects/spring-boot/issues/18382
The change looks technical and probably he missed that was an #ConditionalOnClass
None of the annotations that you've described have an impact on runtime semantics:
#AutoConfigureOrder works only on auto-configurations (so if you've copied the auto-configuration as a user config, it won't even be considered). This is used to order how auto-configurations are parsed (typical use case: make sure an auto-config contribute a bean definition of type X before another one check if a bean X is available).
#Order orders components of the same type. Doesn't have any effect as to "when" something occurs
Forcing initialisation using an injection point is a good idea but it'll only work if the component you're injecting it into is initialised itself at the right time.
The auto-configuration has a bunch of post-processors that create links between various components that use the DataSource and the FlywayMigrationInitializer. For instance, FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor makes sure that FlywayMigrationIntializer is a dependency of the entity manager so that the database is migrated before the EntityManager is made available to other components. That link is what's making sure Flyway is executed at the right time. I don't know why that's not working with your copy, a sample we can run ourselves shared on GitHub could help us figure out.
With all that said, please don't copy an auto-configuration in your own project. I'd advise to describe your use case in more details and open an issue so that we consider improving the auto-configuration.
Trying to register hystrix concurrency strategy fails after migrating to Spring-boot-2.0 with spring actuator enabled with java.lang.IllegalStateException stating that "Another strategy was already registered" . This is although I have not used registerConcurrencyStrategy anywhere else in my code.
I'd like to register concurrency strategy to carry-forward Log MDC context so that I'm able to log both within and outside the Hystrix wrapped method equally well, which includes thread-locals. And this used to work perfectly in spring-boot-1.5
After having migrated to spring-boot 2.0 (from 1.5), the HystrixPlugins.getInstance().registerConcurrencyStrategy(this); fails with IllegalStateException
As per https://github.com/Netflix/Hystrix/issues/1057, this issue can come if either (a) Any other code flow would have registered its own or default ConcurrencyStrategy before this is invoked (b) any call would have come via Hystrix before this is invoked
Since the above invocation is within the constructor of a class which is annotated with #Component, this should get invoked ideally before any method call happens (except initialization of other beans, including their constructors).
We even tried moving this code inside the SpringBoot Application Class's main method before invoking the SpringApplication.run(MyApplication.class, args); but that also didn't work
#Component
public class ContextCopyHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private static final String EVENT = "HystrixConcurrencyStrategy";
private static final String ACTION = "ContextCopy";
public ContextCopyHystrixConcurrencyStrategy(Logger logger, LoggerUtil defaultLoggerUtil) {
try {
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
} catch (IllegalStateException e) {
defaultLoggerUtil.logEvents(logger, Level.WARN, e.getMessage(), EVENT, ACTION, "", "Race condition! Could not register strategy. HystrixConcurrencyStrategy is already initialized.");
}
Expected: My registering should have happened before any other code and registering should have been successful
Actual: My registering fails with IllegalStateException
How do I make sure that my registering happens well before any other registering (which is not present in my code, but may be inside some of the libraries that I may be transitively using)
By default, Spring boot 2 accuator registers Hystrix Metric Binder beans which reset already set HystrixConcurrencyStrategy and sets HystrixConcurrencyStrategyDefault.
So, disabling that bean by
management.metrics.binders.hystrix.enabled=false
would help not resetting your custom HystrixConcurrencyStrategy
We took a close look at my maven .m2 directory classes and looked for registerConcurrencyStrategy inside all the classes in all the jars. And we found that
io.micrometer.core.instrument.binder.hystrix
was internally registering the HystrixConcurrencyStrategy with the default one.
And upon further research we found that setting the following property in application.properties:
management.metrics.binders.hystrix.enabled=false disabled the Hystrix Metrics Binder (I'm actually not sure what it does though!) and then things worked
I was using spring-boot-starter-parent v2.5.3 with Spring Cloud version 2020.0.3.
I had to manually include version for spring-cloud-starter-netflix-hystrix. I was getting "Another strategy was already registered" exception when starting my microservice. I included the
management.metrics.binders.hystrix.enabled=false
in the application.properties file and this issue got resolved.
I am running a Spring Boot 2 Application and added the actuator spring boot starter dependency. I enabled all web endpoints and then called:
http://localhost:8080/actuator/metrics
result is:
{
"names": ["jdbc.connections.active",
"jdbc.connections.max",
"jdbc.connections.min",
"hikaricp.connections.idle",
"hikaricp.connections.pending",
"hikaricp.connections",
"hikaricp.connections.active",
"hikaricp.connections.creation",
"hikaricp.connections.max",
"hikaricp.connections.min",
"hikaricp.connections.usage",
"hikaricp.connections.timeout",
"hikaricp.connections.acquire"]
}
But I am missing all the JVM stats and other built-in metrics. What am I missing here? Everything I read said that these metrics should be available at all times.
Thanks for any hints.
I want to share the findings with you. The problem was that a 3rd party library (Shiro) and my configuration for it. The bean loading of micrometer got mixed up which resulted in a too late initialisation of a needed PostProcessingBean which configures the MicroMeterRegistry (in my case the PrometheusMeterRegistry).
I dont know if its wise to do the configuration of the Registries via a different Bean (PostProcessor) which can lead to situations i had... the Registries should configure themselves without relying on other Beans which might get constructed too late.
In case this ever happens to anybody else:
I had a similar issue (except it wasn't Graphite but Prometheus, and I was not using Shiro).
Basically I only had Hikari and HTTP metrics, nothing else (no JVM metrics like GC).
I banged my head on several walls before finding out the root cause: there was a Hikari auto configure post processor in Spring Boot Autoconfigure that eagerly retrieved a MeterRegistry, so all Metric beans didn't have time to initialize before.
And to my surprise, when looking at this code in Github I didn't find it. So I bumped my spring-boot-starter-parent version from 2.0.4.RELEASE to 2.1.0.RELEASE and now everything works fine. I correctly get all the metrics.
As I expected, this problem is caused by the loading order of the beans.
I used Shiro in the project.
Shiro's verification method used MyBatis to read data from the database.
I used #Autowired for MyBatis' Mapper file, which caused the Actuator metrics related beans to not be assembled by SpringBoot (I don't know what the specific reason is).
So i disabled the automatic assembly of the Mapper file by manual assembly.
The code is as follows:
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String beanId) throws BeansException {
return applicationContext.getBean(beanId);
}
}
Then
StoreMapper userMapper = (UserMapper) SpringContextUtil.getBean("userMapper");
UserModel userModel = userMapper.findUserByName(name);
The problem can be solved for the time being. This is just a stopgap measure, but at the moment I have no better way.
I can not found process_update_seconds in /actuator/prometheus, so I have spent some time to solve my problem.
My solution:
Rewrite HikariDataSourceMetricsPostProcessor and MeterRegistryPostProcessor;
The ordered of HikariDataSourceMetricsPostProcessor is Ordered.HIGHEST_PRECEDENCE + 1;
package org.springframework.boot.actuate.autoconfigure.metrics.jdbc;
...
class HikariDataSourceMetricsPostProcessor implements BeanPostProcessor, Ordered {
...
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}
The ordered of MeterRegistryPostProcessor is Ordered.HIGHEST_PRECEDENCE;
package org.springframework.boot.actuate.autoconfigure.metrics;
...
import org.springframework.core.Ordered;
class MeterRegistryPostProcessor implements BeanPostProcessor, Ordered {
...
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
In my case I have used shiro and using jpa to save user session id. I found the order of MeterRegistryPostProcessor and HikariDataSourceMetricsPostProcessor cause the problem. MeterRegistry did not bind the metirc because of the loading order.
Maybe my solution will help you to solve the problem.
I have a working sample with Spring Boot, Micrometer, and Graphite and confirmed the out-of-the-box MeterBinders are working as follows:
{
"names" : [ "jvm.memory.max", "process.files.max", "jvm.gc.memory.promoted", "tomcat.cache.hit", "system.load.average.1m", "tomcat.cache.access", "jvm.memory.used", "jvm.gc.max.data.size", "jvm.gc.pause", "jvm.memory.committed", "system.cpu.count", "logback.events", "tomcat.global.sent", "jvm.buffer.memory.used", "tomcat.sessions.created", "jvm.threads.daemon", "system.cpu.usage", "jvm.gc.memory.allocated", "tomcat.global.request.max", "tomcat.global.request", "tomcat.sessions.expired", "jvm.threads.live", "jvm.threads.peak", "tomcat.global.received", "process.uptime", "tomcat.sessions.rejected", "process.cpu.usage", "tomcat.threads.config.max", "jvm.classes.loaded", "jvm.classes.unloaded", "tomcat.global.error", "tomcat.sessions.active.current", "tomcat.sessions.alive.max", "jvm.gc.live.data.size", "tomcat.servlet.request.max", "tomcat.threads.current", "tomcat.servlet.request", "process.files.open", "jvm.buffer.count", "jvm.buffer.total.capacity", "tomcat.sessions.active.max", "tomcat.threads.busy", "my.counter", "process.start.time", "tomcat.servlet.error" ]
}
Note that the sample on the graphite branch, not the master branch.
If you could break the sample in the way you're seeing, I can take another look.
I've got a project (this will be used as a dependency) which uses SpringBoot and which populates a POJO from a queues.properties file in the following way:
#Component
#PropertySource({"classpath:queues.properties"})
#ConfigurationProperties("queue")
public class QueuesConfig {
private String messagingBrokerXml;
private String messagingBrokerJolokia;
private String messagingBrokerApi;
private List<QueuesConfig.QueueModel> queues = new ArrayList();
public QueuesConfig() {
}
public String getMessagingBrokerXml() {
return this.messagingBrokerXml;
}
...
By dragging this dependency in a parent SpringBoot project which has a "queues.properties" file on its classpath, QueuesConfig object gets populated with the right values.
I am currently trying to achieve the same behaviour by using this dependency in a Plain Spring project. I can confirm that the PropertySource annotation gets "executed" and that the queues.properties file is part of the StandardServletEnvironment (being an entry in the propertySourceList).
The thing is that the "ConfigurationPropertiesBindingPostProcessor" bean is not being registered (not part of the singletonObjects) and therefore the code which is supposed to populate the POJO is not being executed.
Is there any workaround for this?
Many thanks !
Try to add this in your parent Spring #Configuration class:
#EnableConfigurationProperties(QueuesConfig.class)
For more details read this section of Spring Boot documentation. It is few screens of text, but it worth it ;-)
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
Probably it worked well in your test app because you used some hi-level magic with #SpringBootApplication :)
I use spring-boot-starter-data-solr and would like to make use of the schmea cration support of Spring Data Solr, as stated in the documentation:
Automatic schema population will inspect your domain types whenever the applications context is refreshed and populate new fields to your index based on the properties configuration. This requires solr to run in Schemaless Mode.
However, I am not able to achieve this. As far as I can see, the Spring Boot starter does not enable the schemaCreationSupport flag on the #EnableSolrRepositories annotation. So what I tried is the following:
#SpringBootApplication
#EnableSolrRepositories(schemaCreationSupport = true)
public class MyApplication {
#Bean
public SolrOperations solrTemplate(SolrClient solr) {
return new SolrTemplate(solr);
}
}
But looking in Wireshark I cannot see any calls to the Solr Schema API when saving new entities through the repository.
Is this intended to work, or what am I missing? I am using Solr 6.2.0 with Spring Boot 1.4.1.
I've run into the same problem. After some debugging, I've found the root cause why the schema creation (or update) is not happening at all:
By using the #EnableSolrRepositories annotation, an Spring extension will add a factory-bean to the context that creates the SolrTemplate that is used in the repositories. This template initialises a SolrPersistentEntitySchemaCreator, which should do the creation/update.
public void afterPropertiesSet() {
if (this.mappingContext == null) {
this.mappingContext = new SimpleSolrMappingContext(
new SolrPersistentEntitySchemaCreator(this.solrClientFactory)
.enable(this.schemaCreationFeatures));
}
// ...
}
Problem is that the flag schemaCreationFeatures (which enables the creator) is set after the factory calls the afterPropertiesSet(), so it's impossible for the creator to do it's work.
I'll create an issue in the spring-data-solr issue tracker. Don't see any workaround right now, other either having a custom fork/build of spring-data or extend a bunch of spring-classes and trying to get the flag set before by using (but doubt of this can be done).