6

I thought I was being clever by putting my Scheduled job classes inside a wrapper class:

global class Scheduler {
    global class Foo implements System.Schedulable {
        global void execute(SchedulableContext sc) {
            //Foo stuff
        }
    }

    global class Bar implements System.Schedulable {
        global void execute(SchedulableContext sc) {
            //Bar stuff
        }
    }
}

However once scheduled, any code inside the execute() block either silently fails or does absolutely nothing. Not even logging occurs.

The jobs look completely normal. Everything is scheduled correctly and says that my jobs are firing and logs a PreviousFireTime when monitoring with this SOQL query:

SELECT CreatedDate,CreatedById,CreatedBy.Name,CronExpression,EndTime,TimesTriggered,State,NextFireTime,PreviousFireTime,StartTime,CronJobDetail.Name,CronJobDetail.JobType,Id
FROM CronTrigger

I can't think of any namespace collisions that could be happening and the System.schedule() method is using dependency injection--so I wouldn't think using an inner class would matter. At the very least, would assume the compiler would prevent me from saving if this was an issue.

I can't find any documentation that states a Scheduled class should be top-level. So I'm at a lost if this is a bug or an implementation issue?

1
  • Can you add a little more detail around how you're so sure the execute block does not fire? Have you tried throwing exceptions therein to be extra sure?
    – Adrian Larson
    Dec 21, 2016 at 18:12

1 Answer 1

8

The compiler allowed it (?), but the system doesn't support it. This is because the jobs are scheduled by ApexClassId, and only top-level classes have an ApexClassId. This is probably a bug in a recent release, as prior releases would prohibit implementing most system and database interfaces in a non-top-level class, including execute anonymous blocks.

However, you can work around it by being clever:

public class ScheduleWrapper implements Schedulable {
  // Local variable
  executeLogic logic;

  // Interface for executing logic
  interface executeLogic {
    void execute();
  }

  // Implementations
  class Foo implements executeLogic {
    public void execute() {
      // Do stuff here
    }
  }
  class Bar implements executeLogic {
    public void execute() {
      // Do stuff here
    }
  }

  // Factory methods
  public static ScheduleWrapper getFooScheduler() {
    return new ScheduleWrapper(new Foo());
  }
  public static ScheduleWrapper getBarSchduler() {
    return new ScheduleWrapper(new Bar());
  }

  // Private constructor
  ScheduleWrapper(executeLogic logic) {
    this.logic = logic;
  }

  // Schedulable Interface logic
  public void execute(SchedulableContext context) {
    logic.execute();
  }
}

The only down side is that in the Scheduled Apex logs, the top-level class name will be displayed (here, ScheduleWrapper), so you won't be able to determine which of the two methods are being executed. Of course, the trick is to make sure you name your jobs appropriately so you can determine which is which.

4
  • Works great @sfdcfox, thanks! My only tweak was to make the interface and construct public so I could call the scheduled jobs from other classes. Also, thanks for the explanation on the ApexClassIds! Makes much more sense. Dec 21, 2016 at 22:47
  • @MatthewMitchener That's what the factory methods are for: System.schedule(jobName, cronString, ScheduleWrapper.getFooScheduler()). It's really just my personal preference so I don't have to worry about typing more code than necessary in my other classes, but you can do it any way that you like.
    – sfdcfox
    Dec 21, 2016 at 23:41
  • 2
    So frustrating! I've wasted a whole day trying to figure out why an inner class that implements Schedulable looked like it was working but also not working. Took a while to finally uncover this post, but thanks for the answer @sfdcfox! Jun 8, 2018 at 19:30
  • @ScottMcClung I'm glad you found this answer useful.
    – sfdcfox
    Jun 8, 2018 at 21:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .