Hi i have two jobs instances in quartz that i want to not run in parallel, i simplified the code in the example below to show what is not in line with my expectations.
public class QuartzTest {
public static void main( String[] args ) throws SchedulerException {
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start();
JobDetail job1 = newJob( TestJob.class ).withIdentity( "job1", "group1" ).build();
CronTrigger trigger1 = newTrigger().withIdentity( "trigger1", "group1" ).startAt( new Date() ).withSchedule( cronSchedule( getCronExpression( 1 ) ) ).build();
scheduler.scheduleJob( job1, trigger1 );
JobDetail job2 = newJob( TestJob.class ).withIdentity( "job2", "group1" ).build();
CronTrigger trigger2 = newTrigger().withIdentity( "trigger2", "group1" ).startAt( new Date() ).withSchedule( cronSchedule( getCronExpression( 1 ) ) ).build();
scheduler.scheduleJob( job2, trigger2 );
for ( int i = 0; i < 5; i++ ) {
System.out.println( trigger1.getNextFireTime() );
System.out.println( trigger2.getNextFireTime() );
try {
Thread.sleep( 1 * 60 * 1000 );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
}
}
private static String getCronExpression( int interval ) {
return "0 */" + interval + " * * * ?";
}
}
This is the job class
#DisallowConcurrentExecution
public class TestJob implements Job {
#Override
public void execute( JobExecutionContext context ) throws JobExecutionException {
System.out.println( "Job started" );
System.out.println( "Job sleeping 30s..." );
try {
Thread.sleep( 30 * 1000 );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
System.out.println( "Job finished." );
}
}
So here i am scheduling two jobs to run each minute (in the real case one is running every minute the other every 5) and this is the output i am getting:
Job started
Job sleeping 30s...
Job started
Job sleeping 30s...
Job finished.
Job finished.
So both job are running in parallel, because a sequential sequence where job1 waits for job2 to complete before running would give me this sequence
Job started
Job sleeping 30s...
Job finished.
Job started
Job sleeping 30s...
Job finished.
So why is this not happening ?
From docs:
#DisallowConcurrentExecution:
An annotation that marks a Job class as one that must not have multiple instances executed concurrently (where instance is based-upon a JobDetail definition - or in other words based upon a JobKey).
JobKey is composed of both a name and group
In your example the name is not the same, so this is two different jobs.
DisallowConcurrentExecution is ensuring that job1#thread1 is completed before you trigger another job1#thread2.
Related
we can use SimpleTrigger getTimesTriggered to know how many times this schedule have been triggered, for CronTriggerImpl, is there a similar way to achieve this?
for CronTriggerImpl, is there a similar way to achieve this?
TLDR no.
But you can implement JobListener and register it with your scheduler like this:
scheduler.getListenerManager().addJobListener(myJobListener);
That way, you will be notified everytime job started and finished.
You can retrieve Trigger instance from JobExecutionContext
Or similarly you can do with TriggerListener
scheduler.getListenerManager().addTriggerListener(myTriggerListener);
Here is an example program based on #rkosegi suggestion.
Using org.quartz.TriggerListener or org.quartz.JobListener
ByeJob.java
public class ByeJob implements Job {
private ByeService bs = new ByeService();
public void execute(JobExecutionContext context) throws JobExecutionException {
bs.sayGoodbye();
}
}
ByeService.java
public class ByeService {
public void sayGoodbye() {
System.out.println("Testing " + new Date());
}
}
Using GlobalJobListner.java
public class GlobalJobListner implements JobListener {
private static final String TRIGGER_LISTENER_NAME = "GlobalJobListner";
AtomicInteger count = new AtomicInteger(0);
#Override
public String getName() {
return TRIGGER_LISTENER_NAME;
}
#Override
public void jobWasExecuted(JobExecutionContext context) {
String triggerName = context.getTrigger().getKey().toString();
String jobName = context.getJobDetail().getKey().toString();
count.incrementAndGet();
System.out.println("trigger : " + triggerName + " is fired and " + "total count=" + count.get());
System.out.println("job : " + jobName + " is fired and " + "total count=" + count.get());
}
.....................
}
or Using GlobalTriggerListener.java
public class GlobalTriggerListener implements TriggerListener {
private static final String TRIGGER_LISTENER_NAME = "GlobalTriggerListener";
AtomicInteger count = new AtomicInteger(0);
#Override
public String getName() {
return TRIGGER_LISTENER_NAME;
}
#Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
String triggerName = context.getTrigger().getKey().toString();
String jobName = context.getJobDetail().getKey().toString();
count.incrementAndGet();
System.out.println("trigger : " + triggerName + " is fired and " + "total count=" + count.get());
System.out.println("job : " + jobName + " is fired and " + "total count=" + count.get());
}
................
}
Verify
public class Tester {
public static void main(String[] args) {
try {
JobDetail job = JobBuilder.newJob(ByeJob.class).withIdentity("byeJob", "group1").build();
CronTriggerImpl trigger = new CronTriggerImpl();
trigger.setName("T1");
trigger.setCronExpression("0/5 * * * * ?");
Scheduler scheduler2 = new StdSchedulerFactory().getScheduler();
// register global trigger
scheduler2.getListenerManager().addTriggerListener(new GlobalTriggerListener());
// register global listner
scheduler2.getListenerManager().addJobListener(new GlobalJobListner());
scheduler2.start();
scheduler2.scheduleJob(job, trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Output:
13:46:43.402 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 0 triggers
13:46:43.408 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
13:46:45.004 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'group1.byeJob', class=com.example.demo.ByeJob
trigger : DEFAULT.T1 is fired and total count=1
job : group1.byeJob is fired and total count=1
trigger : DEFAULT.T1 is fired and total count=1
job : group1.byeJob is fired and total count=1
13:46:45.009 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
13:46:45.009 [DefaultQuartzScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.byeJob
Testing Fri Apr 22 13:46:45 IST 2022
13:46:50.002 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'group1.byeJob', class=com.example.demo.ByeJob
13:46:50.003 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
trigger : DEFAULT.T1 is fired and total count=2
job : group1.byeJob is fired and total count=2
trigger : DEFAULT.T1 is fired and total count=2
job : group1.byeJob is fired and total count=2
13:46:50.003 [DefaultQuartzScheduler_Worker-2] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.byeJob
Testing Fri Apr 22 13:46:50 IST 2022
13:46:55.003 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'group1.byeJob', class=com.example.demo.ByeJob
trigger : DEFAULT.T1 is fired and total count=3
job : group1.byeJob is fired and total count=3
trigger : DEFAULT.T1 is fired and total count=3
job : group1.byeJob is fired and total count=3
I'm using drools 7.29 in STREAM mode, and i would like to collect events that are not followed by a certain event.
$transaction: TransactionOmDto(service_type == "CASHOUT", transfer_status == "TS", $requestId: transfer_id, $msisdn: msisdn) over window:length(1)
not(TransactionOmDto(service_type == "CONFIRMATION", transfer_status == "TS", transfer_id == $requestId, msisdn == $msisdn, this after [0s, 5m] $transaction))
//How to collect such $transaction in a List ???
For example in my system, when i receive a transaction $t1 that matches the following patterns,
$t1.transfer_id = "my_uniq_transfer_id"
$t1.msisdn == "07xxxxxxxx"
$t1.service_type == "CASHOUT"
$t1.transfer_status == "TS"
then I am also supposed to receive within the next 5 minutes another transaction $t2 from the same POS which should meet the following conditions:
$t2.transfer_id = "my_uniq_transfer_id"
$t2.msisdn == "07xxxxxxxx"
$t2.service_type == "CONFIRMATION"
$t2.transfer_status == "TS"
Sometimes $t2 transactions do not arrive. And the goal of my rule is to report all $t1 transactions that have not had their $t2 confirmation transaction in the system.
Common 'heartbeat arrival problem'. Could you please check existing simplified example whether it suits your needs from here. Please ask additional questions if any.
Rule
dialect 'mvel'
import TemporalReasoningTest.Heartbeat
declare Heartbeat #role (event) end
global java.io.PrintStream stdout
rule "Sound the Alarm"
when
$h: Heartbeat() from entry-point "MonitoringStream"
not(Heartbeat(this != $h, this after[0s,10s] $h) from entry-point "MonitoringStream")
then
stdout.println('No heartbeat')
end
Model and test
#DroolsSession("classpath:/temporalReasoning.drl")
public class TemporalReasoningTest {
public static class Heartbeat {
public int ordinal;
public Heartbeat(int ordinal) {
this.ordinal = ordinal;
}
}
#RegisterExtension
public DroolsAssert drools = new DroolsAssert();
#BeforeEach
public void before() {
drools.setGlobal("stdout", System.out);
}
#Test
#TestRules(expected = {})
public void testRegularHeartbeat() {
Heartbeat heartbeat1 = new Heartbeat(1);
drools.insertAndFireAt("MonitoringStream", heartbeat1);
drools.advanceTime(5, SECONDS);
drools.assertExist(heartbeat1);
Heartbeat heartbeat2 = new Heartbeat(2);
drools.insertAndFireAt("MonitoringStream", heartbeat2);
drools.assertExist(heartbeat1, heartbeat2);
drools.advanceTime(5, SECONDS);
drools.assertDeleted(heartbeat1);
drools.assertExist(heartbeat2);
Heartbeat heartbeat3 = new Heartbeat(3);
drools.insertAndFireAt("MonitoringStream", heartbeat3);
drools.assertExist(heartbeat2, heartbeat3);
drools.advanceTime(5, SECONDS);
drools.assertDeleted(heartbeat2);
drools.assertExist(heartbeat3);
Heartbeat heartbeat4 = new Heartbeat(4);
drools.insertAndFireAt("MonitoringStream", heartbeat4);
drools.assertExist(heartbeat3, heartbeat4);
drools.advanceTime(5, SECONDS);
drools.assertDeleted(heartbeat3);
drools.assertExist(heartbeat4);
drools.assertFactsCount(1);
assertEquals(4, drools.getObject(Heartbeat.class).ordinal);
drools.printFacts();
}
#Test
#TestRules(expectedCount = { "1", "Sound the Alarm" })
public void testIrregularHeartbeat() {
drools.insertAndFireAt("MonitoringStream", new Heartbeat(1));
drools.advanceTime(5, SECONDS);
drools.advanceTime(5, SECONDS);
drools.insertAndFireAt("MonitoringStream", new Heartbeat(2), new Heartbeat(3));
drools.advanceTime(5, SECONDS);
drools.assertFactsCount(2);
drools.printFacts();
}
}
I want to delete a job for which I need job key. I only know the job class name. Please suggest how to get the same using job class name.
You can find this information if you iterate over all jobgroups of your scheduler instances. From there you get the jobKey. With the jobKey you can ask for the jobDetail, which holds the class information. If it matches just return the key.
public JobKey getJobKeyByJobClass (Scheduler scheduler, String className){
for (final String group : scheduler.getJobGroupNames()) {
for (final JobKey jobKey : scheduler.getJobKeys(org.quartz.impl.matchers.GroupMatcher.groupEquals(group))) {
if(className.equals(scheduler.getJobDetail(jobKey).getJobClass().getName())){
return jobKey;
}
}
}
return null;
}
You can obtain JobKey by several ways. Let's imagine that your Job implemenation is MyJob class.
From JobExecutionContext. If your job is executing you can
Scheduler scheduler = schedulerFactory.getScheduler();
JobKey jobKey = null;
for (JobExecutionContext jobCtx : scheduler.getCurrentlyExecutingJobs()) {
JobDetail jobDetail = jobCtx.getJobDetail();
if (MyJob.class.equals(jobDetail.getJobClass())) {
jobKey = jobDetail.getKey();
break;
}
}
same with streams
Scheduler scheduler = schedulerFactory.getScheduler();
Optional<JobDetail> job = scheduler.getCurrentlyExecutingJobs()
.stream()
.map(JobExecutionContext::getJobDetail)
.filter(jobDetail -> MyJob.class.equals(jobDetail.getJobClass()))
.findFirst();
JobKey jobKey = job.isPresent() ? job.get().getKey() : null;
By group name. Ususally, when you're submitting new job for execution you're providing group and job names. If you're not doing so, start, it will make the things easier:)
Scheduler scheduler = schedulerFactory.getScheduler();
JobKey jobKey = null;
for (JobKey jk : scheduler.getJobKeys(GroupMatcher.jobGroupEquals("myGroup"))) {
if (MyJob.class.equals(scheduler.getJobDetail(jk).getJobClass())) {
jobKey = jk;
break;
}
}
same with streams
Scheduler scheduler = schedulerFactory.getScheduler();
Optional<JobDetail> job = scheduler.getJobKeys(GroupMatcher.jobGroupEquals("myGroup"))
.stream()
.map(jk -> scheduler.getJobDetail(jk))
.filter(jobDetail -> MyJob.class.equals(jobDetail.getJobClass()))
.findFirst();
JobKey jobKey = job.isPresent() ? job.get().getKey() : null;
Hope it helps!
I'm trying to get FutureCallbacks from Guava working because I want to use it in my java api for cloudflare.
Test class:
import com.google.common.util.concurrent.*;
import javax.annotation.Nullable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
#org.junit.Test
public static void apiUsersCode( ) {
apiMethodThatReturnsCloudflareInfos( new FutureCallback<String>() {
#Override
public void onSuccess( #Nullable String result ) {
System.out.println( "WORKING" );
}
#Override
public void onFailure( Throwable t ) {
System.out.println( t );
}
} );
}
public static void apiMethodThatReturnsCloudflareInfos( FutureCallback<String> usersFutureCallback ) {
Callable<String> solution = ( ) -> {
Thread.sleep( 1000L ); // simulate slowness of next line
return "http output"; // this is this other method that sends a http request
};
// runs callable async, when done: execute usersFutureCallback
ExecutorService executor = Executors.newFixedThreadPool( 10 );
ListeningExecutorService service = MoreExecutors.listeningDecorator( executor );
ListenableFuture<String> listenableFuture = service.submit( solution );
Futures.addCallback( listenableFuture, usersFutureCallback, service );
}
}
When someone uses this api simplified:
He calls a method and pass a FutureCallback object (usersFutureCallback)
This method runs another method where its output is returned in the callable
Done
Example Api method that is beeing executed by the user
apiMethodThatReturnsCloudflareInfos( new FutureCallback<String>() {
#Override
public void onSuccess( #Nullable String cloudflareOutput ) {
System.out.println( "WORKING" );
}
#Override
public void onFailure( Throwable t ) {
System.out.println( t );
}
} );
Example api methods simplefied doing
public static void apiMethodThatReturnsCloudflareInfos( FutureCallback<String> usersFutureCallback ) {
Callable<String> solution = ( ) -> {
Thread.sleep( 1000L ); // simulate slowness of next line
return "http output"; // this is this other method that sends a http request
};
// runs callable async, when done: execute usersFutureCallback
ExecutorService executor = Executors.newFixedThreadPool( 10 );
ListeningExecutorService service = MoreExecutors.listeningDecorator( executor );
ListenableFuture<String> listenableFuture = service.submit( solution );
Futures.addCallback( listenableFuture, usersFutureCallback, service );
}
"WORKING" is only printed when changing and adding this lines to add the listener after the callable is done.
But that is not the solution of cause.
public static void apiMethodThatReturnsCloudflareInfos( FutureCallback<String> usersFutureCallback ) {
Callable<String> solution = ( ) -> {
Thread.sleep( 1000L ); // simulate slowness of next line
return "http output"; // this is this other method that sends a http request
};
// runs callable async, when done: execute usersFutureCallback
ExecutorService executor = Executors.newFixedThreadPool( 10 );
ListeningExecutorService service = MoreExecutors.listeningDecorator( executor );
ListenableFuture<String> listenableFuture = service.submit( solution );
// i don't want that
try {
Thread.sleep( 10001L );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
Futures.addCallback( listenableFuture, usersFutureCallback, service );
}
What am I doing wrong?
The problem lies in how you are testing that method. You basically create a new thread that runs your Callable<String> solution, but nothing will wait for that thread to finish. Once your test method is finished, your test suite will terminate and with it the thread that is running solution.
In a real life scenario, where your application lives longer than solution, you won't encounter that problem.
To solve this, you must update your test case to not terminate prematurely. You could do this by updating the apiUsersCode() to set some value in onSuccess and onFailure, which you wait for after calling apiMethodThatReturnsCloudflareInfos.
Solved!
The problem was the test suit JUnit.
It worked when testing it in a main(String[] args) method.
...everything is correct only what should help me with the tests makes the mistake :D
#Test
public void apiUsersCode( ) {
apiMethodThatReturnsCloudflareInfos( new FutureCallback<String>() {
#Override
public void onSuccess( #Nullable String result ) {
System.out.println( "WORKING" );
}
#Override
public void onFailure( Throwable t ) {
System.out.println( t );
}
} );
try {
new CountDownLatch( 1 ).await(2000, TimeUnit.MILLISECONDS);
} catch ( InterruptedException e ) {
e.printStackTrace();
}
}
public static void apiMethodThatReturnsCloudflareInfos( FutureCallback<String> usersFutureCallback ) {
Callable<String> solution = ( ) -> {
Thread.sleep( 1000L ); // simulate slowness of next line
return "http output"; // this is this other method that sends a http request
};
// runs callable async, when done: execute usersFutureCallback
ExecutorService executor = Executors.newFixedThreadPool( 10 );
ListeningExecutorService service = MoreExecutors.listeningDecorator( executor );
ListenableFuture<String> listenableFuture = service.submit( solution );
Futures.addCallback( listenableFuture, usersFutureCallback, service );
}
I am trying to execute a series of jobs where one job execute other two, and one of the two execute another.
Job 1 --> Job 3
--> Job 2 -->Job 4
The jobs are for sending data from db.
This is what i have done
#PersistJobDataAfterExecution
#DisallowConcurrentExecution
public class MembersJob implements Job{
List<Member>unsentMem=new ArrayList<Member>();
JSONArray customerJson = new JSONArray();
Depot depot;
public MembersJob(){
depot = new UserHandler().getDepot();
}
public void execute(JobExecutionContext jec) throws JobExecutionException {
if ( Util.getStatus() ){
runSucceedingJobs(jec);
} else {
System.out.println("No internet connection");
}
}
public void runSucceedingJobs(JobExecutionContext context){
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
Object milkCollectionJobObj = jobDataMap.get("milkCollectionJob");
MilkCollectionsJob milkCollectionsJob = (MilkCollectionsJob)milkCollectionJobObj;
Object productsJobObj = jobDataMap.get("milkCollectionJob");
ProductsJob productsJob = (ProductsJob)productsJobObj;
try {
milkCollectionsJob.execute(context);
productsJob.execute(context);
} catch (JobExecutionException ex) {
System.out.println("Error...");
Logger.getLogger(MembersJob.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Call jobs in series
//Members Job
JobKey membersJobKey = new JobKey("salesJob", "group1");
JobDetail membersJob = JobBuilder.newJob(MembersJob.class)
.withIdentity(membersJobKey).build();
membersJob.getJobDataMap().put("milkCollectionJob", new MilkCollectionsJob());
membersJob.getJobDataMap().put("productsJob", new ProductsJob());
//
CronTrigger membersTrigger = newTrigger()
.withIdentity("salesTrigger", "group1")
.withSchedule(
cronSchedule("0/10 * * * * ?"))
.forJob(membersJobKey)
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.scheduleJob(membersJob, membersTrigger);
scheduler.start();
The problem is members job starts but does not start other jobs when it is done. What is the easiest and fastest way to achieve this?