Geeks With Blogs

News




View Tarun Arora's profile on LinkedIn

profile for Tarun Arora at Stack Overflow, Q&A for professional and enthusiast programmers

Tarun Arora - Visual Studio ALM MVP ALM, Agile, Automation, Performance Testing, Software QA, Cloud, ...

 

In this blog post I’ll be covering,

  • 01: A few things to consider before you should schedule a Job using Quartz.Net
  • 02: Setting up your solution to use Quartz.Net API
  • 03: Quartz.Net configuration
  • 04: Writing & scheduling a hello world job with Quartz.Net
  • 05: Download Quartz.Net sample project and instructions

If you are new to Quartz.Net I would recommend going through,

 

01 – A few things to consider before you should schedule a Job using Quartz.Net

To schedule a job to run on Quartz.Net windows service at the very minimum you need the 3 items listed below,

    - An instance of the scheduler service

    - A trigger

    - And last but not the least a job

But a few additional things that you should think about are… For example, if I wanted to schedule a script to run on the server, I should be jotting down answers to the below questions,

     a. Considering there are multiple machines set up with Quartz.Net windows service, how can I choose the instance of Quartz.Net where I want my script to be run

     b. What will trigger the execution of the job

     c. How often do I want the job to run

     d. Do I want the job to run right away or start after a delay or may be have the job start at a specific time

     e. What will happen to my job if Quartz.Net windows service is reset

     f. Do I want multiple instances of this job to run concurrently

     g. Can I pass parameters to the job being executed by Quartz.Net windows service

02 – Setting up your solution to use Quartz.Net API

1. Create a new C# Console Application project and call it “HelloWorldQuartzDotNet” and add a reference to Quartz.Net.dll. I use the NuGet Package Manager to add the reference. This can be done by right clicking references and choosing Manage NuGet packages, from the Nuget Package Manager choose Online from the left panel and in the search box on the right search for Quartz.Net. Click Install on the package “Quartz” (Screen shot below).

image

Figure – Adding Quartz.Net reference to my project via Nuget Package Manager

SNAGHTML3af4a28b

Figure – Searching for Quartz.Net reference in Nuget Package Manager

image

Figure – Quartz reference added to my project via Nuget Package Manager

2. Right click the project and choose Add New Item. Add a new Interface and call it ‘IScheduledJob.cs’. Mark the Interface public and add the signature for Run. Your interface should look like below.

namespace HelloWorldQuartzDotNet
{
    public interface IScheduledJob
    {
        void Run();
    }
}

 

3. Right click the project and choose Add new Item. Add a class and call it ‘Scheduled Job’. Use this class to implement the interface ‘IscheduledJob.cs’. Look at the pseudo code in the implementation of the Run method.

using System;

namespace HelloWorldQuartzDotNet
{
    class ScheduledJob : IScheduledJob
    {
        public void Run()
        {

            // Get an instance of the Quartz.Net scheduler

            // Define the Job to be scheduled

            // Associate a trigger with the Job

            // Assign the Job to the scheduler

            throw new NotImplementedException();
        }
    }
}

 

I’ll get into the implementation in more detail, but let’s look at the minimal configuration a sample configuration file for Quartz.Net service to work.

03 – HelloWorldQuartzDotNet configuration file

In the App.Config file copy the below configuration

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="quartz" 
             type="System.Configuration.NameValueSectionHandler, 
             System, Version=1.0.5000.0,Culture=neutral, 
             PublicKeyToken=b77a5c561934e089" />
  </configSections>
  <quartz>
    <add key="quartz.scheduler.instanceName" value="ServerScheduler" />
    <add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz" />
    <add key="quartz.threadPool.threadCount" value="10" />
    <add key="quartz.threadPool.threadPriority" value="2" />
    <add key="quartz.jobStore.misfireThreshold" value="60000" />
    <add key="quartz.jobStore.type" value="Quartz.Simpl.RAMJobStore, Quartz" />
  </quartz>
</configuration>

 

As you can see in the configuration above, I have included the instance name of the quartz scheduler, the thread pool type, count and priority, the job store type has been defined as RAM. You have the option of configuring that to ADO.NET JOB store. More details here.

04 – Writing & scheduling a hello world job with Quartz.Net Windows Service

Once fully implemented the ScheduleJob.cs class should look like below. I’ll walk you through the details of the implementation…

- GetScheduler() uses the name of the quartz.net and listens on localhost port 555 to try and connect to the quartz .net windows service.

- Run() an attempt is made to start the scheduler in case it is in standby mode

- I have defined a job “WriteHelloToLog” (that’s the name of the job), this job belongs to the group “IT”. Think of group as a logical grouping feature. It helps you bucket jobs into groups. Quartz.Net gives you the ability to pause or delete all jobs in a group (We’ll look at that in some of the future posts). I have requested for recovery of this job in case the quartz.net service fails over to the other node in the cluster. The jobType is “HelloWorldJob”. This is the class that would be called to execute the job. More details on this below…

- schd.CheckExists will check to ensure that the job WriteHelloToLog doesn’t already exist. If it does, I’ll delete the job and allow you to recreate the same job. I have added this code just as a precaution to avoid you running into the exception “Quartz.ObjectAlreadyExistsException: Unable to store Job with name: 'WriteHelloWorldToLog' and group: 'IT', because one already exists with this identification.”

- I have defined a trigger for my job. I have called the trigger “WriteHelloToLog”. The Trigger works on the cron schedule “0 0/1 * 1/1 * ? *” which means fire the job once every minute. I would recommend that you look at www.cronmaker.com a free and great website to build and parse cron expressions. The trigger has a priority 1. So, if two jobs are run at the same time, this trigger will have high priority and will be run first.

- Use the Job and Trigger to schedule the job. This method returns a datetime offeset. It is possible to see the next fire time for the job from this variable.

using System.Collections.Specialized;
using Quartz;
using System;
using Quartz.Impl;

namespace HelloWorldQuartzDotNet
{
    class ScheduledJob : IScheduledJob
    {
        public void Run()
        {
            // Get an instance of the Quartz.Net scheduler
            var schd = GetScheduler();

            // Start the scheduler if its in standby
            if (!schd.IsStarted)
                schd.Start();

            // Define the Job to be scheduled
            var job = JobBuilder.Create<HelloWorldJob>()
                .WithIdentity("WriteHelloToLog", "IT")
                .RequestRecovery()
                .Build();

            // Associate a trigger with the Job
            var trigger = (ICronTrigger)TriggerBuilder.Create()
                .WithIdentity("WriteHelloToLog", "IT")
                .WithCronSchedule("0 0/1 * 1/1 * ? *") // visit http://www.cronmaker.com/ Queues the job every minute
                .StartAt(DateTime.UtcNow)
                .WithPriority(1)
                .Build();

            // Validate that the job doesn't already exists
            if (schd.CheckExists(new JobKey("WriteHelloToLog", "IT")))
            {
                schd.DeleteJob(new JobKey("WriteHelloToLog", "IT"));
            }

            var schedule = schd.ScheduleJob(job, trigger);
            Console.WriteLine("Job '{0}' scheduled for '{1}'", "WriteHelloToLog", schedule.ToString("r"));
        }

        // Get an instance of the Quartz.Net scheduler
        private static IScheduler GetScheduler()
        {
            try
            {
                var properties = new NameValueCollection();
                properties["quartz.scheduler.instanceName"] = "ServerScheduler";

                // set remoting expoter
                properties["quartz.scheduler.proxy"] = "true";
                properties["quartz.scheduler.proxy.address"] = string.Format("tcp://{0}:{1}/{2}", "localhost", "555",
                                                                             "QuartzScheduler");

                // Get a reference to the scheduler
                var sf = new StdSchedulerFactory(properties);

                return sf.GetScheduler();

            }
            catch (Exception ex)
            {
                Console.WriteLine("Scheduler not available: '{0}'", ex.Message);
                throw;
            }
        }
    }
}

 

The above highlighted values have been taken from the Quartz.config file, this file is available in the Quartz.net server installation directory.

Implementation of my HelloWorldJob Class below.

The HelloWorldJob class gets called to execute the job “WriteHelloToLog” using the once every minute trigger set up for this job. The HelloWorldJob is a class that implements the interface IJob. I’ll walk you through the details of the implementation…

- If you don’t include an empty constructor in the HelloWorldJob class then Quartz.Net windows service will not be able to create an instance of this class to execute your job. I learned this the hard way #JustSaying In love

- context is passed to the method execute by the quartz.net scheduler service. This has everything you need to pull out the job, trigger specific information. For example. I have pulled out the value of the jobKey name, the fire time and next fire time.

using Common.Logging;
using Quartz;
using System;

namespace HelloWorldQuartzDotNet
{
    class HelloWorldJob : IJob
    {

        private static readonly ILog Log = LogManager.GetLogger(typeof(HelloWorldJob));

        /// <summary> 
        /// Empty constructor for job initilization
        /// <para>
        /// Quartz requires a public empty constructor so that the
        /// scheduler can instantiate the class whenever it needs.
        /// </para>
        /// </summary>
        public HelloWorldJob()
        {
            
        }

        public void Execute(IJobExecutionContext context)        
        {
            try
            {
                Log.DebugFormat("{0}****{0}Job {1} fired @ {2} next scheduled for {3}{0}***{0}", 
                                                                        Environment.NewLine,
                                                                        context.JobDetail.Key,
                                                                        context.FireTimeUtc.Value.ToString("r"), 
                                                                        context.NextFireTimeUtc.Value.ToString("r"));


                Log.DebugFormat("{0}***{0}Hello World!{0}***{0}", Environment.NewLine);
            }
            catch (Exception ex)
            {
                Log.DebugFormat("{0}***{0}Failed: {1}{0}***{0}", Environment.NewLine, ex.Message);
            }
        }
    }
}

 

I’ll add a call to call the scheduler in the Main method in Program.cs

using System;
using System.Threading;

namespace HelloWorldQuartzDotNet
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
               // Infinite loop, so that the console doesn't close on you
                while (true)
                {
                    var sj = new ScheduledJob();
                    sj.Run();

                    Console.WriteLine(@"{0}Check Quartz.net\Trace\application.log.txt for Job updates{0}",
                                        Environment.NewLine);

                    Console.WriteLine("{0}Press Ctrl^C to close the window. The job will continue " +
                                        "to run via Quartz.Net windows service, " +
                                        "see job activity in the Quartz.Net Trace file...{0}",
                                        Environment.NewLine);

                    Thread.Sleep(10000 * 100000);    
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed: {0}", ex.Message);
                Console.ReadKey();
            }
        }
    }
}

 

I am going to hit F5 to fire up “HelloWorldQuartzDotNet” project… Let’s see the Quartz.Net job scheduling in action…

SNAGHTML206c76d

Figure – The program has run as expected. The program has successfully scheduled the “WriteHelloWorldToLog” job to the Quartz.Net Scheduler. As you can see the job has been scheduled to start at ‘Sat, 17 Nov 2012 13:19:00 GMT’ 

Let’s open up the C:\Program Files (x86)\Quartz.Net\Trace\Application.log.txt file to see Quartz.Net scheduler activity…

Note – If you can’t see the log file, then you don’t have logging for Quartz.Net enabled, follow the instructions here

 

SNAGHTML20d5a1b

Figure – The Quartz. Net application log file has the execution details of my “WriteHelloWorldToLog” job.

05 – Download Quartz.Net sample project and instructions

I have packaged the sample that I created for this blogpost and uploaded it to my skydrive. You can download the sample quartz.net helloworld solution from here.

Once you have downloaded and unpackaged the solution follow the steps below to get it to work,

1. Build the solution and resolve any compilation errors. You might run into compilation issues if you don’t have .net 4 installed on your machine. Ensure that Quartz.dll, Common.Logging.dll, Common.Logging.log4Net.dll and log4Net.dll are correctly available under references.

2. Once successfully compiled, go to the bin folder, copy “HelloWorldQuartzDotNet.exe” and paste it to the Quartz.Net windows service directory. In the example above, the Quartz.Net windows service directory is “C:\Program Files (x86)\Quartz.Net”

3. Stop the Quartz.Net service from Services.msc and start the service again.

4. Hit F5 on the “HelloWorldQuartzDotNet” project in Visual Studio.

And… that’s it…

This was third in the series of posts on enterprise scheduling using Quartz.net, in the next post I’ll be covering how to configure logging for the Quartz.net windows service. All Quartz.Net specific blog posts can listed here. Thank you for taking the time out and reading this blog post. If you enjoyed the post, remember to subscribe to http://feeds.feedburner.com/TarunArora. Stay tuned!

Posted on Saturday, November 17, 2012 1:07 AM Quartz.Net | Back to top


Comments on this post: Quartz.Net Writing your first Hello World Job

# re: Quartz.Net Writing your first Hello World Job
Requesting Gravatar...
What about memory issues? Will excpetion take effect on all scheduled tasks?
Left by Vitaly on Nov 30, 2012 1:53 AM

# re: Quartz.Net Writing your first Hello World Job
Requesting Gravatar...
Hi,
One question. is it necessary to copy my job dll's to the root.or can we configure the path.
Left by Aby Kurian on Dec 31, 2012 10:02 AM

# re: Quartz.Net Writing your first Hello World Job
Requesting Gravatar...
It is necessary that your job dll is available in the folder. How you get it there is your choice. Also note that the service needs to be restarted after the job dll has been placed there.
Left by Tarun Arora on Dec 31, 2012 10:32 AM

# re: Quartz.Net Writing your first Hello World Job
Requesting Gravatar...
God bless you. I m looking for two days even for a simple application. With your tutorial, i achieved what i would like to. QuartzNet 2.x is not well documented in offical site and other websites did not help me.

Man, you made my day. Thanks a lot.
Left by Yusuf on Jan 02, 2013 2:51 PM

# re: Quartz.Net Writing your first Hello World Job
Requesting Gravatar...
Hi Yusuf,

Delighted with your feedback. Glad you found it useful. Stay tuned for more blog posts on Quartz.Net.

Regards,
Tarun
Left by Tarun Arora on Jan 02, 2013 4:09 PM

# re: Quartz.Net Writing your first Hello World Job
Requesting Gravatar...
Hi Tarun, your blog is good find for me, as i was looking for alternative windows services for my various sehduling task, i was trying install your sample code, i am getting following error. any idea what i am doing wrong to cause this error!

Windows could not start the quart server service on local computer
Eror1053: The service did not respond to the start or control request in a timely fashion.

Thank you very much for your help.
Kumar

Left by Kumar on Jan 07, 2013 11:06 PM

# re: Quartz.Net Writing your first Hello World Job
Requesting Gravatar...
Tarun, can you also please let me know how to setup tcp://localhost:555/QuartzScheduler

Thank you
Kumar
Left by Kumar on Jan 07, 2013 11:21 PM

# re: Quartz.Net Writing your first Hello World Job
Requesting Gravatar...
Thank you for this guide I found it very useful. I have managed to setup an executable to run my job via quartz.net. My question is that how can I make quartz pick up this exe and schedule the job upon restart of server or service. At the moment I have to run the exe manually to schedule the job.
Thank you.
Left by Soap Distant on Jan 24, 2013 9:32 AM

# re: Quartz.Net Writing your first Hello World Job
Requesting Gravatar...
I downloaded your hello-world example.
When I start the server and the program I receive following error:
"Could not load file or assembly 'HelloWorldQuartzDotNet, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."
Left by schmitti81 on Jan 30, 2013 7:23 AM

comments powered by Disqus

Copyright © Tarun Arora [Microsoft MVP] | Powered by: GeeksWithBlogs.net | Join free