Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Quartz.Net scheduler exposed via a Web Service

, 23 Jan 2013
Rate this:
Please Sign up or sign in to vote.
  I had been given a business requirement to provide a self service Website for job scheduling. Having worked with Quartz.Net, it seemed the perfect backend engine to schedule and process jobs. I decided to expose the functionality of the Quartz.Net via a few web methods using Web services to allow

I had been given a business requirement to provide a self service Website for job scheduling. Having worked with Quartz.Net, it seemed the perfect backend engine to schedule and process jobs. I decided to expose the functionality of the Quartz.Net via a few web methods using Web services to allow for it to be consumed in Excel, SharePoint, a Web or a desktop app. In this blog post I’ll show you how to expose Quartz.Net via a Web Service.

image

01 – Architecture

  • The Scheduler Web Service (Scheduler.asmx) is hosted on a Web Server.

  • I currently have 3 App Servers which are running Quartz.Net as a windows service. The names of these app servers are ‘Alpha’, ‘Beta’ and ‘Gamma’. I have added a firewall exception for port 555 for Quartz.Net to listen on port 555.

  • Tags: Interesting! Now you would ask, what are tags? Tags are basically my way of identifying which type of scheduled jobs is my quartz app server capable of running. For instance Alpha is only equipped to schedule PowerShell specific jobs, while Beta can schedule VBA jobs and Gamma can schedule any job that requires the cmd process. You can scale this out to include .Net, Python or just different versions of a process.

  • XML: The xml config you see flowing from the user to the web server is the job description i.e. the request for scheduling Job X while specifying the schedule, priority, etc.

image

02 – Business Logic

Please see the business Logic below for the methods you have seen in the web service signature above.

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Linq;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Rte.Model.Entities;

namespace Scheduler.Business
{
    public class Scheduler
    {
        public readonly IScheduler Instance;
        public string Address { get; private set; }
        public string JobName { get; set; }
        public string JobGroup { get; set; }
        public int Priority { get; set; }
        public string CronExpression { get; set; }

        private readonly ISchedulerFactory _schedulerFactory;


        public Scheduler(string server, int port, string scheduler)
        {
            Address = string.Format("tcp://{0}:{1}/{2}", server, port, scheduler);
            _schedulerFactory = new StdSchedulerFactory(GetProperties(Address));

            try
            {
                Instance = _schedulerFactory.GetScheduler();

                if (!Instance.IsStarted)
                    Instance.Start();
            }
            catch (SchedulerException ex)
            {
                throw new Exception(string.Format("Failed: {0}", ex.Message));
            }
        }

        private static NameValueCollection GetProperties(string address)
        {
            var properties = new NameValueCollection();
            properties["quartz.scheduler.instanceName"] = "ServerScheduler";
            properties["quartz.scheduler.proxy"] = "true";
            properties["quartz.threadPool.threadCount"] = "0";
            properties["quartz.scheduler.proxy.address"] = address;
            return properties;
        }

        public IScheduler GetScheduler()
        {
            return Instance;
        }

        public List<GroupStatus> GetGroups()
        {
            var results = new List<GroupStatus>();
            foreach (var gp in Instance.GetJobGroupNames())
            {
                results.Add(new GroupStatus()
                    {
                        Group = gp,
                        IsJobGroupPaused = Instance.IsJobGroupPaused(gp),
                        IsTriggerGroupPaused = Instance.IsTriggerGroupPaused(gp)
                    });
            }
            return results;
        }

        public JobSchedule GetSchedule()
        {
            var jobKey = new JobKey(JobName, JobGroup);

            var trigger = Instance.GetTriggersOfJob(jobKey).FirstOrDefault();

            var js = new JobSchedule();

            if (trigger != null)
            {
                js.Name = trigger.Key.Name;
                js.Group = trigger.Key.Group;
                js.Description = trigger.Description;
                js.Priority = trigger.Priority;
                js.TriggerType = trigger.GetType().Name;
                js.TriggerState = Instance.GetTriggerState(trigger.Key).ToString();

                DateTimeOffset? startTime = trigger.StartTimeUtc;
                js.StartTime = TimeZone.CurrentTimeZone.ToLocalTime(startTime.Value.DateTime);

                var nextFireTime = trigger.GetNextFireTimeUtc();
                if (nextFireTime.HasValue)
                {
                    js.NextFire = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTime.Value.DateTime);
                }

                var previousFireTime = trigger.GetPreviousFireTimeUtc();
                if (previousFireTime.HasValue)
                {
                    js.LastFire = TimeZone.CurrentTimeZone.ToLocalTime(previousFireTime.Value.DateTime);
                }
            }

            return js;
        }

        public List<JobSchedule> GetSchedules()
        {
            var jcs = new List<JobSchedule>();

            foreach (var group in Instance.GetJobGroupNames())
            {
                var groupMatcher = GroupMatcher<JobKey>.GroupContains(group);
                var jobKeys = Instance.GetJobKeys(groupMatcher);

                foreach (var jobKey in jobKeys)
                {
                    var triggers = Instance.GetTriggersOfJob(jobKey);
                    foreach (var trigger in triggers)
                    {
                        var js = new JobSchedule();
                        js.Name = jobKey.Name;
                        js.Group = jobKey.Group;
                        js.TriggerType = trigger.GetType().Name;
                        js.TriggerState = Instance.GetTriggerState(trigger.Key).ToString();
                        js.Priority = trigger.Priority;

                        DateTimeOffset? startTime = trigger.StartTimeUtc;
                        js.StartTime = TimeZone.CurrentTimeZone.ToLocalTime(startTime.Value.DateTime);

                        DateTimeOffset? nextFireTime = trigger.GetNextFireTimeUtc();
                        if (nextFireTime.HasValue)
                        {
                            js.NextFire = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTime.Value.DateTime);
                        }

                        DateTimeOffset? previousFireTime = trigger.GetPreviousFireTimeUtc();
                        if (previousFireTime.HasValue)
                        {
                            js.LastFire = TimeZone.CurrentTimeZone.ToLocalTime(previousFireTime.Value.DateTime);
                        }

                        jcs.Add(js);
                    }
                }
            }
            return jcs;
        }

        public List<JobSchedule> GetSchedules(string groupName)
        {
            var jcs = new List<JobSchedule>();

            var groupMatcher = GroupMatcher<JobKey>.GroupContains(groupName);
            var jobKeys = Instance.GetJobKeys(groupMatcher);

            foreach (var jobKey in jobKeys)
            {
                var triggers = Instance.GetTriggersOfJob(jobKey);
                foreach (var trigger in triggers)
                {
                    var js = new JobSchedule();
                    js.Name = jobKey.Name;
                    js.Description = trigger.Description;
                    js.Group = jobKey.Group;
                    js.TriggerType = trigger.GetType().Name;
                    js.TriggerState = Instance.GetTriggerState(trigger.Key).ToString();
                    js.Priority = trigger.Priority;

                    DateTimeOffset? startTime = trigger.StartTimeUtc;
                    js.StartTime = TimeZone.CurrentTimeZone.ToLocalTime(startTime.Value.DateTime);

                    DateTimeOffset? nextFireTime = trigger.GetNextFireTimeUtc();
                    if (nextFireTime.HasValue)
                    {
                        js.NextFire = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTime.Value.DateTime);
                    }

                    DateTimeOffset? previousFireTime = trigger.GetPreviousFireTimeUtc();
                    if (previousFireTime.HasValue)
                    {
                        js.LastFire = TimeZone.CurrentTimeZone.ToLocalTime(previousFireTime.Value.DateTime);
                    }

                    jcs.Add(js);
                }
            }
            return jcs;
        }

        public string GetMetaData()
        {
            var metaData = Instance.GetMetaData();

            return string.Format(
                "{0}Name: '{1}'{0}Version: '{2}'{0}ThreadPoolSize: '{3}'{0}IsRemote: '{4}'{0}JobStoreName: '{5}'
                 {0}SupportsPersistance: '{6}'{0}IsClustered: '{7}'",
                Environment.NewLine, metaData.SchedulerName, metaData.Version, metaData.ThreadPoolSize,
                metaData.SchedulerRemote, metaData.JobStoreType.Name, metaData.JobStoreSupportsPersistence,
                metaData.JobStoreClustered);
        }

        public bool UnscheduleJob()
        {
            var jobKey = new JobKey(JobName, JobGroup);

            if (Instance.CheckExists(jobKey))
            {
                return Instance.UnscheduleJob(new TriggerKey(JobName, JobGroup));
            }
            return false;
        }

        public bool UnscheduleAll()
        {
            foreach (var group in Instance.GetTriggerGroupNames())
            {
                var groupMatcher = GroupMatcher<JobKey>.GroupContains(group);
                var jobKeys = Instance.GetJobKeys(groupMatcher);

                foreach (var triggers in jobKeys.Select(jobKey => Instance.GetTriggersOfJob(jobKey)))
                {
                    return Instance.UnscheduleJobs(triggers.Select(t => t.Key).ToList());
                }
            }
            return false;
        }

        public void DeleteAll()
        {
            Instance.Clear();
        }

        public void RescheduleJob()
        {
            // Build new trigger
            var trigger = (ICronTrigger)TriggerBuilder.Create()
                .WithIdentity(JobName, JobGroup)
                .WithCronSchedule(CronExpression)
                .WithPriority(Priority)
                //.StartAt(StartAt.ToUniversalTime())
                .Build();

            Instance.RescheduleJob(new TriggerKey(JobName, JobGroup), trigger);
        }
    }
}

I hope this is useful for you…

This was sixth in the series of posts on enterprise scheduling using Quartz.net, in the next post I’ll be covering how to build a sample MVC website to look at the jobs scheduled via Quartz.Net scheduling windows service. 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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Tarun_Arora
Software Developer (Senior) Avanade
United Kingdom United Kingdom
Solution Developer - C# .NET, ALM
 
Tarun Arora is a Microsoft Certified professional developer for Enterprise Applications. He has over 5 years of experience developing 'Energy Trading & Risk Management' solutions using Microsoft Technologies. Tarun has great passion for technology and travel (not necessarily in the same order)!
Follow on   Twitter

Comments and Discussions

 
GeneralAccessing Java Quartz Scheulers Via a Web-service Pinprofessionalxmoravej4-May-14 23:38 
GeneralMy vote of 4 Pinprofessionalnikko25619-Jun-13 4:09 
GeneralMy vote of 5 Pinmembermgonzales326-Apr-13 10:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140921.1 | Last Updated 23 Jan 2013
Article Copyright 2013 by Tarun_Arora
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid