Error hiding

Exception/error handling is a very common process. In fact, it is one of the most prominent topics when learning a new programming language. Exception handling helps programmers to manage adverse conditions and ensures that the software/application can recover from any expected or unexpected failures. Often, programmers ignore the importance of exception handling, which results in: 

  • Erroneous screens/messages being visible to end users
  • Catastrophic program failures leaving programs/data in an unstable state
  • Incorrect/improper error messages being displayed to users

The first step in exception handling is to ensure that all the probable areas should be equipped with an appropriate exception handling logic. As commonly stated "at minimum, all publicly accessible methods should have an appropriate try/catch". It solves the first point mentioned earlier, that is, erroneous screens/messages being visible to end users.

Let's focus on the second and third points now.

In Apex, database transactions are automatically committed; that is, any database transaction will be committed only if there are no unhandled exceptions.

Similar to various other programming languages, Apex has a top-level exception class as well that can handle any type of exception (excluding exceptions related to governor limits).

Now, let's take a look at a very simple example. Let's take a controller method, which saves an account:

public class GenerateAccountController { 
    Account NewAccount {get; set;} 
    //..some awesome variable to be used by class 
 
    public GenerateAccountController(){ 
        NewAccount = new Account(); 
    } 
     
    //..some awesome code here 
 
    public void createAccount(){ 
        insert NewAccount; 
    } 
} 

In the preceding example, it is quite evident that any exception thrown while inserting an account will result in a non-user-friendly error message being displayed to the user, as shown in the following figure:

Error hiding

As per the first point discussed earlier, we should avoid erroneous screens for end users. So, let's wrap the code in the try-catch block, as shown in the following code:

public class GenerateAccountController { 
    public Account NewAccount {get; set;} 
     
    public GenerateAccountController(){ 
        NewAccount = new Account(); 
    } 
     
    public void createAccount(){ 
        try{ 
         insert NewAccount; 
        } 
        catch(Exception ex){ 
             
        } 
    } 
} 

Error hiding

This is a fantastic example of Visualforce's clever error handling and notification process, even though the catch block is empty in code. Here, any validation rule that is attached to a field will result in an error being displayed at the top as well as at the field level automatically (if apex:inputField is used).

Note

If we use apex:inputText instead of apex:inputField, the error with the associated field will still be displayed at the top of the page but not at the field level.

To display error messages attached to the page, use the apex:pageMessages component.

It is a good start, but it only handles validation errors appropriately. Let's modify our code even further and try to create a contact as soon as an account is created:

public class GenerateAccountController { 
    public Account NewAccount {get; set;} 
     
    public GenerateAccountController(){ 
        NewAccount = new Account(); 
    } 
     
    public void createAccount(){ 
        try{ 
         insert NewAccount; 
             
            Contact c; 
            //intentionally contact c is not initialized 
            insert c; 
        } 
        catch(Exception ex){ 
             
        } 
    } 
} 

In the preceding code, we will get an error while inserting the contact because we did not initialize the contact object. When the preceding controller is executed, we will notice that:

  • The account is inserted successfully because the exception in our transaction was handled and there were no unhandled exceptions. The data was partially committed because the account was saved without the contact.
  • No error message is displayed to the user because our catch block doesn't display any error message to the user.

This is a classic example of error hiding. We are hiding the errors and not handling them. This anti-pattern signifies improper exception handling processes. Sometimes, error hiding can be unavoidable. For example, in the case of unexpected errors or web service integrations, where there can be myriads of errors and there is no need to handle them separately. In cases where we need to hide an error, we can either show a generic message to end users or log it. But, as a good practice, any known or expected error should be handled appropriately to ensure system stability and user-friendliness.

As we can see in the preceding code snippet, this is the behavior that we definitely do not want. Either accounts and contacts should be saved together or nothing should be saved. So, we modify our code to instruct the Force.com platform to not commit any data unless the entire operation is completed successfully:

public class GenerateAccountController { 
    public Account NewAccount {get; set;} 
     
    public GenerateAccountController(){ 
        NewAccount = new Account(); 
    } 
     
    public void createAccount(){ 
        System.savepoint sp = Database.setSavepoint(); 
        try{ 
         insert NewAccount; 
             
            Contact c; 
            insert c; 
        } 
        catch(Exception ex){ 
            Database.rollback(sp); 
        } 
    } 
} 

Tip

We have to consider the following limitations while using transactions in Apex:

  1. In the case of multiple save points, when we roll back to a save point, all the save points created after this save point will become invalid. Trying to roll back to those save points will cause a runtime error.
  2. Each rollback counts against the DML operation.
  3. If an object is inserted and then rolled back, it will not clear the ID from the object in memory. If you try to reinsert the same object, it will cause a runtime error.

This additional code helps ensure that the entire operation, that is, the insertion of the account and contact, are treated as one transaction. In case of a failure, the entire transaction is rolled back. Only when the entire transaction is successfully executed, Apex auto-commits the entire transaction. Now, when we run this code, we will see that the page refreshes but no account is created. However, we are still not displaying any error message. In the following code, we added the logic to display the error message in the catch block:

public class GenerateAccountController { 
    public Account NewAccount {get; set;} 
    public String ContactLimit {get; set;} 
     
    public GenerateAccountController(){ 
        NewAccount = new Account(); 
    } 
     
    public void createAccount(){ 
        System.savepoint sp = Database.setSavepoint(); 
        try{ 
         //insert NewAccount; 
             
            if(Newaccount.Type == 'Prospect'){ 
                Contact c = new Contact(LastName = NewAccount.Name,  
                                        Phone = NewAccount.Phone,  
                                        Limit__c = Integer.valueOf(ContactLimit), 
                                        Accountid = NewAccount.id); 
                insert c; 
         } 
        } 
        catch(Exception ex){ 
            /* rollback transaction */ 
            Database.rollback(sp); 
             
            /* display error message */ 
            ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR, ex.getMessage()); 
            System.debug(ex); 
            Apexpages.addMessage(msg); 
        } 
    } 
} 

Now, after the execution, the user will see the following message on the page:

Error hiding

The preceding code fulfills all the three issues that we discussed at the start of this topic to demonstrate how to avoid the error hiding anti-pattern.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset