Using Quartz.Net 2.0 With SQL Compact 4.0

Now that SQL Compact 4.0 is out, you might want to use it to as your Quartz.Net job store. Here is how to set up a Quartz.Net 2.0 scheduler with a SQL Compact 4.0 database.

Prerequisites
First, you will need to download the SQL Compact 4.0 runtime and install it on the same box that will run the Quartz.Net scheduler. You can download the runtime directly from the Microsoft site. Be sure to download the correct version for the bit-ness of your operating system. Once it is installed, we’ll configure Quartz.Net to use it as the default job store.
Configuring Quartz.Net 2.0 to Use SQL Compact 4.0 as the Jobstore
By default, Quartz.Net uses a memory based jobstore. This means that all of the jobstore information (jobs, triggers, next fire times, etc.) is lost every time that the scheduler is stopped. A database job store lets you persist this information so that the next time the scheduler starts you will not need to create all of your job and trigger information.
To use a database as the jobstore, you need to set several configuration properties. Here is a sample configuration that uses SQL Compact 4.0 as the job store:
quartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz
quartz.jobStore.driverDelegateType = Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz
quartz.jobStore.dataSource = quartzDataSource
quartz.dataSource.quartzDataSource.connectionString = Data Source=C:\quartz\quartz.sdf;Persist Security Info=False;
quartz.dataSource.quartzDataSource.provider = SqlServerCe-400

Setting Up the Database File
You will need a SQL Compact 4.0 database file in which to store the job store information. You can either create the database yourself (that’s a whole separate post itself, which I might write if there is enough interest…) or you can just download this empty database that I have already created. Once you have your database file ready to go, just change the data source for the connection string to point to the location of your database file.
Starting the Scheduler
Let’s start our scheduler so that we can see what to expect. Here’s what the console output looks like when everything works correctly:
image
If you already have a running instance of the scheduler, this post should tell you all that need to do to get your scheduler to use SQL Compact 4.0 as a job store.

Continue Reading

Installing Multiple Instances of Quartz.Net on One Machine

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.

Continue Reading

Installing Quart.Net 2.0 Beta 1 as a Windows Service

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.

Continue Reading

Configuring a DirectoryScanJob in Quartz.Net 2.0

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.

Continue Reading

Implementing a Job Listener in Quartz.Net 2.0

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.

Continue Reading