0

Scenario: I have two objects - Object A and Object B.Object A has recursion checking mechanism using Static Boolean variables covering different events(IsBeforeUpdate,IsBeforeInsert etc..) I have two apex classes (Class 1 and Class 2) running on Object B that updates Object A on after update.

Question: Let us say an update operation happens on Object B. whats happens to the Static Boolean (IsBeforeUpdate on Object A) will it be set to false by the first class 1 ? and second class 2 will not be able do its update on Object A? if this is true , how can i make sure both my class 1 and 2 from Object B update Object A?

//MasterTaskTrigger
if (trigger.isUpdate && trigger.isAfter) 
{
    handler.afterUpdate(trigger.old, trigger.oldMap, trigger.new, trigger.newMap);
    system.debug('inside After Update');
}

//This is an handler for Task Object
public class TriggerhandlerTask extends TriggerHandler
{
    public void afterUpdate(List<Task> originalTask,Map<ID, Task> originalTaskMap,List<Task>updatedTask,Map<ID, Task>  updatedTaskMap) 
    {
        if(afterUpdateHasRun()) 
        {
            return;
        }
        //Call Class 1 - has logic to update Object A 
        Class1 Cs =  new Class1();
        //Call Class 2 - has logic to update Object A
        Class2 Cs =  new Class2();
    } 
}

//this handler is extended across all other objects Triggerhandlers
public virtual class TriggerHandler 
{
    public   static  boolean isBeforeInsertFirstRun   = true;
    public   static  boolean isBeforeUpdateFirstRun   = true;
    public   static  boolean isBeforeDeleteFirstRun   = true;
    public   static  boolean isAfterInsertFirstRun    = true;
    public   static  boolean isAfterUpdateFirstRun    = true;
    public   static  boolean isAfterDeleteFirstRun    = true;
    public   static  boolean isAfterUndeleteFirstRun  = true;

    protected boolean beforeInsertHasRun() 
    {
        if (isBeforeInsertFirstRun) 
        {       
            return isBeforeInsertFirstRun = false;
            System.debug('---->isBeforeInsertFirstRun'+isBeforeInsertFirstRun); 
        }
        return true;
    }
    protected boolean beforeUpdateHasRun() 
    {
        if (isBeforeUpdateFirstRun) 
        {
            return isBeforeUpdateFirstRun = false;
            System.debug('---->beforeUpdateHasRun'+beforeUpdateHasRun); 
        }
        return true;
    }  
}
  • You must edit your post to illustrate the code pattern. Please try to only include relevant code if you can. Imagine if you were going to try to reproduce this issue in a new developer org. – Adrian Larson May 19 '16 at 0:42
  • @AdrianLarson i have updated the original post with the pattern that i am trying to implement, my question is will my class2 on task be able to update Object A ? why i am asking this because i have a doubt that if class1 completes the update , the boolean isBeforeUpdateFirstRun is set to false (for Object A instance) and second class 2 will not be able to update Object A – Kiran May 19 '16 at 1:10
2

You should handle recursion as a locking mechanism. That means that the trigger should first see if it's locked, and if not, lock itself, and when done, unlock itself. Here's the basic design:

public class TriggerLocks {
    public static Boolean triggerA_beforeUpdate = false;
    // and so on
}

trigger triggerA_BeforeUpdate on A (before update) {
    if(TriggerLocks.triggerA_beforeUpdate) {
        return;
    }
    TriggerLocks.triggerA_beforeUpdate = true;
    // Do your logic here
    // When done...
    TriggerLocks.triggerA_beforeUpdate = false;
}

This way, if you have two or more updates on A records, you can do both of them without the trigger flag getting in the way.

Note: "Class 1" nor "Class 2" should update the Boolean flag without good reason. The data is conceptually internal to the trigger itself, but due to language limitations, can't actually be protected in the same way. The trigger, and only the trigger, should modify that flag directly. This limits the responsibility of breaking recursion to just the single trigger's logic.


Here's a "not very optimized" version of a handler I might build to help with consistency.

public virtual class BaseTriggerHandler {
    static Map<Type, BaseTriggerHandler> cache = new Map<Type, BaseTriggerHandler>();

    Boolean inBeforeInsert = false, inBeforeUpdate = false, inBeforeDelete = false,
            inAfterInsert = false, inAfterUpdate = false, inAfterDelete = false, inAfterUndelete = false;

    protected virtual void beforeInsert(SObject[] records) {

    }
    protected virtual void beforeUpdate(SObject[] oldRecords, SObject[] records) {

    }
    protected virtual void beforeDelete(SObject[] oldRecords) {

    }
    protected virtual void beforeUndelete(SObject[] records) {

    }
    protected virtual void afterInsert(SObject[] records) {

    }
    protected virtual void afterUpdate(SObject[] oldRecords, SObject[] records) {

    }
    protected virtual void afterDelete(SObject[] oldRecords) {

    }
    protected virtual void afterUndelete(SObject[] records) {

    }
    void doBeforeInsert(SObject[] records) {
        if(!inBeforeInsert) {
            inBeforeInsert = true;
            beforeInsert(records);
            inBeforeInsert = false;
        }
    }
    void doBeforeUpdate(SObject[] oldRecords, SObject[] records) {
        if(!inBeforeUpdate) {
            inBeforeUpdate = true;
            beforeUpdate(oldRecords, records);
            inBeforeUpdate = false;
        }
    }
    void doBeforeDelete(SObject[] oldRecords) {
        if(!inBeforeDelete) {
            inBeforeDelete = true;
            beforeDelete(oldRecords);
            inBeforeDelete = false;
        }
    }
    void doAfterInsert(SObject[] records) {
        if(!inAfterInsert) {
            inAfterInsert = true;
            afterInsert(records);
            inAfterInsert = false;
        }
    }
    void doAfterUpdate(SObject[] oldRecords, SObject[] records) {
        if(!inAfterUpdate) {
            inAfterUpdate = true;
            afterUpdate(oldRecords, records);
            inAfterUpdate = false;
        }
    }
    void doAfterDelete(SObject[] oldRecords) {
        if(!inAfterDelete) {
            inAfterDelete = true;
            afterDelete(oldRecords);
            inAfterDelete = false;
        }
    }
    void doAfterUndelete(SObject[] records) {
        if(!inAfterUndelete) {
            inAfterUndelete = true;
            afterUndelete(records);
            inAfterUndelete = false;
        }
    }
    // The only method you need to call
    public void dispatch() {
        if(Trigger.isInsert && Trigger.isBefore) {
            doBeforeInsert(Trigger.new);
        }
        if(Trigger.isUpdate && Trigger.isBefore) {
            doBeforeUpdate(Trigger.old, Trigger.new);
        }
        if(Trigger.isDelete && Trigger.isBefore) {
            doBeforeDelete(Trigger.old);
        }
        if(Trigger.isInsert && Trigger.isAfter) {
            doAfterInsert(Trigger.new);
        }
        if(Trigger.isUpdate && Trigger.isAfter) {
            doAfterUpdate(Trigger.old, Trigger.new);
        }
        if(Trigger.isDelete && Trigger.isAfter) {
            doAfterDelete(Trigger.old);
        }
        if(Trigger.isUndelete && Trigger.isAfter) {
            doAfterUndelete(Trigger.new);
        }
    }

    protected BaseTriggerHandler() {

    }

    // Singleton/factory
    public static BaseTriggerHandler getHandlerFor(Type handlerType) {
        if(!cache.containsKey(handlerType)) {
            cache.put(handlerType, (BaseTriggerHandler)handlerType.newInstance());
        }
        return cache.get(handlerType);
    }
}

And here's a possible implementation:

public class TaskTriggerHandler extends BaseTriggerHandler {
    protected override void afterUpdate(SObject[] records) {
        Class1 c1 = new Class1();
        Class2 c2 = new Class2();
    }
}

And here's the trigger you use to actually call this entire framework:

trigger TaskTrigger on Task (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
    BaseTriggerHandler.getHandlerFor(TaskTriggerHandler.class).dispatch();
}
  • Thank you for you answer, i am trying to implement a trigger design pattern in my Org, lot of trigger design patterns are using the handlers in the middle to manage Recursion , if you can share a pattern that can handle complex requirements and recursion without running into this type of issue i can use it a base and build on it. – Kiran May 19 '16 at 1:18
  • This is intriguing as it looks like it deals with the use case when more than 200 sobjects are in a DML statement where other "traditional" static recursion flags basically prevent the trigger from doing anything on sobjects 201+ – cropredy May 19 '16 at 3:34
  • @cropredy Shh. It's a secret to everybody. – sfdcfox May 19 '16 at 5:12
  • @Kiran It took me a bit to write something, but let me know if this example is useful. – sfdcfox May 19 '16 at 6:02
  • Always get uncomfortable when I see one boolean flag for all columns (and all rows). If the trigger is handling changes to two different fields, a change to the first field will mean a change to the second field made after the trigger has fired once will be not acted upon by the trigger. – Keith C May 19 '16 at 8:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.