Saturday, January 27, 2024

Provide a detailed explanation for the error "Maximum stack depth has been reached"?

When chaining jobs with System.enqueueJob, you can add only one job from an executing job. Only one child job can exist for each parent queueable job as shown in below example. Starting multiple child jobs from the same queueable job isn’t supported.

public class SampleQueueableJob implements Queueable {

    public void execute(QueueableContext context) {

        system.debug('Queueable job called');

       System.enqueueJob(new SampleQueueableJob2() );

    }

}

Because no limit is enforced on the depth of chained jobs, you can chain one job to another. You can repeat this process with each new child job to link it to a new child job. For Developer Edition and Trial organizations, the maximum stack depth for chained jobs is 5, which means that you can chain jobs four times. The maximum number of jobs in the chain is 5, including the initial parent queueable job. If you try to chain more than 5 jobs in developer edition you will get the error Maximum stack depth has been reached.

Let us understand this with an example in developer edition org.

We will call SampleQueueableJob2 from SampleQueueableJob, SampleQueueableJob3 from SampleQueueableJob2, SampleQueueableJob4 from SampleQueueableJob3, SampleQueueableJob5 from SampleQueueableJob4.

public class SampleQueueableJob implements Queueable {

    public void execute(QueueableContext context) {

        system.debug('Queueable job called');

        System.enqueueJob(new SampleQueueableJob2() );

    }

}


public class SampleQueueableJob2 implements Queueable {

    public void execute(QueueableContext context) {

        system.debug('Queueable job called');

        System.enqueueJob(new SampleQueueableJob3() );

    }

}


public class SampleQueueableJob3 implements Queueable {

    public void execute(QueueableContext context) {

        system.debug('Queueable job called');

        System.enqueueJob(new SampleQueueableJob4() );

    }

}


public class SampleQueueableJob4 implements Queueable {

    public void execute(QueueableContext context) {

        system.debug('Queueable job called');

        System.enqueueJob(new SampleQueueableJob5() );

    }

}


public class SampleQueueableJob5 implements Queueable {

    public void execute(QueueableContext context) {

        system.debug('Queueable job called');

    }

}

Let us try to call SampleQueueableJob from developer edition.

We can see the jobs are successfully executed.

Maximum stack depth has been reached

Maximum stack depth has been reached


Let us now try to call SampleQueueableJob6 from SampleQueueableJob5.

Update the class SampleQueueableJob5 to call SampleQueueableJob6.


public class SampleQueueableJob5 implements Queueable {

    public void execute(QueueableContext context) {

        system.debug('Queueable job called');

        System.enqueueJob(new SampleQueueableJob6() );

    }

}


public class SampleQueueableJob6 implements Queueable {

    public void execute(QueueableContext context) {

        system.debug('Queueable job called');

 

    }

}


Let us try to call SampleQueueableJob from developer edition. 

We can now see the error "Maximum stack depth has been reached". This is because for Developer Edition and Trial organizations, the maximum stack depth for chained jobs is 5.

Maximum stack depth has been reached



Friday, January 26, 2024

Provide a detailed explanation for the error "Too many queueable jobs added to the queue"?

The error "Too many queueable jobs added to the queue" occurs when you attempt to queue more than the allowed number of queueable jobs in a single transaction.

In a queueable apex, You can add up to 50 jobs to the queue with System.enqueueJob in a single transaction.

Let us understand this with an example:

Below is sample queueable class.

public class SampleQueueableJob implements Queueable {

    public void execute(QueueableContext context) {

        system.debug('Queueable job called');

    }

}

We are going to call the above queueable class from below class which is enqueuing the queueable class 50 times. 

public class LaunchQueueableJobClass{

    public static void launchQueueableJobs() {

        // Chain multiple instances of the Queueable job

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

         System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

         System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

         System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

         System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

    }

}

Let's call the LaunchQueueableJobClass apex class from developer console as shown below.


Now, check the status in Apex Jobs under setup. You will be able to see the queueable apex is executed 50 times.

Too many queueable jobs added to the queue

Now, let us try to add one more System.enqueueJob(new SampleQueueableJob()); statement in LaunchQueueableJobClass apex class. If you try to run the code from developer console now you will be able to see the below error.

public class LaunchQueueableJobClass{

    public static void launchQueueableJobs() {

        // Chain multiple instances of the Queueable job

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

         System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

         System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

         System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

         System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        System.enqueueJob(new SampleQueueableJob());

        

        System.enqueueJob(new SampleQueueableJob());   // Added 51 time...

    }

}

Too many queueable jobs added to the queue

Saturday, January 13, 2024

Explain chaining of Batch Jobs in Salesforce?

Before API version 26.0 it was not possible to start another batch job from an existing batch job. Starting with API version 26.0, you can start another batch job from an existing batch job to chain jobs together.

Chain a batch job is useful when you want to start a job after another one finishes and when your job requires batch processing, such as when processing large data volumes. Otherwise, if batch processing isn’t needed, consider using Queueable Apex.

Let’s us understand with a real time example of how to invoke a batch apex from another batch apex.  

The provided code defines two Salesforce Apex batch classes, BatchClassToUpdateAccountIndustry and BatchClassToUpdateContactIndustry, demonstrating a sequence of data processing tasks.

The provided code represents two Salesforce Apex batch classes designed to update the Industry field on Account and Contact records. Here's a short description of the code:

These batch classes work together to update the Industry field on Account where Type is Technology Partner and all contacts where Account Industry is Communications, ensuring that all Contacts have Industry as Communications where Account Industry is Communications. The second batch class is invoked from the finish method of the first batch class to maintain the sequence of updates.

global class BatchClassToUpdateAccountIndustry implements Database.Batchable<sObject> {

 String query = 'Select Id,Name,Rating, Industry,Type from Account WHERE (Type = \'Technology Partner\') ';

 String accIndustry='Communications';

 List<Account> upddateAccount= new List<Account>();

 global Database.QueryLocator start(Database.BatchableContext bc) {

 

 return Database.getQueryLocator(query);

 }

 

 global void execute(Database.BatchableContext bc, List<Account> records){

 

    System.debug('records'+records.size());

    for (Account accountobj : records) {

          accountobj.Industry = accIndustry;

          upddateAccount.add(accountobj);

        }

        try{

        update upddateAccount;

        }

        catch(Exception e){

        System.debug('Error is '+e.getMessage());

        }

 }

 

 global void finish(Database.BatchableContext bc){

 //Note: We are calling second batch class i.e BatchClassToUpdateContactRating from Finish Method of this batch.

 BatchClassToUpdateContactIndustry b = new BatchClassToUpdateContactIndustry ();

 Database.executeBatch(b, 200);

 }

 }

 

global class BatchClassToUpdateContactIndustry implements Database.Batchable<sObject> {

List<Contact> updatedContacts= new List<Contact>();

 String query = 'Select FirstName, LastName,Account.Rating from Contact WHERE (Account.Industry = \'Communications\') ';

 global Database.QueryLocator start(Database.BatchableContext bc) {

 return Database.getQueryLocator(query);

 }

 global void execute(Database.BatchableContext bc, List<Contact> records){

 for(Contact con : records){

 con.Industry__c ='Communications';

 updatedContacts.add(con);

 }

 update updatedContacts;

 }

 global void finish(Database.BatchableContext bc){

 

 }

 }

Now, let’s test the working of the above batch classes.

Open execute anonymous window and paste the below code and click execute.

BatchClassToUpdateAccountIndustry batchObj= new BatchClassToUpdateAccountIndustry();

Database.executebatch(batchObj,200);


Now, we can verify one of the account where the type is technology partner to check if account industry and contact industry is updated or not as shown below.


Sunday, October 1, 2023

What are different OAuth Authorization Flows in Salesforce?

OAuth authorization flows grant a client application restricted access to protected resources.

Salesforce offers multiple OAuth authorization flows. As a Salesforce developer, you can choose from several OAuth authorization flows considering the use cases.

Each OAuth flow offers a different process for approving access to a client app, but in general the flows consist of three main steps.

1)      A client app requests access to a protected resource.

2)      In response, an authorizing server grants access tokens to the client app.

3)      A resource server then validates these access tokens and approves access to the protected resource.

Some of the important OAuth authorization flows are mentioned below.

1)      OAuth 2.0 Web Server Flow for Web App Integration

This flow is used to integrate an external web app with the Salesforce API, it implements the OAuth 2.0 authorization code grant type. With this flow, the server hosting the web app must be able to protect the connected app’s identity, defined by the client ID and client secret.

2)      OAuth 2.0 User-Agent Flow for Desktop or Mobile App Integration

With the OAuth 2.0 user-agent flow, users authorize a desktop or mobile app to access data by using an external or embedded browser. Client apps running in a browser using a scripting language such as JavaScript can also use this flow. This flow uses the OAuth 2.0 implicit grant type.

3)      OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration

Sometimes you want to authorize servers to access data without interactively logging in each time the servers exchange information. For these cases, you can use the OAuth 2.0 JSON Web Token (JWT) bearer flow. This flow uses a certificate to sign the JWT request and doesn’t require explicit user interaction. However, this flow does require prior approval of the client app.

4)      OAuth 2.0 Client Credentials Flow for Server-to-Server Integration

In this flow, the client app exchanges its client credentials defined in the connected app—its consumer key and consumer secret—for an access token. This flow eliminates the need for explicit user interaction which is needed in username-password flow, though it does require you to specify an integration user to run the integration. You can use this flow as a more secure alternative to the OAuth 2.0 username-password flow.

5)      OAuth 2.0 Username-Password Flow for Special Scenarios

You can use the username-password flow to authorize a client via a connected app that already has the user’s credentials. It is not a recommended flow because it passes credentials back and forth. This should be used only if there’s a high degree of trust between the resource owner and the client. In these cases, set user permissions to minimize access and protect stored credentials from unauthorized access.

Note: Some flows have important security considerations. For example, when using the web server flow, you must store the client secret securely. We recommend avoiding the user-agent and username-password flows because they transmit credentials.

We will see each of the above mentioned flows in details in upcoming articles.

Explain the difference between Identity Provider initiated SAML flow and Service Provider initiated SAML flow?

Identity Provider-Initiated SAML Flow:

The user logs in to the identity provider.

The user clicks a button or link to access the service provider. For example, the user clicks an app on the App Launcher page in a Salesforce org.

The identity provider initiates login by sending a cryptographically signed SAML response to the service provider. The SAML response contains a SAML assertion that tells the service provider who the user is.

The service provider validates the signature in the SAML response and identifies the user.

The user is now logged in to the service provider.

The example which we have seen above under How to configure a Connected App for SAML 2.0 Flow in Salesforce for integration service provider? is of Identity Provider-Initiated SAML Flow.

Service Provider-Initiated SAML Flow:

Now, we know how Identity Provider-Initiated SAML Flow works. Let us now try to understand Service Provider-Initiated SAML Flow.   

In service provider initiated flow the user starts from service provider trying to access a service.

The service provider initiates login by sending a SAML request to the identity provider, asking it to authenticate the user.

The identity provider sends the user to a login page.

The user enters their identity provider login credentials and the identity provider authenticates the user.

The identity provider now knows who the user is, so it sends a cryptographically signed SAML response to the service provider. The SAML response contains a SAML assertion that tells the service provider who the user is.

The service provider validates the signature in the SAML response and identifies the user.

The user is now logged in to the service provider and can access the protected resource.

To setup Service Provider-Initiated SAML Flow follow the below steps:

1)      Login to service provider org.

2)     Go to “My Domain” under setup and click edit against “Authentication Configuration” as shown below.

Explain the difference between Identity Provider initiated SAML flow and Service Provider initiated SAML flow?

3)     Now, enable the checkbox against the Authentication Service as shown below.

Explain the difference between Identity Provider initiated SAML flow and Service Provider initiated SAML flow?


4)     The name displayed above is nothing but the name you have provided while configuring Single Sign On in Service Provider org as show below.

Explain the difference between Identity Provider initiated SAML flow and Service Provider initiated SAML flow?

5) Now, go to the login URL of service provider org you will be able to see the link to login service provider org using identity provider org credentials as shown below.

Explain the difference between Identity Provider initiated SAML flow and Service Provider initiated SAML flow?