23

Probably a stupid question... but here goes anyway...

I have set up quartz, and can schedule jobs, and I can confirm that jobs (implementing the IJob interface) are working.

Looking at the documentation on the site, (Lesson 3 of the tutorial):

The only type of exception that you are allowed to throw from the execute method is JobExecutionException.

I would like that when an exception occurs that I haven't explicitly handled, it should throw a JobExecutionException, so that I can log it in the 'parent' application. I have wrapped my code in a try catch, and have thrown the JobExecutionException, but now where to handle it?

I don't call the execute method anywhere, that is handled by Quartz (on a separate thread). So, how do I handle that error when it occurs. I don't really want to swallow the error in the Job

tardomatic
  • 2,396
  • 3
  • 21
  • 29
  • 1
    OK, did a bit more research, and it looks like a JobListener is what I'm looking for. Now to figure out how it would handle the exception. – tardomatic Jul 14 '10 at 13:15
  • annnnnd.... no luck. even with a listener, app crashes when throwing exception. HELP! – tardomatic Jul 14 '10 at 14:21
  • @tardomatic-If you have a job listener wired to a job/group name the listener will be called regardless of if the job succeeds or fails. After you throw a JobExecutionException (based on you detecting an error), Quartz will consider the job not complete (by definition, Quartz considers a job complete *unless* you fire a JobExecutionException). Now, when you are in the listener's JobWasExecuted() method you can check the JobExecutionException parameter to see if it is set. Now you know if the job failed and you can optionally perform cleanup etc tasks prior to job executing again in the future. – Matthew Jul 05 '15 at 01:53

3 Answers3

20

I solved this problem by using a base class to catch all the exceptions:

public abstract class JobBase : IJob
{
    protected JobBase()
    {
    }

    public abstract void ExecuteJob(JobExecutionContext context);

    public void Execute(JobExecutionContext context)
    {
        string logSource = context.JobDetail.FullName;

        try
        {
            ExecuteJob(context);
        }
        catch (Exception e)
        {
           // Log exception
        }
    }
}

Your Job class should like this:

public class SomeJob : JobBase
{
    public SomeJob()
    {
    }

    public override void ExecuteJob(JobExecutionContext context)
    {
        // Do the actual job here
    }
}
Eran Betzalel
  • 4,105
  • 3
  • 38
  • 66
  • 2
    Nice solution for enabling common logging among otherwise disparate quartz jobs. Thanks! – Shawn J. Molloy Jun 22 '13 at 16:56
  • ... you can't be serious ... *gonna catch 'em all* is not supposed to be best practice ... it would be way better to add a listener and react to job-endings having an exception ... –  Oct 24 '13 at 11:32
  • I'm not sure I follow your line of thought, care to show an example? (a link to pastebin code would be perfect) – Eran Betzalel Oct 24 '13 at 13:14
  • 1
    Just came to say I've implemented pretty much this exact same solution for this problem. For anybody wondering about @AndreasNiedermair's comment - it may be correct that having a catch-all is bad practice, but you're not required to do this. Jobs should handle their exceptions, but in the case that you have an uncaught exception, using this solution is preferable to simply allowing the underlying exception to travel any further, as this will at least allow you to handle the issue in the 'quartz way' later on. – HalliHax Sep 26 '14 at 13:14
  • @TomFromThePool I agree with you, that one is not required to stick to any practice. But, separating the exception-handling to a listener is far more convenient (as eg you could do it generically ...). –  Sep 27 '14 at 09:47
  • @EranBetzalel http://andreas.niedermair.name/unhandeled-exception-in-quartz-net-job –  Sep 27 '14 at 09:48
  • @AndreasNiedermair: Clearly I'm missing something here. How does the `IJobListener` differ in concept than catching unhandled exceptions in a base `IJob` class? I can see the different implementations, of course, but how is the former 'better' than the latter? As TomFromThePool indicates, it's not a catch-all—it's a catch-*unhandled*. Most handling should be done inside the job itself. – InteXX Feb 24 '15 at 08:43
  • @InteXX It's not about 'better' (which is imho very subjective in this scenario), but instead of forcing your jobs to be a subclass of a custom `JobBase`, you can solve this with a listener. A listener also works with any external job and does not pollute your hierarchy. And, this is *gonna catch 'em all* as the scope is the whole `ExecuteJob` method, which may (or may not) have some `try`/`catch` in itself. I am more the *decoupled*-guy though - and implementing a base-class for a logging scenario is just too ... tightly coupled for me ;) –  Feb 24 '15 at 09:40
  • @InteXX as a side-note: I have not yet digged into Quartz.net itself to explore how the execption handling is done there. So I can't, at this point, do any guessing on "Is Quartz.net istelf is a gonna-catch-em-all solution?" ... But, when I find time I am going on a hunt :) –  Feb 24 '15 at 09:43
  • @AndreasNiedermair: Great reply. Happy Hunting! :-) – InteXX Feb 24 '15 at 11:18
12

Typically you would set up the execute method of your job as follows:

try
{
    // the work you want to do goes here
}
catch (ExceptionTypeYouWantToHandle1 ex1)
{
    // handle exception
}
catch (ExceptionTypeYouWantToHandle2 ex2)
{
    // handle exception
}
// and so on
catch (Exception ex)
{
    // something really unexpected happened, so give up
    throw new JobExecutionException("Something awful happened", ex, false); // or set to true if you want to refire
}

At this point the scheduler itself will log the exception to wherever it is logging (based on the configuration).

jvilalta
  • 6,679
  • 1
  • 28
  • 36
  • 1
    It's been so long since I've worked with this, I don't have the code available anymore to test to see if this works. Doubtful though, as throwing the JobExecutionException is the problem. I throw that, but Quartz does not handle it and log it as you mention, it just bubbled all the way up as an unhandled exception. – tardomatic Jul 13 '11 at 11:41
  • ... you can't be serious ... *gonna catch 'em all* is not supposed to be best practice ... it would be way better to add a listener and react to job-endings having an exception ... –  Oct 24 '13 at 11:32
  • Best practice is to have your job handle its own exceptions. Are you recommending creating a separate listener for every job type just to handle exceptions? – jvilalta Oct 25 '13 at 19:02
  • 1
    I am having similar trouble. Do you know what would be worth so much for the Quartz community? A youtube video series + code samples of one of you quartz ninjas setting up a full (simple) quartz implementation with jobStore, unit testing, and some debugging that you deliberately cause & discover many of the common "gothchas". – gooddadmike Apr 24 '14 at 04:23
  • @jvilalta when you know which **line** is going to might throw an exception, why not catch it there? yes, that would totally correct - and not a `try` in the beginning and a `catch` in the end of the method - but .net is forcing this not the same way as java does, so a global exception handler - i did not say global `catch` - is quite common (like you'd utilize the `AppDomain`'s `UnhandledException`-handler in a console app for this), and the "best" and most reusable version of this is a listener for exactly this task. –  Sep 27 '14 at 19:11
  • @jvilalta I must revert my thinking on "best": Usually you should do a `try`/`catch` for one specific operation and not 100s of lines ... Whatever then makes its way through is usually a "not handled in any way"-exception which, as of its rare appearance, should just be logged (aka "something awful happened") - this logging can easily done with a generic listener. –  Feb 24 '15 at 09:46
7

As already mentioned, the correct way to "detect" JobExecutionException's on a global level is to implement and register an IJobListener and check if the JobExecutionException parameter in the JobWasExecuted() method is != null.

However, the problem I had (and judging from the additional comment of the OP, he faced this too) was that Quartz did not handle the JobExecutionException (as it should) which resulted in an unhandled exception killing the application.

So far, I was using the precompiled DLL from the Quartz.NET 2.0.1 release (.NET3.5) package. To get to the bottom of the problem, i referenced the Quartz project/sourcecode and to my astonishment it was suddenly working?!

As a point of interest, this is the Quartz library code that executes the IJob and handles the JobExecutionException:

try {
    if (log.IsDebugEnabled) {
       log.Debug("Calling Execute on job " + jobDetail.Key);
    }
    job.Execute(jec);
    endTime = SystemTime.UtcNow();
} catch (JobExecutionException jee) {
    endTime = SystemTime.UtcNow();
    jobExEx = jee;
    log.Info(string.Format(CultureInfo.InvariantCulture, "Job {0} threw a JobExecutionException: ", jobDetail.Key), jobExEx);
} catch (Exception e) {
   // other stuff here...
}

The next thing was to reference my freshly compiled DLL direcly and this was working as well. Sadly i can't tell you why this works and i currently don't have any time to get into it any further, but maybe this helps someone. Maybe some else can confirm this and even contribute an explanation. It may have something to do with different target platforms (x86/64bit)?

Rev
  • 5,827
  • 4
  • 27
  • 51
  • This is incorrect. catch (JobExecutionException jee) { endTime = SystemTime.UtcNow(); jobExEx = jee; Catches and handles the JobExcecutionException.. I don't see a throw, therefore, it's handled at this point and it won't get thrown causing an unhandled exception. The exception is then assigned to jobExEx and ALSO sent to the listener. I question the sanity of having a single listener handling exceptions for all jobs or even having one listener per job only to handle exceptions. All jobs should know how to handle their own exceptions. – jvilalta Oct 25 '13 at 18:57
  • 4
    @jvilalta: There may be a misunderstanding. The quoted code is the actual code from the Quartz.NET library. NOT the code from the application. I just wanted to point out how Quartz handles the JobExecutionException internally. – Rev Oct 26 '13 at 08:39
  • Which file is this? The code, what is the name of this class or file? – Nikhil Vandanapu Feb 09 '17 at 09:30
  • 1
    @NikhilVandanapu: Its from \src\Quartz\Core\JobRunShell.cs – Rev Feb 09 '17 at 09:35
  • @Rev1.0 Thanks. Could you tell me if this is the file I should be reading if I wanted to catch Exceptions related to Sql Server and quartz. In my use case I should be notified if Sql Server is down which quartz uses is down. – Nikhil Vandanapu Feb 09 '17 at 09:43