Queueable classes

With the recent Spring '15 release, Apex developers have a new asynchronous tool, queueable Apex classes. At first glance, queueable classes are very similar to @future annotated methods. A closer look, however, reveals their true power. There are three key differences between @future and queueable classes:

  • The method for enqueueing a job returns an ID, allowing you to monitor its progress much like a batch or scheduled job
  • Queueable jobs accept sObjects
  • Finally, and perhaps most importantly, queueable code can invoke queueable code—no more exceptions complaining about how you are trying to call the @future methods or batch methods from an @future context

Implementing the queueable interface is very similar to implementing the batchable or schedulable interfaces. Simply define your class as implementing the queueable interface and implement the execute() method. Likewise, your implementation of the execute() method must accept one argument of the QueueableContext type. This method does not have to be the only method in the class; in fact, you can use constructors and other methods as you see fit. The general pattern is to write the constructor so that it accepts sObjects and assigns them to class level instance variables. The execute() method is then used to implement asynchronous logic. Importantly, inside the execute() method, you can access the JobId by invoking the getJobId() method on the QueueableContext object that the system injects at runtime. With this Id, you can write SOQL queries to gather data about the object.

Perhaps the clearest real-world example of using the Queueable interface is doing a call out to a web service where you need to log both successes and failures. While an @future method is capable of making callouts, the Queueable interface is capable not only of callouts but also monitoring and, as of the Spring '15 release, automatically retrying. To help us log successes and failures, we will create a new sObject called AuditLog__c, with the following fields:

Field Type

Name

Purpose

Boolean

Success__c

Flag for success or failure

Long Text

RequestJson__c

JSON representation of the request

Long Text

ResponseJson__c

JSON representation of the server response

Id

JobId__c

ID of the async job

Long Text

stacktrace__c

Text of the Stacktrace

Id

AccountId__c

ID of the Account this audit log is relates too

Queueable jobs can be enqueued by calling System.enqueueJob() with an instance of our queueable class. In this case, we will instantiate our AuditLogGenerator queueable class with an Account object. Executing the System.enequeJob() method with an instance of our AuditLogGenerator class will return a jobId. Once the system picks up the job and calls the execute() method, we can query for the Audit Log record information with that jobId field. With both the account object passed in via the constructor and the auditlog__AuditLog__c record, we can make our callout while logging successes and failures as well as automated retries. Here is what that AuditLogGenerator class looks like:

Public with sharing class auditLogGenerator AuditLogGenerator implements Queueable, Database.allowsCallouts {
  private account a {get; set;}
  public AuditLogGenerator(Account incomingAccount) {
    this.a = incomingAccount;
  }

  public void execute(QueueableContext qc) {
    Audit_Log__c log = [SELECT Id, Success__c, RequestJson__c, ResponseJson__c, Stacktrace__c, AccountID__c
                        FROM Audit_Log__c
                        WHERE JobId__c = :qc.getJobId()];

    try {
      log.RequestJson__c = Rest.GenerateRequestFromAccount(this.a);
      HTTPResponse response = Rest.makeRestRequestWithUrlMethodAndBody('https://www.example.com', Rest.GET, log.RequestJson__c);

      if (response.getStatusCode() == 200) {
        log.Success__c = true;
      }
      log.ResponseJson__c = response.getBody();
    } catch (Exception e) {
      log.Success__c = false;
      log.Stacktrace__c = e.getStackTraceString();
    }
    if (!log.Success__c) {
      Id retryJobId = System.EnqueueJob(new auditLogGenerator(this.a));
      Audit_Log__c retryAuditLog = new Audit_Log__c(JobId__c = retryJobId, AccountID__c = this.a.Id);
    }
    update log;
  }
}

Testing Queueable classes

Testing Queueable classes is much like testing the @future methods. The only requirement is proper use of Test.startTest() and Test.stopTest(). The asynchronous code executed between Test.startTest() and Test.stopTest() methods is forced to run immediately when Test.stopTest() is called. This greatly simplifies testing. We need only to call our System.EnqueueJob() method between Test.startTest() and Test.stopTest() and make assertions as if it were synchronous, rather than asynchronous code. Consider the following example:

@isTest
private class AuditLogGenerator_Tests {
  
  static testmethod void test1() {
    Account firstAccount = (Account) TestFactory.createSObject(new Account());
    insert firstAccount;
    Audit_Log__c log;
    HTTPMockCalloutFactory fakeResponse = new 
      HTTPMockCalloutFactory(200,
        'OK',
        '{"results":"Ok"}',
        new Map<String, String>()
    );
    Test.setMock(HttpCalloutMock.class, fakeResponse);
    
    Id jobId;
    Test.startTest();
      jobId = System.EnqueueJob(new auditLogGenerator(firstAccount));
      log = new Audit_Log__c(JobId__c = jobId, AccountID__c = firstAccount.Id);
    Test.stopTest();
      
    Audit_Log__c resultingLog = [SELECT success__c 
                                FROM Audit_Log__c
                                WHERE jobId = :jobId];
    System.assert(resultingLog.success__c);
  }
}

As you can see in the preceding example, Test.startTest() and Test.stopTest() bracket our actual enqueueing code. This is all that's required to force the testing framework to run our asynchronous code synchronously within the execution flow of our test.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.99.152