Salesforce External Tool Executor

During development on Salesforce platform, you probably hit more than once Salesforce limit. Might be SQOL limit, DML Statement, CPU time and others. In fact, before development, you should analyze which Salesforce limits you might hit and how to overcome this.
The most common solution is running in asynchronous mode where the limits are wider. This can provide answer to most cases, but still the limits are there.

Other common approach that some were taken is to execute your process externally from Salesforce, Usually with webservices written in other language. It does require writing a code + integration with Salesforce, but still it is good solution as your business logic kept inside the webservices and you will have less effort making it work with other system in future, if needed.

Here I tried different approach for the issue. I added several objects inside Salesforce that can be used to describe the business process. Such setup can be done by Salesforce admin. Then I used external app (Java in this case) that connect into the org, read the process setup and translate it to executable code.
Since everything run externally and not in Salesforce server, it is not bound to Salesforce limits (except the limits of API callout), and it have the advantage that no code is needed.

The app contains 2 main components:
1.Tools for setup in Salesforce the business logic declarative.
2.External tool that run externally in any server. At every run it will find the business logic that was setup in Salesforce and run it.

I described the tool with details example in this document.

The app can be installed from this link:
https://login.salesforce.com/packaging/installPackage.apexp?p0=04t1r000001eYnY

Encryption/Decryption in Salesforce

When developing integration between Salesforce and other application we sometimes face issue regarding storing sensitive information like password and security token.

Salesforce encrypted fields wasn't able to provide good enough solution, as users with permission View Encrypted Data can view it, in addition during summer 17' upgrade Salesforce update the encryption field functionality and those values are no longer masked or encrypted when viewed in Salesforce.
However, Salesforce does provide Ecnrypto class that provide some method for encrypt/decrypt. Still, you might face new challenge when you need to use the encryped data in other application (in my case Java).

In this example I demonstrate how we can use such encryption process. It contains the following steps:
1. Page for entering encrypted data.
2. Encryption setup in Salesforce.
3. Utility process that get text and encrypt it based on the setup from 1.
4. Trigger for the specific object that encrypt the data
5. Process for replacing the encrypted data.
6. Java util class that decode the data.

Lets start working:
1. Page for entering encrypted data
This is not required, but without this step, when user will enter his security information it will be visible during typing. It's preferred to mask it, and it isn't complex to solved it.
The component apex:inputSecret does the masking for us.

Assume we have custom object User_Credential__c, with the fields User_Name__c, Password__c, Security_Token__c, then we can create use the following visualforce page.


<apex:page standardController="User_Credential__c" tabStyle="User_Credential__c">  
 <apex:form >  
 
  <apex:sectionHeader title="Enter Credentials" subtitle="Enter Credentials"/>  

  <apex:pageBlock>  

   <apex:pageBlockButtons >  
    <apex:commandButton action="{!save}" value="Save"/>  
    <apex:commandButton action="{!cancel}" value="Cancel"/>  
   </apex:pageBlockButtons>  
  
   <apex:pageBlockSection columns="2">  
    <apex:inputField value="{!User_Credential__c.Name}"/>  
    <apex:inputField value="{!User_Credential__c.User_Name__c}"/>  
    <apex:inputSecret value="{!User_Credential__c.Password__c}"/>  
    <apex:inputSecret value="{!User_Credential__c.Security_Token__c}"/>  
   </apex:pageBlockSection>  
  </apex:pageBlock>  
 </apex:form>  
</apex:page>  




2. Encryption setup in Salesforce
We don't want to use hard coded values. Therefore we need to setup few settings + place holder for our encryption key. I use custom setting. Named it "Encryption Settings", and added 3 fields there:
After the setup create record in this custom settings. The Encryption Method that we will use is AES128, the Encryption Self Len we will use is 10. This is just dummy text that will added to the text before encryption in order to defend against hacking.

How to get your Encryption Key? You can either wait to step 4, where we will add step that generate such key, or use script that generate key and store it in the custom settings:

Blob key = Crypto.generateAesKey(128);
String keyString = EncodingUtil.base64Encode(key);
System.debug('Encryption Key: ' + keyString);

3. Utility process that get text and encrypt it based on the setup from 1
We need function that receive input String and encode it based on the custom setting. Below simply code does the job. Most of logic is based on standard functions from classes EncodingUtil and Crypto.



public class EncryptionUtilities{  

 public static String hashString(String input){  

  String encryptKey = Encryption_Settings__c.getInstance().Encryption_Key__c;  
  String encryptMethod = Encryption_Settings__c.getInstance().Encryption_Method__c;  
  Integer saltLen = Integer.valueOf(Encryption_Settings__c.getInstance().Encryption_Salt_Len__c);  
  String salt = EncodingUtil.convertToHex(Crypto.generateAesKey(128)).substring(0, saltLen);  
 
  Blob encrypted = Crypto.encryptWithManagedIV(encryptMethod, EncodingUtil.base64Decode(encryptKey), Blob.valueOf(input + salt));  

  return EncodingUtil.base64Encode(encrypted);  
 }  
}  


4.Trigger for the specific object that encrypt the data
In the custom object User_Credential__c we will setup trigger that when new record created or the password/token were changed it should encrypt those values.

The trigger will use the function from step 2


trigger UserCredential_Trigger on User_Credential__c (before insert, before update) {  

 if(Trigger.isInsert || Trigger.isUpdate){  
  for(User_Credential__c userCredential: Trigger.new){  

   if((userCredential.Password__c != null)  
    && (Trigger.isInsert || (Trigger.isUpdate && userCredential.Password__c != Trigger.oldMap.get(userCredential.Id).Password__c))){  

    userCredential.Password__c = EncryptionUtilities.hashString(userCredential.Password__c);  
   }  

   if((userCredential.Security_Token__c != null)  
    && (Trigger.isInsert || (Trigger.isUpdate && userCredential.Security_Token__c != Trigger.oldMap.get(userCredential.Id).Security_Token__c))){  

    userCredential.Security_Token__c = EncryptionUtilities.hashString(userCredential.Security_Token__c);  
   }  
  }  
 }  
}  




5. Process for replacing the encrypted data
It might be good idea to provide process for replacing the encryption key, for cases it was stolen. In such process, we need to change the Encryption Key in the custom setting and replace all existing values that encrypted with the old key, as we won't be able to decode them with the new key. Also note that from the code, you can learn how to decode the encryption data in using Salesforce standard Encrypto class.

This function can be added to the class that used in step 2 - EncryptionUtilities - and you might want to provide page/button that invoke it. 


//Used to replace the Encryption Key in custom setting, and recreate  
//all the encryptions in existing records  
public static String replaceEncriptionKey(){  

 String retMsg = '';  

 Savepoint sp = Database.setSavepoint();  

 try{  
  //Get current setting from custom metadata  
  Encryption_Settings__c encryptionSettings = [SELECT Id, Encryption_Key__c FROM Encryption_Settings__c LIMIT 1];  
  String currentEncriptionKey = encryptionSettings.Encryption_Key__c;  

  //Generate new encryption key  
  Blob key = Crypto.generateAesKey(128);  
  String keyString = EncodingUtil.base64Encode(key);  

  //save new encryption key to custom metadata - use apex Metadata API  
  encryptionSettings.Encryption_Key__c = keyString;  

  update encryptionSettings;  

  //Replace existing encrypted values  
  String encryptMethod = Encryption_Settings__c.getInstance().Encryption_Method__c;  
  Integer saltLen = Integer.valueOf(Encryption_Settings__c.getInstance().Encryption_Salt_Len__c);  

  List<User_Credential__c> l_userCredentials = new List<User_Credential__c>();  

  for(User_Credential__c userCredential : [SELECT Id, Password__c, Security_Token__c FROM User_Credential__c]){  

   Blob blobAfter64DecodePass = EncodingUtil.base64Decode(userCredential.Password__c);  
   Blob blobAfterDecodePass = Crypto.decryptWithManagedIV(encryptMethod, EncodingUtil.base64Decode(currentEncriptionKey), blobAfter64DecodePass);  
   String originalPass= blobAfterDecodePass.toString();  
   userCredential.Password__c = originalPass.substring(0, originalPass.length() - saltLen);  

   Blob blobAfter64DecodeToken = EncodingUtil.base64Decode(userCredential.Security_Token__c );  
   Blob blobAfterDecodeToken = Crypto.decryptWithManagedIV(encryptMethod, EncodingUtil.base64Decode(currentEncriptionKey), blobAfter64DecodeToken);  
   String originalToken = blobAfterDecodeToken.toString();  
   userCredential.Security_Token__c = originalToken.substring(0, originalToken.length() - saltLen);  

   l_userCredentials.add(userCredential);  
  }  

  //save all records - this should invoke the trigger that will use the new custom setting  
  update l_userCredentials;  
 }  
 catch(Exception ex){  
  Database.rollback(sp);  

  retMsg = 'Excpetion : ' + ex.getMessage();  
 }  
 return retMsg;  
}  


6. Java util class that decode the data.
The main method  - getDescryptedAsString  - get String encrypted and the key for decrypt and return the original text.


import javax.crypto.Cipher;  
import javax.crypto.spec.IvParameterSpec;  
import javax.crypto.spec.SecretKeySpec;  
import java.util.Arrays;  
import java.util.Base64;  

public class ExternalDecoder {  

 private static final String characterEncoding = "UTF-8";  
 private static final String cipherTransformation = "AES/CBC/PKCS5Padding";  
 private static final String aesEncryptionAlgorithm = "AES";  
   
 public static String getDecryptedAsString(String encryptedText, String key) throws Exception{  
  return new String(decryptBase64EncodedWithManagedIV(encryptedText, key), characterEncoding);  
 }  
     
 public static byte[] decryptBase64EncodedWithManagedIV(String encryptedText, String key) throws Exception {  
  byte[] cipherText = Base64.getDecoder().decode(encryptedText.getBytes());  
  byte[] keyBytes = Base64.getDecoder().decode(key.getBytes());  
  return decryptWithManagedIV(cipherText, keyBytes);  
 }  
   
 public static byte[] decryptWithManagedIV(byte[] cipherText, byte[] key) throws Exception{  
  byte[] initialVector = Arrays.copyOfRange(cipherText,0,16);  
  byte[] trimmedCipherText = Arrays.copyOfRange(cipherText,16,cipherText.length);  
  return decrypt(trimmedCipherText, key, initialVector);  
 }  
   
 public static byte[] decrypt(byte[] cipherText, byte[] key, byte[] initialVector) throws Exception{  
  Cipher cipher = Cipher.getInstance(cipherTransformation);  
  SecretKeySpec secretKeySpecy = new SecretKeySpec(key, aesEncryptionAlgorithm);  
  IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);  
  cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy, ivParameterSpec);  
  cipherText = cipher.doFinal(cipherText);  
  return cipherText;  
 }  
}  


Usage of the code:

String encryptPassword = <get value from SF>;
String encryptionKey =<get value from SF or store it in some setup file>;

String decodeValue = ExternalDecoder.getDecryptedAsString(encryptPassword, encryptionKey);
String password = decodeValue.substring(0, decodeValue.length()-10);













Salesforce Deployment with Java

Perhaps the most familiar deployment tool for SF admins/developers is the change set.
However for many of them, this is only method known for deployment. Some knows there are additional "advance" option (ant, eclipse, IDE, workbench, metadata API) but most are not really know how to use them.
In many cases, after getting familiar with other methods, admins/developers find it much more comfortable.

In some projects, where I was the only developer, I found it useful to deploy any changes directly from my local file system. After implementing the changes in my Developer sandbox, I import all the components into my local file system using IDE, then using java tool to zip the relevant changes and deploy the changes to QA sandbox. I added some simple options like saving the deployed ZIP file for deploying it later to other environments, or running validation only.
I'm adding here the code mostly because good way to learn how to use SF metadata api.

The usage of the tool is quite simple. First time you need to setup the property file in the app folder or you can setup the properties from the app menu Options -> Option.

ZIP_FILES_FOLDER - the folder where you want to store the zip files
TARGET_URL - should be either login/test (login for production)
PACAKGE_API - package API you want to user during deployment
LOCAL_FOLDER - folder where your local SF files are stored. This must be valid src folder, and the folder inside must match the folder names according to SF API. Note, that if you generated the folder using retrieve or by other tools that SF support (like Eclipse IDE) then the folder will be valid.
SF_USER - SF user name
DEPLOT_METHOD - should be either validate/deploy

After opening the app, click button 'Add Files' to select the components you want to deploy.

  • Click clear to remove the files you added
  • Click create package to save the ZIP file with the components you added
  • Click Load ZIP in case you want to use ZIP file that saved previously
  • Click Deploy ZIP to start deployment (you will need to enter your SF credential.











Technical side (code)
Of course I'm note going to go thru all the code, I'll just explain the main and important points. The rest you can review by yourself. Note, that you should have some background in Java or DotNet in order to understand it.
See full code + jar file for download at:
https://github.com/liron50/sf-local-deployment-tool

1. Main classes: I have the main class LocalDeploymentTool. This contains all the GUI - buttons, messages, menus etc... In this case, I added in this class also the actions for the buttons, although it can be good idea to separate them to different classes (view and controller).
However, you can see that each of the button have dedicated Action, and for most of the action, when it's require more than few lines of code, I moved the logic into class - DeploymentUtilities. In addition, constant and some other setup values are located in class - DeploymentParams.

2. Note, in the class DeploymentParams, there is setup regarding the different SF component types.
See map folder_SFType_Map. It hold per each type it SF API name and boolean indicating if this type contains meta-xml file in addition to the main file.
Also note that it doesn't contains all components types in SF. Other types, can be added, but there are some types in SF that require special logic.

3. When working with ZIP I'm using the ZipArchive library. For some cases I notice that when using library from java.util.zip I might get error during deployment.

4. I'm using 2 additional classes. First is InputDialog. It's actually stand alone class that can be used anywhere to get several user inputs. It is in used when I need to deploy and should setup the username/password/some other parameters.
Second class SyncWorker. It's only used to run the deploy process in background. Otherwise the app GUI will be locked during deployment.

5. The app uses several external jar files. 2 of them are generated from SF wsdl (partner.jar, metadata.jar), if you need to create them please refer to SF documentation.

6. The deployment logic is based on SF example. See documentation + example:
https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_deploy.htm
















Simple Text Comparison Tool

I know there are many such tool for such process, many of them online. My favorite site is DiffNow. Main issue that the site limits the number of comparison you are allowed to use per day. Other site/tool have some logic issues. I think if you will try to develop such tool, you might discover that it cannot be perfect. You will always have the special case that your logic doesn't parse perfectly.
I came to such need, and indeed find the difficulties in such process.
Main target of this article is to assist you with the design.
Finally you can test it with this link.

Design
At first you probably thinking lets start with the basic - loop over the lines per their numbers, if the line from the first file (file1) not exists in the second file (file2) then the line was removed, if it in the other position the line was added, if lines are different it was modified.

However, next you came to some other issues that need more than a the basic design:
Issue: If a new line was added at start, then it re-positioning all the other lines, and then you cannot compare the rest of the files line by line (otherwise it will find that all lines were modified).
Solution: Identify those cases in advance that lines removed/added and add "empty line" in the relevant file, in order to keep the lines in both files align as possible.

Issue: In many cases there are only differences in indentation (space, tab, etc) due to different editors. How your tool should handle those cases?
Solution: I decided that during comparing each 2 line I will check if lines are equals using trim function, in such case need to mark those line in special color.

Issue: In case line was modified, am I simply going to highlight this line or should I highlight the specific change (word(s)) ?
Solution/Decision: In my solution in case 2 lines found different, the program calculate number of different words. In case only 1 word is the different it mark only this words, but in other cases it mark the whole lines.

Screen
User will enter 2 text and press compare button. This will run the compare logic and show in output the 2 files that in each there are some marks for the changed lines. I decided to mark in red line that was exists at file1 but not exists in file2 (line was removed), green is the opposite (line was added), cyan indicate line that was changed, and grey will indicate "dummy" line that there just to keep the alignment between the 2 files.

Few Addition Points:
-Should handle the scroll functionality:
Add navigation buttons next to each file (I used "<<" and ">>"). To support this need to setup the Id attribute per each changed section in some specific format, later you can use JS code to navigate to each change according to the format you decided.
When user scroll in 1 file, the page should automatically scroll the other file, this way the user always see the same section in both files.

-Add lines numbers as user will probably want to know the lines number that was changed.
This part, obviously, need to be done after the compare logic, as you don't want that the lines number will be part of the compare.

-Summary details: it is a good idea to show some summary information (e.g. total lines that was removed, total added, etc), this means that during the compare logic need to count the changes.


Simple Compare Tool.



Automatically Refresh Lightning Record Page

Salesforce implementers/developers often encounter a case where several processes, some of them background processes, update the data that t...