Design Continuation Server in Salesforce

How to implement Continuation Server in Salesforce with the help of Action Function and Continuation Object

Continuation Server using ActionFunction in Salesforce
Continuation Server using Action Function in Salesforce

If you are new to Continuation Object in Salesforce then would suggest to read introduction post first. In last post, we discussed how to implement limitless chaining of Continuation Object using JavaScript remoting. In this post, we would try to achieve same, but instead of using JavaScript remoting, we would be using ActionFunction.

Why do we need chaining of Continuation Object

  • From Apex, we can only chain 3 Continuation Object
  • If we call API synchronously, chances are very high that it will run for more than 5 sec and contributing towards Concurrent Apex limit
  • Getting CPU timelimit error is also high
  • Making synchronous call also means risking scalability of Salesforce instance
  • You don’t want user to wait till call out completed, let them work and update user interface once response is received

Below code is assuming that Salesforce Lightning design system is uploaded as a static resource.

What happens when we try to chain more than 3 Continuation Object call
What happens when we try to chain more than 3 Continuation Object call
Continuation Server in Salesforce - Demo
Continuation Server in Salesforce – Demo

ContinuationDemoController.class

/**
 * @Author		:		Jitendra Zaa
 * @Date		:		31 May 2017
 * @Desc		:		Demo of Continuation Server using Action Function
 * 
 * */
public class ContinuationDemoController {  
    
    public String result {get;set;} 
    private static final String CALLOUT_URL = 
        'https://node-count.herokuapp.com/'; 
    public String apiCount {get;set;}   
    public String callBackHandlerName {get;set;}
    String continuationState = '' ;
    public ContinuationDemoController(){
        result = '';
    } 
    
    public Object startRequest() {   
      return constructCallout(callBackHandlerName,CALLOUT_URL, apiCount);
    } 
      
    public Object callout1Response() {  
        HttpResponse response = Continuation.getResponse(continuationState);  
        result = result + 'API'+ apiCount +' processing completed 
 ' ;  
        return null; 
    }    
      

    private Continuation constructCallout(String callbackMethodName,String endpointURL, String apiCount){
        Continuation chainedContinuation = null;  
        chainedContinuation = new Continuation(60); 
        chainedContinuation.continuationMethod=callbackMethodName; 
        HttpRequest req = new HttpRequest();
        req.setMethod('GET');
        req.setEndpoint(endpointURL+apiCount); 
        continuationState = chainedContinuation.addHttpRequest(req); 
        return chainedContinuation;  
    }
}

ContinuationDemo.page

<apex:page controller="ContinuationDemoController" standardStylesheets="false" sidebar="false" showHeader="false"
 title="Continuation Server Demo">
 <!-- <apex:slds /> -->
 <apex:stylesheet value="{!URLFOR($Resource.SLDS2_3_1, 'assets/styles/salesforce-lightning-design-system.min.css')}" /> 
 <div class="slds slds-box">
 <div class=" slds-m-top_xx-large slds-m-bottom_xx-large slds-m-left_xx-large slds-m-right_xx-large"> 
 <apex:form > 
 Total API calls to make : <input type="text" value="3" placeholder="Total API calls to make, Default 3" id="txtAPICallsToMake" />
 <input value="Start Request" type="button" onclick="startRequest()"/> 
 
 <apex:actionFunction name="submitAPI" action="{!startRequest}" reRender="panel" oncomplete="apiComplete()"> 
 <apex:param name="apiCount" assignTo="{!apiCount}" value="" /> 
 <apex:param name="callBackHandlerName" assignTo="{!callBackHandlerName}" value="" /> 
 </apex:actionFunction> 
 
 <br /> <br />
 
 
 <apex:outputPanel id="panel"> 
 <apex:outputText value="{!result}" escape="false" /> 
 </apex:outputPanel> 
 </apex:form>
 </div> 
 
 <div id="modalWindowMsg" style="height:640px;" class="hide">
 <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-describedby="modal-content" class="slds-modal slds-fade-in-open">
 <div class="slds-modal__container">
 <header class="slds-modal__header"> 
 <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">API Callout status</h2>
 </header>
 <div class="slds-modal__content slds-p-around_medium" id="modal-content" >
 <p>
 <span id="modal-content-status"> Callout is in progress</span>
 </p> 
 <p>
 <div class="demo-only" style="height:3rem;">
 <div role="status" class="slds-spinner slds-spinner_medium">
 <span class="slds-assistive-text">Loading</span>
 <div class="slds-spinner__dot-a"></div>
 <div class="slds-spinner__dot-b"></div>
 </div>
 </div>
 </p> 
 </div>
 <footer class="slds-modal__footer">
 <button class="slds-button slds-button_neutral" onclick="cancelModal()">Cancel</button> 
 </footer>
 </div>
 </section>
 <div id="modalWindowOverlay" class="slds-backdrop slds-backdrop_open"></div>
 </div> 

 </div>
 
 <style>
 .modal{
 visibility: visible !important;
 opacity: 1 !important;
 transition: opacity 0.4s linear;
 }
 .hide{
 display:none;
 }
 #modal-content{
 padding: 1rem;
 }
 </style>
 
 <script>
 var calloutNum = 1 ;
 var totalCallout = 4;

 function openModal(){
 document.getElementById("modalWindowOverlay").classList.add('modal');
 document.getElementById("modalWindowMsg").classList.remove('hide'); 
 }
 function cancelModal(){ 
 document.getElementById("modalWindowOverlay").classList.remove('modal');
 document.getElementById("modalWindowMsg").classList.add('hide');
 calloutNum = totalCallout+1;
 }
 function startRequest(){ 
 openModal();
 var callnumbers = document.getElementById("txtAPICallsToMake").value;
 if(callnumbers){
 totalCallout = callnumbers;
 }
 
 document.getElementById("modal-content-status").innerHTML = 'Callout request started for API - '+calloutNum; 
 submitAPI(calloutNum,'callout1Response');
 }
 
 function apiComplete(){
 if(calloutNum < totalCallout){
 calloutNum++;
 document.getElementById("modal-content-status").innerHTML = 'Callout request started for API - '+calloutNum; 
 submitAPI(calloutNum,'callout1Response');
 }else{
 document.getElementById("modalWindowOverlay").classList.remove('modal');
 document.getElementById("modalWindowMsg").classList.add('hide'); 
 }
 }
 </script>
</apex:page>

 

Related posts