+ Start a Discussion
College ManagementCollege Management 

Calling Future method from Batch Class ?

Hi , I got a situation where i need to invoke future method from batch class. And i know that it is not possible to do. But there might be some workarounds to achieve it, right? And i dont want to invoke future method using shedule apex as my requirement will hit governor limits of "Total number of schedule jobs exceed". So i tried in below ways, but still fighting with same issue.

My design is
Scenario 1 :
1. Schedule Apex ===> 2. BatchClass (Runs every hour) (Generates a map with "N" records for every batch context, and passes it to Future method in an apex class)  ===> 3. Future method ( then it will login to a salesforce account and generates an attachment and sends an email to a corresponding email address) .

Scenario 2 :

Since, It is not possible to invoke future method from batch, i tried implementing an apex class with a normal static method  invoking future method and in a batch class, for every context i.e., execute() method, it will invoke a normal method which in turn invokes future method, but still got the same error ( FATAL ERROR: Future method cant be called from future or Batch).
ie., 
global class BatchClass implements Database.batachable<Sobject> {
   global Iterable<ScheduledReport__c> start(Database.BatchableContext BC) {
    query=[];
    return query;
    }
     global void execute(Database.BatchableContext BC, List<ScheduledReport__c> scope) {
         FutureClass.normalMethod(mapRecords);
      }
      global void finish(Database.BatchableContext BC){
      }
}

Future Class: 

global class FutureClass {
    global static void normalMethod(Map<> mapR) {
        futureMethod(mapR);
    }
    @future(callout=true)
    global static void futureMethod(Map<> m) {
        //some code;
     }
}

Scenario 3 :

Instead of invoking future class method from batch i tried updating list of records and done checkbox value to true and i've implemented after update event trigger to get the all the records in which checkbox values are true and from trigger i tried invoking future method , still didnt work , i got an error (  FATAL ERROR: Future method cant be called from future or Batch). 

Please help me out. 

Thanks !
Best Answer chosen by College Management
magicforce9magicforce9
The maximum time out you can specify for a HTTP request is 120sec(default is 10), so you'll need to decide if the services that you are using are feasable to work in that time limit...You can always handle random api call time outs using try - catch block, but if you think that the service that you are using would almost for every call out need more time then I'm afraid you'll need to look for a better solution as Batch apex approach would not work in your case.
For your Ref: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_callouts_timeouts.htm

I suggest give a try putting the callout logic in execute() method as 120 sec is massive time and majority of the services give you response a few seconds.
 

All Answers

magicforce9magicforce9
Hi,

Due to salesforce Limitation, you can't call a future method from inside a batch job and if you have a DML statement inside execute() method which triggers a call to future method it won't work because every run/instance of execute() method is a single transaction and so the trigger execution will be part of that single transaction.

I suggest you leverage the power of batch job to maximum and so you perform all the logic thats inside future method in an instance of execute() - you might need to adjust the batch size if you suspect to hit any governor limits

==> Move the logic that creates mapRecords(make the batch to be Stateful) into Start method of batch job and I'm guessing that key of this map is a salesforce record Id, so at the end of start method return the list of sObjects which has the ids populated and then in execute method you can pickup those records and perform the rest of the logic with them....(if you are using the futureMethodsame then make sure you remove the @future annotation)...to give you a pecise example see below..
global class BatchClass implements Database.batachable<Sobject>, Database.AllowsCallouts, Database.Stateful{
   
	private List<ScheduledReport__c> schReports;
	global Iterable<ScheduledReport__c> start(Database.BatchableContext BC) {
		schReports = new List<ScheduledReport__c>();
    	query=[];
		/*Processing and creating mapRecords, now this should be a key value pair of salesforce 
		recordId to record/object(You can use a wrapper class for more finite data structure)..*/
		List<ScheduledReport__c> results = new List<ScheduledReport__c>();
		for(Id key : mapRecords)
			results.add(new ScheduledReport__c(Id = key));
	   return results;
    }
    global void execute(Database.BatchableContext BC, List<ScheduledReport__c> scope) {

    	//Again you can't call any future method here, you can adjust the batch size so you don't hit any other limits
    	for(ScheduledReport__c sr : scope){

        	NormalClass.normalMethod(mapRecords.get(sr.Id));
    	}
    }
    global void finish(Database.BatchableContext BC){
    
    }
}


The solution may or may not work for you...as it completely depends on your requirements.

Thanks,
Mohammed

 
College ManagementCollege Management
@Mohammed, First of all i appreciate your time. I tried it using above scenario i.e., Invoking normal method from batch but since i am doing rest call , before completion of first context in batch, second context request is hitting and finally i am getting STATUS CODE : 500 and Server Error. That is the reason i opted future method, so that it can process asynchronously.

Thanks !
magicforce9magicforce9
In which case you might need to schedule two batch jobs, one that updates the checkbox to true(which you've mentioned earlier) and then the next batch job should pickup the records whose checkbox is true and performs rest.
College ManagementCollege Management
If we implement rest code in batch class execute(), Suppose we are sending a request and if we get response very late, what if the batch process next context ? there wont be any problem like server error 500?? 
magicforce9magicforce9
The maximum time out you can specify for a HTTP request is 120sec(default is 10), so you'll need to decide if the services that you are using are feasable to work in that time limit...You can always handle random api call time outs using try - catch block, but if you think that the service that you are using would almost for every call out need more time then I'm afraid you'll need to look for a better solution as Batch apex approach would not work in your case.
For your Ref: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_callouts_timeouts.htm

I suggest give a try putting the callout logic in execute() method as 120 sec is massive time and majority of the services give you response a few seconds.
 
This was selected as the best answer
College ManagementCollege Management
I've implemented the rest code in batch execute(), it is working fine for now as it is processing nearly 100 records, but i am afraid if it processes for thousands of records. And thank you soo much for your support.
magicforce9magicforce9
I'm glad it worked,.,..You can always limit the batch size to a small number if that helps
College ManagementCollege Management
Yep, I changed the batch size limit to 1 and i again modified the rest code in queueable class and it is working gr8. But i am again stuck with one different issue. ISSUE: Scheduling batch for every user ie., N - users , N- batches. I know 5 is the limit, but is there any work around for that ?

Thanks !