I am trying to call @future
method from trigger and got CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY
Error, because of update operation within the @future
method. Class and triggers given below.
Error Message
Update failed. First exception on row 0 with id 003o0000019gMu6AAE; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, xrx_mf_crm.RepWebserviceCall: execution of AfterUpdate
caused by: System.AsyncException: Future method cannot be called from a future o.
Class
@future(callout=true)
public static void AuthCallout(Set<Id> contactIds) {
List<Contact> ContactUpdate = [SELECT id FROM Contact where Id IN :contactIds];
String ContactID;
HttpRequest req = new HttpRequest();
req.setTimeout(60000);
req.setHeader('Accept','*/*');
req.setHeader('Content-Type','application/json'); // Content Type
req.setMethod('GET');
for (Contact c : ContactUpdate)
{
ContactID = c.id;
req.setEndpoint('https://xxx/xxxx/xxxxx/xxx/xxx-lookup?ContactID=' + ContactID);
Http http = new Http();
HTTPResponse res = http.send(req);
System.debug(res.getBody());
JSONParser parser = JSON.createParser(res.getBody());
String GMMID;
while (parser.nextToken() != null) {
if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'GCGMM')) {
// Get the value.
parser.nextToken();
// Compute the grand total price for all invoices.
GMMID = parser.gettext();
}
}
//ContactUpdate.IsFutureContext__c = true;
C.Group_ID__c = GMMID;
update c;
}
}
Trigger:
trigger ContactWebserviceCall on Contact (After insert) {
for (Contact co : Trigger.new)
{
If(co.Rep_ID__c.substring(0,3) == 'CRM')
{
ContactWebServiceCall. AuthCallout (Trigger.newMap.keySet());
}
}
}
Best Answer
The problem you're having isn't that you can't call an
@future
method from a trigger, but rather that you end up trying to call an@future
method when you're already in an@future
context.It isn't happening because of your trigger, but somehow, running a DML
update
on yourContacts
in your@future
method is causing your@future
method (or perhaps some other@future
method) to be called again. It can't be because of the trigger that you've provided because that trigger only runsAfter Insert
(not on an update).The likely solution here is to add some extra code that exercises some extra caution when handling
@future
calls.The basic example of the pattern I'm talking about is this
Using this class is pretty straightforward, you'd replace your direct call to the
@future
method withSaferFurure.execute(someIdList);
With that out of the way, we can turn attention to removing the
@future
call from inside a loop.Given your provided trigger, it appears that you might not want to call your
@future
method with every record being inserted. I also doubt that you want to be repeatedly calling your@future
method with the same data multiple times (which is a very likely possibility with the trigger that you currently have).Instead, making your trigger bulk-friendly would involve first preparing the data that you want to send to your
@future
method, and, in a separate section of trigger code, then calling your@future
method.An example based on your provided trigger: