quartz.net simple example as per the time given - quartz-scheduler

I am new to quartz.net. I want to build a simple window based application which scheduled the task. Suppose I have 4 task ans it start and end time
Example
Breakfast ; 8:00;8:30
Lunch;13:00;13:30
dinner;19:30;20:00
Now I want when I click on button at 8:00 AM a message box should appear with a text "breakfast started!!!" at 8:30 AM again a message box should appear with a text as "Breakfast end!!!" and so on.
I had gone through tutorial. But confuse how to proceed. Can any one help me out?
EDIT
Is it possible to use one job and one triggers for this?
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
for (int i = 0; i < listBox1.Items.Count; i++)
{
string[] strArr = Regex.Split(listBox1.Items[i].ToString(), #";", RegexOptions.Multiline);
// define the job and tie it to our HelloJob class
IJobDetail jobStart = JobBuilder.Create<HelloJob>()
.WithIdentity("job" + i, "group1") // name "myJob", group "group1"
.StoreDurably()
.UsingJobData("jobSays", strArr[0].ToUpper().Trim() + " " + "starts")
.Build();
string[] ArrStart = strArr[1].Trim().Split(':');
ITrigger triggerstart = TriggerBuilder.Create()
.WithIdentity("trigger" + i, "group1")
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(Convert.ToInt32(ArrStart[0]), Convert.ToInt32(ArrStart[1])))
// .WithSimpleSchedule(x => x.WithMisfireHandlingInstructionIgnoreMisfires()
.ForJob(jobStart)
.Build();
// .WithSchedule(CronScheduleBuilder.CronSchedule("0 4 06 1/1 * ? *"))
// Tell quartz to schedule the job using our trigger
scheduler.ScheduleJob(jobStart, triggerstart);
scheduler.Start();
string[] Arrend = strArr[2].Trim().Split(':');
IJobDetail jobend = JobBuilder.Create<HelloJob>()
.WithIdentity("job1" + i, "group1")
.StoreDurably()
.UsingJobData("jobSays", strArr[0].ToUpper().Trim() + " " + "end")
.Build();
ITrigger triggerend = TriggerBuilder.Create()
.WithIdentity("trigger1" + i, "group1")
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(Convert.ToInt32(Arrend[0]), Convert.ToInt32(Arrend[1])))
// .WithSimpleSchedule(x => x.WithMisfireHandlingInstructionIgnoreMisfires()
.ForJob(jobend)
.Build();
scheduler.ScheduleJob(jobend, triggerend);
scheduler.Start();
}
}
catch (SchedulerException se)
{
Console.WriteLine("Scheduler Exception : " + se);
}
}
public class HelloJob : IJob
{
public void Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key;
JobDataMap dataMap = context.JobDetail.JobDataMap;
string jobSays = dataMap.GetString("jobSays");
MessageBox.Show(jobSays);
}
}

You can schedule a job as follows and use this simple example:
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
IJobDetail job = JobBuilder.Create<MealAlertJob>()
.WithIdentity("mealJob", "mealJobGroup")
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("breakfastStartTrigger", "mealTriggerGroup")
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(8, 0))
.ForJob(job)
.Build();
ITrigger trigger1 = TriggerBuilder.Create()
.WithIdentity("breakfastEndTrigger", "mealTriggerGroup")
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(8, 30))
.ForJob(job)
.Build();
scheduler.ScheduleJob(job, trigger);
scheduler.ScheduleJob(trigger1);
scheduler.Start();
You should implement your logic (let's say showing a message box) in your job class's "Execute" method.
Defining multiple triggers for your job results in multiple execution. In the job class for example "MealAlertJob" you may check the time and show an alert based on it.
In the above example you can add more triggers for the other four times (lunch start/end and dinner start/end).
public class MealAlertJob : IJob
{
public void Execute(IJobExecutionContext context)
{
var now = DateTime.Now;
var hour = now.Hour;
var minute = now.Minute;
if (hour == 8 && minute == 0)
System.Windows.Forms.MessageBox.Show("Breakfast started!!!");
//and so on....
}
}
or something like this.

Related

How to do with Quartz put some trigger to one job?

public static void sendEmailForNewTrigger(String jobName, String triggerName, Date sendDate) {
try {
Scheduler sched = QuartzSchedulerManager.getInstanceScheduler();
JobKey jobKey = JobKey.jobKey(jobName, "group_email");
JobDetail job = sched.getJobDetail(jobKey);
SimpleTrigger trigger = (SimpleTrigger) newTrigger().withIdentity(triggerName)
.startAt(sendDate)
.build();
if (job == null) {
job = newJob(SendEmailJob.class).withIdentity(jobName, "group_email").build();
}
sched.scheduleJob(job, trigger);
log.info(jobName + " will run at: " + sendDate);
} catch (SchedulerException e) {
log.error(e.toString());
throw new RuntimeException(e.getMessage());
}
}
in my code ,it can't work, always tell me 'Unable to store Job : 'group_email.send_1', because one already exists with this identification.' i don't know how to do ,just want to add two or more trigger to one job.

how to run multiple jobs in quartz scheduler in struts

I implemented multiple jobs in quartz scheduler as a plugin in struts but only first job is running by using the tutorial from http://www.mkyong.com/struts/struts-quartz-scheduler-integration-example/
public class QuartzPlugin implements PlugIn {
private JobDetail job = null;
private Trigger trigger = null;
private Scheduler scheduler = null;
private static Class<QuartzPlugin> clazz = QuartzPlugin.class;
public static final String KEY_NAME = clazz.getName();
private static Logger logger = Logger.getLogger(clazz);
#Override
public void destroy() {
try {
String METHODNAME = "destroy";
logger.debug("entering " + KEY_NAME + " " + METHODNAME);
scheduler.shutdown();
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void init(ActionServlet servlet, ModuleConfig modConfig) throws ServletException {
String METHODNAME = "init";
logger.debug("entering " + KEY_NAME + " " + METHODNAME);
job = JobBuilder.newJob(SchedulerJob.class).withIdentity("anyJobName","group1").build();
try {
trigger = TriggerBuilder.newTrigger().withIdentity("anyTriggerName", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?")).build();
scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
servlet.getServletContext().setAttribute(KEY_NAME, scheduler);
// define the job and tie it to our HelloJob class
JobDetail job2 = JobBuilder.newJob(HelloJob.class).withIdentity("job2", "group2").build();
// Trigger the job to run on the next round minute
Trigger trigger2 = TriggerBuilder.newTrigger().withIdentity("trigger2", "group2")
.withSchedule(CronScheduleBuilder.cronSchedule("15/45 * * * * ?")).build();
// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);
scheduler.scheduleJob(job2, trigger2);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}

getNextFireTime of my existing job

I tried Quartz.com documentation & googled for couple for hours...but could not find single good article on how to get Next Job (which is supposr to fire in future).
I am using CronTrigger Expression to Schedule jobs, using VB.net (winforms). jobs works fine...however I would like my users to see when will it FIRE next. I am storing CronExpression in my database, Can I use that Expression to show next Fire Date/Time to my end users? or if there is any other way possible please advise (with a simple samply).
Thanks
Edit
Following Code Returns next job will fire after 12 minutes instead of 20 minute
Dim exp As CronExpression = New CronExpression("0 0/20 * * * ?")
Dim nextFire As String = exp.GetNextValidTimeAfter(DateTime.Now)
MsgBox(nextFire)
You can create a new JobKey
JobKey jobKey = new JobKey(jobName, groupName);
and use the key to fetch the job detail:
var detail = scheduler.GetJobDetail(jobKey);
this is a simple function which does what you're looking for:
private DateTime getNextFireTimeForJob(IScheduler scheduler, string jobName, string groupName = "")
{
JobKey jobKey = new JobKey(jobName, groupName);
DateTime nextFireTime = DateTime.MinValue;
bool isJobExisting = Scheduler.CheckExists(jobKey);
if (isJobExisting)
{
var detail = scheduler.GetJobDetail(jobKey);
var triggers = scheduler.GetTriggersOfJob(jobKey);
if (triggers.Count > 0)
{
var nextFireTimeUtc = triggers[0].GetNextFireTimeUtc();
nextFireTime = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTimeUtc.Value.DateTime);
}
}
return (nextFireTime);
}
It works only if you have one trigger per job.
If there's more than one trigger in your job you can loop through them:
foreach (ITrigger trigger in triggers)
{
Console.WriteLine(jobKey.Name);
Console.WriteLine(detail.Description);
Console.WriteLine(trigger.Key.Name);
Console.WriteLine(trigger.Key.Group);
Console.WriteLine(trigger.GetType().Name);
Console.WriteLine(scheduler.GetTriggerState(trigger.Key));
DateTimeOffset? nextFireTime = trigger.GetNextFireTimeUtc();
if (nextFireTime.HasValue)
{
Console.WriteLine(TimeZone.CurrentTimeZone.ToLocalTime(nextFireTime.Value.DateTime).ToString());
}
}
or using Linq (System.Linq) :
var myTrigger = triggers.Where(f => f.Key.Name == "[trigger name]").SingleOrDefault();
If you already know the cronExpression then you can call GetNextValidTimeAfter , eg
CronExpression exp = new CronExpression("0 0 0/1 1/1 * ? *");
var nextFire = exp.GetNextValidTimeAfter(DateTime.Now);
Console.WriteLine(nextFire);
and if you want more fire times, then
for(int i=0 ; i< 9; i++)
{
if (nextFire.HasValue)
{
nextFire = exp.GetNextValidTimeAfter(nextFire.Value);
Console.WriteLine(nextFire);
}
}
If you are looking for a more general way of showing next fire times for existing jobs, then check out the answer to this question which works even if you aren't using cron expressions. This is for Quartz Version 2
The Quartz Version 1 way of getting job and trigger information is something like the method below.
public void GetListOfJobs(IScheduler scheduler)
{
var query =
(from groupName in scheduler.JobGroupNames
from jobName in scheduler.GetJobNames(groupName)
let triggers = scheduler.GetTriggersOfJob(jobName, groupName)
select new
{
groupName,
jobName,
triggerInfo = (from trigger in triggers select trigger)
}
);
foreach (var r in query)
{
if (r.triggerInfo.Count() == 0)
{
var details = String.Format("No triggers found for : Group {0} Job {1}", r.groupName, r.jobName);
Console.WriteLine(details);
}
foreach (var t in r.triggerInfo)
{
var details = String.Format("{0,-50} {9} Next Due {1,30:r} Last Run {2,30:r} Group {3,-30}, Trigger {4,-50} {5,-50} Scheduled from {6:r} Until {7,30:r} {8,30:r} ",
t.JobName,
t.GetNextFireTimeUtc().ToLocalTime(),
t.GetPreviousFireTimeUtc().ToLocalTime(),
t.JobGroup,
t.Name,
t.Description,
t.StartTimeUtc.ToLocalTime(),
t.EndTimeUtc.ToLocalTime(),
t.FinalFireTimeUtc.ToLocalTime(),
((t.GetNextFireTimeUtc() > DateTime.UtcNow) ? "Active " : "InActive")
);
Console.WriteLine(details);
}
}
}
I had problem where i am getting next execution time even for the date which are past the end time.
Following is my implementation which eventually returning the correct result in Quartz 3.0.4
internal static DateTime? GetNextFireTimeForJob(IJobExecutionContext context)
{
JobKey jobKey = context.JobDetail.Key;
DateTime? nextFireTime = null;
var isJobExisting = MyQuartzScheduler.CheckExists(jobKey);
if (isJobExisting.Result)
{
var triggers = MyQuartzScheduler.GetTriggersOfJob(jobKey);
if (triggers.Result.Count > 0)
{
var nextFireTimeUtc = triggers.Result.First().GetNextFireTimeUtc();
if (nextFireTimeUtc.HasValue)
{
nextFireTime = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTimeUtc.Value.DateTime);
}
}
}
return nextFireTime;
}

Quartz jobs - disallow concurrent execution group-wide?

using Quartz, I'd like few jobs (say about 10) to execute as a chain - i.e. NOT concurrently.
They should be executed after an "accounting day change" event occur but since they all access the same DB, I dont want them to start all together. I want them to be executed sequentially instead (order doesnt matter).
I have an idea to put them into a group - say "account_day_change_jobs" and configure Quartz somehow to do the rest for me :-) Means - run sequentially all jobs from the group. I tried the API doc (both 1.8 and 2.1), tried google but didnt find anything.
Is it possible? Is it even reasonable? Other ideas how to achieve the behavior I want?
Thanks very much for any ideas :-)
Hans
The Trigger Listener class below should re-schedule any jobs that attempt to execute while another job that the listener has been configured for is running.
Ive only lightly tested it but for simple cases it should be suitable.
public class SequentialTriggerListener extends TriggerListenerSupport {
private JobKey activeJob;
private Scheduler activeScheduler;
private Queue<JobDetail> queuedJobs = new ConcurrentLinkedQueue<JobDetail>();
public String getName() {
return "SequentialTriggerListener";
}
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
synchronized (this) {
if (activeJob != null) {
getLog().debug("Queueing Sequential Job - " + context.getJobDetail().getKey().getName());
JobDetail jd = context.getJobDetail();
activeScheduler = context.getScheduler();
jd = JobBuilder.newJob().usingJobData(jd.getJobDataMap()).withIdentity(getName() + ":" + jd.getKey().getName(), jd.getKey().getGroup())
.ofType(jd.getJobClass()).build();
queuedJobs.add(jd);
return true;
} else {
activeJob = trigger.getJobKey();
getLog().debug("Executing Job - " + activeJob.getName());
return false;
}
}
}
public void triggerMisfired(Trigger trigger) {
triggerFinalized(trigger);
}
public void triggerComplete(Trigger trigger, JobExecutionContext context, CompletedExecutionInstruction triggerInstructionCode) {
triggerFinalized(trigger);
}
protected void triggerFinalized(Trigger trigger) {
synchronized (this) {
try {
if (trigger.getJobKey().equals(activeJob)) {
getLog().debug("Finalized Sequential Job - " + activeJob.getName());
activeJob = null;
JobDetail jd = queuedJobs.poll();
if (jd != null) {
getLog().debug("Triggering Sequential Job - " + jd.getKey().getName());
activeScheduler.scheduleJob(jd,TriggerBuilder.newTrigger().forJob(jd).withIdentity("trigger:" + jd.getKey().getName(), jd.getKey().getGroup())
.startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0).withIntervalInMilliseconds(1)).build());
}
} else {
// this should not occur as the trigger finalizing should be the one we are tracking.
getLog().warn("Sequential Trigger Listener execution order failer");
}
} catch (SchedulerException ex) {
getLog().warn("Sequential Trigger Listener failure", ex);
}
}
}
}
its has been a long time since I used quartz, however I would try two job listeners registered to listen to two different groups
the basic idea is to have one job fire from a group / list ("todayGroup"), the '("todayGroup") job listener detects the completion for good or bad. then kicks off the next job in the list. However, it saves the 'just finished' job back in the scheduler under the ("tomorrowGroup").
public class MyTodayGroupListener extends JobListenerSupport {
private String name;
private static String GROUP_NAME = "todayGroup";
public MyOtherJobListener(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {
Scheduler sched = context.getScheduler();
// switch the job to the other group so we don't run it again today.
JobDetail current = context.getJobDetail();
JobDetail tomorrows = current.getJobBuilder().withIdentity(current.getKey().getName(), "tomorrow").build();
sched.addJob(tomorrows,true);
//see if there is anything left to run
Set<JobKey> jobKeys = sched.getJobKeys(groupEquals(GROUP_NAME ));
Iterator<JobKey> nextJob = null;
if(jobKeys != null && !jobKeys.isEmpty() ){
nextJob = jobKeys.iterator();
}
if(nextJob != null){
// Define a Trigger that will fire "now" and associate it with the first job from the list
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.forJob(nextJob =.next())
.build();
// Schedule the trigger
sched.scheduleJob(trigger);
}
}
}
likewise, you'll need two 'group triggers' that will fire the first job from their respective groups at the given time you want.
public class TriggerGroupDisallowConcurrentExecutionTriggerListener : ITriggerListener
{
private IScheduler activeScheduler;
private readonly object locker = new object();
private ConcurrentDictionary<string, JobsQueueInfo> groupsDictionary = new ConcurrentDictionary<string, JobsQueueInfo>();
public string Name => "TriggerGroupDisallowConcurrentExecutionTriggerListener";
public Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default)
{
//JobKey key = context.JobDetail.Key;
//Console.WriteLine($"{DateTime.Now}: TriggerComplete. {key.Name} - {key.Group} - {trigger.Key.Name}");
TriggerFinished(trigger, cancellationToken);
return Task.CompletedTask;
}
public Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
{
//JobKey key = context.JobDetail.Key;
//Console.WriteLine($"{DateTime.Now}: TriggerFired. {key.Name} - {key.Group} - {trigger.Key.Name}");
return Task.CompletedTask;
}
public Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default)
{
//JobKey key = trigger.JobKey;
//Console.WriteLine($"{DateTime.Now}: TriggerMisfired. {key.Name} - {key.Group} - {trigger.Key.Name}");
TriggerFinished(trigger, cancellationToken);
return Task.CompletedTask;
}
public Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
{
//JobKey key = context.JobDetail.Key;
//Console.WriteLine($"{DateTime.Now}: VetoJobExecution. {key.Name} - {key.Group} - {trigger.Key.Name}");
lock (locker)
{
//if (!groupsDictionary.ContainsKey(context.JobDetail.Key.Group))
//{
groupsDictionary.TryAdd(context.JobDetail.Key.Group, new JobsQueueInfo { QueuedJobs = new ConcurrentQueue<IJobDetail>(), ActiveJobKey = null });
var activeJobKey = groupsDictionary[context.JobDetail.Key.Group].ActiveJobKey;
//}
if (activeJobKey != null && activeJobKey != context.JobDetail.Key)
{
var queuedJobs = groupsDictionary[context.JobDetail.Key.Group].QueuedJobs;
if (queuedJobs.Any(jobDetail => jobDetail.Key.Name == context.JobDetail.Key.Name) == true)
{
//NOTE: Джоба уже есть в очереди, нет необходимости её добавлять повторно
return Task.FromResult(true);
}
else
{
//NOTE: Добавить джобу в очередь на выполнение, и не выполнять её сейчас, т.к. она будет выполнена как только подойдёт её очередь
activeScheduler = context.Scheduler;
var newJob = JobBuilder.Create(context.JobDetail.JobType).WithIdentity(context.JobDetail.Key).Build();
queuedJobs.Enqueue(newJob);
return Task.FromResult(true);
}
}
groupsDictionary[context.JobDetail.Key.Group].ActiveJobKey = trigger.JobKey;
return Task.FromResult(false);
}
}
protected void TriggerFinished(ITrigger trigger, CancellationToken cancellationToken = default)
{
lock (locker)
{
try
{
if (!groupsDictionary.ContainsKey(trigger.JobKey.Group))
{
return;
}
var queuedJobs = groupsDictionary[trigger.JobKey.Group].QueuedJobs;
if (queuedJobs.TryDequeue(out IJobDetail jobDetail))
{
//Console.WriteLine($"dequeue - {jobDetail.Key.Name}");
var task = activeScheduler.TriggerJob(jobDetail.Key, cancellationToken);
task.ConfigureAwait(false);
task.Wait(cancellationToken);
groupsDictionary[trigger.JobKey.Group].ActiveJobKey = jobDetail.Key;
}
else
{
groupsDictionary[trigger.JobKey.Group].ActiveJobKey = null;
}
}
catch (SchedulerException ex)
{
throw;
}
}
}
private class JobsQueueInfo
{
public ConcurrentQueue<IJobDetail> QueuedJobs { get; set; }
public JobKey ActiveJobKey { get; set; }
}
}

CRM 2011, Stopping custom workflow programmatically

i've been trying to stop a workflow programmatically.
I've read both in various posts and in the msdn that this can be done by updating
the Asyncoperation status via update request.
However everytime i update the request. the workflow get stuck on a mid stage such as cancelling or pausing and dosen't reach a final state.
any ideas?
protected void ExecutePostAccountUpdate(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
string fetchXML = "<fetch mapping='logical' count='50' version='1.0'>" +
"<entity name='asyncoperation'>" +
"<filter>" +
"<condition attribute='regardingobjectid' operator='eq' value='" +
localContext.PluginExecutionContext.PrimaryEntityId + "' />" +
"</filter>" +
"</entity>" +
"</fetch>";
EntityCollection col = localContext.OrganizationService.RetrieveMultiple(new FetchExpression(fetchXML));
if (col.Entities.Count > 0)
{
AsyncOperation a = (AsyncOperation)col[0];
a.StateCode = AsyncOperationState.Completed;
a.StatusCode = new OptionSetValue(32);
localContext.OrganizationService.Update(a);
}
}
Have a look at my blog: How to Cancel Workflow Programmatically using C#
Make sure the user have permissions to Cancel System Jobs.
QueryExpression queryExpression = new QueryExpression("asyncoperation") { ColumnSet = new ColumnSet("statuscode") };
queryExpression.Criteria.AddCondition("name", ConditionOperator.Equal, Name of Workflow);
queryExpression.Criteria.AddCondition("regardingobjectid", ConditionOperator.Equal, regardingobjectId);
var asyncOperations = organizationService.RetrieveMultiple(queryExpression);
foreach (var asyncOperation in asyncOperations.Entities)
{
if (((OptionSetValue)asyncOperation["statuscode"]).Value == 10 || // Waiting
((OptionSetValue)asyncOperation["statuscode"]).Value == 20 || // In Process
((OptionSetValue)asyncOperation["statuscode"]).Value == 0)
{
Entity operation = new Entity("asyncoperation")
{
Id = asyncOperation.Id,
["statecode"] = new OptionSetValue(3),
["statuscode"] = new OptionSetValue(32)
};
organizationService.Update(operation);
}
}
Make sure the user have permissions to Cancel System Jobs.
It seems you can un-publish a workflow via code, according to this post.
NOTE: This does not necessarily halt an in-progress workflow, but it will prevent any new workflows of that type from being started.
const int WorkflowStatusDraft = 1;
const int WorkflowStatusPublished = 2;
public void PublishWorkflow(Guid workflowId)
{
SetStateWorkflowRequest publishRequest = new SetStateWorkflowRequest();
publishRequest.EntityId = workflowId;
publishRequest.WorkflowState = WorkflowState.Published;
publishRequest.WorkflowStatus = WorkflowStatusPublished;
this.CrmService.Execute(publishRequest);
}
public void UnpublishWorkflow(Guid workflowId)
{
SetStateWorkflowRequest unpublishRequest = new SetStateWorkflowRequest();
unpublishRequest.EntityId = workflowId;
unpublishRequest.WorkflowState = WorkflowState.Draft;
unpublishRequest.WorkflowStatus = WorkflowStatusDraft;
this.CrmService.Execute(unpublishRequest);
}