It is my understanding that when you use Spring Cloud's RefreshScope annotation, a Proxy to the data is injected, and the proxy is automatically updated if the backing information is changed. Unfortunately, I need to find a way to be alerted when that refresh occurs, so that my code can re-read the data from the refresh-scoped bean.
Simple example: A scheduled task whose schedule is stored in Cloud Config. Unless you wait until the next execution of the task (which could take a while) or regularly poll the configuration (which seems wasteful), there's no way to know if the configuration has changed.
EnvironmentChangeEvent is fired when there's a change in Environment. In terms of Spring Cloud Config it means it's triggered when /env actuator endpoint is called.
RefreshScopeRefreshedEvent is fired when refresh of #RefreshScope beans has been initiated, e.g. /refresh actuator endpoint is called.
That means that you need to register ApplicationListener<RefreshScopeRefreshedEvent> like that:
#Configuration
public class AppConfig {
#EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh(RefreshScopeRefreshedEvent event) {
// Your code goes here...
}
}
When the refresh occurs EnvironmentChangeEvent would be raised in your config client, as the documentation states:
The application will listen for an EnvironmentChangedEvent and react
to the change in a couple of standard ways (additional
ApplicationListeners can be added as #Beans by the user in the normal
way).
So, you can define your event listener for this event:
public class YourEventListener implements ApplicationListener<EnvironmentChangeEvent> {
#Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
// do stuff
}
}
I think an approach can be to annotate with #RefreshScope all your bean that have properties externalized by the configuration and annotated within #Value ( "${your.prop.key}" ) annotation.
These properties are updated when they changed on configuration.
More specifically, after the refresh of properties and application context under scope RefreshScope, an event RefreshScopeRefreshedEvent is triggered. You can have a listener for this given the understanding that the properties has finished updates (you can be sure to capture updated values only).
Related
We have converted an existing web app over to Spring Boot.
All I want to to do is be able to use #Profile("production"). We store that in myProps.properties in the resources folder.
I can access the .properties file all over the app just fine using #Value, but it seems in order to get the #Profile to work, you have to set the active profiles before the ServletContext is set/final which seems to happen during the main() method.
I've tried dozens of things and failed. I've found you can't set a static field with #Value in the main application class. I've tried making a class that implements WebApplicationInitializer like https://www.baeldung.com/spring-profiles but my onStartup() method that I override doesn't ever get run.. I put in a break point and it never gets hit. The only way I can get a break point to hit is if I #Autowire the servletContext on that method, but then the context is already set and cannot be altered.
I've tried AnnotationConfigWebApplicationContext, I've tried ServletContainerInitializer, I've tried ConfigurableEnvironment, etc. I feel like I'm going in circles.
I feel like there is a big piece I'm missing here in order to do things the "Spring way". Can anyone offer Java annotation-configured way for me to get a property and set the active profiles for using later in the product? Thanks.
Why do you need it before main? Why don't you just create multiple properties files and define the profile in the top-level property file?
(parent) application.properties
spring.profiles.active = development-application.properties
(profile) development-application.properties
my.value = foo
Based on your comments, I suggest something like the following as a solution:
Define your tasks as beans with #Components and annotate them with #Profile to divide them up by the environment you want them to be used in (I prefer setting profiles as environment variables).
#Component
#Profile("production")
public class TaskA implements Task {
public void doWork(){}
}
#Component
#Profile("staging")
public class TaskB implements Task {
public void doWork(){}
}
Marking these classes with #Component means Spring will manage them and they can be injected, perhaps into a #Component that executes the tasks:
#Component
public class TaskDoer {
private List<Task> tasksToDo;
#Inject
public TaskDoer(List<Task> tasksToDo) {
this.tasksToDo = tasksToDo;
}
}
When deploying your app, set the profile to use in the environment variables in whatever way is appropriate for your setup:
SPRING_PROFILES_ACTIVE = production, someOtherProfile
Now, when the application starts, Spring will see that the active profiles are "production" and "someOtherProfile" (multiple profiles can be set) and will not load any beans with an #Profile that aren't for that profile.
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'm using spring boot. I was new to spring and started a spring project. So I didn't know about pre defined repositories (JPA, CRUD) which can be easily implemented. In case, I wanted to save a bulk data, so I use for loop and save one by one, Its taking more time. So I tried to use #Async. But it doesn't also work, is my concept wrong?
#Async has two limitation
it must be applied to public methods only
self-invocation – calling the async method from within the same class won’t work
1) Controller
for(i=0;i < array.length();i++){
// Other codes
gaugeCategoryService.saveOrUpdate(getEditCategory);
}
2) Dao implementation
#Repository
public class GaugeCategoryDaoImpl implements GaugeCategoryDao {
// Other codings
#Async
#Override
public void saveOrUpdate(GaugeCategory GaugeCategory) {
sessionFactory.getCurrentSession().saveOrUpdate(GaugeCategory);
}
}
After removing #Async , it working normally. But with that annotation it doesn't work. Is there any alternative method for time consuming? Thanks in advance.
the #Async annotation creates a thread for every time you call that method. but you need to enable it in your class using this annotation #EnableAsync
You also need to configure the asyncExecutor Bean.
You can find more details here : https://spring.io/guides/gs/async-method/
In my opinion, there are several issues with your code:
You overwrite the saveOrUpdate() method without any need to do so. A simple call to "super()" should have been enough to make #Async work.
I guess that you somewhere (within your controller class?) declare a transactional context. That one usually applies to the current thread. By using #Async, you might leave this transaction context as (because of the async DAO execution), the main thread may already be finished when saveOrUpdate() is called. And even though I currently don't know it exactly, there is a good change that the declared transaction is only valid for the current thread.
One possble fix: create an additional component like AsyncGaugeCategoryService or so like this:
#Component
public class AsyncGaugeCategoryService {
private final GaugeCategoryDao gaugeCategoryDao;
#Autowired
public AsyncGaugeCategoryService(GaugeCategoryDao gaugeCategoryDao) {
this.gaugeCategoryDao = gaugeCategoryDao;
}
#Async
#Transactional
public void saveOrUpdate(GaugeCategory gaugeCategory) {
gaugeCategoryDao.saveOrUpdate(gaugeCategory);
}
}
Then inject the service instead of the DAO into your controller class. This way, you don't need to overwrite any methods, and you should have a valid transactional context within your async thread.
But be warned that your execution flow won't give you any hint if something goes wrong while storing into the database. You'll have to check the log files to detect any problems.
I have a method which is scheduled to run after every 20 minutes. I simply used #Scheduled annotation in Spring boot , However I need a scheduler which takes delay time at runtime. E.g. If I want to be able to change the delay time/frequency of method execution at runtime without stopping the application i.e. change the frequency in DB and Code should adapt it.
#Scheduled(initialDelay=15*60*1000, fixedRate=20*60*1000)
public void MyMethod() {
// Code to repeat after every 20 minutes
}
}
The fixed rate in the code should be variable and taken at the runtime. Is it possible to achieve it ?
You can do it with refresh the spring context after change the fixed rate in the properties file, or using spring cloud config, but this will raise some issues - refresh() should destroy all beans currently living in the context (singletons etc) and recreate them, so any bootstrapping that might happen will happen again.
Here is an reference:
Is spring application context reloading via ConfigurableApplicationContext refresh() considered bad-practice
How can I run a job configured using Spring-Batch right after application startup?
Currently I'm specifying an exact time using cron job, but that requires to change the cron every time I restart the application:
#JobRegistry, #Joblauncher and a Job.
I execute the job as follows:
#Scheduled(cron = "${my.cron}")
public void launch() {
launcher.run(job, params);
}
Checking aroud Spring code I have found SmartLifecycle
An extension of the Lifecycle interface for those objects that require
to be started upon ApplicationContext refresh and/or shutdown in a
particular order. The isAutoStartup() return value indicates whether
this object should be started at the time of a context refresh.
Try creating a custom bean implementing SmartLifecycle and setting autoStartup; when this custom bean start method is invoked launch your job.
A few options that I can think of on the places to put your startup logic:
.1. In a bean #PostConstruct annotated method, reference is here - http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-postconstruct-and-predestroy-annotations
.2. By implementing an ApplicationListener, specifically for either ContextStartedEvent or ContextRefreshedEvent. Reference here - http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#context-functionality-events