Contents tagged with Quartz

  • Quartz Scheduling Much More Flexible

    Tags: Quartz, Autofac, Atlas, Entity Framework, Closed XML

    I’ve created a new extremely flexible service using Quartz, through a configuration file that I wanted to share with you. In my example I’ve followed the standard Domain Driven Design principals and used the repository pattern. This service is a good tool to see log4net, Atlas, Quartz, Autofac, ClosedXML and Entity Framework using code first all in action. I will explain a few key areas of my service, but you will need to download the application to really get a feel for its full power and see it in action.

    First I will point out the quartz configuration. I’ve wired up in my app.config to tell quartz to look at the quartzjobs.config file. In the quartz file I’ve setup a job to run every 30 seconds on the 30 second and 60 second tick. This differs from previous posts where I wired up the job and triggers inside of the code. Doing the quartz plumbing inside of a configuration file will allow me to change scheduling, without having to touch a line of code.

    <?xml version="1.0" encoding="UTF-8"?>
    <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>CreateReportsJob</name>
          <group>MY_JOBS_GROUP</group>
          <description>creates the my service reports</description>
          <job-type>application.CreateReportsJob, application</job-type>
          <job-data-map>
            <entry>
              <key>MessageToLog</key>
              <value>Hello from MyJob</value>
            </entry>
          </job-data-map>
        </job>
        <trigger>
          <cron>
            <name>CreateReportsTrigger</name>
            <group>MY_TRIGGER_GROUP</group>
            <description>the trigger to create the my service reports</description>
            <job-name>CreateReportsJob</job-name>
            <job-group>MY_JOBS_GROUP</job-group>
    
            
            <!-- (1)Seconds  (2)Minutes  (3)Hours   (4)Day-of-Month   (5)Month   (6)Day-of-Week   (7)Year -->
            <!--<cron-expression>0 0 10 3 * ?</cron-expression>-->   <!-- Fires Every Month on the 3rd day at 10AM -->
            <cron-expression>0/30 * * * * ?</cron-expression>    <!-- Fires Every 30 seconds on the 30/60 second tick -->
          </cron>
        </trigger>
      </schedule>
    </job-scheduling-data>
    

    Within the app.config there is a Quartz section where I tell quartz to check the configration file every 10 seconds to see if there was an update to the quartz jobs and triggers. This will help to alleviate having to restart the service when you make a change to the quartz configuration.

    <add key="quartz.plugin.xml.scanInterval" value="10" />
    

    Now I will explain the job, CreateReportsJob.cs. This class implements the Quartz IJob which implements the execute method which does a couple things when it executes. First it will write a message out to the log telling you that its running, it pulls the message from the Quartz job-data-map in the configuration file. It will then write out to the log file and to the console all the brokers and clients. The clients differs in one key area from the broker, it has a foreign key relationship to the contact address table. Which I put in to display how a foreign key relationship can working using Entity Framework code first. Finally it uses ClosedXML to write out the brokers to a Excel file.

    [DisallowConcurrentExecution]
    public class CreateReportsJob : IJob
    {
        public IBrokerRepository BrokerRepository { get; set; }
        public IClientRepository ClientRepository { get; set; }
        public IExcelRepository ExcelRepository { get; set; }
        public ICreateFileFactory CreateFileFactory { get; set; }
        public ILog Log { get; set; }
    
        public void Execute(IJobExecutionContext context)
        {
            var data = context.MergedJobDataMap;
            var msg = data.GetString("MessageToLog") ?? string.Empty;
    
            Console.WriteLine(DateTime.Now + " " + msg);
            Log.InfoFormat(DateTime.Now + " " + msg);
    
    
            WriteOutCollectionOfBrokers();
            WriteOutCollectionOfClients();
    
            CreateExcelOfBrokers();
        }
    
        private void CreateExcelOfBrokers()
        {
            var fileName = string.Format("Brokers {0} {1}.xlsx", DateTime.Now.ToShortDateString().Replace("/", ""), DateTime.Now.ToShortTimeString().Replace(":", ""));
            Log.Info("FileName:" + fileName);
    
            var brokers = BrokerRepository.Get();
            ExcelRepository.CreateBrokerExcelDocument(brokers, fileName);
        }
    
        private void WriteOutCollectionOfBrokers()
        {
            var brokers = BrokerRepository.Get();
            foreach (var broker in brokers)
            {
                Log.InfoFormat(broker.NameOf);
                Console.WriteLine(broker.NameOf);
            }
        }
    
        private void WriteOutCollectionOfClients()
        {
            var clients = ClientRepository.Get().Take(5000);
    
            foreach (var client in clients)
            {
                Log.InfoFormat("Client | Name: {0} Address 1: {1}", client.NameOf, client.ContactAddressAccount != null ? client.ContactAddressAccount.Address1 : string.Empty);
                Console.WriteLine("Client | Name: {0} Address 1: {1}", client.NameOf, client.ContactAddressAccount != null ? client.ContactAddressAccount.Address1 : string.Empty);
            }
        }
    }
    

     

    Those really are the key differences I wanted to point out in this new service that differ from my previous post a quick service that sends emails. I originally got the idea from a reader who was looking to have a more configurable option with quartz, rather than using application settings. I decided to explore a bit and pieced this solution together which I’m really happy with and wanted to share.

    Sample Excel File Output

    image

    Sample Console Output

    Notice that this console shows that it was triggered twice, once at 12:00 and once at 12:30.

    image

    Give me your thoughts and feel free to download the solution here.

  • a quick service that sends emails

    Tags: EmailNotification, Atlas, Autofac, AutoMapper, Email, Quartz

    As promised in my previous post I will show you how to put together a service in a matter of a few minutes using what I showed you in sending emails made easy and a quick way to create a windows service using Autofac, Quartz and Atlas. In this example I am going to wire up a service that will be powered by atlas so that it makes the service easy to install/uninstall and debug. I then use autofac to manage my IoC container. I also will use quartz for managing the schedule of my service, instead of just a standard timer. I then will use auto mapper to handle the mapping between entities. Finally I will use EmailNotification to send my emails. The final product will query a table every minute and send out emails, it will then update the table with the sent date for history tracking.

    To get started you will just create a new console application. You can quickly reference all of the references to atlas, autofac, auto mapper, email notification, and quartz using nuget.

    Once things are referenced lets make sure our database table and the entity that is used is wired up using auto mapper. Auto mapper takes care of the translation/mapping between my entity and the email notification MessageQueueEntity. So I will wire that up painlessly.

    public class AutomapperRegistrations
    {
        public void RegisterMappings()
        {
            CreateEmailMessageToMessageQueueMapping();
            CreateMessageQueueToEmailMessageMapping();
        }
    
        private void CreateEmailMessageToMessageQueueMapping()
        {
            Mapper.CreateMap<MessageQueueEntity, EmailMessage>()
                  .ForMember(from => from.EmailMessageId, dest => dest.MapFrom(from => from.Identifier));
        }
    
        private void CreateMessageQueueToEmailMessageMapping()
        {
            Mapper.CreateMap<EmailMessage, MessageQueueEntity>()
                  .ForMember(from => from.Identifier, dest => dest.MapFrom(from => from.EmailMessageId));
    
        }
    }
    

    Now I will create my autofac module which handles all the registrations into my IoC container.

    public class ServiceModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            LoadQuartz(builder);
            LoadServices(builder);
    
            builder.Register(c => new EmailRepository()).As<IEmailRepository>();
        }
    
        private static void LoadQuartz(ContainerBuilder builder)
        {
            builder.Register(c => new StdSchedulerFactory().GetScheduler()).As<IScheduler>().InstancePerLifetimeScope();
            builder.Register(c => new AutofacJobListener(ContainerProvider.Instance)).As<IJobListener>();
        }
    
        private static void LoadServices(ContainerBuilder builder)
        {
            builder.RegisterType<EmailService>().As<IAmAHostedProcess>().PropertiesAutowired();
        }
    }
    
    

    Now I will setup my job which processes the email(s). Since I am using Quartz it will inherit an IJob and I will implement the execute method. When Execute is called it will retrieve the unsent message from the database and send them using the power of EmailNotification.

    public class SendEmailJob : IJob
    {
        public IEmailRepository EmailRepository { get; set; }
    
        public void Execute(IJobExecutionContext context)
        {
            SendEmails();
        }
        
        public void SendEmails()
        {
            //Get Emails from the repository and translate them to the messagequeueentity. MessageQueueEntity is the defined email object.
            var emails = EmailRepository.GetUnsentEmails().ToMessageQueueEntities();
    
            //Use the web config configuration.
            var configuration = Master.UseAppConfig().WithEmails(emails);
    
            //The service will now send out the emails and return the results.
            var result = Master.Execute(configuration);
    
            //Set the sent date for each email that has been sent.
            foreach (var email in result.Emails.Where(e => e.IsSent))
            {
                var e = EmailRepository.GetEmail((long)email.Identifier);
                e.IsSent = email.IsSent;
                e.Sent = email.Sent;
    
                e.Save();
            }
    
            //Save any errors that were caught.
            foreach (var email in result.Emails.Where(e => !e.IsSent && e.SentException != null))
            {
                var e = EmailRepository.GetEmail((long) email.Identifier);
                e.ErrorMessage = email.SentException.Message;
    
                e.Save();
            }
        }
    }
    

    Now that we have our job I will go ahead and create the service using Quartz. Quartz will need a job that will call our SendEmailJob and a trigger. I will set the trigger to fire on an interval, every minute. You can set the trigger up many different ways, take a look at Quartz trigger options in there documentation.

    public class EmailService : IAmAHostedProcess
    {
        public IScheduler Scheduler { get; set; }
        public IJobListener AutofacJobListener { get; set; }
    
        public void Start()
        {
            //create the job with quartz
            var job = JobBuilder.Create<SendEmailJob>()
            .WithIdentity("SendEmail")
            .Build();
    
            //define the trigger in quartz to fire every minute
            var trigger = TriggerBuilder.Create()
                .WithIdentity("SendEmailTrigger")
                .StartNow()
                .WithCalendarIntervalSchedule(x => x.WithIntervalInMinutes(1))  //Run's every minute
                .Build();
    
            //schedule the job within quartz
            Scheduler.ScheduleJob(job, trigger);
            Scheduler.ListenerManager.AddJobListener(AutofacJobListener);
            Scheduler.Start();  //kick off the quartz job
        }
    
        public void Stop()
        {
            //shut quartz down
            Scheduler.Shutdown();
        }
    
        public void Resume()
        {
            //resume the quartz jobs
            Scheduler.ResumeAll();
        }
    
        public void Pause()
        {
            //pause the quartz jobs
            Scheduler.PauseAll();
        }
    }
    

    Finally we wire up the final piece which is to wire up the entry point of the application. I chose to use atlas since it makes it real easy for me to install/uninstall/debug a windows service. I will use the application config, tell it to use my autofac module and run my automapper registrations before atlas begins.

    static class Program
    {
        //Standard entry point for an application.
        static void Main(string[] args)
        {
            //Register Atlas using app.config, register the autofac module and setup any automapper registrations.
            var configuration = Host.UseAppConfig<EmailService>()
                .WithRegistrations(b => b.RegisterModule(new ServiceModule()))
                .BeforeStart(() => new AutomapperRegistrations().RegisterMappings())
                .WithArguments(args);
    
            //Kick off atlas
            Host.Start(configuration);
        }
    }
    

    Last thing we need to do is setup the app.config for both EmailNotification and atlas.

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <configSections>
        <section name="atlas" type="Atlas.Configuration.XmlConfiguration, Atlas" />
        <section name="EmailNotificationSettings" type="EmailNotification.Config.EmailNotificationConfigurationSectionHandler, EmailNotification" allowLocation="false" allowDefinition="Everywhere" />
      </configSections>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
      </startup>
      <atlas>
        <host name="Email Service" displayName="My Sample Email Service" description="This sample email service will send emails using the email notification service." allowMultipleInstances="true">
          <runtime accounttype="networkservice" startup="automatic" />
        </host>
      </atlas>
      <EmailNotificationSettings isEnabled="true">
        <ServerSettings smtpServer="10.252.3.157" smtpServerPort="25" isSSLEnabled="false" smtpServerConnectionLimit="-1" smtpServerPassword="" smtpServerUser="" smtpServerRequiredLogin="false" useDefaultCredentials="true" />
        <TestEmailAccounts isTestEmailAccountsBlocked="false">
          <add account="mailinator.com"></add>
        </TestEmailAccounts>
      </EmailNotificationSettings>
    </configuration>
    

    That’s it were complete! Lets fire the thing up and see it fly. Remember when you go to install all you have to do is run at the command prompt Service.exe –i it will install and can be found in your windows service list using the name you set in the atlas section of your app.config. I hope you found setting up the service to be painless and easy, I’ve included the code for you to download.

  • a quick way to create a windows service using Autofac, Quartz and Atlas

    Tags: Autofac, Quartz, Atlas

    How often have you cringed when you thought about writing a windows service? I bet the first thoughts that came to you were:

    • I hate having to write an installer or bat file to install the service!
    • I hate having to write redundant code to manage the schedule!
    • I really don’t like debugging services!
    • I would like to write very little code and have the service re-use the code my application or site is using.

    Laying out the basics

    For simplicity in my example I am going to set these requirements for my service since they are a pretty common reason for needing a service. I will want a service that runs every minute (could be hourly, daily, monthly, etc.) and will run a specific job in my case write to a file.

    I’ve found three tools that I’ve found to be extremely useful and take the pain out of it. The first tool is Autofac an IoC (Inversion of Control) container which is the only IoC container supported by Atlas. I use a IoC container so that it can manage my registrations which I pass off to Atlas. Next I use Quartz a job scheduling system which allows us to not worry about setting up timers and triggers, we’ll let Quartz do this for us. Finally I chose Atlas a framework which allows us to write, run, debug and deploy a windows service painlessly.

    The Solution

    Each time the job is executed I want to create a new container for the job and inject any properties that the job might need. I also want to dispose of the container when the job is complete. So to do this I create a new job Quartz job listener.

    public class AutofacJobListener : IJobListener
    {
        private readonly IContainerProvider _containerProvider;
        private IUnitOfWorkContainer _container;
    
        public AutofacJobListener(IContainerProvider containerProvider)
        {
            _containerProvider = containerProvider;
        }
    
        public void JobToBeExecuted(IJobExecutionContext context)
        {
            _container = _containerProvider.CreateUnitOfWork();
            _container.InjectUnsetProperties(context.JobInstance);
        }
    
        public void JobExecutionVetoed(IJobExecutionContext context)
        {
            /*noop*/
        }
    
        public void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException)
        {
            _container.Dispose();
        }
    
        public string Name
        {
            get { return "AutofacInjectionJobListener"; }
        }
    }
    
    

    Now let’s create our job. The job will implement the Quartz.IJob. This job is really simple in our case, I will write “Job Executing “ and the date to a file in the directory where the application is running.

    public class MyJob : IJob
    {
        public void Execute(IJobExecutionContext context)
        {
            //Get the current directory to behave like a normal application, else the service will log to \Windows\System32 folder
            Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
    
            var dateTime = DateTime.Now;
            var sw = new StreamWriter("MyFile.txt",true);
    
            sw.WriteLine("Job Executing " + dateTime);
            Console.WriteLine("Job Executing " + dateTime);
    
            sw.Close();
            sw.Dispose();
        }
    }
    
    

    Now its time to setup the actual service and use Atlas. I will inherit off of the Atlas.IAmAHostedProcess and implement the methods. You will notice in the Start method I will use Quartz to create my job, my trigger and schedule it to run.

    public class MyService : IAmAHostedProcess
    {
        const int IntervalInMinutes = 1;
        
        public IScheduler Scheduler { get; set; }
        public IJobListener AutofacJobListener { get; set; }
    
        #region Implementation of IAmAHostedProcess
    
        public void Start()
        {
            var job = JobBuilder.Create<MyJob>()
                .WithIdentity("Job1")
                .Build();
    
            var trigger = TriggerBuilder.Create()
                .WithIdentity("Trigger1")
                .StartNow()
                .WithCalendarIntervalSchedule(x => x.WithIntervalInMinutes(IntervalInMinutes))
                .Build();
    
            Scheduler.ScheduleJob(job, trigger);
            Scheduler.ListenerManager.AddJobListener(AutofacJobListener);
            Scheduler.Start();
        }
    
        public void Stop()
        {
            Scheduler.Shutdown();
        }
    
        public void Resume()
        {
            Scheduler.ResumeAll();
        }
    
        public void Pause()
        {
            Scheduler.PauseAll();
        }
    
        #endregion
    }
    
    

    Now lets register our Quartz scheduler and the service with autofac.

    public class MyAutofacModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            LoadQuartz(builder);
            LoadServices(builder);
        }
    
        private static void LoadQuartz(ContainerBuilder builder)
        {
            builder.Register(c => new StdSchedulerFactory().GetScheduler()).As<IScheduler>().InstancePerLifetimeScope();
            builder.Register(c => new AutofacJobListener(ContainerProvider.Instance)).As<IJobListener>();
        }
    
        private static void LoadServices(ContainerBuilder builder)
        {
            builder.RegisterType<MyService>().As<IAmAHostedProcess>().PropertiesAutowired();
        }
    }
    
    

    Finally, we will wire up our Main method, the entry point when the console/service runs. We just configure Atlas and tell it to start.

    public class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main(string[] args)
        {
            var configuration =
                Host.UseAppConfig<MyService>()
                    .AllowMultipleInstances()
                    .WithRegistrations(b => b.RegisterModule(new MyAutofacModule()))
                    .WithArguments(args);
    
            Host.Start(configuration);
        }
    }
    
    

    So now lets go ahead an lets get ready to run the application. Since Atlas can be run as a console application easily by executing it with the following argument –c. Let’s go into the Properties of the project and on the Debug tab set the Command line arguments to be –c

    image

    Ok go ahead and run the application, and notice that you can press P to pause the application or click ESC key to exit the application. It will fire every minute both writing to the console and a text file called MyFile.txt.

    So now that everything is running correctly and we’re ready to deploy the application, lets do one more thing to make installation a breeze. Lets add an Atlas section to the configuration file with the name, description and user to run as for the service to be installed.

    <?xml version="1.0"?>
    <configuration>
      <configSections>
        <section name="atlas" type="Atlas.Configuration.XmlConfiguration, Atlas" />
      </configSections>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
      </startup>
      <atlas>
        <host name="AtlasExample" displayName="Atlas Sample" description="a sample project for atlas" allowMultipleInstances="true">
          <runtime accounttype="networkservice" startup="automatic" />
        </host>
      </atlas>
    </configuration>
    

    Service Installation with Atlas

    Alright let’s install the service, watch how easy this is.

    1. Copy the installation files for the service to a folder where you want the service to run (copy the Bin folder contents to a folder)
    2. Open a command prompt window to the directory where the service exists.
    3. Run the following command line AtlasExample.exe –i (to uninstall all you have to do is run these same steps but instead of -i use –u)
    4. That’s it! you can now go to the Services manager in windows and find the service is ready to be turned on. Notice the name and description are what we setup inside of the configuration file.
      image

    Conclusion

    Look at how easy that was! In a matter of 15 minutes I was able to write a service, have it manage scheduling itself, manage memory and registrations efficiently, run as both a console and service and have it self install through a baked in installer. Quartz handles the scheduling and the job, Atlas handles the painless debug and install of the service and autofac manages our containers.

    Writing your services like this will allow you to write a single windows service that can be executing a lot of different jobs all at different times or at the same time. It lets you abstract any non-windows service code to be injected into a job using autofac from another class so that code can be shared with other parts of a solution and tested.

    I hope my way of writing services is a help to you and that the next time you are asked to write a service you don’t feel like its such a pain to write. I’ve included a copy of the source code for you to download and play with.
    download source code