Question

I'm attempting to implement Quartz.NET as a Windows Service in C#. My jobs are not triggering when I expect them to trigger... at all, actually, as far as I can tell?

I have My Job schedule running starting on the next even minute after the and running "minutely". However, when the next minute comes, I cannot seem to tell if anything actually runs.

I would assume that when my job runs, a CLI window would pop on job execution, and the Console operations would be visible, (I even put a Console.ReadKey() in there to ensure the window isn't opening and closing so fast I can't see it), but as far as I can tell the schedule is simply not executing jobs.

I noticed that all the times are in UTC, and that the StartTimeUtc will be set to the UTC time which is +6 hours from my local computer time, but I would also assume that the Quartz scheduler handles that by calculating execution time from my TimeZone setting, though I have no way that I know of to confirm that, or to confirm the ACTUAL times that my schedule is set for.

I imagine there's some way to setup the Common Logging assembly and utilize it to help me know what my status is, but I have yet to figure out what to do with that to enable a log of any sort for feedback from my Windows Service, aside from writing to the event log I created for it.

My OnStart function of my windows service

protected override void OnStart(string[] args)
    {
        eventLog.WriteEntry("--- STARTING eLoyalty Scheduler Service ---");

        // construct a scheduler factory
        ISchedulerFactory schedFact = new StdSchedulerFactory();

        // get a scheduler
        IScheduler sched = schedFact.GetScheduler();

        // construct job info
        JobDetail jobDetail = new JobDetail("eLoyaltySchedulerService", null, typeof(PortalSchedulerJob));
        jobDetail.JobDataMap["jobSays"] = "eLoyalty Scheduler Service Executing!";
        jobDetail.JobDataMap["myStateData"] = new ArrayList(); 

        // fire every minute
        Trigger trigger = TriggerUtils.MakeMinutelyTrigger();

        // start on the next even minute
        trigger.StartTimeUtc = TriggerUtils.GetEvenMinuteDate(DateTime.UtcNow);

        // name it
        trigger.Name = "NextEvenMinute";

        // schedule it
        sched.ScheduleJob(jobDetail, trigger);

        // start the schedule
        sched.Start();

        eventLog.WriteEntry("--- STARTED eLoyalty Scheduler Service ---");
    }

My Job's Execute() function is as follows:

public void Execute(JobExecutionContext context)
    {
        try
        {
            string instName = context.JobDetail.Name;
            string instGroup = context.JobDetail.Group;
            JobDataMap dataMap = context.MergedJobDataMap;
            string jobSays = dataMap.GetString("jobSays");
            ArrayList state = (ArrayList)dataMap["myStateData"];
            state.Add(DateTime.UtcNow);

            Console.WriteLine("Instance {0} of PortalSchedulerJob says: {1} @ {2}", instName, jobSays, DateTime.UtcNow);
            Console.ReadKey();
        }
        catch (JobExecutionException Ex)
        {
            throw Ex;
        }
    }

If you can help me figure out how to troubleshoot my ACTUAL schedule activity, I may be able to solve this on my own... ?

Was it helpful?

Solution 3

Thanks for the input everyone... I couldn't get any of this to work, unfortunately, but when I added an event logger to my Job execution function, I found that it wrote to the eventlog every minute on the minute as expected.

I guess the Console.WriteLine() and Console.ReadKey() funcs do nothing in a windows service environment?

NOTE: They do something, but in the background running as a service rather than in a console window...

OTHER TIPS

In Quartz.NET tasks, you must only raise JobExecutionException exceptions: Quartz.net- Lesson 3:

The Job.Execute(..) Method Finally, we need to inform you of a few details of the IJob.Execute(..) method. The only type of exception that you are allowed to throw from the execute method is the JobExecutionException. Because of this, you should generally wrap the entire contents of the execute method with a 'try-catch' block. You should also spend some time looking at the documentation for the JobExecutionException, as your job can use it to provide the scheduler various directives as to how you want the exception to be handled.

Instead of:

catch (JobExecutionException Ex)
{
   throw Ex;
}

Do something like this:

catch (Exception err)
{
    // Only allow JobExecutionException exceptions to be thrown...
    throw new Quartz.JobExecutionException(err);
}

You can then handle the Exception centrally:

_globalJobListener = new GlobalJobListener();
sched.AddGlobalJobListener(_globalJobListener);


public class GlobalJobListener : Quartz.IJobListener
{
    public GlobalJobListener()
    {
    }

    public virtual string Name
    {
        get { return "MainJobListener"; }
    }

    public virtual void JobToBeExecuted(JobExecutionContext context)
    {       
    }

    public virtual void JobWasExecuted(JobExecutionContext inContext, JobExecutionException inException)
    {
        if (inException != null)
        {
            // Log/handle error here
        }
    }


    public virtual void JobExecutionVetoed(JobExecutionContext inContext)
    {

    }
}

You could always run your Windows service in the console when you're debugging:

static class Program
{
    static void Main()
    {
        var servicesToRun = new ServiceBase[] 
            { 
                new CassetteDeckService() 
            };

        if (Environment.UserInteractive)
        {
            var type = typeof(ServiceBase);
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var method = type.GetMethod("OnStart", flags);

            foreach (var service in servicesToRun)
            {
                method.Invoke(service, new object[] { null });
            }

            Console.WriteLine("Service Started!");
            Console.ReadLine();

            method = type.GetMethod("OnStop", flags);

            foreach (var service in servicesToRun)
            {
                method.Invoke(service, null);
            }
            Environment.Exit(0);
        }
        else
        {
            ServiceBase.Run(servicesToRun);
        }
    }
}

Just make sure to change the application type to Console Application in the properties of your Windows service project (this won't affect it running as a Windows service when it is not run in interactive mode).

if you want to debug your job keep visual studio open, in your job code put :

System.Diagnostics.Debugger.Launch();

that will allow you to attach to the debugger in visual studio and you can see what is going on.

alternatively, if you want to get common logging working, send me a message as I have it all working fine in my windows service.

We had some problems with triggers misfiring. I added a global trigger listener to catch events related to the firing of jobs and that provided the information needed to debug the issue.

We also hooked up NLog through Common.Logging to catch Quartz's internal logging (you may notice we use Simple Logging Facade ourselves, so that component is also configured here). Here's our app config, hopefully that can jump start you:

<?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" />
        <section name="slf" type="Slf.Config.SlfConfigurationSection, slf"/>
        <sectionGroup name="common">
            <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
        </sectionGroup>
    </configSections>


    <quartz>
        <add key="quartz.threadPool.threadCount" value="20" />
        <add key="quartz.jobStore.misfireThreshold" value="420000" /><!-- 7 minutes -->
    </quartz>


    <slf>
        <factories>
            <factory name="nlog" type="SLF.NLogFacade.NLogLoggerFactory, SLF.NLogFacade"/>
        </factories>

        <loggers>
            <logger factory="nlog"/>
        </loggers>
    </slf>

    <common>
        <logging>
            <factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog">
                <arg key="configType" value="FILE" />
                <arg key="configFile" value="~/NLog.config" />
            </factoryAdapter>
        </logging>
    </common>

</configuration>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top