15

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?

29

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
        }
    }
}
  • 2
    That covers the first sentence of my answer! :) – Boris Bachovski Jul 13 '16 at 5:48
  • 1
    wow. lots of information here. – Ratan Paul Jul 13 '16 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.. – Pranay Jaiswal Jul 13 '16 at 6:06
  • 1
    Another one: Url.getCurrentRequestUrl() can identify native UI, Bulk API context etc – bigassforce Jul 9 '17 at 19:51
12

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.

  • 2
    Yeah Enum is definitely cleaner. – Adrian Larson Jul 13 '16 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 :). – Boris Bachovski Jul 13 '16 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. – Pranay Jaiswal Jul 13 '16 at 5:45
  • 1
    Unfortunately there isn't such mechanism available at this point in time. – Boris Bachovski Jul 13 '16 at 5:47

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.