I'm struggling with Quartz's triggers. I've created a service which is invoked through a cron expression but also it's a org.quartz.Job itself, the idea behind this is that once the service is invoked I check some variables and based on that I decide to delay (or not) the execution of the service for 1 day; for that, I get the DefaultScheduler, create a new org.quartz.JobDetail and a org.quartz.SimpleTrigger, and schedule them. As you may guess, it's not working, the new trigger is never started.
public class MyService implements MyInterface, org.quartz.Job {
#Override
//method declared in MyInterface
public void generateFile() {
org.quartz.Scheduler scheduler;
try {
scheduler = org.quartz.impl.StdSchedulerFactory.getDefaultScheduler();
} catch (SchedulerException e1) {
scheduler = null;
}
if (condition) {
//regular processing
} else {
try {
org.quartz.JobDetail jobDetail;
SimpleTrigger simpleTrigger;
String name = "jobName";
if (scheduler != null && scheduler.isInStandbyMode()){
jobDetail = new org.quartz.JobDetail(name, org.quartz.Scheduler.DEFAULT_GROUP, MyService.class);
jobDetail.getJobDataMap().put("myService", this);
Calendar c = Calendar.getInstance();
int date = c.get(Calendar.DATE);
c.set(Calendar.DATE, date + 1);
simpleTrigger = new SimpleTrigger("simpleTrig", c.getTime());
simpleTrigger.setJobName(name);
jobDetail.setDurability(true);
scheduler.addJob(jobDetail, true);
scheduler.scheduleJob(simpleTrigger);
//also tested with
//scheduler.scheduleJob(jobDetail, simpleTrigger);
//and same result
scheduler.start();
}
else{
scheduler.shutdown();
}
} catch (java.text.ParseException e) {
}
catch (org.quartz.SchedulerException ex) {
}
}
}
#Override
public void execute(JobExecutionContext jobContext) throws JobExecutionException {
MyInterface jobParamLogService = (MyInterface)jobContext.getJobDetail().getJobDataMap().get("myService");
jobParamLogService.generateFile();
}
As i said, my problem is that the SimpleTrigger never gets invoked (off course, I've tested adding a few minutes, not a day), does any have any tip about what's going on? Any help would be really appreciated.
As #GuillaumePolet suggested, I've tried removing the isInStandByMode condition, but still didn't work. The trick was to replace the constructor with arguments for the default one (empty), and set the variables through the setter methods.Thank you all for your help, kind regards
Related
I am trying to stop a long running method after 10 seconds of execution, so far i followed the timer instructions on baeldung.
https://www.baeldung.com/java-stop-execution-after-certain-time#1-using-a-timer
When the method is a simple call to a thread sleep it works, but when I call my function with sub methods it doesn't stop.
My implementation looks like this:
class TimeOutTask extends TimerTask {
private Thread t;
private Timer timer;
TimeOutTask(Thread t, Timer timer){
this.t = t;
this.timer = timer;
}
public void run() {
if (t != null && t.isAlive()) {
t.interrupt();
timer.cancel();
}
}
}
class Execution implements Runnable {
private String carpeta;
private Experiment exp;
public Execution(String carpeta, Experiment exp) {
this.carpeta = carpeta;
this.exp = exp;
}
#Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
exp.executeExperiment(carpeta);
}
} catch (InterruptedException e) {
System.out.println("Fin de ejecución por tiempo");
}
}
}
And the way I am calling this execution is throught the executeTimedExperiment method
public Experiment() {
this.cases = new ArrayList<>();
}
private void executeTimedExperiment(String carpeta){
Thread t = new Thread(new Execution(carpeta,this));
Timer timer = new Timer();
timer.schedule(new TimeOutTask(t, timer), 10000);
t.start();
}
private void executeExperiment(String carpeta) throws InterruptedException {
String[] files = getFiles(carpeta);
Arrays.sort(files);
for (String file : files) {
executeCase(carpeta, file);
}
}
private boolean executeCase(String carpeta, String file) {
Graph g = readDataToGraph(carpeta + "/" + file);
Solution s = new ExactSolutionGenerator().ExactSolution(g);
addNewCase(file, s);
}
The executeExperiment method is the long running and I marked it with InterruptedException but the compiler tells me the exception is never throw.
What happens now when I execute it is that it runs normally without stoppping.
I am not sure if I need to add InterruptedException to the submethods or something else, but I would like to not touch the submethods if possible.
Thanks in advance.
You will need to do more than add throws InterruptedException to all of those ‘submethods’ (and your own methods). The body of each of those methods must be altered to properly respond to interrupts.
It is not possible to arbitrarily stop running code. Interrupts are cooperative—they only mean something if the thread being interrupted pays attention to them.
Your run() method does this properly: by placing the entire loop inside a try/catch, any InterruptedException will cause the loop to terminate and thus the thread will terminate.
But the methods it calls must do the same thing. Your run method calls executeExperiment, which does this:
String[] files = getFiles(carpeta);
I don’t know how long that method takes, but if it takes any significant amount of time at all (more than a fraction of a second), it needs to be capable of throwing InterruptedException in the middle of the file reading.
executeExperiment also calls executeCase, which calls the ‘submethods’ readDataToGraph, ExactSolution, and addNewCase. As above, each of those methods which takes more than a fraction of a second needs to respond to an interrupt by throw InterruptedException. So, I’m afraid you will need to modify them.
An example would be:
private Graph readDataToGraph(String filename)
throws InterruptedException {
Graph graph = new Graph();
try (BufferedReader reader = Files.newBufferedReader(Path.of(filename))) {
String line;
while ((line = reader.readLine()) != null) {
graph.addData(convertDataToGraphEntry(line));
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
Compiler tells you the exception is never throw is beacuse your executeExperiment method is uninterruptable(Unlike some blocking methods, e.g. Object#wait), so thread.interrupt does not make the thread executing this method receive an InterruptedException.
Maybe you need to check whether the current thread has been interrupted every time you iterate files in your executeExperiment method, if it is, then throw an InterruptedException.(But this may still be inaccurate, because the executeCase method may be executed for a long time.)
I want a process to run after I start my webservice, and then every 30 minutes or so afterwards, (I'm testing it with a smaller delay for now, just to see if it works), but my process never runs more than once. What am I doing wrong?
Here is my code:
#WebListener
public class SchedulerService implements ServletContextListener{
#Autowired
UpdateSubscriberService updateSubscriberService;
ScheduledExecutorService scheduledExecService;
public SchedulerService(){
scheduledExecService = Executors.newSingleThreadScheduledExecutor();
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
scheduledExecService.shutdown();
}
#Override
public void contextInitialized(ServletContextEvent arg0) {
scheduledExecService.scheduleWithFixedDelay(new Runnable(){
#Override
public void run() {
Date date = new Date(System.currentTimeMillis());
System.out.println("Running scheduled update check " + date.toString());
updateSubscriberService.checkForUpdates();
}
}, 60, 30, TimeUnit.SECONDS);
}
}
See this longer Answer of mine on a similar Question.
Wrap run code with try catch
Just a guess: An exception is being thrown. A ScheduledExecutorService halts silently if it encounters an Exception, with no further scheduled work performed.
The run method’s code should always be surrounded by a try-catch to handle and absorb any thrown Exception.
#Override
public void run() {
try { // Let no Exception reach the ScheduledExecutorService.
Date date = new Date(System.currentTimeMillis());
System.out.println("Running scheduled update check " + date.toString());
updateSubscriberService.checkForUpdates();
} catch ( Exception e ) {
System.out.println( "ERROR - unexpected exception" );
}
}
Stub out run method
Take baby steps. Begin with a run method that does nothing but a System.out.println.
Just in case you are ever in a position where the code MUST run once every-so-many-seconds even if the last run hasn't completed yet (which can be very dangerous if not managed properly), you can launch your process inside a different thread inside the timer. Here is sample code.
ScheduledExecutorService scheduledExecService = newSingleThreadScheduledExecutor();
scheduledExecService.scheduleWithFixedDelay(new Runnable()
{
#Override
public void run()
{
// This should be in a try-catch because any error here
// will stop the recurrence
try
{
// The timer will only repeat if the last run is finished. So
// we put each new process in a different thread than the timer
// itself, so the last timer call "finishes" as soon as the process
// leaves the timer's thread.
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
MyProcessThatShouldRunEverySoManySecondsNoMatterWhat();
}
catch (Exception erTimerThread)
{
Log.e("Error", erTimerThread.toString());
}
}
});
t.setPriority(2);
t.start();
}
catch (Exception erTimer)
{
Log.e("Error", erTimer.toString());
}
}
}, 0, 60, java.util.concurrent.TimeUnit.SECONDS);
I want to skip a job execution if another instance of the same job is already executing (if the same "JobDetail" is running). I know the annotation "#DisallowConcurrentExecution" avoid concurrent execution of the same job. But the downside is that when the job (a job that executes is a longer time then the periodicity of the trigger) finishes the job will be instantly re-executed. I want the job to be executed in the next schedule fire time.
Example
Job Implementation
public class LongJob implements Job {
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
System.out.println("Executed: " + LocalDateTime.now().toString());
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Scheduling
JobDetail job2 = JobBuilder.newJob(LongJob.class)
.withIdentity("job2")
.build();
Trigger trigger2 = TriggerBuilder.newTrigger()
.forJob(job2)
.startNow()
.withIdentity("job2-trigger")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/3 * * ? * *")
.withMisfireHandlingInstructionIgnoreMisfires())
.build();
scheduler.scheduleJob(job2, trigger2);
Expected output
Executed: 2016-02-18T10:11:15
Executed: 2016-02-18T10:11:21
Executed: 2016-02-18T10:11:27
Executed: 2016-02-18T10:11:33
Output using #DisallowConcurrentExecution
Executed: 2016-02-18T10:11:15
Executed: 2016-02-18T10:11:20
Executed: 2016-02-18T10:11:25
Executed: 2016-02-18T10:11:30
My Solution
I made a solution using Trigger Listeners, But I was wondering if there is a simpler one. In this approach I would have use a Listener Instance for each group of Trigger that fires the same job (I can solve my "bigger" problem using different trigger for different, avoiding the use of the same trigger for different jobs).
class CustomTriggerListener extends TriggerListenerSupport {
private JobExecutionContext lastJobExecutionContext;
#Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
boolean vetoExecution = false;
if (lastJobExecutionContext == null) {
lastJobExecutionContext = context;
} else {
boolean lastJobIsDone = lastJobExecutionContext.getJobRunTime() >= 0;
if (lastJobIsDone) {
lastJobExecutionContext = context;
} else {
vetoExecution = true;
}
}
return vetoExecution;
}
#Override
public String getName() {
return "CustomTriggerListener";
}
}
At the outset let me confess that I am quite an experienced Java developer to be aware of wait/notify problems, but have not been getting clues and that's why felt that more inputs from people can help when we are stuck in something and missing the obvious. I am writing a test to verify a Quartz job class and I do it as follows (Sample):
public class SchedulerTest {
private static final Object LOCK = new Object();
#Test
public void shouldValidateSomethingInJob() {
Object[] paramsForTesting = ...
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
...
CustomJobListenerSupport jobListener =
new CustomJobListenerSupport(LOCK, paramsForTesting);
scheduler.getListenerManager().addJobListener(jobListener,
KeyMatcher.keyEquals(jobKey));
scheduler.start();
// synchronized (LOCK) {
// // Wait for scheduler to end to avoid DB rollback before that
// LOCK.wait();
// }
}
private static class CustomJobListenerSupport extends JobListenerSupport
{
private final Object lock;
public CustomJobListenerSupport(Object lock, Object ... paramsForTesting) {
...;
this.lock = lock;
}
#Override
public String getName() {
return "TestJobListener";
}
#Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {
if (isNotBlank(jobException.getMessage())) {
try {
cleanup(context.getScheduler());
} catch (SchedulerException e) {
logger.error(e);
}
throw new RuntimeException(jobException);
}
performAssertions();
try {
cleanup(context.getScheduler());
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
}
private void performAssertions() {
// Some assertions
}
#SneakyThrows
private void cleanup(Scheduler scheduler) throws SchedulerException {
scheduler.clear();
scheduler.shutdown();
System.out.println("\nLock in cleanup: "+lock);
synchronized (lock) {
lock.notify();
}
}
}
}
Now with the wait code commented out, the test completes but fails because DB changes are rolled back before the scheduler can complete, while if I put the wait the main thread seems to hang (atleast it took very long and did not seem to end) and I am not able to figure out if the job is still running because Junit is not writing the HTML report which contains the log statements. The debug statements indicate that both the classes are using the same LOCK object. Agreed that there are better options than using plain object to lock, but want to try them later as I am not familiar with classes like ReentrantLock
Any pointers will be of much help.
Thanks,
Paddy
I'm using Spring 3.0.5 with an Around aspect.
The #Around aspect works perfectly. The AOP expression targets the interfaces of a bunch of beans.
The aspect executes some logic before and after the invokation:
#Around(...)
public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
// some code
Obj o = pjp.proceed();
// some code
}
No big deal.
Now, I'm trying to create another aspect that throws an exception if the intercepted method takes too long.
private static ExecutorService executor = Executors.newCachedThreadPool();
#Around(...)
public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
Object obj = null;
Callable<Object> task = new Callable<Object>() {
public Object call() {
return pjp.proceed();
}
};
Future<Object> future = executor.submit(task);
try {
obj = future.get(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
...
} catch (InterruptedException e) {
// we ignore this one...
} catch (ExecutionException e) {
throw e.getCause(); // rethrow any exception raised by the invoked method
} finally {
future.cancel(true); // may or may not desire this
}
return obj;
}
When I execute the code with only this aspect applied I get the following exception:
java.lang.RuntimeException: java.lang.IllegalStateException: No
MethodInvocation found: Check that an AOP invocation is in progress,
and that the ExposeInvocationInterceptor is in the interceptor chain.
From the Spring documentation I read:
"Class ExposeInvocationInterceptor
Interceptor that exposes the current MethodInvocation as a thread-local object."
So it looks like that the target got lost because I basically start a new thread and the new thread doesn't have access to thread local. Is there a way to solve this problem or a better approach?
Thanks
The solution was quite trivial. The Aspect that checks how long a method takes must be the last in the "chain" of aspects. I have used the #Order annotation on the Aspect to make it the last one to be executed.
That did the trick.
If the Aspect is not the last to be executed, the new Thread is not able to access the ThreadLocal variable containing the ExposeInvocationInterceptor class.
You could try interrupting the current thread from another thread, if the pjp.proceed() call is amenable to interruption. E.g. your aspect looks like:
new Interrupter(Thread.currentThread()).start();
// Following call will get interrupted if it takes too long
try {
return pjp.proceed();
} catch (InterruptedException ex) {
// do something?
}
where the Interrupter class would be something like:
static class Interrupter extends Thread {
private final Thread other;
Interrupter(final Thread other) {
this.other = other;
}
#Override
public void run() {
try {
Thread.sleep(500); // or whatever your timeout is
} catch (final InterruptedException e) {
e.printStackTrace();
}
if (other.isAlive()) {
other.interrupt();
}
}
}