Day: March 8, 2018

Apex Callouts in Read-Only Mode

let’s understand how to handler the apex callouts during the salesforce read-only mode.During read-only mode, Apex callouts to external services execute and aren’t blocked by the system. Typically, you might execute some follow-up operations in the same transaction after receiving a response from a callout. For example, you might make a DML call to update a Salesforce record. But write operations in Salesforce, such as record updates, are blocked during read-only mode. Instance refreshes result in periods of read-only mode to facilitate infrastructure upgrades. Another is site switches. Continuous site switching enables Salesforce to improve our operations and infrastructure and meet the compliance requirement of many of our customers. Planned instance refreshes and site switches will put your Salesforce org in read-only mode for a portion of your preferred maintenance windows. To check whether the org is in read-only mode, call  System.getApplicationReadWriteMode(). The following example checks the return value of System.getApplicationReadWriteMode(). If the return value is equal to ApplicationReadWriteMode.READ_ONLY enum value, the org is in read-only mode and the callout is skipped. Otherwise (ApplicationReadWriteMode.DEFAULT value), the callout is performed.

To test read-only mode in the sandbox, contact Salesforce to enable the read-only mode test option. Once the test option is enabled, you can toggle read-only mode on and verify your apps. Here is the  code

public class AccountMatchReadOnly {
    
    public class MyReadOnlyException extends Exception {}
    public void getCalloutResponseContents(List<Account> acc) {
        
        // Get Read-only mode status
        ApplicationReadWriteMode mode = System.getApplicationReadWriteMode();
        if (mode == ApplicationReadWriteMode.READ_ONLY) {
            // Prevent the callout
            throw new MyReadOnlyException('Read-only mode. Skipping callouts!');
        } else if (mode == ApplicationReadWriteMode.DEFAULT) {
            // Instantiate a new http object
            for(Account a :acc){
                String addStr = EncodingUtil.urlEncode(a.BillingStreet+','+a.BillingCity+','+a.BillingCountry , 'UTF-8');
                Http h = new Http();
                HttpRequest request = new HttpRequest();
                request.setEndpoint('https://api.addressfinder.io/api/nz/address/cleanse?key=68JQFL39HNBKDUGWTVY4&secret=MHXT7LJBWYNGPF98KRV&q='+addStr+'&format=json');
                request.setMethod('POST');
                request.setHeader('Content-Type','application/json');
                HttpResponse response = h.send(request);
                
                if (response.getStatusCode() == 200) {
                    Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
                    //System.debug('results'+results);
                    Boolean isMatched =(Boolean) results.get('matched') ; 
                    if(isMatched){
                        a.Verification_Status__c = 'Completed' ;
                        // update a ; 
                    }else{
                        a.Verification_Status__c = 'Pending' ;
                        //  update a ;     
                    }
                    
                }
            }
            update acc ;
            
        }
    }
}

 

 

Chatter Private Messages Trigger

Write a trigger for ChatterMessage to automate the moderation of private messages in an organization or community. Use triggers to ensure that messages conform to your company’s messaging policies and don’t contain blacklisted words.Although you can create an after insert trigger, ChatterMessage is not updatable, and consequently, any after insert trigger that modifies ChatterMessage will fail at runtime with an appropriate error message.Here the trigger
trigger ChatterMessageTrigger on ChatterMessage (before insert) {
    ChatterMessage[] messages = Trigger.new;
    MessageModerator moderator = MessageModerator.getInstance();
    for (ChatterMessage currentMessage : messages) {
        moderator.review(currentMessage);   
        
    }
    
}

This example shows a before insert trigger on ChatterMessage that is used to review each new message. This trigger calls a class method, moderator.review(), to review each new message before it is inserted. If a message violates your policy, for example when the message body contains blacklisted words, you can prevent the message from being sent by calling the Apex addError method. You can call addError to add a custom error message on a field or on the entire message. The following snippet shows a portion of the review content method that adds an error to the message Body field.

 

public class MessageModerator {
    private Static List<String> blacklistedWords=null;
    private Static MessageModerator instance=null;
    public void review(ChatterMessage theMessage) {
        reviewContent(theMessage);
        reviewSender(theMessage);
    }
    public void reviewContent(ChatterMessage theMessage) {
        String proposedMsg=theMessage.Body.toLowerCase();  
        boolean problemsFound=false; // Assume it's acceptable
        // Iterate through the blacklist looking for matches
        for (String nextBlackListedWord : blacklistedWords) {
            if (proposedMsg.contains(nextBlackListedWord)) {
                theMessage.Body.addError(
                    'This message does not conform to the acceptable use policy');
                System.debug('moderation flagged message with word: ' 
                             + nextBlackListedWord);
                problemsFound=true;
                break;
            }
        }
        
        // For demo purposes, we're going to add a "seal of approval" to the 
        // message body which is visible.
        if (!problemsFound) {
            theMessage.Body = theMessage.Body + 
                ' *** approved, meets conduct guidelines';
        }
        
    }
    
    public void reviewSender(ChatterMessage theMessage) {
        // Are we in a Community Context?
        boolean isCommunityContext = (theMessage.SendingNetworkId != null);
        
        // Get the User
        User sendingUser = [SELECT Id, Name, UserType, IsPortalEnabled 
                            FROM User where Id = :theMessage.SenderId ];  
        // ...          
    }   
    
    public static MessageModerator getInstance() {
        if (instance==null) {
            instance = new MessageModerator();
        }
        return instance;
    }
    
    
    private MessageModerator() {
        initializeBlackList();
    }
    private void initializeBlackList() {
        if (blacklistedWords==null) {
            blacklistedWords = new List<String> ();
            blacklistedWords.add('heavy lifting') ; 
            blacklistedWords.add('Real-time') ; 
            blacklistedWords.add('UnHealthy') ; 
        }
    }
}

The below image show when you are trying to send the chatter message with the body that contains blacklisted words then it will through an error message.

Salesforce Apex Scheduler

Introduction:-

In this blog post, we are going to see how to use salesforce apex scheduling capabilities which support invoking the Apex class at specific times like daily or weekly or even every minute. To invoke Apex classes to run at specific times, first implement the Schedulable interface for the class, then specify the schedule using either the Schedule Apex page in the Salesforce user interface or the System.schedule method.

Apex Syntax

To schedule an Apex class to run at regular intervals, first, write an Apex class that implements the Salesforce-provided interface Schedulable.The schedulable interface is having the only execute method.

global void execute(SchedulableContext sc){}

execute method will take the SchedulableContext object as an argument which will help to track the scheduled job once it’s scheduled. The SchedulableContext getTriggerID method returns the ID of the CronTrigger object associated with this scheduled job as a string. You can query CronTrigger to track the progress of the scheduled job.To stop the execution of a job that was scheduled, use the System.abortJob method with the ID returned by the getTriggerID method.Here is the sample code

global class scheduledApex implements Schedulable {
    global void execute(SchedulableContext SC) {
        
        DateTime dt = System.now();
        Long timeinNumber = dt.getTime();
        Account a = new Account() ; 
        a.Name ='Account @'+String.valueOf(timeinNumber);
        insert a;
        
    }
    
}

Scheduling Apex

You can schedule the salesforce apex by using System.Schedule method or by using Salesforce UI. The main difference is Salesforce UI won’t support the Schedule job in Seconds and minutes which can be done with System.Schedule.The System.Schedule method uses the user’s timezone on the basis of all schedules, but runs in system mode—all classes are executed, whether or not the user has permission to execute the class.The System.Schedule method takes three arguments: a name for the job, an expression used to represent the time and date the job is scheduled to run, and the name of the class. This expression has the following syntax:

Seconds Minutes Hours Day_of_month Month Day_of_week Optional_year

The following are some examples of how to use the expression.

Expression Description
0 0 13 * * ? Class runs every day at 1 PM.
0 0 22 ? * 6L Class runs the last Friday of every month at 10 PM.
0 0 10 ? * MON-FRI Class runs Monday through Friday at 10 AM.
0 0 20 * * ? 2010 Class runs every day at 8 PM during the year 2010.

Below code shows how to schedule apex by using the system.schedule which will run every day at 1 PM.Run this code from execute anonymous window.

scheduledApex apexSch = new scheduledApex();
String sch = '0 0 13 * * ?';
system.schedule('scheduledApex_1', sch, apexSch);

Scheduling a Job from the UI

 You can also schedule a class using the user interface.

  1. Go to Apex Classes.
  2. Click Schedule Apex 
  3. Click Save.
 

Monitoring 

After the Apex job has been scheduled you can track the schedule details from the Salesforce UI or by running a SOQL query on CronTrigger as shown below.

CronTrigger ct = 
    [SELECT TimesTriggered, NextFireTime
    FROM CronTrigger WHERE Id = :jobID];

The jobID variable holding the ID of the job. The System.schedule method returns the job ID. If you’re performing this query inside the execute method of your schedulable class, you can obtain the ID of the current job by calling getTriggerId on the SchedulableContext argument variable. Assuming this variable name is sc, the modified example becomes:

CronTrigger job = 
    [SELECT Id, CronJobDetail.Id, CronJobDetail.Name, CronJobDetail.JobType 
    FROM CronTrigger ORDER BY CreatedDate DESC LIMIT 1];

 

You can also get the job’s name and the job’s type from the CronJobDetail record associated with the CronTrigger record. To do so, use the CronJobDetail relationship when performing a query on CronTrigger. This example retrieves the most recent CronTrigger record with the job name and type from CronJobDetail.

CronJobDetail ctd = 
    [SELECT Id, Name, JobType 
    FROM CronJobDetail WHERE Id = :job.CronJobDetail.Id];

Test Class

@isTest
private class scheduledApexTest {
    static testmethod void test() {
        Test.startTest();
        Account a = new Account();
        a.Name = 'testscheduledApex';
        insert a;
        String jobId = System.schedule('testscheduledApex',
                                       '0 0 0 3 9 ? 2022' ,
                                       new scheduledApex());
        // Get the information from the CronTrigger API object
        CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, 
                          NextFireTime
                          FROM CronTrigger WHERE id = :jobId];
        
        // Verify the expressions are the same
        System.assertEquals('0 0 0 3 9 ? 2022',ct.CronExpression);
        
        // Verify the job has not run
        System.assertEquals(0, ct.TimesTriggered);
        
        Test.stopTest();
        
    }
    
}

 

Things to Remember

  1. Salesforce schedules the class for execution at the specified time. Actual execution may be delayed based on service availability.
  2. You can only have 100 scheduled Apex jobs at one time
  3. Use extreme care if you’re planning to schedule a class from a trigger. You must be able to guarantee that the trigger won’t add more scheduled classes than the limit.
  4. If there are one or more active scheduled jobs for an Apex class, you cannot update the class or any classes referenced by this class through the Salesforce user interface. However, you can enable deployments to update the class with active scheduled jobs by using the Metadata API (for example, when using the Force.com IDE
  5. Synchronous Web service callouts are not supported from scheduled Apex.
  6. The maximum number of scheduled Apex executions per a 24-hour period is 250,000 or the number of user licenses in your organization multiplied by 200, whichever is greater. This limit is for your entire org and is shared with all asynchronous Apex: Batch Apex, Queueable Apex, scheduled Apex, and future methods.
  7. Apex jobs scheduled to run during a Salesforce service maintenance downtime will be scheduled to run after the service comes back up, when system resources become available. If a scheduled Apex job was running when downtime occurred, the job is rolled back and scheduled again after the service comes back up.

 

 

Asynchronous Callouts from Visualforce Pages with Continuations

In this blog, I am going to explain how to use salesforce  Continuations Asynchronous Callouts from the visualforce page.Continuations  Apex feature that allows you to escape the limit of ten concurrent long-running callouts. Asynchronous Callouts work through a pattern called a Continuation; your controller creates its HTTP request as it normally would, but, instead of firing it off there and then, you create a Continuation object, pass the HTTP request and an Apex callback method to the Continuation, and return it to the platform for processing.Use asynchronous callouts to make long-running requests from a Visualforce page to an external Web service and process responses in callback methods. Asynchronous callouts that are made from a Visualforce page don’t count toward the Apex limit of 10 synchronous requests that last longer than five seconds. As a result, you can make more long-running callouts and you can integrate your Visualforce pages with complex back-end assets.An asynchronous callout is a callout that is made from a Visualforce page for which the response is returned through a callback method. An asynchronous callout is also referred to as a continuation.This diagram shows the execution path of an asynchronous callout, starting from a Visualforce page. A user invokes an action on a Visualforce page that requests information from a Web service (step 1). The app server hands the callout request to the Continuation server before returning to the Visualforce page (steps 2–3). The Continuation server sends the request to the Web service and receives the response (steps 4–7), then hands the response back to the app server (step 8). Finally, the response is returned to the Visualforce page (step 9).

Diagram for the execution flow of a continuation

Using Continuation 

Step1: Action Method

To use asynchronous callouts, create a Continuation object in an action method of a controller, and implement a callback method.To invoke an asynchronous callout, call the external service by using a Continuation instance in your Visualforce action method. When you create a continuation, you can specify a timeout value and the name of the callback method. For example, the following creates a continuation with a 60-second timeout and a callback method name of processResponse.

Continuation cont = new Continuation(60);
cont.continuationMethod = 'processResponse';

Next, associate the Continuation object to an external callout.

String requestLabel = cont.addHttpRequest(request);

The method that invokes the callout (the action method) must return the Continuation object to instruct Visualforce to suspend the current request after the system sends the callout and waits for the callout response. The Continuation object holds the details of the callout to be executed.Here is the complete code to invoke from the action.

 public Object startRequest() {
        // Create continuation with a timeout
        Continuation con = new Continuation(40);
        // Set callback method
        con.continuationMethod='processResponse';
        
        // Create callout request
        HttpRequest req = new HttpRequest();
        req.setMethod('GET');
        req.setEndpoint(LONG_RUNNING_SERVICE_URL);
        
        // Add callout request to continuation
        this.requestLabel = con.addHttpRequest(req);
        
        // Return the continuation
        return con;  
    }

Step 2: Defining a Callback Method

The response is returned after the external service finishes processing the callout. You can specify a callback method for asynchronous execution after the callout returns. This callback method must be defined in the controller class where the callout invocation method is defined. You can define a callback method to process the returned response, such as retrieving the response for display on a Visualforce page as shown below .

 // Callback method 
    public Object processResponse() {   
        // Get the response by using the unique label
        HttpResponse response = Continuation.getResponse(this.requestLabel);
        // Set the result variable that is displayed on the Visualforce page
        //this.result = response.getBody();
        this.jsonParserResult = (List<JSONAlbums>) System.JSON.deserialize(response.getBody(), List<JSONAlbums>.class);
        // Return null to re-render the original Visualforce page
        return null;
    }

 

Example code

In the following example application, the button action is implemented in an Apex controller method. The action method creates a Continuation and returns it. After the request is sent to the service, the Visualforce request is suspended. The user must wait for the response to be returned before proceeding with using the page and invoking new actions. When the external service returns a response, the Visualforce request resumes and the page receives this response.

 

Simple Apex class

public class ContinuationRestCall {
    // Unique label corresponding to the continuation
    public String requestLabel;
    // Result of callout
    public String result {get;set;}
    // Callout endpoint as a named credential URL 
    // or, as shown here, as the long-running service URL
    private static final String LONG_RUNNING_SERVICE_URL = 'https://jsonplaceholder.typicode.com/users/1/albums';
    public List<JSONAlbums> jsonParserResult {get;set;}
    // Action method
    public Object startRequest() {
        // Create continuation with a timeout
        Continuation con = new Continuation(40);
        // Set callback method
        con.continuationMethod='processResponse';
        
        // Create callout request
        HttpRequest req = new HttpRequest();
        req.setMethod('GET');
        req.setEndpoint(LONG_RUNNING_SERVICE_URL);
        
        // Add callout request to continuation
        this.requestLabel = con.addHttpRequest(req);
        
        // Return the continuation
        return con;  
    }
    
    // Callback method 
    public Object processResponse() {   
        // Get the response by using the unique label
        HttpResponse response = Continuation.getResponse(this.requestLabel);
        // Set the result variable that is displayed on the Visualforce page
        //this.result = response.getBody();
        this.jsonParserResult = (List<JSONAlbums>) System.JSON.deserialize(response.getBody(), List<JSONAlbums>.class);
        // Return null to re-render the original Visualforce page
        return null;
    }
    
    public class JSONAlbums {
        public Integer userId{get;set;}
        public Integer id{get;set;}
        public String title{get;set;}
    }
    
    
}

 

Visuaforce page

<apex:page controller="ContinuationRestCall" showChat="false" showHeader="false" >
    
    <apex:form >
        <!-- Invokes the action method when the user clicks this button. -->
        <apex:commandButton action="{!startRequest}" 
                            value="Start Request" reRender="result" /> 
    </apex:form>
    
    <apex:pageBlock id="result"  title="Process for Using Asynchronous Callouts">
        <apex:pageBlockSection  columns="1">
            
            <apex:pageBlockTable value="{!jsonParserResult}" var="res" >
                <apex:column value="{!res.userId}"  headerValue="userId"/>
                <apex:column value="{!res.id}"  headerValue="id"/>
                <apex:column value="{!res.title}"  headerValue="title"/>
            </apex:pageBlockTable>
        </apex:pageBlockSection>        
    </apex:pageBlock>
</apex:page>
@isTest
private class ContinuationRestCall_Test {
    public static testmethod void testWebService() {
        
        ContinuationRestCall controller = new ContinuationRestCall();
        // Invoke the continuation by calling the action method
        Continuation conti = (Continuation)controller.startRequest();
        
        // Verify that the continuation has the proper requests
        Map<String, HttpRequest> requests = conti.getRequests();
        system.assert(requests.size() == 1);
        system.assert(requests.get(controller.requestLabel) != null);
        
        // Perform mock callout 
        // (i.e. skip the callout and call the callback method)
        HttpResponse response = new HttpResponse();
        response.setBody('Mock response body');   
        // Set the fake response for the continuation     
        Test.setContinuationResponse(controller.requestLabel, response);
        // Invoke callback method
        Object result = Test.invokeContinuationMethod(controller, conti);
        // result is the return value of the callback
        System.assertEquals(null, result);
        // Verify that the controller's result variable
        //   is set to the mock response.
        System.assertEquals('Mock response body', controller.result);
    }
    
}

 

Continuation Status Code 

  • 2000: The timeout was reached, and the server didn’t get a chance to respond.
  • 2001: There was a connection failure.
  • 2002: Exceptions occurred.
  • 2003: The response hasn’t arrived (which also means that the Apex asynchronous callout framework hasn’t resumed).
  • 2004: The response size is too large (greater than 1 MB).

Limitation 

  • You can make up to three asynchronous callouts in a single continuation.
  • Asynchronous callouts are supported only through a Visualforce page. Making an asynchronous callout by invoking the action method outside a Visualforce page, such as in the Developer Console, isn’t supported.
  • Asynchronous callouts are available for Apex controllers and Visualforce pages saved in version 30.0 and later. If JavaScript remoting is used, version 31.0 or later is required.
  • This Continuation pattern does not allow you to make DML operations before the web service is invoked.
  • The server has to return within 2 minutes, or there will be a timeout

 

 

Continuation Multiple Asynchronous Callouts

To make multiple callouts to a long-running service simultaneously from a Visualforce page, you can add up to three requests to the Continuation instance. An example of when to make simultaneous callouts is when you’re making independent requests to a service, such as getting inventory statistics for two products.When you’re making multiple callouts in the same continuation, the callout requests run in parallel and suspend the Visualforce request. Only after all callout responses are returned does the Visualforce process resume.The following Visualforce and Apex examples show how to make two asynchronous callouts simultaneously by using a single continuation.
public class MultipleContinutationController {
    
    // Unique label for the first request
    public String requestLabel1;
    // Unique label for the second request
    public String requestLabel2;
    // Result of first callout
    public String result1 {get;set;}
    // Result of second callout
    public String result2 {get;set;}
    // Endpoints of long-running service
    private static final String LONG_RUNNING_SERVICE_URL1 = 'http://inqstatsapi.inqubu.com?api_key=27b3c066fcedd91a&data=population&countries=us';
    private static final String LONG_RUNNING_SERVICE_URL2 = 'http://inqstatsapi.inqubu.com?api_key=27b3c066fcedd91a&data=population&countries=gb';
    public List<JSON2Apex> jsonApexURL1 {get;set;}
    public List<JSON2Apex> jsonApexURL2 {get;set;}
    
    // Action method
    public Object startRequestsInParallel() {
        // Create continuation with a timeout
        Continuation con = new Continuation(60);
        // Set callback method
        con.continuationMethod='processAllResponses';
        
        // Create first callout request
        HttpRequest req1 = new HttpRequest();
        req1.setMethod('GET');
        req1.setEndpoint(LONG_RUNNING_SERVICE_URL1);
        
        // Add first callout request to continuation
        this.requestLabel1 = con.addHttpRequest(req1);     
        
        // Create second callout request
        HttpRequest req2 = new HttpRequest();
        req2.setMethod('GET');
        req2.setEndpoint(LONG_RUNNING_SERVICE_URL2);
        
        // Add second callout request to continuation
        this.requestLabel2 = con.addHttpRequest(req2);     
        
        // Return the continuation
        return con;  
    }
    
    // Callback method.
    // Invoked only when responses of all callouts are returned.
    public Object processAllResponses() {   
        // Get the response of the first request
        HttpResponse response1 = Continuation.getResponse(this.requestLabel1);
        this.result1 = response1.getBody();
        jsonApexURL1 = (List<JSON2Apex>) System.JSON.deserialize(response1.getBody(), List<JSON2Apex>.class);
        // Get the response of the second request
        HttpResponse response2 = Continuation.getResponse(this.requestLabel2);
        this.result2 = response2.getBody();
        jsonApexURL2 = (List<JSON2Apex>) System.JSON.deserialize(response2.getBody(), List<JSON2Apex>.class);
        
        // Return null to re-render the original Visualforce page
        return null;
    }
    
    public class JSON2Apex {
        public String countryCode{get;set;}
        public String countryName{get;set;}
        public List<Population> population{get;set;}
    }
    
    public class Population {
        public String year{get;set;}
        public String data{get;set;}
    }
    
    
    public static List<JSON2Apex> parse(String json) {
        return (List<JSON2Apex>) System.JSON.deserialize(json, List<JSON2Apex>.class);
    }
}
<apex:page controller="MultipleContinutationController" showChat="false" showHeader="false">
    <apex:form >
        <!-- Invokes the action method when the user clicks this button. -->
        <apex:commandButton action="{!startRequestsInParallel}" value="Start Request" reRender="panel"/>  
    </apex:form>
    
    <apex:outputPanel id="panel">
        <apex:pageBlock id="result" >
            <apex:pageBlockSection >
                
                Result 1 - {!jsonApexURL1}
                <br/>
                <br/>
                <br/>
                
                Result 2 - {!jsonApexURL2}
                
            </apex:pageBlockSection>        
            
        </apex:pageBlock>
    </apex:outputPanel> 
    
</apex:page>