Monday, December 19, 2011

Installing Multiple Instances of Quartz.Net on One Machine

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.

It’s possible to have multiple instances of Quartz.Net on one machine. This is especially useful if you want to run an instance of Quartz.Net 2.0 alongside an existing Quartz.Net 1.0 instance. However, you can install 2 (or more) instances of Quartz.Net 2.0 on the same box if that’s what you want. Fortunately for us, this can all be done via the configuration file.

Pre-Requisite (Step 0)

First you need to create a separate directory for the new Quartz.Net instance that we are about to create. You can/should follow the steps outlined in this post all the way up to and including step 3. Do not install the service just yet. This is where the configuration changes come into play.
At this point I am going to assume that you have all of your Quartz.Net files in a new directory and that you have verified that the scheduler starts up properly. We’re now ready to configure the scheduler so that we can install an instance of Quartz.Net with a name of our choosing.

Step 1 – Change the Configuration File

In order to the change the Quartz.Net service name, we’re going to be modifying the Quartz.Server.exe.config file. Open up the file in your favorite editor and look for the <quartz> section. In the default distribution, this section is commented out (shown in green below).
image
We’ll uncomment it and add a few settings in there. Here is what the configuration file looks with our changes:
image
And here is the <quartz> section itself, so that you can cut and paste it into your configuration file. Feel free to name your service appropriately.
<quartz >
  <add key="quartz.checkConfiguration" value="false"/>
  <add key="quartz.server.serviceName" value="JQuartz"/>
  <add key="quartz.server.serviceDisplayName" value="JQuartz"/>
  <add key="quartz.server.serviceDescription" value="JQuartz description"/>
</quartz>

Step 2 – Install the Service

Installing the service is now pretty straightforward. Open a command prompt as administrator in the same folder where your new service files are. Now type Quartz.Server.exe install in the command prompt and watch as the service installs. You can also refer to this post if you want a few more details on the install itself. After installing, you should now have a Quartz.Net service with the same name you specified in the configuration file. We’ll talk about the configuration itself now, but if you already know what the configuration keys above do, feel free to skip the next section and I’ll see you next time.

Appendix – The Configuration Details

If you’re still here then you probably want to know a bit more about the changes we made, so let’s take a look at the configuration itself. First, let me start by saying that you MUST make these changes to the Quartz.Server.exe.config file. Modifying the Quartz.config file will not work. Also, you must set the checkConfiguration key to false, otherwise the scheduler will throw an exception.
The keys themselves are quite explanatory, but I’ll illustrate with a picture of where these keys end up once you install the service. For the configuration above, this is what the installed service looks like:
image
Finally, be sure to provide both the serviceName and serviceDisplayName values when doing a custom install and make sure that the names are unique so that the installation process works properly.
I’ve tested this on Windows 7 and Windows 2K3, so let me know if you run into any issues.

Friday, December 2, 2011

Installing Quart.Net 2.0 Beta 1 as a Windows Service

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.

Now that there is an official beta release of Quartz.Net 2, I thought it would be a good idea to go through the installation process and describe it step by step. Before we get started I’d like to point out that this post was written using Windows 7, so the screenshots may not match exactly what you see if you are on a different version.

Step 1: Download and Unpack the Binaries

The latest Quartz.Net 2.0 binaries are available from Sourceforge, here. Download the zip file and unpack the contents to a folder. If you are using Windows 7, be sure to unblock the downloaded zip file before extracting the files. This is done by right clicking on the downloaded file and clicking on the unblock button BEFORE unzipping the file.
image
I will be unzipping the file to c:\quartznet2 for this article. Once you’ve downloaded and unpacked the files, you should have the following directory structure:
image

Step 2: Select the Appropriate Version

The beta 1 distribution provides binaries for the 3.5 and 4.0 versions of the .Net framework. Select the appropriate folder for your version from the following:
image

Step 3: Sanity Check

Before installing Quartz.Net 2 as a service, let’s check that everything is working properly. I’m running version 4.0 of the framework so I will open a command prompt as Administrator in the 4.0 folder. Note: If you don’t open the command prompt as administrator, you will get no error messages or UAC prompts. The installation will simply fail.
image
Now that we have a command prompt, let’s try starting the Quartz.Net server. To do this, simply run Quartz.Server.exe by typing it into the command prompt.
If nothing happens, then you probably forgot to unblock the files you downloaded. Go back to Step 1 and start over. If everything is well, you will see some output in the command prompt. This means Quartz.Net is running and that the sample job that is configured out of the box is running correctly.
image
You may also get a firewall warning window, similar to this one:
image
This is because by default the scheduler exposes a remoting interface that allows you to interact with it. If you wish to enable access to remoting, then go ahead and click allow access. otherwise, click cancel. You’ll have to configure the firewall manually later if you wish to enable remoting access on the scheduler.
Press CTRL-C to stop the scheduler.

Step 4: Installing the Service

At this point you should be quite confident that the base distribution will work once installed as a windows service. Now it’s time to install the service. To do this, type Quartz.Server.exe install in the command prompt. If all goes well, you should see the following in your command prompt.
image
Now type services.msc in the command prompt to open the services administration window. There you should see the quartz service installed as “Quartz Server” with automatic startup and running under the Local System account. 
image
You should now be able to start and stop the service through the administration window.

Friday, July 15, 2011

Configuring a DirectoryScanJob in Quartz.Net 2.0

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 today’s post I will explain how to configure a DirectoryScanJob in Quartz.Net 2.0. Configuring the DirectoryScanJob can be done either programmatically or using the quartz_jobs.xml file. We will cover how to configure the job using the xml file in this post as it is not exactly straightforward to do so. If you’re interested in a post on how to configure the job programmatically, please leave a comment.

How to Configure the DirectoryScanJob using quartz_jobs.xml

For our example we will configure a DirectoryScanJob that looks at given directory for changes and then writes out to the log how many files were updated. Then we will add a file to the directory and we will see what happens on the scheduler. Let’s get started.
First, we’ll create the folder that will be our scan folder: C:\FolderToScan.
Now, let’s create the quartz_jobs.xml file and configure it appropriately. It will look something like this:
   1:  <schedule>


   2:   


   3:  <job>


   4:    <name>directoryScanJobExample</name>


   5:    <group>directoryScanJobExample</group>


   6:    <description>Sample job for Quartz Server</description>


   7:    <job-type>Quartz.Job.DirectoryScanJob, Quartz</job-type>


   8:    <job-data-map>


   9:      <entry>


  10:        <key>DIRECTORY_NAME</key>


  11:        <value>C:\FolderToScan</value>


  12:      </entry>


  13:      <entry>


  14:        <key>DIRECTORY_SCAN_LISTENER_NAME</key>


  15:        <value>DirectoryScanListenerExample</value>


  16:      </entry>


  17:    </job-data-map>


  18:  </job>


  19:   


  20:  <trigger>


  21:    <simple>


  22:      <name>directoryScanJobExampleSimpleTrigger</name>


  23:      <group>directoryScanJobExampleSimpleTriggerGroup</group>


  24:      <description>Simple trigger to simply fire sample job</description>


  25:      <job-name>directoryScanJobExample</job-name>


  26:      <job-group>directoryScanJobExample</job-group>


  27:      <misfire-instruction>SmartPolicy</misfire-instruction>


  28:      <repeat-count>-1</repeat-count>


  29:      <repeat-interval>10000</repeat-interval>


  30:    </simple>


  31:  </trigger>


  32:   


  33:  <job>


  34:    <name>addDirectoryScanListener</name>


  35:    <group>directoryScanJobExample</group>


  36:    <description>Sample job for Quartz Server</description>


  37:    <job-type>Examples.DirectoryScanListenerExample, Examples</job-type>


  38:  </job>


  39:   


  40:  <trigger>


  41:    <simple>


  42:      <name>addDirectoryScanListenerSimpleTrigger</name>


  43:      <group>directoryScanJobExampleSimpleTriggerGroup</group>


  44:      <description>Simple trigger to simply fire sample job</description>


  45:      <job-name>addDirectoryScanListener</job-name>


  46:      <job-group>directoryScanJobExample</job-group>


  47:      <misfire-instruction>SmartPolicy</misfire-instruction>


  48:      <repeat-count>0</repeat-count>


  49:      <repeat-interval>10000</repeat-interval>


  50:    </simple>


  51:  </trigger>


  52:  </schedule>




Lines 3-31 of the xml file are used to define the DirectoryScanJob and to set the required properties on it. The job is set to look at the C:\FolderToScan folder that we created earlier (lines 9-12). The listener that will be notified when files are updated in the folder is called DirectoryScanListenerExample (lines 13-16). This is just the listener’s friendly name (the key used to look it up actually). We could have called it Jim.  Next up is the trigger definition (lines 20-31), which creates a SimpleTrigger that fires every 10 seconds until the scheduler is stopped.


Right after the configuration of the DirectoryScanJob you will find another job configuration. This extra job is necessary because we have no way other way of adding the DirectoryScanListener to the scheduler through the quartz_jobs.xml. What we have done is created a job that runs once and adds the DirectoryScanListener to the scheduler. Let’s take a look at the code for Examples.DirectoryScanListenerExample, which is both the job that adds the listener as well as the listener itself. This is done by implementing both IJob and IDirectoryScanListener. You probably don’t want to do this in your production code, but for a quick example it works fine. Here’s the code for DirectoryListenerExample:


public class DirectoryScanListenerExample : IDirectoryScanListener, IJob
 {
    public void FilesUpdatedOrAdded(FileInfo[] updatedFiles)
    {
        logger.InfoFormat("Found {0} updated files", updatedFiles.Length);
    }

    public void Execute(IJobExecutionContext context)
    {
        logger.Info("Adding the listener to the context");
        context.Scheduler.Context.Add("DirectoryScanListenerExample", new DirectoryScanListenerExample());
        logger.Info("Added the listener to the context");   
    }

    private static readonly ILog logger = LogManager.GetLogger(typeof(JobListenerExample));
 }



This implementation of the IDirectoryScanListener is quite simple. The FilesUpdatedOrAdded method gets called when the job detects changes and it prints out to the log how many files were updated. The Execute method is called once, and here is where we add the listener to the scheduler’s context. The listener’s name is hard coded here, but you could also make that a parameter that gets passed in from the quartz_jobs.xml file via the job data map.


Running the DirectoryScanJob



Let’s start up our scheduler with this quartz_jobs.xml and see what happens. Here is the output from the scheduler’s start up process (I’ve removed the non interesting bits…).



   1:  Parsing XML file: C:\quartznet2\quartz_jobs.xml with systemId: ~/quartz_jobs.xml


   2:  Adding 2 jobs, 2 triggers.


   3:  Adding job: directoryScanJobExample.addDirectoryScanListener


   4:  Adding job: directoryScanJobExample.directoryScanJobExample


   5:  Scheduler ServerScheduler_$_NON_CLUSTERED started.


   6:  Adding the listener to the context


   7:  Job directoryScanJobExample.directoryScanJob


   8:  Example threw a JobExecutionException:


   9:  Parameters: refire = False, unscheduleFiringTrigger = False, unscheduleAllTriggers = False


  10:   Quartz.JobExecutionException: DirectoryScanListener named 'DirectoryScanListenerExample' not found in SchedulerContext


  11:     at Quartz.Job.DirectoryScanJob.Execute(IJobExecutionContext context) in C:\git\quartznet\src\Quartz\Job\DirectoryScanJob.cs:line 90


  12:     at Quartz.Core.JobRunShell.Run() in C:\git\quartznet\src\Quartz\Core\JobRunShell.cs:line 188


  13:  Added the listener to the context




Lines 1 – 5 show us the scheduler starting, the xml file being parsed and the jobs being added. So far so good. It gets a little confusing from now on because we have several threads running, but I think we can make sense of it. Line 6 tells us that the job to add the listener has kicked off. However, lines 7 – 12 tell us that the scan job is trying to run, but it fails to run because the listener is not available. That’s because the job that adds the listener is not finished yet. Line 13 tells us that the listener is ready and has been added to the scheduler. From now on, the scan job will run successfully because it has its listener in place. Let’s add a file to the folder and see what happens.


After adding a file to the folder, we see the following:


Directory 'C:\FolderToScan' contents updated, notifying listener.
Found 1 updated files



This is what we expected, so all is good. We’ve successfully configured a DirectoryScanJob and a listener using the quartz_jobs.xml.

Saturday, June 25, 2011

Implementing a Job Listener in Quartz.Net 2.0

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 post we will be implementing the job listener that we discussed in the previous post. The previous post showed us how to add a scheduler listener to the scheduler using a plugin. Now we will describe what you need to do to implement a job listener in Quartz.Net 2.0. If you’re interested in learning how to implement a job listener in version 1.0 of Quartz.Net, take a look at this post.

Implementing the IJobListener Interface

To create a job listener, we must write a class that implements the IJobListener interface. This interface is defined as follows:
public interface IJobListener
 {
    string Name { get; }
    void JobExecutionVetoed(IJobExecutionContext context);
    void JobToBeExecuted(IJobExecutionContext context);
    void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException);
 }


The Name property will hold the name of the job listener and is read-only. The rest of the interface is what is of most interest to us. These three methods give you the opportunity to interact with a job during its lifecycle. The first method, JobExecutionVetoed gets called whenever a trigger listener vetoes the execution of a job. If this method is called, no other methods in this interface will be called. The second method, JobToBeExecuted, is called right before the job is about to be executed. The third method, JobWasExecuted, is called after the job has been executed. You do not have to provide implementations for all 3 methods unless you want to, but the methods do need to be present in your class to satisfy the interface.


The JobListenerExample



In our example we will provide very simple implementations of these methods, only to illustrate how to build a listener. All the JobListenerExample does is log an informational message whenever one of the methods of the interface are called. The code for the simple listener is listed below.


class JobListenerExample : IJobListener
 {
    public void JobExecutionVetoed(IJobExecutionContext context)
    {
        logger.Info("JobExecutionVetoed");
    }
    public void JobToBeExecuted(IJobExecutionContext context)
    {
        logger.Info("JobToBeExecuted");
    }
    public void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException)
    {
        logger.Info("JobWasExecuted");
    }
    public string Name
    {
        get { return "JobListenerExample"; }
    }
    private static readonly ILog logger = LogManager.GetLogger(typeof(JobListenerExample));
 }



If you would like a more in depth example of a job listener that actually does something useful, take look at this post, which describes how to implement a job listener that logs job execution information to a database. The example is for Quartz.Net 1.0, but you’ll notice that the only difference between the two interfaces is that the method signatures are now defined using interfaces instead of concrete types.


Loading and Executing the Listener



In order to load and execute your listener, you will have to add it to the scheduler upon start up. You must also specify whether it should executed for all jobs, or for some job in particular. How to do this using a plugin is described in the previous post. Finally, you must make sure that the assembly that contains your listener is available somewhere that the scheduler can load it upon startup.

Monday, June 6, 2011

Implementing a Plugin in Quartz.Net 2.0

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.

Today we will implement a plugin for Quartz.Net 2.0. If you’re looking for a way to implement a plugin for version 1.0 of Quartz.Net, take a look at this post. Most of the steps are the same, but there are some API changes, so the plugin isn’t exactly the same, as you will see.
What are plugins used for in Quartz.Net 2.0? Well, plugins come in handy if there is something you want to do whenever the scheduler is started or stopped. Some examples of handy plugins are:
  • plugins to load jobs into a RAM jobstore upon scheduler startup (this one is available out of the box)
  • plugins to register listeners (listeners must be registered with the scheduler upon startup since they are not persisted)
In order to implement a listener in Quartz.Net 2.0 there are several steps we need to take. First we must implement the ISchedulerPlugin interface and then we must configure the scheduler to load our plugin. Let’s get started.

Implementing the ISchedulerPlugin Interface

In order to create your own plugin, you have to implement the ISchedulerPlugin interface. Here is the ISchedulerPlugin interface definition:
public interface ISchedulerPlugin
 {
    void Initialize(string pluginName, IScheduler sched);
    void Start();
    void Shutdown();
 }



As you can see it’s not a very complicated interface. Let’s take some time to look at these methods in a little more detail.


Initialize


The Initialize method is called before the scheduler is started and it gives your plugin a chance to initialize itself. Keep in mind that at this point the scheduler itself is not running nor fully initialized. Here is what the code comments have to say:



Called during creation of the IScheduler in order to give the ISchedulerPlugin a chance to Initialize. At this point, the Scheduler's IJobStore is not yet (initialized?). If you need direct access (to) your plugin, you can have it explicitly put a reference to itself in the IScheduler’s SchedulerContext (in the) Initialize(string, IScheduler) method.


Start


The Start method is probably the method where most of the logic for your plugin will go. I will let the method’s comments describe the method’s purpose:


Called when the associated IScheduler is started, in order to let the plugin know it can now make calls into the scheduler if it needs to.


Shutdown


The shutdown method is there to allow you to do any cleanup that might be necessary for your plugin. The code comments are as follows:



Called in order to inform the ISchedulerPlugin that it should free up all of it's resources because the scheduler is shutting down.


Sample Implementation



Now we will go ahead and actually implement the plugin. In our sample implementation all that our plugin will do is to register a job listener. We are following the same approach we took while discussing how to implement a plugin in version 1.0 of Quartz.Net. This is intentional, to allow you see what the differences are between the two versions.


The implementation of our plugin is still very simple, albeit a little different from the one on the previous post:




public class PluginExample : ISchedulerPlugin
 {
    public void Initialize(string pluginName, IScheduler sched)
    {
        sched.ListenerManager.AddJobListener(new JobListenerExample(), EverythingMatcher<JobKey>.AllJobs());
    }

    public void Shutdown()
    {
        //Do Nothing
    }

    public void Start()
    {
        //Do Nothing
    }
 }



Configuring the Scheduler to Load the Plugin



Now that we have created our plugin, the next step is to tell the scheduler to load it. This is done by adding this line to our properties file :

quartz.plugin.myplugin.type = Examples.PluginExample, Examples



The plugin property needs to be structured like this:


quartz.plugin.{name}.type = {type name}, {assembly name}

Let’s discuss the components of the property. First, the quartz.plugin part of the property tells the scheduler that this is a plugin that needs to be loaded. The {name} part is the name that you want to give to your plug-in, and is the name that is passed in to the Initialize method above. The value of the property {type name}, {assembly name} tells the scheduler the Type of the plug-in, so that it can be loaded into memory. {type name} is the full name of your plugin, which in our case would be Examples.PluginExample. {assembly name} is the name of your assembly file, minus the .dll extension. Here’s another example of a plugin configuration property, lifted from the Quartz.Net default configuration file:


quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz


Final Step



Now that your plugin has been added to the configuration, all you need to do is start the scheduler and check the log for errors. Remember that the dll that contains your plugin needs to be in the same folder as the Quartz.Net binaries so that it can be found by the .Net framework and loaded into memory.

Tuesday, March 15, 2011

New in Quartz.Net 2.0–New Job File Format

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.

The xml format for the quartz_jobs.xml file has changed in Quartz.Net 2.0. This is a breaking change, so you won’t be able to use your existing jobs file with the new version of Quartz.Net without updating it to the 2.0 format. If you’re interested in the details of what can be configured in this file, I would recommend looking at the xsd file that defines the schema for the file. The xsd file that defines the new file format can be downloaded directly from the source code repository, here.
Today I’m going to provide a sample quartz_jobs.xml file in the new format and walk you through the creation of the file. Alternatively, you can take a look at the default file (that will be) provided with the Quartz.Net 2.0 distribution, here.
The new file format starts off with the following root element:
<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">

Right after the root node, our configuration file has two elements, which we will describe in detail below:
  1. The <processing-directives> element

  2. The <schedule> element

The <processing-directives> Element

The processing directives element contains two elements. The first element, <pre-processing-commands> is used to run commands prior to adding jobs and triggers to the scheduler. I’m not going to spend much time going over these in detail, but here is a list of the commands available to you:

  • delete the jobs in a group
  • delete the triggers in a group
  • delete a job
  • delete a trigger
If you find yourself in need of help with these elements, leave a comment and I will write a follow-up post.

The second element, <processing-directives>, lets you specify how the xml file is to be processed. Here you will find a familiar configuration setting (from Quartz.net 1.0), the <overwrite-existing-data> element. Setting this element’s value to true will replace any jobs currently scheduled with the new schedule given in the xml file (true is the default setting!). The <ignore-duplicates> element is the other element allowed under the <processing-directives> element.

The <schedule> Element

The <schedule> element is where we describe the jobs and triggers that we want to schedule. This section has also changed its format. Now, instead of grouping jobs and triggers under an element, jobs and triggers are all specified at the same level, and are not grouped under the job element, as was the case in version 1.0.

Under the <schedule> element, we can have <job> and <trigger> elements, which are the building blocks that we use to put together our schedule. Let’s take a look at these elements now.

The <job> Element

The <job> element is used to describe the IJob that we want the scheduler to execute. Job details are specified by using the following elements:
  • <name>
  • <group>
  • <description>
  • <job-type>
  • <durable>
  • <recover>
  • <job-data-map>
All of these elements serve the same purpose as they did in version 1 and, except for the job-data-map, they’re all simple types, so I’m not going to go into great detail here either. If you need a little more help with these, leave a comment and I’ll expand on the explanation as needed.

The job-data-map, being a complex element, supports an <entry> element inside. The <entry> element has <key> and <value> elements inside, which describe the job property to be added to the job map.

The <trigger> Element
The <trigger> element is used to describe the trigger that we want to attach to a given job. The xml file loader plugin supports 3 types of triggers:
  • simpleTriggerType
  • cronTriggerType
  • calendarIntervalTriggerType
These three trigger types are based on the abstractTriggerType, which has the following common elements:
  • <name>
  • <group>
  • <description>
  • <job-name>
  • <job-group>
  • <priority>
  • <calendar-name>
  • <job-data-map>
In addition to the elements listed above, each trigger type supports some additional elements. These additional elements are used to set parameters that are not common across all of the trigger types. The table below summarizes each of the trigger types and the elements they support.

Trigger Type Additional Elements
SimpleTrigger <misfire-instruction>,<repeat-count>,<repeat-interval>
CronTrigger <misfire-instruction>,<cron-expression>,<time-zone>
CalendarIntervalTrigger <misfire-instruction>,<repeat-interval>,<repeat-interval-unit>


I’m not going to go into detail here either. This is already a pretty long post, so if anybody needs more information leave me a comment and I will write a follow-up post.

Putting It All Together

At this point we’ve described all of the elements that are necessary to describe a job in the new Quartz.Net 2.0 xml format. To put it all together, here are the contents of a sample quartz_jobs.xml file that you can use as a guide:

<?xml version="1.0" encoding="UTF-8"?>

<!-- This file contains job definitions in schema version 2.0 format -->

<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
  <processing-directives>
    <overwrite-existing-data>true</overwrite-existing-data>
  </processing-directives>
  <schedule>
    <job>
      <name>nativeJobExample</name>
      <group>nativeJobExampleGroup</group>
      <description>Sample job for Quartz Server</description>
      <job-type>Quartz.Job.NativeJob, Quartz</job-type>
      <job-data-map>
        <entry>
          <key>command</key>
          <value>native_job_example.bat</value>
        </entry>
        <entry>
          <key>consumeStreams</key>
          <value>true</value>
        </entry>
      </job-data-map>
    </job>
    <trigger>
      <simple>
        <name>nativeJobExampleSimpleTrigger</name>
        <group>nativeJobExampleSimpleTriggerGroup</group>
        <description>Simple trigger example</description>
        <job-name>nativeJobExample</job-name>
        <job-group>nativeJobExampleGroup</job-group>
        <misfire-instruction>SmartPolicy</misfire-instruction>
        <repeat-count>5</repeat-count>
        <repeat-interval>10000</repeat-interval>
      </simple>
    </trigger>
  </schedule>
</job-scheduling-data>

This is a working example of a quartz_jobs.xml file that schedules a NativeJob using a SimpleTrigger. This example also shows how to configure the <job-data-map> to pass configuration information to the job.

I hope this post helps you in building a job configuration file for Quartz.Net 2.0.

Wednesday, March 9, 2011

Scheduling Jobs Programmatically in Quartz.Net 2.0

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.

Scheduling jobs programmatically in Quartz.Net 2.0 is very similar to scheduling a job in Quartz.Net 1.0. The overall process is the same, but the scheduler API has changed for version 2.0, so the code used will be significantly different.
In this post we will only highlight the differences between the two versions, so if you’re not familiar with the process, then read this post first. Then come back to this post to understand what has changed in version 2.0.

What Has Changed

Only a couple of things have changed in Quartz.Net 2.0 when it comes to scheduling a job programmatically: the way you build jobs and triggers.
The new Quartz.Net scheduler API uses more interfaces now. It also provides builder objects to create triggers (ITrigger now) and job details (IJobDetail now). I think the simplest way to highlight the differences is to take the code from my original post and convert it to the new API. Here’s the updated code:
public void ScheduleOneTimeJob(Type jobType, JobDataMap dataMap)
{
  string name = DateTime.Now.ToString("yyyyMMddHHmmss"));
  string group = "OneTimeJobs";
  // create job
  IJobDetail jobDetail = JobBuilder
    .Create()
    .OfType(jobType)
    .WithIdentity(new JobKey(name, group))
    .UsingJobData(dataMap)
    .Build();

  // create trigger
  ITrigger trigger = TriggerBuilder
    .Create()
    .ForJob(jobDetail)
    .WithIdentity(new TriggerKey(name, group));
    .WithSchedule(SimpleScheduleBuilder.Create())
    .StartNow()
    .Build();
  // get reference to scheduler (remote or local) and schedule job
  GetScheduler().ScheduleJob(jobDetail, trigger);
}



As you can see, the changes to the code are pretty significant, but the overall process remains the same. I’d like to provide you with some links to documentation or some other examples, but I’m unaware of any at this point, as Quartz.Net 2.0 has not yet been released.

Scheduling Jobs Programmatically in Quartz.Net 1.0

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 post is one of those back to basics posts and is inspired by a comment left by Jan on one of the Getting Started posts. Today we’ll take a look at how to schedule a job programmatically in Quartz.Net 1.0. I’ll follow up with a post on how to schedule a job programmatically in Quartz.Net 2.0 as well.

Scheduling Jobs in Quartz.Net

There are two ways you can schedule a job to run on Quartz.Net. The first way is to schedule it using the plugin that reads an xml file containing the job and scheduling information. If you want more information on how to schedule jobs using the xml plugin, take a look at this post.
You can also schedule jobs by interacting directly with the scheduler API. The Quartz.Net scheduler supports remote access via the .Net remoting facility, so this method of scheduling jobs is available to you regardless of whether the scheduler is running as a standalone windows service or whether it is embedded in your application. The remote scheduler does have some limitations, but we will not run into them while scheduling jobs. Let’s take a look at how we can schedule jobs using the scheduler API.

Scheduling Jobs via the Scheduler API

In a nutshell, these are the steps you would follow to schedule a job using the API:
  1. Get a reference to the scheduler object. How you do this will depend on whether you are running a standalone or embedded scheduler.
  2. Create a JobDetail object and set all the necessary properties. At a minimum you must set the Name, the Group and JobType. Please note that because the type of JobType is Type, the dll containing the JobType MUST be available to the code that is scheduling the job AS WELL as to the scheduler.
  3. Create a Trigger object (Trigger itself is an abstract class, so you will actually be creating one of these concrete types: SimpleTrigger, CronTrigger or NthIncludedDayTrigger) and set all the necessary properties. At a minimum you must set the Name, and the Group. The JobGroup and the JobName are also necessary as they are what links the JobDetail you created in the previous step with the Trigger you are creating now. However, you do not need to set these properties right now. Read on to step 4 to understand why.
  4. Call the ScheduleJob method on your scheduler object passing in your Trigger and your Job. If you call this overload of the ScheduleJob method, then the scheduler will link your trigger and your job, so there is no need for you to set the link explicitly.

Code Example

I’d like to close out this post with a code sample that implements the steps outlined above.
public void ScheduleOneTimeJob(Type jobType, JobDataMap dataMap)
{
  string name = DateTime.Now.ToString("yyyyMMddHHmmss"));
  string group = "OneTimeJobs";

  // create job
  JobDetail jobDetail = new JobDetail(name, group, jobType);
  jobDetail.Description = "One time job.";
  jobDetail.JobDataMap = dataMap;
  
  // create trigger
  SimpleTrigger trigger = new SimpleTrigger();
  trigger.Name = name;
  trigger.Group = group;
  trigger.StartTimeUtc = DateTime.UtcNow;
  trigger.RepeatCount = 0;
  trigger.RepeatInterval = TimeSpan.Zero;
 
  // get reference to scheduler (remote or local) and schedule job
  GetScheduler().ScheduleJob(jobDetail, trigger);
}



This sample code demonstrates one way of creating and scheduling a job that runs once, immediately.


In Quartz.Net 2.0 the scheduler API has changed significantly, so we will cover that topic in another post.

Tuesday, March 1, 2011

How Does Quartz.Net Configuration Work?

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 post we will explain in great detail how the Quartz.Net scheduler loads its configuration upon startup. Since the scheduler configuration did not change between versions 1.0 and 2.0, the information here is applicable to both Quartz.Net versions.
In Quartz.Net, the StdSchedulerFactory is responsible for configuring the scheduler. When the Quartz.Net scheduler is started, the factory will try to automatically configure a scheduler by looking for configuration information in different places:
  • the hosting application’s configuration file
  • a file specified in an environment variable
  • the quartz.config file
  • the embedded configuration file

Configuring From the Hosting Application’s Configuration File

First, the factory will try to load the scheduler configuration from the <quartz> section of your application’s config file. If you are running Quartz.Net as a windows service, then the service’s configuration file will be used. This file (Quartz.Server.exe.config) can be found in the same folder as the server executable. If you are hosting the Quartz.Net scheduler in your web application, then the web.config file will be checked.

Configuring From A File Specified in an Environment Variable

If the factory was not able to load the quartz configuration section, then the second place it will check is the environment variables of the process. Specifically, the factory will check to see if the quartz.config environment variable has been defined. If such a variable exists and is not empty, the factory will try to load the configuration from whatever file is specified as the value of the variable. For example, let’s say that your configuration file is called myconfig.config. If you set the quartz.config environment variable equal to myconfig.config, then the scheduler will load the configuration information from the file named myconfig.config.

Configuring From the Quartz.config File

Let’s assume that so far, the factory has not been able to load the scheduler’s configuration. The next step that the factory will take is to try to load a file called quartz.config from the same directory where the hosting application’s assembly was loaded. In fact, this is the file that an out-of-the-box Quartz.Net server/service installation uses to configure itself.

Configuring From the Embedded Configuration File

If all of the previous configuration options fail, then the factory falls back on loading the configuration file that is embedded in the quartz assembly. In case you’re curious, here are the configuration properties that are embedded in the assembly:
Property Value
quartz.scheduler.instanceName DefaultQuartzScheduler
quartz.threadPool.type Quartz.Simpl.SimpleThreadPool, Quartz
quartz.threadPool.threadCount 10
quartz.threadPool.threadPriority Normal
quartz.jobStore.type Quartz.Simpl.RAMJobStore, Quartz
quartz.jobStore.misfireThreshold 60000
If after checking all of the above locations for configuration information, the factory was not able to configure the scheduler, then an exception is thrown. As you can probably gather from the previous paragraph, unless you actually change the quartz assembly, this configuration exception will not be raised, because a configuration file is already embedded in the quartz assembly by default.

One Final Step

You’d think that by now, the whole configuration process is finished and the scheduler has been configured successfully. Well, the default scheduler factory takes one last step before giving you the configured scheduler.
Here’s what happens just after your configuration is loaded: if any of the configuration properties that you set in a configuration are also present in the environment variables, then the factory will overwrite them with the environment value. This the expected behavior. However, due to a bug, this does not work in Quartz.Net 1.0, but is fixed in Quartz.Net 2.0.

Tuesday, February 8, 2011

New in Quartz.Net 2.0-No More IStatefulJob

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.

Welcome to the the third post of my New in Quartz.Net 2.0 series.

Goodbye IStatefulJob

Yes, it’s true. In Quartz.Net 2.0 the IStatefulJob has been marked as obsolete. To be sure, it’s still available to use, but if you are using IStatefulJobs you should think about migrating them to use the new Quartz.Net 2.0 features.

The Disallow Concurrent Execution Attribute

So how do I implement an IStatefulJob in Quartz.Net 2.0? Fortunately, it’s not very hard to do. You just need to mark your IJob with the new DisallowConcurrentExecutionAttribute. Any jobs that have this attribute set are only allowed to run one instance of the job at a time. Keep in mind that an instance is defined by a JobKey (job name and group combination), which means that if you have the same job type scheduled with a different job name or a different job group, you may get more than one instance of that type of job running at the same time.

The Persist Job Data After Execution Attribute

Seems simple enough so far? Well, it is! There is only one other thing to consider. If you want the scheduler to persist the job’s state between executions, you must also add the PersistJobDataAfterExecutionAttribute to the class. This attribute tells the scheduler that you would like for it store the job’s JobDataMap once it is finished executing.

One Last Thing

While you can use each of these attributes separately, if you are going to use the PersistJobDataAfterExecutionAttribute to persist your job’s state, you should always use the DisallowConcurrentExecutionAttribute, because if you don’t, you might find that your job state is corrupted due to race conditions.

Friday, February 4, 2011

New in Quartz.Net 2.0–Directory Scan Job

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 post of the New in Quartz.Net 2.0 series. Today we will look at the DirectoryScanJob, which is a new job in Quartz.Net 2.0.
The DirectoryScanJob is very similar to the FileScanJob, which is available in Quartz.Net 1.0. As you can probably tell from the names, the main difference between the two jobs is that the DirectoryScanJob scans for changes in… you quessed it… a directory , whereas the FileScanJob scans for changes in… yes, you guessed it again… a file. If you’re not familiar with the FileScanJob, don’t worry, we will look at the DirectoryScanJob in detail.

Introducing the DirectoryScanJob

First, let’s take a look at what the documentation says about this new job:
“Inspects a directory and compares whether any files' "last modified dates" have changed since the last time it was inspected.  If one or more files have been updated (or created), the job invokes a "call-back" method on an identified DirectoryScanListener that can be found in the SchedulerContext.”
That seems pretty self explanatory, no? Basically, you tell the job that it needs to check a certain directory to see if new files have been added or if existing files have been modified. When the job is executed it checks the files and stores the file information in its JobDataMap. The next time the job runs, it compares the information stored in the job’s JobDataMap, with the files in the directory. If any changes are found, the job calls the FilesUpdatedOrAdded method of the IDirectoryScanListener that was configured for that job, passing in an arrary of FileInfo objects.

Configuring the DirectoryScanJob

To configure and use a DirectoryScanJob, you will need to set the following keys in the JobDataMap:
  1. DIRECTORY_NAME: this is the path to the directory that you want to monitor.
  2. DIRECTORY_SCAN_LISTENER_NAME: this is the name of the DirectoryScanListener that should called if changes are detected
  3. MINIMUM_UPDATE_AGE: this is an optional parameter which lets you specify how many milliseconds must have passed since the file’s last modified time in order to consider the file modified. If this parameter is not specified, it defaults to 5000 milliseconds.

Possible Uses for the DirectoryScanJob

There are many uses for the DirectoryScanJob, so let’s list a few here:
  • You could replace any FileWatcher type services that you might have implemented
  • Scan files uploaded by users for viruses
  • Schedule other jobs to process files that have been copied to the directory for processing
The next post in the series will cover a change to the IStatefulJob interface: it has been marked as obsolete and is replaced by a class attribute!

Sunday, January 30, 2011

New in Quartz.Net 2.0–The Calendar Interval Trigger

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 first post of what will become a series of posts highlighting the new features that will be available in Quartz.Net 2.0.
To kick off the series, we will take a look at the CalendarIntervalTrigger. This trigger is used to fire jobs based on a recurring calendar time interval. The following time intervals are available:
IntervalUnit
Minute
Hour
Day
Week
Month
Year
This trigger has a RepeatInterval property, which can be used to indicate that the trigger should fire every N intervals. For example, if you set the RepeatIntervalUnit to Week and the RepeatInterval to 2, your trigger will fire every two weeks.
Here is what the source code documentation says about this trigger:
A concrete ITrigger that is used to fire a IJobDetail based upon repeating calendar time intervals.
The trigger will fire every N (see RepeatInterval) units of calendar time (see RepeatIntervalUnit) as specified in the trigger's definition.  This trigger can achieve schedules that are not possible with ISimpleTrigger (e.g because months are not a fixed number of seconds) or ICronTrigger (e.g. because "every 5 months" is not an even divisor of 12).
If you use an interval unit of IntervalUnit.Month then care should be taken when setting the startTime value that is on a day near the end of the month.  For example, if you choose a start time that occurs on January 31st, and have a trigger with unit IntervalUnit.Month and interval 1, then the next fire time will be February 28th, and the next time after that will be March 28th - and essentially each subsequent firing will occur on the 28th of the month, even if a 31st day exists.  If you want a trigger that always fires on the last day of the month - regardless of the number of days in the month, you should use ICronTrigger.
That’s it for today’s “New in Quartz.Net 2.0” post. Stay tuned for more, coming soon!