Thursday, 27 February 2014

Preventing Recursive Future Method Calls in Salesforce

Have you ever come across this error “System.AsyncException: Future method cannot be called from a future method…” while trying to work on a trigger which calls @future annotated methods?
Sooner or later we have. 

As a best Apex coding practices, we should make sure that we do not come across this error. I found this BLOG elucidating enough to comprehend.

How to write Test Class for a Batch Apex Class?


In continuation to my previous post on Batch Apex, here we will try to understand how to write test class for a batch apex class.
So let's have a sneak peek into it. 

Apex Class

global class AccountUpdate implements Database.Batchable {
     String query, field, value;
     global AccountUpdate(String f, String v){
          field = f;
          value = v;
          query = 'SELECT' + field + 'FROM Account';
     }
     global Database.QueryLocator start(Database.BatchableContext BC){
          return Database.getQueryLocator(query);
     }
     global void execute(Database.BatchableContext BC, List scope){
          for(sObject s: scope){
               s.put(field, value);
          }
     update scope;
     }
     global void finish(Database.BatchableContext BC){
     //
     }
}


Test class for above Batch Class


@isTest
public class AccountUpdateTest {
    static testMethod void test() {
        // Create an instance of query locator
        Database.QueryLocator QL;
        // Create an instance of batchable context
        Database.BatchableContext BC;
        List AcctList = new List();
        AccountUpdate oAccountUpdate = new AccountUpdate('Name', 'Test');
        // execute start method
        QL = oAccountUpdate.start(bc);       
        // Create an instance of Query Locator Iterator
        Database.QueryLocatorIterator QLI =  QL.iterator();
        while (QLI.hasNext())
        {
            Account Acc = (Account)QLI.next();        
            System.debug(Acc);
            AcctList.add(Acc);
        }               
        // initiate execute method
        oAccountUpdate.execute(BC, AcctList);
        // execute finish method
        oAccountUpdate.finish(BC);    
    }
}

Batchable and Schedulable APEX are mutually exclusive but together they are just awesome



Batchable and Schedulable APEX are mutually exclusive; you can have one without the other, but together they are just that much more awesome.

Batch Apex is exposed as an interface that must be implemented by the developer. Batch jobs can be programmatically invoked at runtime using Apex.

Need of Batch Apex

As you all might know about the salesforce governor limits on its data. When you want to fetch thousands of records or fire DML on thousands of rows on objects it is very complex in Salesforce and it does not allow you to operate on more than certain number of records which satisfies the Governor limits. But for medium to large enterprises, it is essential to manage thousands of records every day. Adding/editing/deleting them when needed.
Salesforce has come up with a powerful concept called Batch Apex. Batch Apex allows you to handle more number of records and manipulate them by using a specific syntax.

Let's understand it through one of its implications in S2E instance.

Requirement: Write a Batch Apex to schedule populating Offer Code from Campaign object to Offer Code on Opportunity object.

Enumerating the whole concept/process into the following categories:

1.     Schedule Apex:

Schedule an Apex class that implements the 'Schedulable' interface to be automatically executed on a weekly or monthly interval.

Some code snippets

global class scheduleOfferCodeUpdateOnOpportunity implements Schedulable{


    global void execute(SchedulableContext SC){
        BatchUpdateOfferCodeOnOppty oBatchUpdateOfferCodeOnOppty = new BatchUpdateOfferCodeOnOppty();
        Database.executeBatch(oBatchUpdateOfferCodeOnOppty);
    }

}

Note: You can use the Database.executeBatch method to programmatically begin a batch job.

2.     Call Batch Apex to run the process:

Write an Apex class that implements Database.Batchable, Database.Stateful to start(getQueryLocator), execute(update list of Object records) and finish(Async Apex job and send an email) the Batch job.

  •   The start method is called at the beginning of a batch Apex job.
    •    Use the start method to collect the records or objects to be passed to the interface method execute.
            This method returns either a Database.QueryLocator object or an iterable that contains the records or objects being passed into the job.


  • The execute method is called for each batch of records passed to the method.
    • Use this method to do all required processing for each chunk of data.
    • This method takes the following:
      • A reference to the Database.BatchableContext object.
      • A list of sObjects, such as List, or a list of parameterized types. If you are using a Database.QueryLocator, the returned list should be used.
      • Batches of records are not guaranteed to execute in the order they are received from the start method.

  • The finish method is called after all batches are processed.
    • Use this method to send confirmation emails or execute post-processing operations.


Note:

  • All of the methods in the Database.Batchable interface require a reference to a Database.BatchableContext object. Use this object to track the progress of the batch job.
  • Each execution of a batch Apex job is considered a discrete transaction.
    • For example, a batch Apex job that contains 1,000 records and is executed without the optional scope parameter from Database.executeBatch is considered five transactions of 200 records each.
    • The Apex governor limits are reset for each transaction. If the first transaction succeeds but the second fails, the database updates made in the first transaction are not rolled back.


Some code snippets

global class BatchUpdateOfferCodeOnOppty implements Database.Batchable {

      //Constructor
    global BatchUpdateOfferCodeOnOppty(){
        listCampaign = [select Id, Offer_code__c from Campaign where Has_Offer_Code__c = true];
    }

    global Database.QueryLocator start(Database.BatchableContext BC){
        return Database.getQueryLocator([select Id, Offer_code__c, CampaignId from Opportunity where CampaignId IN: listCampaign]);
    }

    global void execute(Database.BatchableContext BC, List scope){
        ......
        //Prepare map of Campaign Offer Code to update on Opportunity
        ......
        //Use scope to get the list of Opportunity returned from start method
        ......
        //Update the offer code on Opportunity
        ......
        Database.update(listOpportunity);
        ......
        //Get the list of save result to check for failed updates
          .........
        // Update the failed campaign
        ......
    }

    global void finish(Database.BatchableContext ctx){
        /Compose Async Apex Job info
        AsyncApexJob oAsyncApexJob = [ SELECT Id, ApexClassId,
                                       JobItemsProcessed,
                                       TotalJobItems,
                                       NumberOfErrors,
                                       ExtendedStatus,
                                       CreatedBy.Email
                                       FROM AsyncApexJob
                                       WHERE id =: ctx.getJobId()];
        ......
        ......
       //Compose Single EmailMessage service             
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();     
        String[] sToAddress = new String[]{'sudhir.kumarjaiswal@thomsonreuters.com'};
        mail.setToAddresses(sToAddress);
        mail.setReplyTo('noreply@salesforce.com');                      
        mail.setSenderDisplayName('Batch Job Summary');
        mail.setSubject('Batch Job Summary: Update Campaign Offer Code on Opportunity');
        mail.setPlainTextBody(sEmailMessage);
        mail.setHtmlBody(sEmailMessage);
        //send email in case of no errors
        if(oAsyncApexJob.NumberOfErrors > 0 ){
            Messaging.sendEmail( new Messaging.SingleEmailMessage[]{mail});
        }
                     
    }

 Wanted to know how to write Test Class for a Batch Apex Class....?  Coming soon..     Published here

To know more

OData External Data Source Connector

On December 5, 2013, I saw an email stating OData External Data Source Connector pilot has been enabled in all four sandboxes. This provoked me to go and explore what OData is all about and thereby sharing through this blog. So here I'm.... 

First of all, this question arises: What is the motive behind OData ? This video explains clearly Unlock your data with OData.

With reference to the OData website, it says, "The Open Data Protocol (OData) is a Web protocol for querying and updating data that provides a way to unlock your data and free it from silos that exist in applications today.OData does this by applying and building upon Web technologies such as HTTP, Atom Publishing Protocol (AtomPub) and JSON to provide access to information from a variety of applications, services, and stores."

In general, the OData Protocol is an application-level protocol for interacting with data via RESTful web services.

To know more about questions like:

  • What is the Open Data Protocol?
  • Why use the Open Data Protocol?
  • How to create or consume the Open Data Protocol?


To know  OData w.r.t Salesforce, watch this video


If you go across these links and some others too, please share your understanding (be it simple or complex) about this. 

Workaround for the error "System.CalloutException: Callout from Apex triggers are currently not supported"

While working on Order Capture, there is a requirement to keep the SAP Quote Status in sync with that in SAP CRM.
I come to know that from Apex trigger, we cannot make synchronous callout to Web Service. For that I had to use Apex Trigger and make a future callout to the external Web Service.

I thought of jotting down my own steps. However, I found this blog quite elucidating to comprehend, and thereby saving my time to write down on my own. 

Here it goes:   Callout from APEX Triggers

Friday, 18 October 2013

Simplifying the way to understand Dynamic Apex in Salesforce: Part - 3


Keeping the momentum with the previous two blog-posts Part - 1 and Part - 2 , here I would like to cover the way to write Dynamic SOQL/SOSL/DML.

Creation of SOQL/SOSL/SObjects at the runtime with Apex code is referred to as Dynamic SOQL/SOSL/DML which can be used to create more flexible applications like creating a search based on end user's input.

DYNAMIC SOQL

Database query methods are used to create a dynamic SOQL.

Example:

                  Return a single sObject when query comes back with one record
                   sObject s = Database.query(query_string_limit_1);

                  Return a list of sObjects when query comes back with more than one record
                   List listSObject = Database.query(query_string);

Points to remember(SOQL)

  • We can use simple bind variables in dynamic SOQL query strings.
          String sTestString = 'Test Name';          
          List listSObject = Database.query('Select Id from MyCustomObject__c where Name =: sTestString');

  • Unlike inline SOQL, Dynamic SOQL can't use bind variable fields in the query string.

          MyCustomObject__c oCustomObject = new MyCustomObject__c(field1__c = 'TestField');
          List listSObject = Database.query('Select Id from MyCustomObject__c  where field1__c =: oCustomObject.field1__c'); 


          MyCustomObject__c oCustomObject = new MyCustomObject__c(field1__c = 'TestField');
          String myField = oCustomObject.field1__c;
          List listSObject = Database.query('Select Id from MyCustomObject__c  where field1__c =' + myField'); 


DYNAMIC SOSL

Search query  methods are used to create a dynamic SOSL.

Example:

          List> myQuery = search.query(SOSL_search_string);

          String searchquery = 'FIND \'Edge*\' IN ALL FIELDS RETURNING Account(id,name),Contact, Lead';
          List> searchList = search.query(searchquery);


Points to remember(SOSL)

  • Dynamic SOSL statements evaluate to a list of lists of sObjects, where each list contains the search results for a particular sObject type.
  • The result lists are always returned in the same order as they were specified in the dynamic SOSL query.
  • From the example above, the results from Account are first, then Contact, then Lead.


DYNAMIC DML

We can also create sObjects dynamically and insert them into the database using DML.

Example

//Get a new Account
Account A = new Account();
//Get the token for that Account
Schema.sObjectType tokenA = oAccount.getSObjectType();
// Error because the token is a generic object, not ac Account
Account B = tokenA.newSObject(); 
//Correct way
Account B = (Account) tokenA.newSObject(); 


public class DynamicSObjectCreation{

     public static SObject createObject(String sObjectTypeName){
          Schema.SObjectType objectType = Schema.getGlobalDescribe().get(sObjectTypeName);
          if(objectType == null){
              // throw an exception
          }
         // instantiate an SObject with the type passed in as an argument at run time
          return objectType.newSObject();
     }
}

Points to remember(DML)

  • To create a new sObject of a given type, use the newSObject() method on an sObject token.
  • The token must be cast into a concrete sObject type (such as Account).