[SalesForce] Calling future method from batch class

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

Definitely you can use an HTTP callout as an escape mechanism. But this is pretty naughty. You'll need a Remote Site Setting and a Session ID and a global class etc etc etc (red flags, alarm bells).

May I humbly recommend the use of System.Queueable call instead of @Future. You can create a class that is constructed with any kind of argument that you want to hand in, like so:

public class DeferredHandler implements System.Queueable
{

    private Map<String,String> key2val;

    public DeferredHandler(Map<String,String> key2val) {
        this.key2val = key2val;
    }

    public void execute(System.QueueableContext objContext)
    {
        //do your @Future stuff here with this.key2val
    }

}

And invoke him from inside your batch like this:

//defer stuff
System.Queueable job = new DeferredHandler(key2val);
System.enqueueJob(job);

There is some literature about this in the docs and Josh Kaplan's awesome Queueable blog post.