24

We have various ways of invoking apex. Triggers, Inbound Webservices, @InvocableMethod,@RemoteAction and what not. Often we call some or other utility methods from these execution contexts. I have some generic utility method,that should behave differently from the context it is called?

I know about Trigger.isExecuting but it only tells if its running in trigger context or not. I have a utility method that creates lead from Webservices, VF button click and via Lightning . Whenever we create a lead I want some special operations to be done via the source it was originated.(Its not just updating a simple field leadSource, I have some methods that creates some childs and update other records of other type) . I can achieve this by passing an extra parameter to utility method to tell about source, but i have then to edit hundred of other class that uses this utility method. If i could dynamically find out via which context this piece of code is running,then i would have to change only one class to satisfy my requirement.

Is there something in APEX that tells in what context the apex method was called?

0

4 Answers 4

40

Just to play devil's advocate, I think you may be somewhat missing the point of a generic utility method. Generic is powerful. Now you seek to pollute that. To some extent, context specific logic belongs in the calling context.

I understand that sometimes, reality doesn't play nice, and you may need to do source dependent processing. But a lot of times, those calling contexts leave artifacts. For example, what are you setting as the value of the LeadSource field? Does it vary depending on these contexts? Are there other field indicators that can lead you to conclude which context a record was created in? My preference is to put that sort of logic in a trigger if you can conclusively determine calling context another way.


Here is the extent to which I know you can determine calling context:

  • Batch - System.isBatch()
  • @future - System.isFuture()
  • Queueable - System.isQueueable()
  • Schedulable - System.isScheduled()
  • Trigger - Trigger.isExecuting

As far as I know you can't tell if you are running Execute Anonymous, WebService, or Visualforce. The following might be somewhat helpful:

  • Visualforce - ApexPages.currentPage() != null
  • Apex REST - RestContext.request != null

You can also get a some information by querying the AuthSession object. Not sure how reliable this approach would be but worth mention.

SELECT LoginType, SessionType FROM AuthSession
WHERE UsersId = :UserInfo.getUserId()
AND IsCurrent = true
ORDER BY CreatedDate DESC

You could also try this strategy but it's more dubious:

try
{
    throw new DmlException();
}
catch (DmlException e)
{
    String stackTrace = e.getStackTraceString();
    if (stackTrace.contains('AnonymousBlock'))
        system.debug('Execute Anonymous');
    // other parsing...
}

If you really must determine the context, I was going to recommend something similar to what @Boris wrote up. However, in addition to setting a static variable, you could consider method overloads.

public class Utility
{
    public static void method(List<Lead> input)
    {
        // existing logic
    }
    public static void method(List<Lead> input, /*String or Enum*/ context)
    {
        method(input);
        // further processing
    }
}

Or you can at least move the context specific logic to another method to minimize the "pollution" factor:

public class Utility
{
    public static void method(List<Lead> input)
    {
        // existing logic
        contextLogic(input);
    }
    static void contextLogic(List<Lead> input)
    {
        if (System.isBatch()) { batchLogic(input); }
        if (System.isFuture()) { futureLogic(input); }
        if (System.isQueueable()) { queueableLogic(input); }
        if (System.isScheduled()) { schedulableLogic(input); }
        if (Trigger.isExecuting) { triggerLogic(input); }

        if (ApexPages.currentPage() != null) { visualforceLogic(input); }
        if (RestContext.request != null) { apexRestLogic(input); }

        try
        {
            throw new DmlException(); // down the rabbit hole...
        }
        catch (DmlException e)
        {
            // have fun
        }
    }
}
6
  • 3
    That covers the first sentence of my answer! :) Commented Jul 13, 2016 at 5:48
  • 1
    wow. lots of information here.
    – Ratan Paul
    Commented Jul 13, 2016 at 6:05
  • 1
    I agree with you. I ask something that pollute the generic concept. But was looking for some solution that would help me customize and go live as quick as possible. Methods being context aware helps in creating dynamic apps with dynamic run time behavior. ApexPages.currentPage() and Rest Context will help me here to fulfill my requirement to an extent. Thanks for sharing amazing information here.. Commented Jul 13, 2016 at 6:06
  • 2
    Another one: Url.getCurrentRequestUrl() can identify native UI, Bulk API context etc Commented Jul 9, 2017 at 19:51
  • 4
    This is much easier starting with Winter 21 release thanks to Detect Apex Runtime Context with RequestId and Quiddity
    – identigral
    Commented Sep 4, 2020 at 23:07
15

There are different ways how to handle that. In the same fashion as you mentioned Trigger.isExecuting (it's boolean and can only have 2 values), you can create a static variable (of type String or Integer or Enum, whatever type that makes sense) inside your utility class, to which you can assign a different value from each of the callers. For an example if the utility method is called from a VF controller you can set the value to indicate that the context is VF controller. If you're calling from an invocable method, set a different value etc.

Let's assume this is your utility class:

public with sharing class Util
{
    // Variable to hold the current transaction context
    public static String context;

    // Assuming this method can be called from multiple places
    public static Boolean checkAvailability(Date particularDate)
    {
        // If it's called from a trigger, do one thing
        if (context == 'TRIGGER')
        {
            // do something
            return true;
        }
        // If it's called from an invocable method, do something else
        else if (context == 'INVOCABLE')
        {
            // do something else
            return true;
        }
        else
        {
            // Default
            return false;
        }
    }
}

Now inside your trigger for an example you can set the context before calling the checkAvailability method:

trigger Account on Account (before insert)
{
    Util.context = 'TRIGGER';
    Boolean available = Util.checkAvailability(System.today());
    // do something
}

Use the same principle when calling from elsewhere.

4
  • 2
    Yeah Enum is definitely cleaner.
    – Adrian Larson
    Commented Jul 13, 2016 at 5:34
  • 1
    I tend to use Enum too though I used a String in this case to simplify the example and make it easier to understand the concept. Unfortunately not many people are familiar with Enums hence I only mentioned it :). Commented Jul 13, 2016 at 5:36
  • @BorisBachovski amazing answer. But still i would have to edit 30+ other classes that uses this method and deploy again to prod. Wanted to know if editing just the utility class would do it or not. Commented Jul 13, 2016 at 5:45
  • 1
    Unfortunately there isn't such mechanism available at this point in time. Commented Jul 13, 2016 at 5:47
7

Converting comment from @identigral to an answer for completeness of the OP's question & future readers.

Starting Winter 21 this indeed is possible to determine the calling context of the request with System.Request class. The Request class provides a few methods among them we do have a method that returns an enum(System.Quiddity) specifying the context of origin of this request.

Request reqInfo = Request.getCurrent();
System.Quiddity cxt = reqInfo.getQuiddity();

It can be wrapped inside a nice little switch case or a class that implements Callable interface & a call method having a different logic has to be executed based on the calling context.

Request reqInfo = Request.getCurrent();
   switch on reqInfo.getQuiddity(){
      when VF {
         // Visualforce          
      } when AURA {
         // Lightning Component       
      } when INVOCABLE_ACTION {
         // Flow/Process Builder
      } when ANONYMOUS {
         // execute anonymous       
      } when REST {
         // RESTful       
      } when FUTURE, SCHEDULED, QUEUEABLE, BATCH_APEX {
         // A few Async context        
      }
   }

There are more Quiddity context & the entire list is described here.

1

Although it was mentioned in the comments, I think this option deserves a standalone answer.

In my case, System.URL.getCurrentRequestUrl() was a saver. I needed to know when the source of the transaction is Aura component. And what the method returns in this case is something like that: Url:[delegate=https://yourorg.my.salesforce.com/aura?r=42&aura.RecordUi.updateRecord=1. You can then get different parts of the url using other methods from System.URL.

1
  • 1
    As for the getQuiddity(), it was returning "Synchronous", which didn't tell much. Commented Feb 1, 2023 at 18:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .