scheduledExecutorService, timerService and Stateless EJB on scheduled jobs - java

I'm trying to implement a system in server that will do some updates on database on a ragular basis.
Here: Spawning threads in a JSF managed bean for scheduled tasks using a timer
and in some other similar questions, I saw that BalusC strongly recommended to use Stateless Beans, if not possible SchedulerExecuterService rather than Timer.
Here is my situation. I need a JSF page in which I can configure the schedule interval. i.e. I can change its rule from run once in every 5 min to run once in 10 min
First, I tried to use #Schedule annotation and it was great. However, I couldn't find a way to change interval with that. First question, is it possible to change it dynamically like I told above?
I am currently using SchedulerExecutorService which is called from #PostConstruct of a Stateless Bean.
Second question, is the Timer BalusC strongly recommended not to use is the TimerService of EJB?
Third Question, I liked the properties of timerService which is:
using scheduleExpression and timerConfig.
Are there any similar things for ScheduledExecutorService?
Additional Question: Am I on right track? Can the thing that I am trying to pull be done in a better way?

I think #Schedule is used only for fixed cron-like timers, where the EJB container deploys a timer at EJB startup. You obviously need more dynamic scheduling connected with a JSF page.
If you are running on a full Java EE 6 profile, why not use the TimerService with a Stateless Session EJB like this:
#Stateless
public class JobSchedulerBean {
#Resource
private TimerService timerService;
// #PostConstruct
public void initTimer() {
// set initial timer
ScheduleExpression sch = new ScheduleExpression();
// set cron expression into sch
timerService.createCalendarTimer(sch, new TimerConfig("myTimer", false));
}
public void rescheduleTimer(int interval) {
// get timer from timer service
for (Timer timer : timerService.getTimers()) {
if (timer != null && timer.getInfo().equals("myTimer")) {
timer.cancel();
}
}
// schedule new timer, like in initTimer() method
}
#Timeout
public void timeout(Timer timer) {
// do the job
}
}
EDIT:
#ManagedBean(eager=true)
#ApplicationScoped
public class JobRunner {
private ScheduledExecutorService scheduler;
private static final int POOL_SIZE = 1;
ScheduledFuture<?> runHandle;
#PostConstruct
public void init() {
scheduler = Executors.newScheduledThreadPool(POOL_SIZE);
// set initial expiry to 5 minutes after 5 minutes delay
runHandle = scheduler.scheduleAtFixedRate(new MyJob(), 5, 5, TimeUnit.MINUTES);
}
#PreDestroy
public void destroy() {
scheduler.shutdownNow();
}
public void reschedule(int newDelay) {
// cancel old timer, but do not interrupt it
runHandle.cancel(false);
runHandle = scheduler.scheduleAtFixedRate(new MyJob(), newDelay, newDelay, TimeUnit.MINUTES);
}
}
public class MyJob implements Runnable {
public void run() {
// do the job
}
}

Related

Spring TaskScheduler does not schedule task immediatelly

I want to execute the same task couple of times but it seems that every next invocation of my code does not execute the task immediately, for example it executes after one minute.
Since user has to schedule tasks manually I use ScheduledTaskRegistrar.TaskScheduler.
taskRegistrar.getScheduler().schedule(myTask, new Date());
What could be the reason? User clicked schedule button twice on my fronted application and backend invoked the above schedule method twice as expected. First execution of my task was immediate, second run after two minutes.
UPDATE: taskregistrar config, maybe I didn't configure it at all. my tasks are added as cron tasks on application deployment. But they also must be runnable manually if user wants to trigger it. Below is more or less the whole logic:
#Configuration
#EnableAsync
#EnableScheduling
#Component
#Slf4j
#Generated
#Getter
public class ScheduleTaskService implements SchedulingConfigurer {
#Autowired
private List< MyTask> taskList;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
taskList.stream().filter(MyTask::isOn).forEach(this::addTaskToScheduler);
}
public void addTaskToScheduler(GwoTask task) {
taskRegistrar.addCronTask(task, task.getCronExpression());
}
public void scheduleImmediateInvocation(MyTask myTask) {
taskRegistrar.getScheduler().schedule(myTask, new Date());
}
}
By referring to the source code of ScheduledTaskRegistrar,
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
...
If we do not set taskScheduler, Executors.newSingleThreadScheduledExecutor() is used by default. Hence new task will be blocked by processing task.
For your use case in scheduleImmediateInvocation, I recommend to use another thread pool(Probably from Executors) instead as:
It isn't actually a schedule job.
More control on pool size is needed to suit your workload
If you just want to make ScheduledTaskRegistrar execute more concurrently, configure it as:
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// set the desired core pool size
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
this.taskRegistrar = taskRegistrar;
// ...
}

Spring boot: #Scheduler that updates cron espression without restarting

I Need to schedule a task on Spring Boot that reads a cron espression from the database. I did this using the #Scheduled annotation and reading a property inside a database, but my client is asking to be able to update the cron expression in the database and having it affect the scheduled without restarting the application. I know this isnt possible with the #Scheduled annotation, but would It be possible to schedule another task that extracts the cron expression every hour, and then feed the updated expression to the actual scheduled that executes the task? Basically updating the variable that Is Fed to the second scheduled. If this isnt possible, do you know any alternative ways to achieve this without using the #Scheduled annotation? Thank you.
You could try doing this using your own Runnable and a ScheduledExecutorService Which starts a thread to do what you are asking once every hour.
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
public void chronJob Runner() {
final Runnable chronJobWorker = new Runnable() {
public void run() { //Request logic }
};
scheduler.scheduleAtFixedRate(beeper, 1, 60, TimeUnit.MINUTES);
Not sure if this is the best way of doing it, but is certainly one possible way of completing this task at a scheduled rate.
Solved this using SchedulingConfigurer, here's a sample:
#Configuration
#EnableScheduling
public class BatchConfig implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(new Runnable() {
#Override
public void run() {
//run your code here
}
}, new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
//extract cron from database
CronTrigger trigger = new CronTrigger(new CronTrigger(//insert cron that you got from database));
return trigger.nextExecutionTime(triggerContext);
}
});
}
}

how to run scheduler just after inserting data into database in spring boot?

I am new to spring boot and I have a requirement in which I have to run scheduler only if new data is inserted into table. Thanks for any help
Hibernate has an interceptor mecanism that allows you to get notified, at specific times, when database events occurs.
Such events are creation/deletion/flush of the session. As you get access to the objects being subject to the given event, you have a mean to fire a process when a given object of a given class (which you can easily map to a table in your schema) is modified.
The javadoc can be found here :
https://docs.jboss.org/hibernate/orm/4.0/manual/en-US/html/events.html
You can use the interceptor mecanism along with Java's ScheduledExecutorService to schedule a task when hibernate intercepts the save operation. You can create your business logic under that interceptor.
Scheduling is not enabled by default. Before adding any scheduled jobs we need to enable scheduling explicitly by adding the #enableScheduling annotation.
Now with the help of the ScheduledTaskRegistrar#addTriggerTask method, we can add a Runnable task and a Trigger implementation to recalculate the next execution time after the end of each execution.
#EnableScheduling
public class DynamicSchedulingConfig implements SchedulingConfigurer {
#Autowired
private TickService tickService;
#Bean
public Executor taskExecutor() {
return Executors.newSingleThreadScheduledExecutor();
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskRegistrar.addTriggerTask(
new Runnable() {
#Override
public void run() {
tickService.tick();
}
},
new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext context) {
Optional<Date> lastCompletionTime =
Optional.ofNullable(context.lastCompletionTime());
Instant nextExecutionTime =
lastCompletionTime.orElseGet(Date::new).toInstant()
.plusMillis(tickService.getDelay());
return Date.from(nextExecutionTime);
}
}
)
}
}

Whatis the standard way to manage (check and recover) a custom backgtround routine in Spring?

Assume there is a bean MyTask with one method, which can throw an exception. I would like to invoke the method asynchronously (background routine), then check periodically (i.e. each minute) what is its status (in progress, completed or failed) and each time make a decision whether it should be started again or not.
#Component
class MyTask {
public void execute(){
if (System.currentTimeMillis() % 5 == 0){
throw new RuntimeException("Failed");
} else {
// long operation
System.out.println("Hello");
}
}
}
Is there any standard way in Spring Framework 5.3.7+ to manage custom background routine in this manner?
Use #Scheduled:
#EventListener(ApplicationReadyEvent.class)
#Scheduled(fixedDelay = 5)
public void execute() {
(The #EventListener waits until the application is ready.)
You'll need to configure the thread pool in your application.properties:
spring.task.scheduling.pool.size: 4
You can also inject the ThreadPoolTaskExecutor to manage your other background tasks.
#Autowired private ThreadPoolTaskExecutor executor;

Schedule a task to run only once

I'm working on a recovery monitor which waits for 5 minutes and fires an alert if system has not been recovered yet. The monitor needs to be started at start up and to fire alert only once. The source code looks like this:
#Stateless
public class RecoveryMonitor {
#Inject TimerService timerService;
#Inject MyAlertService alertService;
#Inject SystemRecovery systemRecovery;
public void scheduleMonitor() {
timerService.createSingleActionTimer(TimeUnit.MINUTES.toMillis(5),
new TimerConfig);
}
#Timeout
public void timeout() {
if (!systemRecovery.isDone) {
alertService.alert("System recovery failed");
}
}
}
So, the problem here is how to schedule a task, i.e. invoke scheduleMonitor method. I cannot use #PostConstruct as it's not allowed to. I think about using #Schedule, but it executes a method periodically while I only to do it once. Any solutions and/or suggestions are welcome. Thanks.
L
UPDATE: by making the class not Stateless anymore, e.g. make it a #Singleton, I am able to start scheduling using #PostConstruct. This is not a complete solution but it works for me:
#Singleton
public class RecoveryMonitor {
#Inject TimerService timerService;
#Inject MyAlertService alertService;
#Inject SystemRecovery systemRecovery;
#PostConstruct
public void scheduleMonitor() {
timerService.createSingleActionTimer(TimeUnit.MINUTES.toMillis(5),
new TimerConfig);
}
#Timeout
public void timeout() {
if (!systemRecovery.isDone) {
alertService.alert("System recovery failed");
}
}
}
If you have a Servlet Environment you could fire a CDI Event(e.g. ApplicationStartedEvent) within a ServletContextListener and observe that event in your EJB. This kind of startup logic has to be done manually in CDI 1.0. Future versions will probably contain something similar.
If you have questions on how to do that, just ask :)

Categories

Resources