Spring: How to start the logic after initializing the app - java

I have implemented this logic
#Component
public class SomeUpdater {
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
private ExecutorService executorService;
#PostConstruct
public void init(){
executorService = Executors.newSingleThreadExecutor();
executorService.execute(()->{
//some logic
applicationEventPublisher.publishEvent(new PurposeEvent(this, 5));
});
}
}
#Component
public class SomeClass {
#EventListener
public void update(PurposeEvent purposeEvent){
//update
}
}
But I can see from the logs that listener for update (PurposeEvent purposeEvent) is initialized later than the method applicationEventPublisher.publishEvent(new PurposeEvent(this, 5)); is called. How can I call logic after initialization? I tried using #EventListener for ContextRefreshedEvent, but as far as I understand, it can be called multiple times

You can you annotation #DependsOn which can force the Spring container to initialize one or more beans before the bean annotated with #DependsOn annotation.
Then you'll be sure that SomeClass will be initialized before SomeUpdater.
The long term solution would be:
Spring Boot startup hooks
Spring Event types, like ApplicationStartedEvent, ApplicationReadyEvent.
Useful links:
http://dolszewski.com/spring/running-code-on-spring-boot-startup/
http://javainfinite.com/spring-boot/springboot-applicationreadyevent-applicationfailedevent-contextrefreshedevent/

You can make use of spring events like below,
#EventListener(ApplicationReadyEvent.class)
public void init() {
final ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> {
//some logic
applicationEventPublisher.publishEvent(new PurposeEvent(this, 5));
});
}

Related

Right way of implementing a controllable scheduler

For my design I need a controllable schedular. Spring boot offers an #Scheduled annotation but that is more simplified and I do not have granular control.
So I wanted to implement my own scheduler manually.
This is the class I created:
#Slf4j
#Service
public class JobExecutor {
private static ScheduledExecutorService jobExecutor;
private static Environment env;
#Autowired
private JobExecutor(Environment env) {
JobExecutor.env = env;
}
public static ScheduledExecutorService INSTANCE() {
if (null == jobExecutor) {
synchronized (JobExecutor.class) {
if (null == jobExecutor) {
jobExecutor = Executors.newScheduledThreadPool(
Integer.parseInt(Objects.requireNonNull(env.getProperty("scheduler.jobs"))));
}
}
}
return jobExecutor;
}
}
With this approach I could simply call the static method to get a single instance.
Is this correct approach for a schedular? I need to start and stop and shutdown the jobs. I tried guava AbstractScheduledService but that does not seem to be working.
This is not the correct approach for creating a singleton, because double checked locking is broken. You're using Spring, so a) your JobExecutor will be a singleton anyway, and b) will only be created if it is needed. You might as well, therefore, create your executor instance in the constructor and get rid of those static methods.
Even better, you could create schedulers as named beans, and then inject them into classes where you want them:
#Configuration
public class ExecutorConfiguration {
#Bean
public ScheduledExecutorService jobExecutor(#Value("${scheduler.jobs}") jobs) {
return Executors.newScheduledThreadPool(jobs);
}
}
This says that whenever another component needs a ScheduledExecutorService, Spring should call this jobExecutor() method; Spring will automatically populate the jobs parameter from the scheduler.jobs property because of the #Value.
You can then inject your executor wherever you need it, for example with constructor injection (handily you're already using Lombok, so the amount of boilerplate is minimised):
#Service
#AllArgsConstructor
public class MyThingThatNeedsAScheduler {
private final ScheduledExecutorService jobExecutor;
// methods here...
}
You can also use setter or member injection, if you want.

How to run #PostConstruct non-blocking in Spring?

#PostConstruct
public void performStateChecks() {
throw new RuntimeException("test");
}
If I start a spring application with code above, it will prevent the application to start.
What I'm looking for is to execute a method directly after startup, but async. Means, it should not delay the startup, and it should not prevent the application to run even on failure.
How can I make the initialization async?
You can use EventListener instead of PostConstruct, it supports #Async:
#Service
public class MyService {
#Async
#EventListener(ApplicationStartedEvent.class)
public void performStateChecks() {
throw new RuntimeException("test");
}
}
Don't forget enable async support by #EnableAsync annotation
You can also use some other event, see inheritors of SpringApplicationEvent class
The easiest way I can see is by using EventListeners and async task executors.
Adding this code snippet would do the work:
#Component
public class AsyncStartupRunner {
#Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster =
new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return eventMulticaster;
}
#EventListener(ApplicationReadyEvent.class)
public void executeAfterStartup() {
throw new RuntimeException("Oops");
}
}
There are multiple ways this can be done.
First, simple one-liner solution is to create and start a new thread;
#PostConstruct
public void performStateChecks() {
new Thread(() -> { throw new RuntimeException("test"); }).start();
}
The thrown exception only interrupts the separate thread and doesn't block or prevent the application startup. This is useful if you are not interested in result or outcome of the task. Note this is not recommended as it starts separate thread outside spring managed context.
Second is to use executor service and submit a task to it. Spring provides a default ThreadPoolTaskExecutor which can be used to submit the tasks. This will allow you to have access to the future object of the task and do something with it later on;
private final ThreadPoolTaskExecutor executor; // inject via constructor
#PostConstruct
public void performStateChecks() {
Future<?> future = executor.submit(() -> {
throw new RuntimeException("test");
});
// do something with the future later on
}
If you have multiple such methods and requirements for various services/classes etc then create a new AsyncService class to do the actual work and annotate those methods with #Async. inject the AsyncService wherever you need via constructor and then call the required method;
#EnableAsync
#Component
public class AsyncService {
#Async
public void doActualTest() {
throw new RuntimeException("test");
}
}
Then use it like this;
private final AsyncService asyncService; // make sure to inject this via constructor
#PostConstruct
public void performStateChecks() {
asyncService.doActualTest();
}
You can remove #PostConstruct from your method and let that method be a normal method. You can then manualy invoke it when the ApplicatioinContext is already loaded and the application has already started.
#SpringBootApplication
public class ServiceLauncher {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplication(ServiceLauncher.class).run(args);
try {
context.getBean(YourBean.class).performStateChecks(); // <-- this will run only after context initialization finishes
} catch (Exception e) {
//catch any exception here so it does not go down
}
}
}
}

not able to publish custom event in spring before context load

I am trying to publish a custom event in Spring MVC, but is is not firing while context is loading, below are the code snippet,
The onConnectionOpened will be called after connecting to a server which is triggered after bean initialization using #PostConstruct
#Autowired
private ApplicationEventPublisher publisher;
public void onConnectionOpened(EventObject event) {
publisher.publishEvent(new StateEvent("ConnectionOpened", event));
}
I am using annotation in listener part as below
#EventListener
public void handleConnectionState(StateEvent event) {
System.out.println(event);
}
I am able to see events fired after the context is loaded or refreshed, is this expected that custom application events can be published after the context loaded or refreshed?.
I am using Spring 4.3.10
Thanks in advance
The #EventListener annotations are processed by the EventListenerMethodProcessor which will run as soon as all beans are instantiated and ready. As you are publishing an event from a #PostConstruct annotated method it might be that not everything is up and running at that moment and #EventListener based methods haven't been detected yet.
Instead what you can do is use the ApplicationListener interface to get the events and process them.
public class MyEventHandler implements ApplicationListener<StateEvent> {
public void onApplicationEvent(StateEvent event) {
System.out.println(event);
}
}
You should publish event after ContextRefreshedEvent occur, but if you wait ContextRefreshedEvent in #PostConstruct, it will make whole applicaiton hang up, so use #Async will solve this problem.
#EnableAsync
#SpringBootApplication
public class YourApplication
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
}
#Slf4j
#Service
public class PublishHelper {
private final ApplicationEventPublisher publisher;
private final CountDownLatch countDownLatch = new CountDownLatch(1);
#EventListener(classes = {ContextRefreshedEvent.class})
public void eventListen(ContextRefreshedEvent contextRefreshedEvent) {
log.info("publish helper is ready to publish");
countDownLatch.countDown();
}
public PublishHelper(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
#Async
#SneakyThrows
public void publishEvent(Object event) {
countDownLatch.await();
publisher.publishEvent(event);
}
}

Adding scheduler for #Scheduled annotation in spring without using xml annotations

I have several methods with the annotation #Scheduled. For each annotation or a group of them I want a different scheduler to be used. For example:
Group A has 3 methods with #Scheduled annotation which need to use Scheduler X.
Group B has 5 methods with #Scheduled annotation which need to use Scheduler Y.
From what I have read in Does spring #Scheduled annotated methods runs on different threads?, if the scheduler is not specified then only one of those methods will run at a time.
I know how this connection can be done using xml-based annotation. But is there a way that this can be done using Java-based annotation only?
It can be done using java config. But not using an annotation attributes.
You could have a look at the Spring API doc for some extended example.
For example:
#Configuration
#EnableScheduling
public class AppConfig implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler());
}
#Bean(destroyMethod="shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(42);
}
}
#Scheduled group is not yet supported. See this open issue.
If you want use more than one scheduler you have to create and configure them programmatically. For example:
#Configuration
#EnableScheduling
public class AppConfig implements SchedulingConfigurer {
[...]
#Bean(destroyMethod="shutdown", name = "taskSchedulerA")
public Executor taskSchedulerA() {
return Executors.newScheduledThreadPool(42);
}
#Bean(destroyMethod="shutdown", name = "taskSchedulerB")
public Executor taskSchedulerA() {
return Executors.newScheduledThreadPool(42);
}
}
#Service
public class MyService {
#Autowired #Qualifier("taskSchedulerA")
private Executor taskSchedulerA;
#Autowired #Qualifier("taskSchedulerB")
private Executor taskSchedulerB;
#PostConstruct
public void schedule(){
Executors.newScheduledThreadPool(42).schedule(new Runnable() {
#Override
public void run() {
functionOfGroupA();
}
} , ..);
}
}

Configure Spring task scheduler to run at a fixeDelay or run at once based on a boolean

I have a code which runs at a regular interval.
Below is code for that
#EnableScheduling
#Component
public class Cer {
#Autowired
private A a;
#Autowired
private CacheManager cacheManager;
#Scheduled(fixedDelayString = "${xvc}")
public void getData() {
getCat();
getB();
return;
}
}
I want to change#Scheduled(fixedDelayString = "${xvc}") this based on a boolean say runOnce if runOnce is true #scheduled should run once only say on code startup.
Any advice how to achieve this.
Thanks in advance.
I would place the functionality that you want to schedule in a component:
#Component
public class ServiceToSchedule {
public void methodThatWillBeScheduled() {
// whatever
return;
}
}
And have two additional components that will be instantiated depending on a Profile. One of them schedules the task, and the other one just executes it once.
#Profile("!scheduled")
#Component
public class OnceExecutor {
#Autowired
private ServiceToSchedule service;
#PostConstruct
public void executeOnce() {
// will just be execute once
service.methodThatWillBeScheduled();
}
}
#Profile("scheduled")
#Component
#EnableScheduling
public class ScheduledExecutor {
#Autowired
private ServiceToSchedule service;
#Scheduled(fixedRate = whateverYouDecide)
public void schedule() {
// will be scheduled
service.methodThatWillBeScheduled();
}
}
Depending on which profile is active, your method will be executed just once (scheduled profile is not active), or will be scheduled (scheduled profile is active).
You set the spring profiles by (for example) starting your service with:
-Dspring.profiles.active=scheduled

Categories