function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
GuyClairboisGuyClairbois 

@Future and Callout conflict

Hi all,

We built an interface to an external system using SOAP Callouts that trigger upon update on a certain object ('object1'). In order to successfully do the SOAP callout, we needed to put the callout in an @Future context, because callouts are only allowed from @Future classes.

 

This works fine, but....

 

We also have some processes in place that, whenever a certain other object ('object2') is updated, updates the matching object1. Since there can be a lot (thousands)  of matches, we also made this an @Future method. 

 

The problem, however, is that @Future methods cannot call @Future methods, so whenever object1 gets updated because of an update on object2, the export callout does not get triggered.

 

We've tried multiple solutions:

- using Outbound Messages instead. This is not an option since the object that gets exported also draws data from child objects when composing the SOAP message 

- I tried creating a static variable that checks whether an @Future method was already started, and proactively determines whether or not to run the callout as another @Future, but that doesn't help because the system tells me "Callout from triggers are currently not supported." (there is not @Future between the trigger on the object and the callout).

 

Any other suggestions? We'd want the interface to stay as 'online' as possible, so we will only try time-based workflow or batch apex if nothing else helps..

 

Many thanks!

Guy 

 

Best Answer chosen by GuyClairbois
SteveBowerSteveBower

 

Greater minds than mine have probably worked this out somewhere, however I'd think there there must be some way to tackle this via refactoring and flagging the need for @future in the data.   So, it's 2:30am and I'm just typing away, but I think you could build the first trigger to use either an @future, or a normal, intermediate method to make the callout based on a data value in Object 1.   The second trigger could then invoke it's own @future class which would tell trigger 1 through the contents of the data to do "normal" processing.

 

e.g.  (pseudo-code)

--------------------------------------------------------------------------------------

Trigger object1Trigger (before insert, before update, etc.) {

 

   // Put whatever logic you want to handle the trigger and build your outbound messages.

  messages =  whatever...

 

   // ready to send my outbound message(s)

   UtiityClass help;

  If (Trigger.new[0].SomeDataField == 'Do Not Do Future') {

    help = myOutBoundMessageUtilityClass(False);  // 

    Trigger.new[0].someDataField = null;  // reset

  } else {

    help = myOutBoundMEssageUtllityClass(True);  // Use Future by default.

  }

   help.makeMyCalls(messages)

 

}

 

----------------------------------------------------------------------------

 

global Class myOutBoundMessageUtilityClass () {

     Boolean useFuture;

     Messages .....whatever, String, list of Strings, etc.

     myOutBoundMessageUtilityClass(Boolean useFuture) {

         this.useFuture = useFuture;
     }

 

     public makeMyCalls(Messages msgs) {

          if (this.useFuture) {

                futureCall(msgs);

          } else {

                normalCall(msgs)

          }

    }

 

    @future (callout=true)

    public static void futureCall(msgs) {MakeTheCallout(msgs);}

 

    public static void normalCall(msgs) {MakeTheCallout(msgs);}

 

     private static void MakeTheCallout(msgs) {

           callout code....

    }

}

-----------------------------------------------------------------------------

 

// And Object2's trigger essentially does it's own logic and calls it's own @future.

 

Trigger object2Trigger (after/before, etc.) {

    // Logic for object 2 (find the matching Obj1's)

    List<Obj1> obj1ToUpdate;

 

    call my @future class method to do the update.

    obj2TriggerHelper th = new obj2TriggerHelper();

    th.doTheObj1Updates(obj1ToUpdate);

}

 

public class obj2TriggerHelper() {

   @future

    global doTheObj1Updates(List<obj1> obj1ToUpdate) {

          // Telling the object 1 trigger to not do a future invocation of it's own.

          obj1ToUpdate[0].someDataField = 'Do Not Do Future';

    update obj1ToUpdate;

    }

 

 

 

Probably wrong about this somewhere, but too tired to think it through any further.   :-)  Good luck,  Steve.

 

All Answers

santosh duvvurisantosh duvvuri

Hi

 

You can try creating an schedulable class which you will be invoking after 5sec from the time the trigger got invoked .

In the schedulable class you will be calling the second future method.

 

Thanks & Regards

Santosh

SteveBowerSteveBower

 

Greater minds than mine have probably worked this out somewhere, however I'd think there there must be some way to tackle this via refactoring and flagging the need for @future in the data.   So, it's 2:30am and I'm just typing away, but I think you could build the first trigger to use either an @future, or a normal, intermediate method to make the callout based on a data value in Object 1.   The second trigger could then invoke it's own @future class which would tell trigger 1 through the contents of the data to do "normal" processing.

 

e.g.  (pseudo-code)

--------------------------------------------------------------------------------------

Trigger object1Trigger (before insert, before update, etc.) {

 

   // Put whatever logic you want to handle the trigger and build your outbound messages.

  messages =  whatever...

 

   // ready to send my outbound message(s)

   UtiityClass help;

  If (Trigger.new[0].SomeDataField == 'Do Not Do Future') {

    help = myOutBoundMessageUtilityClass(False);  // 

    Trigger.new[0].someDataField = null;  // reset

  } else {

    help = myOutBoundMEssageUtllityClass(True);  // Use Future by default.

  }

   help.makeMyCalls(messages)

 

}

 

----------------------------------------------------------------------------

 

global Class myOutBoundMessageUtilityClass () {

     Boolean useFuture;

     Messages .....whatever, String, list of Strings, etc.

     myOutBoundMessageUtilityClass(Boolean useFuture) {

         this.useFuture = useFuture;
     }

 

     public makeMyCalls(Messages msgs) {

          if (this.useFuture) {

                futureCall(msgs);

          } else {

                normalCall(msgs)

          }

    }

 

    @future (callout=true)

    public static void futureCall(msgs) {MakeTheCallout(msgs);}

 

    public static void normalCall(msgs) {MakeTheCallout(msgs);}

 

     private static void MakeTheCallout(msgs) {

           callout code....

    }

}

-----------------------------------------------------------------------------

 

// And Object2's trigger essentially does it's own logic and calls it's own @future.

 

Trigger object2Trigger (after/before, etc.) {

    // Logic for object 2 (find the matching Obj1's)

    List<Obj1> obj1ToUpdate;

 

    call my @future class method to do the update.

    obj2TriggerHelper th = new obj2TriggerHelper();

    th.doTheObj1Updates(obj1ToUpdate);

}

 

public class obj2TriggerHelper() {

   @future

    global doTheObj1Updates(List<obj1> obj1ToUpdate) {

          // Telling the object 1 trigger to not do a future invocation of it's own.

          obj1ToUpdate[0].someDataField = 'Do Not Do Future';

    update obj1ToUpdate;

    }

 

 

 

Probably wrong about this somewhere, but too tired to think it through any further.   :-)  Good luck,  Steve.

 

This was selected as the best answer
GuyClairboisGuyClairbois

Thanks for your replies!

 

Santhosh, I will look into your suggestion a bit further today.

 

Steve, what you describe is exactly what I did as well. The problem is that whenever the 'normalCall(msgs) ' is started, I get an error message saying " Callout from triggers are currently not supported". So the system does not detect that I am already in an @Future context or at least wants me to put another @future in between..

 

I will also start exploring the possibilities of doing the first update via batch apex.

 

Any other suggestions still very welcome!

Thanks,

Guy 

SteveBowerSteveBower

I suppose you could further refactor Trigger 1 into a declaration and then a chunk of code which does trigger logic and another which does the callout.   Change the logic to NOT do any work if some sort of flag is set... e.g. the trigger would be a no-op.

 

Then trigger two could call it's @future method which can set this flag and do the update (calling trigger 1 which will do nothing) and then use the trigger logic you've refactored out from above, then issue the callout itself.  

 

Really ugly, but then there's no context to carry forward so it should work.

 

Best, Steve.

GuyClairboisGuyClairbois

Hi Steve,

Thanks for your reply. That's really creative and should indeed work. I have 4 or 5 different different 'object2', though, so this might get quite ugly in terms of maintainability etc. However, it's the only non-batch option I see so far. 

 

I raised a ticket for developer support last week, so wonder what SF dev-support says about this whole thing.

 

Will try out some more things today and post here what I ended up with.

Thanks again,

Guy 

GuyClairboisGuyClairbois

Hi ,

a short update..

 

SF Dev Support started with suggesting scheduled APEX (but having a limit of 10 scheduled jobs this would require a complex logic to schedule/unschedule jobs, so not advisable).

Now they are suggesting I look into Batch Apex (which also has a limit of 5 parallel batches and it is unsure whether this supports @future calls and calloutsin the way I require them).

 

@Steve, I liked your idea and startedimplementing it. However, I ran into the following:

When doing the callout, I get:
ERROR: System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out.
This happens because the updates on Object1 are not committed yet.

 

Judging from the boards, there is no decent solution for that.. If anybody has any ideas, those would be more than welcome....

 

Many thanks,

Guy

SteveBowerSteveBower

Is this still a "before" trigger?  Could you write them as "after" triggers in which case the commits, I believe, are done already?

 

Instead of checking a bit of data in the trigger (which was easy to check and reset in a before trigger), you could use a Static class (see Static and Instance in the Apex doc) variable to hold a flag to determine if work should be done.

 

Best, Steve.

 

 

GuyClairboisGuyClairbois

Hi Steve,

 

it's an "after" trigger already. But that might not matter since the whole logic is in @Future context. As far as I know there is no way to commit a transaction while apex is not finished yet.

 

I am now considering splitting the process into 2 parts.

Part 1 handles the online inserts/updates on object1, which result in an immediate Callout

Part 2 handles the inserts/updates on object2 (which do not happen that frequently as this is a configuration table). No immediate callout is made but a scheduled process (x times per hour) checks for object1's that are not yet exported and exports them. Optionally I can create a VisualForce screen in which the user can press a button to manually trigger the callout.

 

If the users accept this, I think I will go for it and wait for the platform to offer me some more flexibility on this end.