You are using an older browser that might negatively affect how this site is displayed. Please update to a modern browser to have a better experience. Sorry for the inconvenience!

Long-Running Callouts in Salesforce


By: Venkata

Continuation Class:

Continuation Class is used to make asynchronous callout using REST and SOAP services. Using this class, we can make a long running request from a Visualforce page to external systems, and can integrate our Visualforce pages with complex back end systems. Here, an asynchronous callout is made from a Visualforce page and its response is returned through a callback method. An asynchronous callout is also referred to as Continuation.

salesforce callouts

 

The above diagram shows the execution path of an asynchronous callout, starting from a Visualforce page to the External Service.

Step 1:

The user performs an action from the Visualforce page that requests data from the external system.

Step 2:

The app server hands over the request to the Continuation server.

Step 3:

The page execution stops until the response coming from the server.

Step 4-7:

Continuation server sends the request to the external system and receives the response from the external system

Step 8:

Continuation server hands over the response received from the external systems to the app server.

Step 9:

Finally, the response is returned back to the Visualforce page.

 

Let us assume the Contact information is accessed from an external service and the service is called by an action button on a Visualforce page. This page is accessed by thousands of employees in an organization. So, when hundreds of employees click the same button to get the contact information from the external systems, the actions exceed the limit of 10 concurrent long-running requests. Thus, such limits can be overcome by using the asynchronous callout.

In the example below, the callout action is invoked from the button on the Visualforce page. This action creates a continuation and returns the response to the Visualforce page. The Visualforce request is suspended until the response is returned back from the Continuation server to the Visualforce Page; so, the user must wait for the response to the Visualforce page before invoking the new actions. Once the data is return from the Continuation server, the Visualforce page resumes its action.

 

Wrapper Class ( CalloutWrapper )

public class CalloutWrapper{
public class CandidateWrapper {
public String Name{get;set;}
public String Country{get;set;}
}

public class CustomerWrapper {
public String Name{get;set;}
public String City{get;set;}
public String Country{get;set;}
}
public class StudentWrapper {
public String Name{get;set;}
public String AccNumber{get;set;}
public String Source{get;set;}
public String Site{get;set;}
public String Email{get;set;}
}

}

Controller ( ContinuationController )

public with sharing class ContinuationController {

public List<CalloutWrapper.CandidateWrapper> candidateWrapperList{get;set;}

public List<CalloutWrapper.CustomerWrapper> customerWrapperList{get;set;}

public List<CalloutWrapper.StudentWrapper> studentWrapperList{get;set;}

public String requestLabel_1;

private static final String LONG_RUNNING_SERVICE_URL_1 = 'https://raw.githubusercontent.com/parthiban019/samplejson/master/candidateplaces.json';

public String requestLabel_2;

private static final String LONG_RUNNING_SERVICE_URL_2 = 'https://raw.githubusercontent.com/parthiban019/samplejson/master/customer.json';

public String requestLabel_3;

private static final String LONG_RUNNING_SERVICE_URL_3 = 'https://raw.githubusercontent.com/parthiban019/samplejson/master/student.json';

public Object startRequest() {

Continuation con = new Continuation(60);

con.continuationMethod='processResponse';



// Create callout request 1 

HttpRequest req1 = new HttpRequest();

req1.setMethod('GET');

req1.setEndpoint(LONG_RUNNING_SERVICE_URL_1);

this.requestLabel_1 = con.addHttpRequest(req1);



// Create callout request 2

HttpRequest req2 = new HttpRequest();

req2.setMethod('GET');

req2.setEndpoint(LONG_RUNNING_SERVICE_URL_2);

this.requestLabel_2 = con.addHttpRequest(req2);



// Create callout request 3

HttpRequest req3 = new HttpRequest();

req3.setMethod('GET');

req3.setEndpoint(LONG_RUNNING_SERVICE_URL_3);

this.requestLabel_3 = con.addHttpRequest(req3);

return con; 

}



public Object processResponse() {

HttpResponse response1 = Continuation.getResponse(this.requestLabel_1);

if(response1.getBody() != null && response1.getStatusCode() == 200)

candidateWrapperList = (List<CalloutWrapper.CandidateWrapper>)json.deserialize(response1.getBody(),List<CalloutWrapper.CandidateWrapper>.class);



HttpResponse response2 = Continuation.getResponse(this.requestLabel_2);

if(response2.getBody() != null && response2.getStatusCode() == 200)

customerWrapperList = (List<CalloutWrapper.customerWrapper>)json.deserialize(response2.getBody(),List<CalloutWrapper.customerWrapper>.class);



HttpResponse response3 = Continuation.getResponse(this.requestLabel_3);

if(response3.getBody() != null && response3.getStatusCode() == 200)

studentWrapperList = (List<CalloutWrapper.studentWrapper>)json.deserialize(response3.getBody(),List<CalloutWrapper.studentWrapper>.class);

return null;

}

}

 

VisualForce Page ( Continuation_Page )

 

<apex:page controller="ContinuationController" showHeader="false" sidebar="false">

<apex:form id="result"> 

<br /><br />

<center><apex:commandButton action="{!startRequest}" value="Make Parallel Request" reRender="result" status="status1"/> </center> <br /><br />

<apex:actionStatus id="status1">

<Apex:facet name="start" >

<center> Loading..... <img src="/img/loading32.gif"/></center>

</Apex:facet>

<Apex:facet name="stop" >

<apex:pageBlock > 

<apex:pageBlockSection columns="3" >

<apex:pageBlockSectionItem >

<apex:outputPanel rendered="{!candidateWrapperList != null}">

<center> <b> Data From the first Response </b></center> <br /> <br />

<apex:pageBlockTable value="{!candidateWrapperList}" var="cwl" >

<Apex:column value="{!cwl.Name}" headervalue="Name"/>

<Apex:column value="{!cwl.Country}" headervalue="Country"/>

</apex:pageBlockTable>

</apex:outputPanel>

</apex:pageBlockSectionItem>



<apex:pageBlockSectionItem >

<apex:outputPanel rendered="{!customerWrapperList != null}">

<center> <b> Data From the Second Response</b></center> <br /> <br />

<apex:pageBlockTable value="{!customerWrapperList}" var="cwl" >

<Apex:column value="{!cwl.Name}" headervalue="Name"/>

<Apex:column value="{!cwl.City}" headervalue="City"/>

<Apex:column value="{!cwl.Country}" headervalue="Country"/>

</apex:pageBlockTable>

</apex:outputPanel>

</apex:pageBlockSectionItem>



<apex:pageBlockSectionItem >

<apex:outputPanel rendered="{!studentWrapperList != null}">

<center><b> Data From the Third Response </b></center> <br /> <br />

<apex:pageBlockTable value="{!studentWrapperList}" var="cwl" >

<Apex:column value="{!cwl.Name}" headervalue="Name"/>

<Apex:column value="{!cwl.AccNumber}" headervalue="Account Number"/>

<Apex:column value="{!cwl.Source}" headervalue="Source"/>

<Apex:column value="{!cwl.Site}" headervalue="Site"/>

<Apex:column value="{!cwl.Email}" headervalue="Email"/>

</apex:pageBlockTable>

</apex:outputPanel>

</apex:pageBlockSectionItem> 



</apex:pageBlockSection> 

</apex:pageBlock> 

</Apex:facet>

</apex:actionStatus>

</apex:form>

</apex:page>

 

In the above example, the callout is made to the below three endpoints, and we need to add the remote site settings for the endpoint (https://raw.githubusercontent.com).

 

EndPoint 1:

https://raw.githubusercontent.com/parthiban019/samplejson/master/candidateplaces.json

 

EndPoint 2:

https://raw.githubusercontent.com/parthiban019/samplejson/master/customer.json

 

EndPoint 3:

https://raw.githubusercontent.com/parthiban019/samplejson/master/student.json

 

Data from the Response:

salesforce apex callout

 

Limits:

Continuation Controllers State Limit:

There is a limit for using the Continuation in Salesforce, and it is 81,920 bytes, and also the size the data which is processed through Continuous server must be less than the limit.

Limit consumed in the above example:

Continuation Controllers State: 437 out of 81920 bytes used (0.01%), count

Callout Limit:

You can make up to three asynchronous callouts in a single continuation, and each callout request should be added to the same continuation by using the addHttpRequest. The callouts run in parallel for this continuation and suspend the Visualforce request.

Only after all callouts responses are returned by the external service, the Visualforce process resumes.

Asynchronous calls are supported only through the Visualforce Page and you can’t make Asynchronous call by invoking actions from outside of the Visualforce page.

For Example: Invoking an Asynchronous call from Salesforce Developer Console is not supported.

In the above example, three callout requests are sent out from Salesforce to the external systems, but because of using continuation, Salesforce counts only one service call towards the callout limit.

Number of callouts: 1 out of 100

Reference Link:

You can learn more about Continuation and Asynchronous Callouts from here