Trigger .isExecuting Clarification
Excellent questions.
"What trigger.isExecuting is actually telling us"
When decoupling the Apex trigger handler class from the actual trigger, the Apex class has no way to know what context it's called in (unit test, web service, visualforce page, trigger). This flag just means the handler was executed by a trigger.
A better name for this flag might be "isCreatedByTrigger".
"How to best leverage it"
At one point I think the intent was to use this flag in unit testing, but Test.isRunningTest() (introduced in v20?) is probably a better solution today.
"If it can help me get rid of the TriggerContextUtility class that I currently use to avoid recursive trigger calls."
All solutions to this problem require some form of static boolean state management, so your code is correct. The isExecuting flag is not applicable to this class of problem.
In fact, the next iteration of my trigger template will probably need to incorporate a static variable to address the recursion problem. Thanks for pointing this out!
-Mike Leach
There are couple of design concepts/patterns published to handle the apex triggers and some of them had gotten wide acceptance. Check out Dan Appleman's advanced apex programming or Tony Scott's design pattern. You might want to check out my architecture framework to handle the apex triggers as well. This is the most comprehensive architecture framework to handle the apex triggers in Force.com platform that supports recursive triggers along with SOC and others. While none of these design patterns/frameworks can be universal solution, depending on your requirements, I believe they should be hugely beneficial.
Edit:
To answer your question:
a. 'Trigger.isExecuting' can be used anywhere in the apex code. It returns true, if you call it within the trigger context (not just within the trigger, for e.g. if you have a separate class and if you call the method in this class from your trigger, it will still return true).
b. This is best utilized in your helper code, specifically, if the helper code could be called from different places. For e.g. the same helper method in your helper class could be called from your custom controller or trigger code. So call 'Trigger.isExecuting' will help you to identify if the call is from trigger or a visualforce page.
c. At the simplest level, you need a static variable to control the recursion and this is separate from using 'Trigger.isExecuting'. You can either get rid of TriggerContextUtility class, but you still need to have a static variable somewhere and set/unset it to control the recursion.
Here is an example of how to use the static variable and Trigger.isExecuting. This code handles the after update event on the Account object. Note that this example code may not follow the best practices and supposed to demonstrate the concept.
trigger AccountTrigger on Account (after insert, after update) {
AccountTriggerHandler.handleAccountEvents(Trigger.isExecuting, Trigger.isBefore, Trigger.isAfter, Trigger, isInsert, Trigger.isUpdate, Trigger.new, Trigger.old);
}
public class AccountTriggerHandler {
// you need one static variable for each event type.
public static Boolean isAfterInsertFlag = false;
public static Boolean isAfterUpdateFlag = false;
public static void handleAccountEvents(Boolean isExecuting, Boolean isBefore, Boolean isAfter, Boolean isInsert, Boolean isUpdate, List<Account> listNewAccounts, List<Account> listOldAccounts) {
if(isAfter && isUpdate) { // handle after update event
if(isAfterUpdateFlag == false) { // if this is the first call
isAfterUpdateFlag = true; // set the static variable to indicate that the call is in progress for after update event.
AccountHelper.updateOwner(isExecuting, listNewAccounts);
}
}
}
}
public class AccountHelper {
public static void updateOwner(Boolean isTrigger, List<Account> listAccounts) {
if(isTrigger) { // if the running context originated from trigger invocation
// do what ever you want to do as part of the trigger invocation
} else {
// do what ever you want to do if the call originated from different context, such as from controller.
}
}
}