I have a scheduled task using SpringBoot Scheduled to run Monday through Friday at 10 AM.
I'm running my application in a docker container and my machine is suspended from 6pm to 9am overnight.
When I start my machine, my tasks that were scheduled for 10 hours do not run unless I restart the container before the scheduled time.
I have application logs, no log record occurs that is inside the method with the #Scheduled annotation when this occurs.
With that I believe it's a deadlock.
I wonder if there is any way to detect a deadlock in the Springboot Scheduled programmatically.
My Cron expression: "0 0 10 * * MON-FRI"
Note: I'm testing on my machine to later host on an appropriate server.
AFAIK there is no standard way in Java to detect if the system went in standby/hibernate. Scheduling in Spring is based on the timing facilites in Java, facilites which are not intended to work across OS sleep or hibernate conditions. In short, if the JVM cannot detect when system goes in standby neither can Spring.
As I see it you have the following options:
notify the application when the system is resumed then re-schedule the tasks. This is a possible solution built around pm utils on Ubuntu. This one is for Windows.
add an additional task that runs, say every 10 seconds and reads the system time. If there is an appreciable difference between two time readings it means your system went to sleep and then resumed.
The following for example restarts the context if a time gap (delta) greater than 1s is detected:
#SpringBootApplication
#EnableScheduling
public class Application {
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
context = SpringApplication.run(Application.class, args);
}
static LocalTime lastDetectedTime;
static long delta = 1000;
static final long CHECK_INTERVAL = 5000;
#Scheduled(fixedDelay = CHECK_INTERVAL, initialDelay = CHECK_INTERVAL)
public static void restartIfTimeMismatch() {
if(lastDetectedTime == null) {
lastDetectedTime = LocalTime.now();
}
LocalTime currentTime = LocalTime.now();
long diff = Duration.between(lastDetectedTime, currentTime).toMillis();
lastDetectedTime = currentTime;
if(diff > CHECK_INTERVAL + delta) {
Thread thread = new Thread(() -> {
context.close();
context = SpringApplication.run(Application.class);
});
lastDetectedTime = null;
thread.setDaemon(false);
thread.start();
}
}
}
Hope it helps.
Related
I have a scheduler(using #Scheduler) running between 7 to 9 PM for every 15 mins. It looks for a file every 15 mins. If the file is found then the scheduler should stop for today and run next day again. How to achieve this in spring boot?
Probably the easiest way is to implement it at the level of business logic.
Spring provides a way to run a periodic task, that's true, but it can't stop the job for some time if a business case is met (the file is found).
Having said that, you could implement the scheduled job as follows:
#Component
public class MyScheduledJob {
private LocalDateTime runNextTime = null;
private boolean isFileFound = false;
#Scheduled(/**here comes your original cron expression: 7 am to 9 pm with an interval of 15 minutes as you say **/)
public void runMe() {
if(isFileFound && LocalDateTime.now().isBefore(runNextTime)) {
// do not run a real job processing
return;
}
isFileFound = checkWhetherFileExists();
if(isFileFound) {
runNextTime = calculateWhenDoYouWantToStartRunningTheActualJobProcessingNextTime();
}
... do actual job processing... Its not clear from the question whether it should do something if the file is not found as well, but I'm sure you've got the point ...
}
}
Since the bean is a singleton you can safely create a state for it, no-one won't change that state anyway.
You can do something like this. You can write your logic for checking files inside this method.
#Scheduled(fixedDelay = 1000, initialDelay = 1000)
public void scheduleFixedRateWithInitialDelayTask() {
long now = System.currentTimeMillis() / 1000;
System.out.println(
"Fixed-rate task with one-second initial delay - " + now);
}
See this for more details.
I’m currently using selenium in a web bot to purchase items on a website. When I search for the item I want to buy and it cannot be found I use driver.navigate().refresh() to refresh the page to see if it is there now, it will keep doing this until it finds the product when it is released on the page. However, I wish to start my bot a few hours before the release of the product which currently doesn’t work as after roughly 30 seconds of refreshing the page I get banned from the page due to the anti-ddos software they use. One option is to increase the delay between refreshing, however I need to catch the release of this product as soon as possible so I’m trying to find a way that my program can wait/sleep until 30 seconds before the release however I’m struggling to find a way to do this.
Just call Thread.sleep with the appropriate amount of milliseconds:
public static void main(String[] args) throws InterruptedException {
long currentTime = System.currentTimeMillis();
long releaseTime = currentTime + 1000 * 60 * 60 * 24 * 3; // 3 days
Thread.sleep(releaseTime - currentTime);
}
Another way would be to use java.time classes:
public static void main(String[] args) throws InterruptedException {
LocalDateTime now = LocalDateTime.now();
LocalDateTime release = LocalDateTime.of(2019, 10, 30, 13, 30);
long sleepDuration = Duration.between(now, release).toMillis();
TimeUnit.MILLISECONDS.sleep(sleepDuration);
}
Java 9 introduces new methods to the Duration class like toSeconds(), toMinutes() and so on.
You could also consider using a ScheduledExecutorService to schedule your tasks. This is especially useful if you have multiple tasks to schedule and don't want having multiple threads being blocked for that:
private static final ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2);
private static ScheduledFuture<?> scheduleTask(Runnable task, LocalDateTime releaseTime) {
Duration duration = Duration.between(LocalDateTime.now(), releaseTime);
return service.schedule(task, duration.toSeconds(), TimeUnit.SECONDS);
}
In general, to sleep until the next Thursday at 10:59 you could use the following code:
LocalDateTime release = LocalDateTime.now()
.with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY))
.withHour(10)
.withMinute(59);
Duration duration = Duration.between(LocalDateTime.now(), release);
TimeUnit.MILLISECONDS.sleep(duration.toMillis());
I think rather than sleeping you should take a look at scheduled tasks with cron expressions in Spring... that way you don't have a blocked thread just sitting there.
Scheduled Tasks with Spring
Cron Expressions
I have a scheduled job which runs every 100 seconds. Sometimes the execution of this method takes a lot of time (which is ok and there is no problem with that). In this situation, the result of the running method is not important to me and I want to re-schedule the job for next 100 second.
What is the best way to force the running job to terminate (return) after a specific time?
My scheduled code is like below:
#Scheduled(fixedDelay = 100*1000)
fun calculateLastDaysStatistics() {
logger.info("affiliate statistics thread Started Successfully")
val processStartDate = Date()
for (i in 1..prevDaysToConsider) {
logger.info("AdZone-Stats prev days $i")
val yesterday = DateUtility.addDay(Date(), -i)
val startDate = DateUtility.getZeroDayTime(yesterday.time)
val endDate = DateUtility.addDay(startDate, 1)
/* This method is probable to take a lot of time */
calculateStatistics(startDate, endDate)
}
val processLength = (Date().time - processStartDate.time) / 1000
logger.info("affiliate statistics thread finished in " + processLength + "s")
}
Thanks.
Try using Fixed Rate instead of Fixed Delay
Here is the article from
Paraschiv.E. The #Scheduled Annotation in Spring. Referred from https://www.baeldung.com/spring-scheduled-tasks
Schedule a Task at a Fixed Rate
#Scheduled(fixedRate = 1000)
public void scheduleFixedRateTask() {
System.out.println(
"Fixed rate task - " + System.currentTimeMillis() / 1000);
}
Note that the beginning of the task execution doesn’t wait for the completion of the previous execution.
This option should be used when each execution of the task is independent.
You can implement a custom Task scheduler using, org.springframework.scheduling.TaskScheduler instead of Annotation based method.
private final TaskScheduler scheduler;
#Autowired
public SchedulingManager(TaskScheduler scheduler) {
this.scheduler = scheduler;
}
In this case,
ScheduledFuture scheduledeFuture = scheduler.schedule(()->{
....You job goes here..
}, new CronTrigger("*/100 * * * * *"));
You can keep track of the scheduled future to make sure it runs max time intended.
scheduledeFuture.get(100,TimeUnit.SECONDS)
I need to execute a task every 5 minutes on my server to update some datas on a db, i've found that on openshift i have the cron that executes some script every tot time. Is it possibile to make a script that makes a simple call to a servlet or to a java code to run this job?
I am quite new to server side programming so please speak easy!
Ps. I am using a Tomcat 6 (Jboss EWS 1.0), mySQL 5.5 server
AS I understand you, you need your application to run sth every XX minutes.
To calculate the start time I made a helper function "getStartTime" With that I can use the human readable time like "23:30" (attention, I am from german, so it is not for AM/PM, just change for your needs).
Helper Method:
private static long getStartTime(String startTime) {
int hour = Integer.parseInt(startTime.split(":")[0]);
int minutes = Integer.parseInt(startTime.split(":")[1]);
Calendar cal = Calendar.getInstance();
Date dateNow = cal.getTime();
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minutes);
cal.set(Calendar.SECOND, 0);
if(cal.getTime().before(dateNow)) {
cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + 1);
return cal.getTime().getTime();
} else {
return cal.getTime().getTime();
}
}
Now you can use the ScheduledExecutorService from Java. Example:
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
long startClearRequests = getStartTime(DEFAULT_JOB_START_TIME) - System.currentTimeMillis();
And set your needs into the scheduleAtFiexed Rate:
scheduledExecutorService.scheduleAtFixedRate(clearRequests, startClearRequests, Math.round(DEFAULT_JOB_PERIOD_HOURS * 60 * 60 * 1000), TimeUnit.MILLISECONDS);
For example I use:
private static final int NUM_OF_THREADS = 2;
private static final String DEFAULT_JOB_START_TIME = "23:30";
private static final double DEFAULT_JOB_PERIOD_HOURS = 24;
As you see, you can change the number of threads (depends of what your application is doing), the start time (this is just needed for application start (when to start the job the first time).
And also the period (every XX hour the job shall run ... I took hours, but you need ti insert milliseconds at the end, so for 5 minutes (you have to tak 5 * 60 *1000 miliseconds.
Greetings
EDIT in respect to the athors comments:
To start things on application start, you have several methods. One method is to start a servlet on startup like this. Insert into the web.xml
<servlet>
<servlet-name>ServletStartups</servlet-name>
<servlet-class>model.initialization.ServletStartups</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
This will call the Class ServletStartups on Application start (the number in load-on-startup is the priority, because you can have multiple entries and can decide which to start first (1, 2, 3 ...)
Now within your servlet you defines an init() method, which is automatically called, like that:
public class ServletStartups extends HttpServlet{
public void init() throws ServletException{
// HEre you can put your methods as described above //(scheduledExecutorService( ...
}
}
IMPORTANT NOTE:
above I had a method "clearRequests", sorry this was my method, I have not renamed it to add it here. THis method will be called in my application every 24 hours.
the methods you call from the ScheduledExecutorService have to be a callable, like this:
private Runnable clearRequests = new Runnable() {
public void run() {
try {
// Here do your task
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
I have some processes in jBPM which I test with unit tests (sending events, checking if nodes are triggered, etc).
KnowledgeBuilder knowledgeBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
knowledgeBuilder.add(ResourceFactory.newClassPathResource("process.bpmn"), ResourceType.BPMN2);
knowledgeBase = knowledgeBuilder.newKnowledgeBase();
session = knowledgeBase.newStatefulKnowledgeSession();
....
In some of the processes there are fixed timers (for example 3 weeks). Is there a possibility to manipulate the time jbpm is using so that I can simulate that this period of time is already over?
Btw. I don't want to trigger these notes manually or modify the times in it.
I'm using jbpm 5.4.
One possibility is to use the clock of the session and iterate over all actual running timers.
This will not actually shift time, but can be used to cancel timers, which should be fired in a time span.
Example:
public void shiftTime(long timeToShiftInMs) {
long targetTime = System.currentTimeMillis() + timeToShiftInMs + 10; // 10ms ahead ...
JDKTimerService clock = getSession().getSessionClock();
List<TimerJobInstance> jobs = new ArrayList<>();
for (TimerJobInstance job : clock.getTimerJobInstances()) { // go through all jobs
// He keeps already executed timer without nextFirTime
Date nextFireTime = job.getTrigger().hasNextFireTime();
if (nextFireTime != null) {
long jobTime = nextFireTime.getTime();
if (targetTime > jobTime) { // look if it should be fired after this time
jobs.add(job);
}
}
}
for (TimerJobInstance job : jobs) {
job.getJob().execute(job.getJobContext());
clock.removeJob(job.getJobHandle());
}
}