Getting Started With Quartz.Net: Part 3
In Part 2 of this series you learned how to configure the Quartz.Net service, and now it’s time to get your server to actually do something. Let’s look at how to load some jobs via the quartz_jobs.xml file.
First, consider this short description of how the overall process works:
overwrite-existing-jobs="true"
This line tells the xml plug-in to delete any existing jobs that match the jobs that we have in the xml file. So, what makes a job unique in Quartz.Net? The job’s name AND the job’s group. You can create jobs with different names in the same group or jobs with the same name in different groups. If you try to create 2 jobs with the same name and in the same group, you’ll get an error. In this case however, it really doesn’t matter because the job store that we are using (where the job and trigger information is stored) is a RAM job store, therefore once the server is stopped, all this information disappears.
Now it’s time to configure our own job. We’ll configure a NativeJob, which is basically anything that can be run from the command line. In order to get a job to run, we need to configure 2 items: a job, and a trigger. The job is what actually does the work and the trigger is responsible for starting the job.
This xml fragment demonstrates how to configure a NativeJob and cron trigger that fires the 2nd and the 15th of every month at 4:00 am. Ok, that’s not very helpful for testing, so change the cron expression to 0 * * * * ? so that the job runs every minute. Also, if you end up setting your company’s payroll to run every minute, please let me know where I can apply for a job there :-).
<job>
<job-detail>
<name>PayrollProcessor</name>
<group>Payroll</group>
<description>Process paychecks</description>
<job-type>Quartz.Job.NativeJob, Quartz</job-type>
<volatile>false</volatile>
<durable>true</durable>
<recover>false</recover>
<job-data-map>
<entry>
<key>command</key>
<value>"payroll.bat"</value>
</entry>
<entry>
<key>parameters</key>
<value>"IncreaseSalary"</value>
</entry> <entry>
<key>waitForProcess</key>
<value>true</value>
</entry>
<entry>
<key>consumeStreams</key>
<value>true</value>
</entry>
</job-data-map>
</job-detail> <trigger>
<cron>
<name>PayrollProcessorTrigger</name>
<group>Payroll</group>
<description>Trigger payroll</description>
<misfire-instruction>SmartPolicy</misfire-instruction>
<volatile>false</volatile>
<job-name>PayrollProcessor</job-name>
<job-group>Payroll</job-group>
<cron-expression>0 0 4 2,15 1-12 ?</cron-expression>
</cron>
</trigger>
</job>
If you change the command from payroll.bat to the name of your executable (make sure you include the full path!), you should be able to get Quartz.Net to run your own job. I would recommend keeping the quotes there, because if your command has spaces in it and you don’t wrap the command in quotes, Quartz.Net won’t be able to run it.
Now that you’ve configured the job, let’s give it a try. Save the quart_jobs.xml file, start the Quartz.Net service and take a look at the event log. You should see some entries similar to these if everything was set up properly:
Quartz.Xml.JobSchedulingDataProcessor.ProcessFile(:0) - Parsing XML file: C:\quartz\quartz_jobs.xml with systemId: C:\quartz\quartz_jobs.xml validating: False validating schema: True
Quartz.Xml.JobSchedulingDataProcessor.ScheduleJobs(:0) - Scheduling 1 parsed jobs.
Quartz.Xml.JobSchedulingDataProcessor.ScheduleJob(:0) - Adding job: Payroll.PayrollProcessor
Quartz.Xml.JobSchedulingDataProcessor.ScheduleJobs(:0) - 1 scheduled jobs.
Quartz.Job.NativeJob.RunNativeCommand(:0) - About to run cmd.exe /C "C:\quartz\payroll.bat" IncreaseSalary
So there you have it, the scheduler is running the payroll.bat batch file and passing in the IncreaseSalary parameter. Plug in your own batch or program file and parameters and see what happens.
A word of caution: running command line jobs can get tricky because of spaces and parameter formats, so try running your program with the parameters from a command line first to see what happens. Sometimes it takes a little trial and error to get it to work just right. For this reason, you will usually want to create your own job (more on this later, or look at the Quartz.Net tutorial. A custom job gives you more control over what happens when and what to do in case of an emergency.
Because this is already a long post, I’ll defer until my next post the details of each of the job and trigger settings, but if you’re curious or in desperate and immediate need to know more, read through the following documentation links, which should answer most of your questions:
General Job (IJob) documentation
NativeJob documentation
General Trigger documentation
Cron trigger documentation
First, consider this short description of how the overall process works:
- When you start the server, a job store is created in memory.
- This job store is empty until we load some jobs in it.
- As part of the server startup, the xml plug-in runs, and it reads the quartz_jobs.xml.
- The plug-in then creates the jobs and triggers that are described in this file.
overwrite-existing-jobs="true"
This line tells the xml plug-in to delete any existing jobs that match the jobs that we have in the xml file. So, what makes a job unique in Quartz.Net? The job’s name AND the job’s group. You can create jobs with different names in the same group or jobs with the same name in different groups. If you try to create 2 jobs with the same name and in the same group, you’ll get an error. In this case however, it really doesn’t matter because the job store that we are using (where the job and trigger information is stored) is a RAM job store, therefore once the server is stopped, all this information disappears.
Now it’s time to configure our own job. We’ll configure a NativeJob, which is basically anything that can be run from the command line. In order to get a job to run, we need to configure 2 items: a job, and a trigger. The job is what actually does the work and the trigger is responsible for starting the job.
This xml fragment demonstrates how to configure a NativeJob and cron trigger that fires the 2nd and the 15th of every month at 4:00 am. Ok, that’s not very helpful for testing, so change the cron expression to 0 * * * * ? so that the job runs every minute. Also, if you end up setting your company’s payroll to run every minute, please let me know where I can apply for a job there :-).
<job>
<job-detail>
<name>PayrollProcessor</name>
<group>Payroll</group>
<description>Process paychecks</description>
<job-type>Quartz.Job.NativeJob, Quartz</job-type>
<volatile>false</volatile>
<durable>true</durable>
<recover>false</recover>
<job-data-map>
<entry>
<key>command</key>
<value>"payroll.bat"</value>
</entry>
<entry>
<key>parameters</key>
<value>"IncreaseSalary"</value>
</entry> <entry>
<key>waitForProcess</key>
<value>true</value>
</entry>
<entry>
<key>consumeStreams</key>
<value>true</value>
</entry>
</job-data-map>
</job-detail> <trigger>
<cron>
<name>PayrollProcessorTrigger</name>
<group>Payroll</group>
<description>Trigger payroll</description>
<misfire-instruction>SmartPolicy</misfire-instruction>
<volatile>false</volatile>
<job-name>PayrollProcessor</job-name>
<job-group>Payroll</job-group>
<cron-expression>0 0 4 2,15 1-12 ?</cron-expression>
</cron>
</trigger>
</job>
If you change the command from payroll.bat to the name of your executable (make sure you include the full path!), you should be able to get Quartz.Net to run your own job. I would recommend keeping the quotes there, because if your command has spaces in it and you don’t wrap the command in quotes, Quartz.Net won’t be able to run it.
Now that you’ve configured the job, let’s give it a try. Save the quart_jobs.xml file, start the Quartz.Net service and take a look at the event log. You should see some entries similar to these if everything was set up properly:
Quartz.Xml.JobSchedulingDataProcessor.ProcessFile(:0) - Parsing XML file: C:\quartz\quartz_jobs.xml with systemId: C:\quartz\quartz_jobs.xml validating: False validating schema: True
Quartz.Xml.JobSchedulingDataProcessor.ScheduleJobs(:0) - Scheduling 1 parsed jobs.
Quartz.Xml.JobSchedulingDataProcessor.ScheduleJob(:0) - Adding job: Payroll.PayrollProcessor
Quartz.Xml.JobSchedulingDataProcessor.ScheduleJobs(:0) - 1 scheduled jobs.
Quartz.Job.NativeJob.RunNativeCommand(:0) - About to run cmd.exe /C "C:\quartz\payroll.bat" IncreaseSalary
So there you have it, the scheduler is running the payroll.bat batch file and passing in the IncreaseSalary parameter. Plug in your own batch or program file and parameters and see what happens.
A word of caution: running command line jobs can get tricky because of spaces and parameter formats, so try running your program with the parameters from a command line first to see what happens. Sometimes it takes a little trial and error to get it to work just right. For this reason, you will usually want to create your own job (more on this later, or look at the Quartz.Net tutorial. A custom job gives you more control over what happens when and what to do in case of an emergency.
Because this is already a long post, I’ll defer until my next post the details of each of the job and trigger settings, but if you’re curious or in desperate and immediate need to know more, read through the following documentation links, which should answer most of your questions:
General Job (IJob) documentation
NativeJob documentation
General Trigger documentation
Cron trigger documentation