Lets say I have some rest api where arguments are time when to execute method and second argument is name of the method in class. What is the best way for invoking call of this method in certain time (just once) in spring-boot application ?
First, enable scheduling in your spring-boot application:
#SpringBootApplication
#EnableScheduling
public class Application {
// ...
Then, inject the TaskScheduler bean and schedule the task programmatically every time the user invokes the REST method:
public class MyScheduler {
#Autowired
private TaskScheduler scheduler;
public void scheduleNewCall(Date dateTime) {
scheduler.schedule(this::scheduledMethod, dateTime);
}
public void scheduledMethod() {
// method that you wish to run
}
}
However, you should also think about limiting the amount of calls to this method, otherwise the malicious user could schedule a lot of tasks and overflow the task pool.
Related
I need to implement Spring process which checks database table for new rows and makes calculations. I'm thinking to implement infinite loop which is triggered every 10 minutes.
Is there someway to implement this with Spring Boot? I can always use Java Thread but it's for sure it's better to let Spring to manage this.
You can try scheduling with Spring Schedule
Here is a official example
Technically, you enable scheduling with #EnableScheduling and annotated your task with #Scheduled(fixedRate=600000).
Another config you can used to tune your scheduler:
fixedRate: Execute the annotated method with a fixed period in milliseconds between invocations.
fixedDelay: Execute the annotated method with a fixed period in milliseconds between the end of the last invocation and the start of the next.
cron: A cron-like expression, extending the usual UN*X definition to include triggers on the second as well as minute, hour, day of month, month and day of week.
find the below sample code
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
public class AppScheduler {
#Scheduled(fixedRate = 10000)
public void myScheduler() {
System.out.println("Test print");
}
}
It looks like this questions is old, but I would like to answer.
See, you can make an object of ThreadPoolTaskExecutor and assign the background process to it.
Here is my detailed code if anyone wants to see.
https://github.com/xbox2204/SpringBoot-JPA-Swagger-Angular
First thing to do would be, add these two annotation in your application starter class.
#EnableAsync
#EnableScheduling
Now, create you TaskExceutor in the same class and assign it to bean with a name, so that background tasks can be created anywhere in your application and attached with this bean.
#Bean(name="thPlExectr")
public Executor getExecutor(){
return new ThreadPoolTaskExecutor();
}
Now create a component class somewhere in the project, where you you will create the background task.
Here, you will mention the frequency with which you want your task to be exceuted. I wanted to print a statement every 5 second, you can choose your own frequency and give your own method definiton. Also, make it async and attach it with the TaskExecutor bean mentioned above, so that it doesn't interrupt the normal flow of your application.
#Component
public class BackgroundTasks {
private static final Logger log= LoggerFactory.getLogger(BackgroundTasks.class);
#Async("thPlExectr")
#Scheduled(fixedRate = 5000)
public void backTask(){
log.info("Hi Vineet! I am a background task");
}
}
I have an Spring Boot application in which I have a class with two methods likes below.
#Component
public class ClassA {
#Scheduled(fixedDelay = 900000)
public void methodA(){
//do task "A"
}
#Scheduled(fixedDelay = 600000)
public void methodB(){
//do task "B"
}
}
As per my understanding methodA should execute every 15 minutes and methodB should execute every 10 minutes. But as soon as applications starts both the jobs get invoked. Is this the right behavior ? If yes, then how can I stop them from execution immediately after application startup and run methodB after methodA?
As form the documentation: Scheduled (Spring Framework 5.0.4.RELEASE API)
fixedDelay: Execute the annotated method with a fixed period in
milliseconds between the end of the last invocation and the start of
the next.
What you want is
initialDelay:
Number of milliseconds to delay before the first
execution of a fixedRate() or fixedDelay() task.
In what circumstances CommandLineRunner is preferred instead of writing additional code in the main method of SpringBoot application.
I understand that CommandLineRunner gets executed before main gets completed.
In simple cases, there is no difference.
But if the code need to access features provided by spring, such as ioc or only interface repositories/services, you need to wait for the complete application startup. And the call of the overrided run method after completion is garanteed.
Besides, CommandLineRunner has other advantages:
Can be implemented multiple times
The capability to start any scheduler or log any message before application starts to run
I have used it to decouple code. Instead of placing a bunch of code into main method, the CommandLineRunner lets you distribute it more evenly around the codebase. It really depends on what kind of flags you are passing in and why you need to pass them in. Spring offers a lot of flexibility for you to get the job done in the easiest way.
For a full on command line tool, you can decouple initialization and config a little bit by dividing your code between init and core behavior.
A spring boot server can overwrite configuration based on args passed in from the command line.
I would suggest all time time. It adds a lot of flexibility to your "bootstrapping code".
1) For example, command line runners are spring #Beans, so you can activate/deactivate them at run-time.
2) You can use them in an ordered fashion by appending the #Order annotation
3) You can unit test them just like regular classes
4) You can inject dependencies into them. Each runner can then have its own dependencies.
All of the above are more difficult, if not impossible to achieve if you add all your bootstrapping logic in the main() method of the Spring Application class.
Hope my answer helps,
Cheers
I haven't found any good reason for using it over just writing code after starting the application.
The only thing I can think of is that the command line runners are called before any SpringApplicationRunListeners have #finished called.
I have seen people use them to perform main application logic, but I think this is an antipattern.
One of the annoying things about doing so is that the application start timer is still running and when the task completes you will see a log entry like Started DemoApplication in 5.626 seconds (JVM running for 0.968).
It's confusing to see a message about your application starting despite, in reality it having just finished.
I encountered a scenario where i had to keep a certain data from the db loaded into the cache before the method was hit from the controller end point the first time. In this scenario it was desirable to hit the method for populating the cache using the run method after extending CommandLineRunner class so that before the application even starts up the data is already available in the cache.
I use this to populate my Default Data. I usually create ApplicationInitializer class that extends CommandLineRunner.
I have methods like createDefaultUser(), createDefaultSytemData() etc.
This way I do not rely on sql files to populate database for me. :)
ApplicationRunner and CommandLineRunner:
two of them can execute some custom code before your application finished starting up.
ComandLineRunner:
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(CommandLineAppStartupRunner.class);
#Override
public void run(String...args) throws Exception {
logger.info(Arrays.toString(args));
}
}
you can get the args directly
ApplicationRunner:
#Component
public class AppStartupRunner implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(AppStartupRunner.class);
#Override
public void run(ApplicationArguments args) throws Exception {
logger.info(args.getOptionNames());
}
}
ApplicationRunner has many methods to get params
ComandLineRunner can get params directly
if you custom two runner class, you can use annotation #Order to Specify the order of execution
public class Phone {
#Autowired
BeanExample beanExample;
public void print(){
beanExample.fn();
}
}
public class BeansCreatorClass {
#Bean
public BeanExample getBeanExample(){
return new BeanExample();
}
#Bean
public Phone getPhone(){
return new Phone();
}
}
#SpringBootApplication
public class SpringBootRunnerConfigurationPropertiesApplication implements CommandLineRunner, ApplicationRunner {
public static void main(String[] args){
SpringApplication.run(SpringBootRunnerConfigurationPropertiesApplication.class, args);
System.out.println("==== spring boot commandLine is running === ");
// beans creator class is the class contains all beans needed
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeansCreatorClass.class);
Phone phone = applicationContext.getBean(Phone.class);
phone.print();
}
// commandLineRunner
public void run(String... args) throws Exception {
System.out.println("=== commandLine Runner is here ==== ");
}
// application runner
#Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("=== application runner is here ====");
}
}
I mostly use CommandLineRunner to:
Apply initial migrations
Run a code that is independent of REST/SOAP calls.
I have a #Scheduled task in my application which is setup using CRON and run every 4 hours. The problem I face is that the CRON job does not start immediately after application startup but it starts only 4 hours after the application startup.
I tried to use a #PostConstruct method inside the task to invoke it, but that results in an error due to an uninitialized Spring context.
Please tell me how I can make the Scheduled task run immediately on application deployment and then on every 4 hours after the deployment.
EDIT:
I would not use a #PostConstruct since my scheduled method depends on other Beans , which are not initialized when this PostConstruct method runs for some reason.
By 0 */4 * * * you specify "At minute 0 past every 4th hour (0:00, 4:00, 8:00 etc.)", which is not at startup time and then every 4 hours as I think you want.
You can specify initial delay and rate by:
#Scheduled(initialDelay=0, fixedRate=4*60*60*1000)
If you are worried about hard-coded values, you can still provide config value:
#Scheduled(initialDelay=0, fixedRateString = "${some.config.string}")
I had #EnableScheduling in my app config. I had
#Scheduled(fixedDelay=5000) in my scheduled task class. Even then it didn't work.
I added #Service to my scheduled task class and everything worked fine.
I was having the same situation. I need to run cron scheduler every 1st day of the month and also when the application starts.
This is how I achieved using ApplicationListener.
import org.springframework.context.ApplicationListener;
import org.springframework.boot.context.event.ApplicationReadyEvent;
#Component
public class ApplicationReadyListner implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// callYourTask();
}
}
I'm not sure if you have tried this but you can use #Scheduled with an initialDelay
For fixed-delay and fixed-rate tasks, an initial delay may be specified indicating the number of milliseconds to wait before the first execution of the method.
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-annotation-support-scheduled
#Scheduled(initialDelay=0, fixedRate=5000)
public void doSomething() {
// something that should execute periodically
}
I assume this will execute your scheduled method when the application is run.
If is not possible to use initialDelay attribute with the cron rule and your intention is execute this Job after every restart, you can implement a CommandLineRunner on your application class:
#SpringBootApplication
public class MyApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Autowired
private TaskService taskService;
#Override
public void run(final String... args) throws Exception {
taskService.run();
}
}
It's not the best strategy, but works.
Just specify the method you want to run in the init_method attribute of the bean.
Java config: #Bean(init_method="methodWhichStartsTask")
XML config: <bean id="someId" class="com.example.Clazz" init-method="methodWhichStartsTask"/>
This will invoke the method just after the bean is properly initialized and if the method is scheduled, then it will be called afterwards every 4 hours.
Instead of cron inside #Scheduled, use fixedRate like below. By default initialDelay is -1, which will start immediately or you can set 0.
#Scheduled(fixedRate=4*60*60*1000)
public void test() {
System.out.println("helloworld");
}
user #EnableScheduling annotation on your class which contains you1 scheduled method:
see spring document:
To enable support for #Scheduled and #Async annotations add
#EnableScheduling and #EnableAsync to one of your #Configuration
classes:
#Configuration
#EnableAsync
#EnableScheduling
public class AppConfig {
}
link : https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html
#EnableScheduling annotation should be added in the main class for the Spring application.
Example :
#SpringBootApplication
#EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
and above the method where the actual scheduling needed we need to add
#Scheduled(initialDelay = 1000, fixedDelay = 4*60*60*1000)
Example:
#Scheduled(initialDelay = 1000, fixedDelay = 4*60*60*1000)
public void schleduleWork() {
/* Code Here */
}
For the above scheduler the first hit will be at 1000 milli second and subsequest hits will be at the interval of 4 hrs
Currently I am building a spring standalone program in order to learn new methods and architectures.
The last few days I tried to learn scheduler. I never used them before so I read some articles handling the different possible methods. Two of them are especially interesting: The spring nativ #Scheduler and Quartz.
From what I read, Spring is a little bit smaller then Quartz and much more basic. And quartz is not easy to use with spring (because of the autowired and components).
My problem now is, that there is one thing I do not understand:
From my understanding, both methods are creating parallel Threads in order to asynchronously run the jobs. But what if I now have a spring #Service in my main Application, that is holding a HashMap with some information. The data is updated and changed with user interaction. Parallel there are the scheduler. And a scheduler now whants to use this HashMap from the main application as well. Is this even possible?
Or do I understand something wrong? Because there is also the #Async annotation and I did not understand the difference. Because a scheduler itself is already parallel to the main corpus, isn't it?
(summing up, two questions:
can a job that is executed every five seconds, implemented with a scheduler, use a HashMap out of a service inside the main program? (in spring #Scheduler and/or in Quartz?)
Why is there a #Async annotation. Isn't a scheduler already parallel to the main process?
)
I have to make a few assumptions about which version of Spring you're using but as you're in the process of learning, I would assume that you're using spring-boot or a fairly new version, so please excuse if the annotations don't match your version of Spring. This said, to answer your two questions the best I can:
can a job that is executed every five seconds, implemented with a scheduler, use a HashMap out of a service inside the main program? (in spring #Scheduler and/or in Quartz?)
Yes, absolutely! The easiest way is to make sure that the hashmap in question is declared as static. To access the hashmap from the scheduled job, simply either autowire your service class or create a static get function for the hashmap.
Here is an example of a recent Vaadin project where I needed a scheduled message sent to a set of subscribers.
SchedulerConfig.class
#Configuration
#EnableAsync
#EnableScheduling
public class SchedulerConfig {
#Scheduled(fixedDelay=5000)
public void refreshVaadinUIs() {
Broadcaster.broadcast(
new BroadcastMessage(
BroadcastMessageType.AUTO_REFRESH_LIST
)
);
}
}
Broadcaster.class
public class Broadcaster implements Serializable {
private static final long serialVersionUID = 3540459607283346649L;
private static ExecutorService executorService = Executors.newSingleThreadExecutor();
private static LinkedList<BroadcastListener> listeners = new LinkedList<BroadcastListener>();
public interface BroadcastListener {
void receiveBroadcast(BroadcastMessage message);
}
public static synchronized void register(BroadcastListener listener) {
listeners.add(listener);
}
public static synchronized void unregister(BroadcastListener listener) {
listeners.remove(listener);
}
public static synchronized void broadcast(final BroadcastMessage message) {
for (final BroadcastListener listener: listeners)
executorService.execute(new Runnable() {
#Override
public void run() {
listener.receiveBroadcast(message);
}
});
}
}
Why is there a #Async annotation. Isn't a scheduler already parallel to the main process?
Yes, the scheduler is running in its own thread but what occurs to the scheduler on long running tasks (ie: doing a SOAP call to a remote server that takes a very long time to complete)?
The #Async annotation isn't required for scheduling but if you have a long running function being invoked by the scheduler, it becomes quite important.
This annotation is used to take a specific task and request to Spring's TaskExecutor to execute it on its own thread instead of the current thread. The #Async annotation causes the function to immediately return but execution will be later made by the TaskExecutor.
This said, without the #EnableAsync or #Async annotation, the functions you call will hold up the TaskScheduler as they will be executed on the same thread. On a long running operation, this would cause the scheduler to be held up and unable to execute any other scheduled functions until it returns.
I would suggest a read of Spring's Documentation about Task Execution and Scheduling It provides a great explanation of the TaskScheduler and TaskExecutor in Spring