+ Start a Discussion
Eli Flores, SFDC DevEli Flores, SFDC Dev 

Help converting a future method with http callouts to a batch method

Hi, I have this future method that errors out for having too many callouts frequently. I'd like to wrap my head around the batch apex concepts and add it to my toolbox so this project seems as good as any. Can someone help me with converting this methods to one that works by batches. Even just a high level walk through would be very helpful.

    @future (callout = true)
    public static void postToLCS(Map<Id, String> licenseXmlMap) {

        Boolean postToProd = false;
        String lcsEndpoint = '';
        String lcsUname = '';
        String lcsPwd = '';
        Map<String, Environment_Variables__c> envVarMap = Environment_Variables__c.getall();
        Map<Id, String> succLicRespMap = new Map<Id, String>();
        Map<Id, String> failLicRespMap = new Map<Id, String>();

        License__c[] licRecs = [SELECT Id, Last_Post_Date__c,
                                       Last_Post_Response__c
                                FROM   License__c
                                WHERE  Id IN: licenseXmlMap.keySet()];


        // LL - Get environment variables related to license posting
        if (envVarMap.get('use_prod_lcs').Value__c == 'true') { // LL - Check whether to post in DEV or PROD LCS
            // LL - Set PROD LCS endpoint and credentials
            lcsEndpoint = envVarMap.get('prod_lcs_endpoint').Value__c;
            lcsUname = envVarMap.get('prod_lcs_uname').Value__c;
            lcsPwd = envVarMap.get('prod_lcs_pwd').Value__c;
        } else {
            // LL - Set DEV LCS endpoint and credentials
            lcsEndpoint = envVarMap.get('dev_lcs_endpoint').Value__c;
            lcsUname = envVarMap.get('dev_lcs_uname').Value__c;
            lcsPwd = envVarMap.get('dev_lcs_pwd').Value__c;
        }
         
        Blob headerValue = Blob.valueOf(lcsUname + ':' + lcsPwd);
        String authorizationHeader = 'BASIC ' +
        EncodingUtil.base64Encode(headerValue);


        for (License__c l : licRecs) {

            if (!licenseXmlMap.get(l.Id).contains('Error') &&
                !licenseXmlMap.get(l.Id).contains('error')) {

                HttpRequest req = new HttpRequest();
                String lcsResp = '';
    
                req.setEndpoint(lcsEndpoint);
                req.setMethod('POST');
                req.setHeader('Authorization', authorizationHeader);
                req.setBody(licenseXmlMap.get(l.Id));
    
                Http http = new Http();
                HTTPResponse res;
    
                try {
    
                    if (!test.isRunningTest()) {
                        res = http.send(req);
                    }
                    
                    if (res != null) {
                        lcsResp = 'Status=' + res.getStatus() + ', StatusCode=' + res.getStatusCode();
                    }
    
                    // LL - Update LCS response fields if no exception encountered
                    l.Last_Post_Date__c = system.now();
                    l.Last_Post_Response__c = lcsResp;
    
                } catch (CalloutException ex) {

                    Util.sendErrEMail('Error encountered during license posting. Additional message: ' + ex.getMessage() + ' Generated xml: ' + licenseXmlMap.get(l.Id));
                    l.Last_Post_Date__c = system.now();
    
                    // LL - Inject the exception message in the Last Post Response field
                    l.Last_Post_Response__c = string.valueOf(ex);
                }
                    
            } else {

                Util.sendErrEMail('Error encountered in generating xml. License not posted to the LCS. Generated xml: ' + licenseXmlMap.get(l.Id));

                l.Last_Post_Date__c = null;
                l.Last_Post_Response__c = 'Error encountered in generating xml. License not posted to the LCS.';
            }

            // LL - TODO: Need to implement some logger mechanism...
        }

        // LL - Set static class to prevent recursive future calls
        ProcessorControl.inFutureContext = true;

        // Set database savepoint in event db transactions fail
        System.SavePoint sp = Database.setSavepoint();

        try {
            //update licRecs;
            
            Boolean hasError = false;
            Database.SaveResult[] updResult = database.update(licRecs, true);

            // LL - Check if error/s were encountered during the update
            for (Integer i=0; i < updResult.size(); i++) {
                if (!updResult[i].isSuccess()) {

                    // LL - Indicate that an error was encountered
                    hasError = true;

                    // LL - Exit the entire loop since an error was encountered
                    break;
                }
            }


            if (!hasError) {
                updateWowzaFromLicense();               
            }

        } catch (Exception e){
            // LL - Rollback transaction
            Database.rollback(sp);

            Util.sendErrEmail('Encountered an error updating license and/or wowza objects. Additional message ' + e.getMessage());
        }

    }

 

Avidev9Avidev9

batch classes has same limits as future methods for number of callouts!

 

Rootcause in your case is you are having a callout inside for loop. And in a transaction you can have at max 10 callouts.

 

Going back to your code

 

for (License__c l : licRecs) {

Whenever there are more than  10  licRecs  you will get this error.

 

You may have to figure out a way in which the number of callouts remains inside limit that is <= 10

 

 

 

Eli Flores, SFDC DevEli Flores, SFDC Dev
Isn't that the purpose of using a batch -- so I could segment it into chunks of 10?

Sent from my iPhone
Avidev9Avidev9
Well if your code doesnt depend on the data set and then I guess you can do It!
Have a look here
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_batch_interface.htm
Eli Flores, SFDC DevEli Flores, SFDC Dev

Hmmm... i've read the documentation several times and some sample tutorials online but I'm still having a hard time wrapping my mind  about what's happening conceptually. If anyone can provide some guidance, it would be much appreciated.

Avidev9Avidev9
global class calloutBatch implements Database.Batchable<sObject>,Database.AllowsCallouts{
 
   global UpdateAccountFields(){
        
   }

   global Database.QueryLocator start(Database.BatchableContext BC){
      return Database.getQueryLocator('<This is where query for license goes>');
   }

   global void execute(Database.BatchableContext BC, 
                       List<License__c> licReccs){
//This is where for loop and callouts goes,which operates over licRecs } global void finish(Database.BatchableContext BC){ //anything you would like do when the job finsishes } }

 

Database.executeBatch(new calloutBatch,10);//10 specifies number records per batch

 

Eli Flores, SFDC DevEli Flores, SFDC Dev
Awesome! Thanks!

Sent from my iPhone