Monday, May 25, 2009

NOTE: I'm now blogging at http://jayvilalta.com/blog and not updating this blog anymore. For information on the latest version of Quartz.Net, visit me there.

If you use Quartz.Net and you want to do anything other than run a batch file, then chances are you’ll want to create a custom job. Fortunately, creating it is not hard at all. Let’s get started.
Here’s a quick rundown of what you need to do. First, you’ll need to add a reference to Quartz.dll in your project, Second, create a class that implements IJob. Third, put the code you want to run inside the Execute method. That’s it! That’s all you really need to create a custom job.
However, in order to get this job to execute, there are a couple more steps to follow and a few more things to be aware of, including some not-so-evident gotchas.
I’m going to assume that at this point, you have created your MyJob class and that it implements the IJob interface. Let’s talk about what you need to do in addition to implementing the logic that you want your job to execute.

Handle Exceptions

You should wrap the code in the Execute method and handle any exceptions that you can. For exceptions that you cannot handle but that can be solved by running the job again, you should wrap your exception inside a JobExecutionException.
If you want the scheduler to try running the job again, then set the RefireImmediately property to true. Otherwise, set it to false. You also have other options, such as un-scheduling triggers, so take a look at the documentation for JobExecutionException.
If your job doesn’t know how to handle the exception that was thrown, then don’t catch it and let the scheduler handle it.

Deploy Your Custom Job

Deploying the custom job is as simple as copying the dll to the same folder where the Quartz.dll is. You can also deploy your dll in any manner that allows the runtime to locate the dll per the normal rules for dll resolution.

Beware of ConfigurationManager

Finally, I’d like to give you a heads up about using the ConfigurationManager to provide your job with configuration information. Don’t do it! It is best to include all the information that your job needs to execute in the job’s JobDataMap. The reason for this is simple. If you use ConfigurationManager and you are running Quartz.Net as a windows service, then the ConfigurationManager will try to look for configuration information in the Quartz.Net config file. If you are running Quartz.Net embedded inside your application, then this might not be an issue for you, but then again… it might.
Let’s wrap up this post by summarizing the steps needed to create a custom Quartz.Net job.

Add a reference to Quartz.dll in your project.

Create a class that implements IJob

Implement the Execute method of the IJob interface.

Catch exceptions and throw a JobExecutionException as needed.

Copy the CustomJob dll to the same folder as Quartz.dll or somewhere that the runtime can locate it.

If you need more information, please let me know in the comments or read over documentation for IJob and for JobExcecutionException.

Wednesday, April 22, 2009

NOTE: I'm now blogging at http://jayvilalta.com/blog and not updating this blog anymore. For information on the latest version of Quartz.Net, visit me there.

If you’re considering using Quartz.Net, chances are you are using log4net as the logging framework for your application. We’ll assume that you already know how to configure log4net and that you just want to plug that configuration into Quartz.Net. So, how do we configure Quartz.Net to use log4net?
It’s not terribly complicated, since Quartz.Net is using Common.Logging. In this post we’ll consider 2 use cases for configuring Quartz.Net to use log4net: a standalone Quartz.Net server, and Quartz.Net embedded within your application. Fortunately for us, the process we need to follow to configure Quartz.Net is the same for both!

Configuring Quartz.Net with Log4net

To configure log4net for a standalone Quartz.Net server running as a windows service, we have to make some changes to the Quartz.Server.Service.exe.config file. This file should be in the directory where you have your Quartz.Net installation. If Quartz.Net is embedded in your application, then you will be modifying your application’s config file (web.config or yourapp.exe.config for example).
You will need to add 2 elements to the <configSections> element:<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
This bit lets you configure log4net directly from the quart configuration file. Now, add these elements:<sectionGroup name="common"> <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" /> </sectionGroup>
This lets you configure the Commons Logging from the quartz configuration file as well. All in all, your <configSections> section will look something like this:<configSections> <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> <sectionGroup name="common"> <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" /> </sectionGroup> </configSections>
So far all we have done is set up the configuration file to be able to configure both commons logging and log4net from within that one file. Now, let’s configure commons logging to use log4net. Just add this somewhere under the <configuration> element:<common> <logging> <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4net"> <arg key="configType" value="INLINE" /> </factoryAdapter> </logging> </common>
This tells commons logging that we are going to log to log4net. Now, all that is left to do is to configure log4net itself. Here is a sample log4net configuration:<log4net> <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%d [%t] %-5p %l - %m%n" /> </layout> </appender> <root> <level value="INFO" /> <appender-ref ref="EventLogAppender" /> </root> </log4net>
To wrap up this post, let me point out 2 things to keep in mind when configuring log4net with Quartz.net:

You don’t need to configure log4net in the main application config file. You can load an external log4net configuration file, just as you would if you weren’t using Quartz.Net.

You do need to tell commons logging to use log4net as the logging framework, which is what we did in the <common> section.

Thursday, April 16, 2009

NOTE: I'm now blogging at http://jayvilalta.com/blog and not updating this blog anymore. For information on the latest version of Quartz.Net, visit me there.

In Part 4, we explained how to configure jobs in detail. In this final installment of the series we’ll describe how to configure two kinds of triggers: the CronTrigger and the SimpleTrigger.
In keeping with the previous posts, we will configure the triggers in the quartz_jobs.xml file. Part 4 included a sample configuration of a CronTrigger, so let’s start with this. Here is the xml fragment that we are going to work with:<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 * * * * ?</cron-expression> </cron> </trigger>

Common Properties

The first 3 elements should be familiar to you. They are used to describe and identify the trigger. The trigger is identified by both the name and the group. You can have triggers with the same name as long as they are not in the same group.
The next element on the list is the misfire-instruction. This element tells the scheduler what it should do if the trigger misfires. A trigger misfires when the scheduler is unable to fire the trigger when it was supposed to be fired. This could be because the scheduler was down or too busy, for example. The two built-in types of misfire instructions are InstructionNotSet, which is the default setting if no misfire instruction is specified and SmartPolicy. The SmartPolicy instruction is a high level instruction that basically asks each trigger to decide what the “smart” thing to do is.
The Cron trigger provides (in addition to the instructions listed above) the following MisfireInstruction values: FireOnceNow and DoNothing. For a Cron trigger the SmartPolicy instruction translates to FireOnceNow. The names are pretty descriptive as far as what the scheduler should do.
The SimpleTrigger also provides some additional MisfireInstructions: FireNow, RescheduleNowWithExistingRepeatCount, RescheduleNowWithRemainingRepeatCount,RescheduleNextWithRemainingCount and RescheduleNextWithExistingCount. If you select a SmartPolicy MisfireInstruction for the SimpleTrigger, things aren’t that simple anymore. The documentation explains what will happen as follows:

If the Repeat Count is 0, then the instruction will be interpreted as FireNow. If the Repeat Count is RepeatIndefinitely, then the instruction will be interpreted as RescheduleNowWithRemainingRepeatCount. There is also the following warning: using RescheduleNowWithRemainingRepeatCount with a trigger that has a non-null end-time may cause the trigger to never fire again if the end-time arrived during the misfire time span. Finally, if the Repeat Count is > 0, then the instruction will be interpreted as RescheduleNowWithExistingRepeatCount.

As you can see, it’s really not that simple… However, that covers the MisfireInstruction part. Now it’s time to look at the Volatile property, which so happens to be the same as for the Job and that we discussed in Part 4:

The description of the volatile parameter has a double negative in the documentation description, so I’ll try to rephrase it in a way that is a bit less confusing. If you set the volatile parameter to true, the job will not be persisted in the job store when the scheduler is shut down. If you set the volatile parameter to false, the job will get persisted in the job store. Note, however, that if you are using the AdoJobStore, the job will get persisted regardless of the value of this parameter. Also consider the fact that if you are using the in memory data store… no data will get persisted and you will have to re-schedule all the jobs upon start up.

Next up are the JobName and JobGroup properties, which effectively link the trigger to the job. A trigger can only be assigned to one job and the job’s name and group are needed in order to uniquely identify the job. Make sure these match the name and group of the job you want to schedule.
You can also set the trigger’s start and end times, by adding these elements: <start-time></start-time> <end-time></end-time>
Make sure you specify the times in UTC!

Cron Trigger Properties

The last property that we need to provide for a Cron trigger is the Cron expression. This expression follows “mostly” the standard cron syntax, but there are some differences that can cause some frustration, so be sure to look at the documentation. The Quartz.Net cron expression documentation is here and the link also has some examples of the most common expressions.

Simple Trigger Properties

Here is an xml fragment for configuring the simple trigger. We’ll only discuss the last two elements, because the rest of them work just as we described above.<trigger> <simple> <name>sampleSimpleTrigger</name> <group>sampleSimpleGroup</group> <description>Simple trigger to simply fire sample job</description> <misfire-instruction>SmartPolicy</misfire-instruction> <volatile>false</volatile> <job-name>sampleJob</job-name> <job-group>sampleGroup</job-group> <repeat-count>RepeatIndefinitely</repeat-count> <repeat-interval>3000</repeat-interval> </simple> </trigger>
The RepeatCount is used to tell the trigger how many times to fire. You can either specify a number or you can specify RepeatIndefinitely, which basically means the trigger keeps firing over and over again.
The RepeatInterval property sets the time interval at which to repeat the trigger. The example is set in milliseconds.
With this last post I think we have covered pretty much everything that you need to know to set up, configure and run a Quartz.Net server.
Thanks for reading!

Monday, April 13, 2009

NOTE: I'm now blogging at http://jayvilalta.com/blog and not updating this blog anymore. For information on the latest version of Quartz.Net, visit me there.

Part 3 of this series describes how to configure a job to run on Quartz.Net, but it does not go into detail about what each of the job settings does. This post will cover configuring jobs in detail and will provide some examples of job configurations.
Most of the information we will be covering is available in the documentation for Quartz.Net documentation for JobDetail.
First, let’s take a quick look at how things are organized internally. This will help you understand at a high level how Quartz works inside (from an API point of view), without going into great detail into the object model. I’ll use the folder metaphor to simplify things, but keep in mind that the Quartz.net object model is not completely hierarchical. This folder based image is a simplified representation that you can refer to while reading the explanation below:
The highest level item we’ll look at is the scheduler. The scheduler manages pretty much everything, and at a high level, it works with Jobs, or more specifically, JobDetails which it organizes into groups. So, a Scheduler has JobGroups, which in turn contain jobs (JobDetails). Finally, a job can have zero or more triggers. We’re not going to spend too much time looking at triggers (that is the subject of the next post) but, we’ll cover the basics here.
By giving a job a name and a group, you are uniquely identifying it. Within a job group, job names must be unique. If you look at the configuration file we covered in the previous post, the first 2 fields (name and group) are what we are talking about here. The third field, description, is useful for explaining what a job is supposed to do.
Triggers are organized in groups as well, and like jobs, a trigger’s name must be unique within a group. Unlike jobs, triggers can only be assigned to one job. To summarize, while a job can have multiple triggers, a trigger can only have one job.
Now let’s look at the rest of the job parameters, starting with the JobType. As its name states, this is basically the Type (as in .Net Type) that the scheduler should create to run the job. In the xml it is described by first putting the fully qualified type name and then, separated by a comma, the assembly name (without the .dll extension).
The description of the volatile parameter has a double negative in the documentation description, so I’ll try to rephrase it in a way that is a bit less confusing. If you set the volatile parameter to true, the job will not be persisted in the job store when the scheduler is shut down. If you set the volatile parameter to false, the job will get persisted in the job store. Note, however, that if you are using the AdoJobStore, the job will get persisted regardless of the value of this parameter. Also consider the fact that if you are using the in memory data store… no data will get persisted and you will have to re-schedule all the jobs upon start up.
The durable description is easier to understand. Setting this to true means that the job will be kept around even if it doesn’t have any triggers pointing to it. If it is set to false, the job gets deleted if it doesn’t have any triggers pointing to it.
Next up is the recover (RequestsRecovery) parameter. This parameter tells the scheduler whether it should try to recover (re-execute) the job if something goes wrong with the scheduler itself while the job was running.
The last parameter, or list of parameters that we’ll discuss is the JobDataMap. In short, this map is used to pass data to the job. Think of it as a collection of name value pairs that you can consume within your job. In the default quartz_jobs.xml, the data contained in the map is what you would expect to find if you wanted to invoke a command line program: the executable name and the arguments to pass to the executable. For the NativeJob itself, there are 2 parameters that you need to be aware of: the waitForProcess parameter (boolean) and consumeStreams (boolean) .
The WaitForProcess parameter tells the NativeJob whether to wait for the process to finish before considering the Job complete.
The ConsumeStreams parameters should be set to true if your executable produces any output to stderr or stdout. Otherwise, the process might hang. Any output produced by the executable is piped to the event log (if you are using the default configuration).
That’s it for this post. Hopefully this give you a more in depth explanation of how to configure Quartz.Net jobs. In Part 5, we’ll discuss trigger configuration.

Tuesday, April 7, 2009

NOTE: I'm now blogging at http://jayvilalta.com/blog and not updating this blog anymore. For information on the latest version of Quartz.Net, visit me there.

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:

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.

If you have set up your Quartz.net job server as we described in the previous posts, then all you need to do to load some jobs is to add them to quartz_jobs.xml file. The file that comes with the distribution sets up a NoOpJob, which as its name implies, does nothing. Let’s look at the very first line (ok, the second):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: TrueQuartz.Xml.JobSchedulingDataProcessor.ScheduleJobs(:0) - Scheduling 1 parsed jobs.Quartz.Xml.JobSchedulingDataProcessor.ScheduleJob(:0) - Adding job: Payroll.PayrollProcessorQuartz.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) documentationNativeJob documentationGeneral Trigger documentationCron trigger documentation

Friday, March 27, 2009

NOTE: I'm now blogging at http://jayvilalta.com/blog and not updating this blog anymore. For information on the latest version of Quartz.Net, visit me there.

This is the second installment of the Getting Started With Quartz.Net series. Part 1 covered how to set up a standalone Quart.Net server as a windows service. Part 2 covers the configuration of the server.
At this point you should have installed the Quartz.Net server as a standalone windows service. If you haven’t, Part 1 guides you through this process.
Now, let’s look at configuring the server. There are 3 files we need to look at: Quartz.Server.Service.exe.config, quartz.config and quartz_jobs.xml.Quartz.Server.Service.exe.configFirst we will examine Quartz.Server.Service.exe.config, so open this file in your favorite editor. The distribution version of the file does minimal configuration here, only setting up the logging sub systems. Of interest here is the <log4net> section, which you can modify if you don’t want your service to log to the event log. If you’re not sure how to configure log4net, visit the log4net site and look at the log4net configuration examples.quartz.configNext, let’s look at quartz.config, which contains the bulk of the configuration. This file has 3 “sections” that are worth looking at. The first “section” starts on line 7:# configure thread pool infoquartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartzquartz.threadPool.threadCount = 10quartz.threadPool.threadPriority = Normal
Here you can change the number of threads in the threadpool by changing the threadCount property. This basically translates into how many jobs can run at the same time.
The next section starts on line 12 and initializes the JobInitializationPlugin, which will load up a set of jobs from an xml file when the server is started:# job initialization plugin handles our xml reading, without it defaults are used -->quartz.plugin.xml.type = Quartz.Plugin.Xml.JobInitializationPlugin, Quartzquartz.plugin.xml.fileNames = ~/quartz_jobs.xml
The interesting part here is the last line, where you can tell the plugin which file to load the jobs from.
The final section configures the server so that it can be managed remotely. We won’t look at this section right now since we don’t need to change it to run the server.quartz_jobs.xmlThe last configuration file that we will look at is quartz_jobs.xml, which contains the jobs that will be loaded by the initialization plugin. The file available with the distribution includes a sample job configuration of a job that does absolutely nothing. This is the file where you want to set up your own jobs. You can configure your jobs here and then start up the Quartz.Net service. The plugin will then read this file and schedule the jobs. Part 3 of the series will cover how to configure jobs via the xml file.
To wrap up this post, there are a couple of things that are worth pointing out:
Running the server using these default configuration files will start up a server that:

Logs events to the windows event log

Uses a memory based job store (more on this later)

Loads jobs from an xml file

Schedules a NoOpJob that runs every 3 seconds.

Can be managed remotely

Part 3 of the series will cover how to configure jobs via the xml file.

Thursday, March 26, 2009

NOTE: I'm now blogging at http://jayvilalta.com/blog and not updating this blog anymore. For information on the latest version of Quartz.Net, visit me there.

In this series of posts I will try to help you get started using Quartz.Net, the C# port of Quartz. This tutorial assumes that you are using the latest version of the .Net framework, so if you are using a different version, make sure you adapt the instructions accordingly. Also, I assume that you are somewhat familiar with installing and managing windows services.
This first installment will walk you through installing Quartz.Net as a standalone windows service.
1. Download the latest version (which at this time is 1.o) zip file from http://quartznet.sourceforge.net/download.html. Unzip the file to any temporary folder (I’m using C:\Temp\Quartz for the example). We will not be keeping this folder (unless you want to).
2. Locate the C:\temp\Quartz\server\bin\3.5\service folder. It contains all the files that you need to install the service.
3. Copy all the files in this folder to the folder that will serve as your Quartz server installation folder. I will use C:\Quartz as my installation folder.
4. Once you have copied all the files there, it is time to install the service. We will use installutil.exe to do this. The easiest way to run the installer is to open a Visual Studio Command Prompt and navigate to C:\Quartz (or your install folder). If you’re running Vista or Windows 7, you’ll need to run the command prompt as Administrator (thanks sashas and max). Then run the following command to install the service:installutil Quartz.Server.Service.exe
5. If the install ran successfully, you will have a screen that looks like this:
At this point the service should be installed. Open the services manager and change the account and startup type if necessary.Part 2 of this series will discuss how to configure and start the Quartz.net server.