Decouple roles in Quartz.net - c#

I am currently investigating using Quartz.NET for scheduling tasks in my system. As an example of how I am using Quartz.NET, below is a very simple example demonstrating how I am scheduling a task:
class Program
{
static void Main(string[] args)
{
var properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "TestScheduler";
properties["quartz.scheduler.instanceId"] = "instance_one";
properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
properties["quartz.jobStore.useProperties"] = "true";
properties["quartz.jobStore.dataSource"] = "default";
properties["quartz.jobStore.tablePrefix"] = "QRTZ_";
properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";
properties["quartz.dataSource.default.connectionString"] = "Server=.\\SqlExpress;Database=quartz;Trusted_Connection=True;";
properties["quartz.dataSource.default.provider"] = "SqlServer-20";
var scheduler = new StdSchedulerFactory(properties).GetScheduler();
scheduler.Start();
TriggerSimpleJob(scheduler);
Console.WriteLine("Waiting For Job");
Console.ReadLine();
}
private static void TriggerSimpleJob(IScheduler scheduler)
{
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartAt(DateBuilder.EvenSecondDateAfterNow())
.UsingJobData("myTriggerParameter", "myTriggerValue")
.UsingJobData("myParameter", "triggerParameter")
.Build();
IJobDetail jobDetail = JobBuilder.Create<SimpleJob>().WithIdentity("job1", "group1")
.UsingJobData("myParameter", "myValue")
.Build();
scheduler.ScheduleJob(jobDetail, trigger);
}
}
public class SimpleJob : IJob
{
public void Execute(IJobExecutionContext context)
{
Console.WriteLine("Job completed");
}
}
The question I have is this:
I would like to decouple of the scheduling of jobs from the execution of jobs.
In the above example, after the job has been scheduled, if the process is still running when the scheduled time arrives the job is executing within this process. Ideally I would like to be able to have a dedicated server with an instance of the Quartz.NET scheduler running that is dedicated to executing jobs, and be able to schedule jobs from other processes knowing the job will be executed on this dedicated server.
I have tried simply setting the property "quartz.threadPool.threadCount" to "0" on the process that schedules jobs, but this throws an exception. Is there any configuration properties on the scheduler that will achieve what I am trying to do?

Good morning,
you can read my answer here.
What I would suggest is to use ADO.NET Job Store (and it seems you're using it).
The application in charge of scheduling jobs should be configured setting the property threadPool to ZeroSizeThreadPool:
properties["quartz.threadPool.type"] = "Quartz.Simpl.ZeroSizeThreadPool, Quartz";
You can read more about this type of thread-pool here.
the application in charge of the execution of the jobs should be configured with these settings:
properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
properties["quartz.threadPool.threadCount"] = "10";
properties["quartz.threadPool.threadPriority"] = "Normal";

Remove the line
scheduler.Start();

Related

Show next job execution scheduled time

I was wondering if Quartz.Net has a way to tell (or better write in the logs) on the next execution... I mean, I have got a job that runs at 10:00 AM and it's scheduled to run every two hours... is there a way I can write something as Next run on 12:00AM? or do I have to parse the cron expression, then add it to the current date?
Thanks in advance
Not sure what logger you are using, but this approach should work with any logger that you can create instances of. Create instance of logger and pass it to job via JobDataMap and then use it inside of job. IJobExecutionContext.NextFireTimeUtc will tell you next execution time which you can write to a logger
using System;
using System.Threading.Tasks;
using NLog;
using Quartz;
using Quartz.Impl;
namespace QuartzSampleApp
{
public class Program
{
private static async Task Main(string[] args)
{
var logger = LogManager.GetCurrentClassLogger();
StdSchedulerFactory factory = new StdSchedulerFactory();
IScheduler scheduler = await factory.GetScheduler();
await scheduler.Start();
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("job1", "group1")
.Build();
job.JobDataMap["logger"] = logger; // add logger to job data map
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(10)
.RepeatForever())
.Build();
await scheduler.ScheduleJob(job, trigger);
await Task.Delay(TimeSpan.FromSeconds(60));
await scheduler.Shutdown();
Console.WriteLine("Press any key to close the application");
Console.ReadKey();
}
// simple log provider to get something to the console
}
public class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Console.Out.WriteLineAsync("Greetings from HelloJob!");
var logger = context.JobDetail.JobDataMap["logger"] as ILogger;
logger.Log(LogLevel.Info, "Next job execution at " + context.NextFireTimeUtc);
}
}
}

Configuring quartz.net scheduler in .net

I have an application in .Net framework and I'm using quartz scheduler. I need to configure quartz.
Now I have one method which is fired every 15 minutes. These method is used to do some work with database. I want, in case, that work of procedure is complete, then start waiting period and after that period again start these database method.
For procedure there will be maximum time which cannot be longer. For examplpe 60 minutes. Do you have any ideas how to configure length of working procedure, how to stop when work is finished and how to define waiting time between?
// configure Quartz
var stdSchedulerProperties = new NameValueCollection
{
{ "quartz.threadPool.threadCount", "10" },
{ "quartz.jobStore.misfireThreshold", "60000" }
};
var stdSchedulerFactory = new StdSchedulerFactory(stdSchedulerProperties);
var scheduler = stdSchedulerFactory.GetScheduler().Result;
scheduler.Start();
// create job and specify timeout
IJobDetail job = JobBuilder.Create<JobWithTimeout>()
.WithIdentity("job1", "group1")
.UsingJobData("timeoutInMinutes", 60)
.Build();
// create trigger and specify repeat interval
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartNow()
.WithSimpleSchedule(x => x.WithIntervalInMinutes(15).RepeatForever())
.Build();
// schedule job
scheduler.ScheduleJob(job, trigger).Wait();
/// <summary>
/// Implementation of IJob. Represents the wrapper job for a task with timeout
/// </summary>
public class JobWithTimeout : IJob
{
public Task Execute(IJobExecutionContext context)
{
return Task.Run(() => Execute(context));
}
public void Execute(IJobExecutionContext context)
{
Thread workerThread = new Thread(DoWork);
workerThread.Start();
context.JobDetail.JobDataMap.TryGetValue("timeoutInMinutes", out object timeoutInMinutes);
TimeSpan timeout = TimeSpan.FromMinutes((int)timeoutInMinutes);
bool finished = workerThread.Join(timeout);
if (!finished) workerThread.Abort();
}
public void DoWork()
{
// do stuff
}
}

Jobs not chaining using JobChainingJobListener

I have the current code for my Quartz scheduler:
var scheduler = StdSchedulerFactory.GetDefaultScheduler();
// Job1
var Job1 = JobBuilder.Create<Test1>().WithIdentity("job1", "group1").Build();
// Job2
var Job2 = JobBuilder.Create<Test2>().WithIdentity("job2", "group2").Build();
// Triggers
ITrigger trigger1 = TriggerBuilder.Create().WithIdentity("trigger1", "group1").StartNow().Build()
ITrigger trigger2 = TriggerBuilder.Create().WithIdentity("trigger2", "group2").StartNow().WithSimpleSchedule(x => x.WithIntervalInSeconds(1).WithRepeatCount(4)).Build();
// JobKeys
JobKey jobKey1 = new JobKey("Job1", "group1");
JobKey jobKey2 = new JobKey("Job2", "group2");
// Chain jobs
JobChainingJobListener chain = new JobChainingJobListener("testChain");
chain.AddJobChainLink(jobKey1, jobKey2);
scheduler.ScheduleJob(Job1, trigger1);
scheduler.AddJob(Job2, true);
// Global listener here. I am not sure what I have is correct.
scheduler.ListenerManager.AddJobListener(chain, GroupMatcher<JobKey>.AnyGroup());`
scheduler.Start();
(For clarification, the jobs do nothing more than print to console at the moment.)
From the Quartz website, I found that this will add a JobListener that is interested in all jobs: scheduler.ListenerManager.AddJobListener(chain, GroupMatcher<JobKey>.AnyGroup()); I'm not sure that this is equivalent to a global listener.
I also found that some code where people have done scheduler.addGlobalJobListener(chain); in Java. Is there an equivalent method in c#?
My code compiles and seems to run without errors, but Job2 does not trigger. Job1 prints properly to console.
The issue here is that you have misspelled the key the second time ("Job1" vs "job1") which causes there to be no known link to fire. Here's updated code sample with redundancies removed.
var scheduler = StdSchedulerFactory.GetDefaultScheduler();
JobKey jobKey1 = new JobKey("job1", "group1");
JobKey jobKey2 = new JobKey("job2", "group2");
var job1 = JobBuilder.Create<Test1>().WithIdentity(jobKey1).Build();
var job2 = JobBuilder.Create<Test2>().WithIdentity(jobKey2).StoreDurably(true).Build();
ITrigger trigger1 = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartNow()
.Build();
JobChainingJobListener chain = new JobChainingJobListener("testChain");
chain.AddJobChainLink(jobKey1, jobKey2);
scheduler.ListenerManager.AddJobListener(chain, GroupMatcher<JobKey>.AnyGroup());
scheduler.ScheduleJob(job1, trigger1);
scheduler.AddJob(job2, true);
scheduler.Start();
The scheduler.addGlobalJobListener is old API and longer part of 2.x series. You should use the ListenerManager like you have done.

unscheduling job in quartz

I am using Quartz to schedule job in my c# .net application. I am storing all data in database. My code is :
ISchedulerFactory schedFact = new StdSchedulerFactory(properties);
_scheduler = schedFact.GetScheduler();
_scheduler.Start();
job = JobBuilder.Create<JobTask>()
.WithIdentity("job1", "group1")
.Build();
trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.WithSchedule(
CronScheduleBuilder.CronSchedule("0 0/5 * * * ?"))
.Build();
_scheduler.ScheduleJob(job, trigger);
Now I would like to give user function so user can disable(unschedule) job. I have look in quartz tutorial but I can't find the way to do it in c#.
You can call following method in IScheduler
//
// Summary:
// Remove the indicated Quartz.Trigger from the scheduler.
bool UnscheduleJob(string triggerName, string groupName);
Your Job class can implement IInterruptableJob interface and implement
public void Interrupt()
{
JobKey jobKey = new JobKey(JobName, MyService.GroupName);
Scheduler.Interrupt(jobKey);
}

Scheduling a windows service with quartz .NET

I have a custom windows service, and I'd like to use Quartz .NET to schedule when the service runs. Now, I understand the basics of quartz.NET, but I'm not sure how I would hook it up to a windows service.. So, lets say i have Service.exe which I want to run every hour. How would I implement this functionality via Quartz? I know this is kind of a vague question, but there's really no other way to ask it.
Thanks in advance.
You need to setup a job and a trigger. The job is called by a trigger.(http://quartznet.sourceforge.net/tutorial/lesson_3.html). Here's an example running every hour.
// construct a scheduler factory
ISchedulerFactory schedFact = new StdSchedulerFactory();
// get a scheduler
IScheduler sched = schedFact.GetScheduler();
sched.Start();
// construct job info
JobDetail jobDetail = new JobDetail("myJob", null, typeof(DumbJob));
// fire every hour
Trigger trigger = TriggerUtils.MakeHourlyTrigger();
// start on the next even hour
trigger.StartTime = TriggerUtils.GetEvenHourDate(DateTime.UtcNow);
trigger.Name = "myTrigger";
sched.ScheduleJob(jobDetail, trigger);
Here is your class which calls Service.exe.
public class DumbJob : IJob
{
public void Execute(JobExecutionContext context)
{
string instName = context.JobDetail.Name;
string instGroup = context.JobDetail.Group;
// Note the difference from the previous example
JobDataMap dataMap = context.MergedJobDataMap;
string jobSays = dataMap.GetString("jobSays");
float myFloatValue = dataMap.GetFloat("myFloatValue");
ArrayList state = (ArrayList) dataMap.Get("myStateData");
state.Add(DateTime.UtcNow);
Console.WriteLine("Instance {0} of DumbJob says: {1}", instName, jobSays);
}
}
You could also just start a thread in a windows service, keep track of when you last fired the exe and then reset afterwards. It's a bit simpler thatn Quartz, and would accomplish the same things. However, your question was Quartz specific.

Categories