Skip navigation

Category Archives: Scheduled Apex

In Winter 12, Salesforce very quietly, and without any fanfare, introduced a capability to Apex that has been something of a holy grail to insane Force.com geeks like myself: the beginnings of support for Reflection in Apex. Which intrepid explorer blazed the trail that led to this prodigious capability? For those of you who keeping score, our silent hero’s name is not Indiana, but Jason. Or, more precisely, JSON.

This is a bit of a historical post, meant to pave the way for a more radical post coming later this week (stay tuned!). It describes events / features that have occurred / been introduced over the course of the past year, but whose full significance is only now being discovered by many Force.com developers. Understanding these developments will be a crucial preliminary to reading my upcoming post on an extension of Tony Scott’s trigger pattern to allow for scalable, extensible triggers usable by managed package developers / ISV’s (there, I let the cat out of the bag!)

Ancient History: Native JSON Support lands in Apex

In Winter 12, Salesforce added native support for JSON to Apex, meaning that you can, in just 2 lines of code convert (nearly) any Apex class/object into a JSON-formatted String, and then convert it back from a String into the proper Apex class/object. For instance:


Map<String,String> params = new Map<String,String>{
    'foo' => 'bar',
    'hello' => 'world'
};
// Serialize into a JSON string
String s = JSON.serialize(params);
// --> yields '{"foo":"bar","hello":"world"}

// Deserialize back into an Apex object

// Define the Type of Apex object that we want to deserialize into
System.Type t = Map<String,String>.class;

params = (Map<String,String>) JSON.deserialize(s,t)

The addition of support for JSON saved ISV’s the immense hassle (and extremely-limiting obstacle) of implementing their own JSON serialization classes, which could very quickly consume all of the 200,000 Script Statements Governor Limit. So needless to say, this alone made native JSON support a godsend to Apex developers.

The Tip of the Iceberg: System.Type and Apex Interfaces

But notice that little System.Type class that we instantiated prior to deserialization. As the Apex development team was building in JSON support, they laid the foundation for something even bigger – an Apex way to achieve what’s known as “Reflection”, or dynamic instantiation/execution of classes/methods based on name. For developers looking for a parallel in Force.com, this is similar to creating a new Sobject by getting a Schema.SObjectType token from the Global Describe Map using an arbitrary String (e.g. ‘Account’) as the map search key, and then calling the newSObject() method. In this scenario, you could have received the dynamic key (‘Account’) from anywhere — from a text field in a custom object in your database, from a JavaScript prompt in the client, etc., and run totally dynamic logic (the instantiation of an arbitrary SObject type) based on that String value.

How does this Schema example connect to Trigger Patterns? Well, the reason that the dynamic SObject instantiation from Schema data works is that each Custom and Standard Object in your database is an instance of an interface called “SObject”. An interface is a definition of methods that all implementations of that interface (such as Account, Contact, MyCustomObject__c) must support. This is why can cast any Account, Contact, or custom object record into an SObject and then get access to some of the dynamic methods that the SObject class supports, such as getField(String fieldName) or getSObject() — because each Account, Contact, or CustomObject__c record is not just an instance of an Account, Contact, or CustomObject__c — it’s also an implementation of the SObject interface, which defines the methods getField(), getSObject(), etc.

With native JSON in Apex, you can now take this Reflection business a huge step further, thanks again to the magic of interfaces. Before we tackle custom interfaces, let’s consider an important standard Apex interface: Schedulable. The Schedulable interface looks like this:


global interface Schedulable {
    global void execute(SchedulableContext ctx);
}

The power of Apex classes that implement Schedulable is that they can be scheduled for execution at a later date/time using the System.schedule() method, like so:


// Get an instance of MyScheduledClass,
// which implements Schedulable
Schedulable c = (Schedulable) new MyScheduledClass();

// Schedule MyScheduledClass to be run later
System.schedule('CleanUpBadLeads','0 0 8 10 2 ?',c);

Now, for those of you who were excited about Skoodat Relax, you may have just realized how it all works, and you are absolutely right! One of the features of Relax is its ability to store the name of a Scheduled Apex Class, and the CRON schedule that dictates when it should be run, in a custom Object called Job__c in Salesforce, and then dynamically activate, deactivate, and run the desired classes at the desired times. And the not-so-secret secret (Relax is on GitHub) behind it is: it all works because of interfaces… and because of native JSON support.

Taking this a step further, as Relax does, we can define complex custom interfaces, with whatever methods we’d like, and then, when our Apex code is run, we can call these methods on whatever implementation of our interface we are handed:


// Our interface definition
public interface Runnable {
   public void run();
}
// A sample implementation
public class DestroyBadApples implements Runnable {
   public override void run() {
       delete [select Id from Apple__c where CreatedDate <= :Date.today().addYears(-365)];
   }
}

// A sample use of this interface
public class RunStuff {
   public RunStuff() {
      // Define the stuff we'd like to run
      List<Runnable> stuffToRun = new List<Runnable>();
      stuffToRun.add(new DestroyBadApples());

      // Run the stuff
      for (Runnable r : stuffToRun) r.run();
   }
}

Part 3, in which we catch the scent of our prey: Dynamic Class Instantiation with JSON

With Apex support for interfaces, developers already had, prior to Winter 12, one of the two key tools they needed that would make possible the Apex Holy Grail of dynamic method calls / code execution. The missing tool was the ability to create an implementation of an interface without knowing the class’ name beforehand. In our previous examples, notice how we had to hardcode the name of the implementations, e.g. MyScheduledClass and DestroyBadApples.

With Winter 12, our humble hero JSON gave us that missing tool. How so? Well, the JSON.deserialize(string,type) method allows us to deserialize any String into any supported Apex Type, as defined by Apex Classes which have a corresponding representation as a System.Type, a little-noted System class that crept in with Winter 12. The key tricks that turned System.Type into a Holy Grail as early as Winter 12 were

  1. A little trick involving an empty JSON String, e.g. “{}”
  2. System.Type.forName(name)

Combining these two tricks with interfaces, and voila! We have Dynamic Class Instantiation in Apex:


// A sample use of this interface
public class JobRunner {
   public static void ScheduleJobs() {
      // Retrieve the jobs we'd like to schedule from a custom object
      for (Job__c j : [select JobNumber__c, Cron__c, ApexClass__c from Job__c]) {
          try {
              // Get the Type 'token' for the Class
              System.Type t = System.Type.forName(j.ApexClass__c);
              if (t != null) {
                 // Cast this class in to a schedulable interface
                 // (if the cast fails, we'll get an error)
                 Schedulable s = (Schedulable) JSON.deserialize('{}',t);
                 // Schedule our Schedulable using the stored CRON schedule 
                 System.schedule('JobToRun'+JobNumber__c,Cron__c,s);
              }
          } catch (Exception ex) {}
      }
   }
}

Part the 4th, in which our hero’s achievements are acknowledged but his powers not increased

In Summer 12, Salesforce “officially” acknowledged its intentions with System.Type and added a newInstance() instance method allowing for less hackish dynamic class instantiation by name. However, the JSON method remains far more powerful, as it allows us to dynamically populate all manner of fields/properties on our dynamically-instantiated objects — using industry-standard JSON syntax, allowing this dynamic population to be initiated client-side and completed server-side in Apex! If your mind is not spinning with all of the possibilities right now, I’m sure it either did so already during the past 12 months, or will start doing so very soon, particularly when we talk about applying this capability to Triggers (more propaganda for the next post!)

So where can Salesforce go from here? Well, there is still no direct way to dynamically execute a particular method on an Apex class without hard-coding the method’s name. Support for this would take Reflection in Apex to a whole new level. For now, though, well-crafted Interfaces can usually get around this limitation. The real meat of the work was done early last year when Salesforce R&D was “snowed over” in development for Winter 12, and for the fruit of those 4 months, the Force.com Development community should be immensely thankful.

**Quick check to make sure this post is worth 5 minutes of your precious work day:**

If you have ever wanted:

(1) To run long chains of Batch Apex jobs in sequence without using up scheduled Apex jobs
(2) To run batch/scheduled Apex as often as every 5 minutes every day
(3) To manage, mass-edit, mass abort, and mass-activate all your scheduled and aggregable/chained Apex jobs in one place, in DATA
(4) To avoid the pain of unscheduling and rescheduling Apex jobs when deploying

Then keep reading 🙂

A recent (well, maybe not so recent — is it June already???) blog post by Matt Lacey really struck a chord with me. Matt highlights one of the biggest developer woes related to using Scheduled Apex — whenever you want to deploy code that is in some way referenced by an Apex Class (or classes) that is/are scheduled, you have to unschedule all of these classes. With Spring 12, Salesforce upped the limit on the number of Scheduled Apex Classes from 10 to 25 (ha–lellujah! ha–lellujah!). However, with 15 more scheduled jobs to work with, this have-to-unschedule-before-deploying problem becomes even more of a pain.

But this isn’t the only woe developers have related to asynchronous Apex. An issue that has shown up a lot lately on the Force.com  Developer Boards and LinkedIn Groups is that of running Batch Apex jobs in sequence — run one batch job, then run another immediately afterwards. Cory Cowgill published an excellent solution for accomplishing this, and this has been the go-to method for linking Batch Apex jobs ever since. But one of the problems with his method is that it leaves a trail of scheduled jobs lying in its wake — seriously cluttering your CronTrigger table. If you want to run these batch process sequences often — e.g. kicking them off from a trigger, or running them every day (or even every hour!), you have to start “managing” your scheduled jobs more effectively.

A third issue often cited by developers is the frequency at which jobs can be run. Through the UI, a single CronTrigger can only be scheduled to run once a day. Through code, you can get down to once an hour. If you wanted to, say, run a process once every 15 minutes, you’d have to schedule the same class 4 times — using up 4/25 (16%) of your allotted scheduled jobs — and you have to manage this through Apex, not the UI.

As I mulled over these issues, I thought, there’s got to be a better way.

There is.

Enter Relax

Relax is a lightweight app I’m about to release, but before I do, I’m throwing it out for y’all to try as a public beta. (The install link is at the end of the post, but restrain yourself, we’ll get there 🙂 )

Broadly speaking, Relax seeks to address all of these challenges, and a whole lot more.

At a high level, here are a few of the things it lets you do:

  1. Manage all your individually-scheduled Apex jobs in data records (through a Job__c object). Jobs can be mass scheduled and mass aborted, or mass exported between orgs, and then relaunched with clicks, not code.
  2.  Create and schedule ordered Batch Apex “chains” of virtually unlimited length, with each “link” in the chain kicking off the next link as soon as it finishes. And all of your chained processes are handled by, on average, ONE Scheduled Job.
  3. Schedule jobs to be run as often as EVERY 1 MINUTE. ONE. UNO.
  4. Run ad-hoc “one-off” scheduled jobs without cutting into your 25
Let’s jump in.
 
Individual vs. Aggregable
In Relax, there are 2 types of jobs: individual and aggregable. You create a separate Job__c record corresponding to each of your Individual jobs, and all of the detail about each job is stored in its record. You can then separately or mass activate / deactivate your jobs simply by flipping the Active? checkbox. Each Individual job is separately scheduled — meaning there’s a one-to-one mapping between a CronTrigger record and an Individual Job__c record. Here’s what it looks like to create an Individual Job. You simply specify the CRON schedule defining when the job should run, and choose a class to run from a drop-down of Schedulable Apex Classes.

Aggregable jobs, on the other hand, are all run as needed by the Relax Job Scheduler at arbitrary increments. For instance, you could have an aggregable job that runs once every 5 minutes that checks for new Cases created by users of your public Force.com Site, whose Site Guest User Profile does not have access to Chatter, and inserts Chatter Posts on appropriate records. You could have a job that swaps the SLA of Gold/Bronze SLA Accounts once every minute (contrived, yes, but OH so useful 🙂 Or you could have a series of 5 complex batch apex de-duplication routines that need to be run one after the other, set them all up as separate aggregable jobs, assign orders to them, and have the entire series run once every 15 minutes, every day. Here’s what the SLA swapper example looks like:

How do I use it?
 What do you have to do for your code to fit into the Relax framework? It’s extremely simple. For Individual Jobs, your Apex Class just needs to be Schedulable. For Aggregable Jobs, there are several options, depending on what kind of code you’d like to run. For most devs, this will be Batch Apex, so the most useful option at your disposal is to take any existing Batch Apex class you have and have it extend the “BatchableProcessStep” class that comes with Relax:

// relax's BatchableProcessStep implements Database.Batchable<sObject>,
// so all you have to do is override the start,execute,and finish methods
global class SwapSLAs extends relax.BatchableProcessStep {

	// Swaps the SLA's of our Gold and Bronze accounts

        // Note the override
	global override Database.Querylocator start(Database.BatchableContext btx) {
		return Database.getQueryLocator([
                     select SLA__c from Account where SLA__c in ('Gold','Bronze')
                ]);
	}

	global override void execute(Database.BatchableContext btx, List<SObject> scope) {
		List<Account> accs = (List<Account>) scope;
		for (Account a : accs) {
			if (a.SLA__c == 'Gold') a.SLA__c = 'Bronze';
			else if (a.SLA__c == 'Bronze') a.SLA__c = 'Gold';
		}
		update accs;
	}

        // The ProcessStep interface includes a complete() method
        // which you should call at the end of your finish() method
        // to allow relax to continue chains of Aggregable Jobs
	global override void finish(Database.BatchableContext btx) {
		// Complete this ProcessStep
		complete();
	}

}

That’s it! As long as you call the complete() method at the end of your finish() method, relax will be able to keep infinitely-long chains of Batch Jobs going. Plus, this framework is merely an extension to Database.batchable — meaning you can still call Database.executeBatch() on your aggregable Batch Apex and use it outside of the context of Relax.

Relax in action

In our org, we have 4 jobs set up: 1 individual, 3 aggregable. To kick them off, we select all of them, and change their Active? field to true. Here’s what it looks like after we’ve mass-activated them:

And here’s what the Scheduled Jobs table (accessible through the Setup menu) looks like. Since Case Escalator was set to be run individually, it has its own job. Then there’s a single “Relax Job Scheduler”, which runs every 5 minutes (I’ll probably drop this down to every 1 minute once I officially release the app), checking to see if there are any aggregable Jobs that need to be run, and running them:

Every time Relax Job Scheduler runs, the very first thing it does is schedules itself to run again — regardless of what happens during any processing that it initiates. It queries the “Next Run” field on each Active, Aggregable Job__c record that’s not already currently being run, and if Next Run is less than now, it queues it up to be run as part of a Relax “Process”. Each Process can have an arbitrary number of ProcessSteps, which will be executed sequentially until none remain. If both the current and next ProcessSteps are BatchableProcessSteps, Relax uses a “Process Balloon” to keep the Process “afloat” — essentially launching a temporary scheduled job that is immediately thrown away as soon as the next ProcessStep is begun.

One-off Jobs

Another powerful feature of Relax is the ability to effortlessly launch one-off, one-time scheduled jobs, without having to worry about cluttering the CronTrigger table with another scheduled job. It takes just 1 line of code, AND you can specify the name of the class to run dynamically — e.g. as a String! Reflection ROCKS!!!

// Schedule your job to be run ASAP,
// but maintain the Job__c record so that we can review it later
relax.JobScheduler.CreateOneTimeJob('myNamespace.AccountCleansing');

// Schedule your job to be run 3 minutes from now,
// and delete the Job__c record after it's run
relax.JobScheduler.CreateOneTimeJob(
    'SwapSLAs',
    Datetime.now().addMinutes(3),
    true
);

Try it out for yourself!

Does this sound awesome to you? Give it a test run! Install the app right now into any org you’d like! Version 1.1 (managed-released)

Please send me any feedback you may have! What would make this more usable for you? I’ll probably be releasing the app within the next month or so, so stay tuned!