Headed for the Next Milestone

For 2018, I’ve set my sights on becoming a Salesforce Certified Application Architect. (My 2017 goal was Platform Developer II, which I did complete). To reach Application Architect, I need three certifications: Sharing and Visibility Designer, Data Architecture and Management Designer, and App Builder.

Yesterday, I passed the first milestone: Certified Sharing and Visibility Designer, my first architect-tier certification.

Sharing and Visibility Designer

It was a challenging exam and pushed me to study and implement a number of areas of functionality, like Territory Management, with which I hadn’t previously gotten hands-on experience. I’m excited to tackle Data Architecture and Management Designer for the second half of the year.

Talk on Continuous Integration Now on YouTube

My presentation from PhillyForce ‘18, “Continuous Integration with Salesforce DX: Practices and Principles for All”, is now available on YouTube.

This talk draws on several past articles published here:

Some additional resources and examples are available on my GitHub:

  • circleci-sfdx-examples, a compendium of CircleCI/Salesforce DX examples, including a basic project, using the Lightning Testing Service, testing against multiple org shapes, and using PMD static analysis.
  • sfdx-simplesalesforce, demonstrating how to test integrated code written in Python with simple_salesforce via Salesforce DX scratch orgs.
  • septaTrains, my toy Lightning project (ever wanted your SEPTA regional rail commute on your Lightning homepage?) and testing project for different CI and automated testing solutions.
  • DMRNoteAttachmentImporter, a slightly more useful package also building with SFDX on CircleCI.

The Curious Nature of the Salesforce Boolean

The Boolean can be among the simplest data types: it is either true or false, full stop, no complications.

On the Salesforce platform, this could hardly be further than the truth.

Consider the following bug, in a simplified example:

public class BooleanPropertyCheck {
    public Boolean myProperty { get; set; }

    // ... more code here ...
}

@isTest
public class BooleanPropertyCheckTest {
    @isTest
    public static void testTheThing() {
        BooleanPropertyCheck bpc = new BooleanPropertyCheck();

        System.assert(bpc.myProperty);
    }
}

That ought to be fine - we’re asserting a Boolean value, which is what asserts are for. (Although one might reasonably expect the assertion itself to fail here). But in fact we get not an assertion failure or success, but a NullPointerException. This reveals several things, both specific and broad:

  1. The value of myProperty is null when not initialized;
  2. null is not equivalent to false in Apex;
  3. The System.assert() method throws an exception when passed null, but not a failed assertion as such;
  4. Boolean variables in Apex are actually tri-valued: they can be true, false, or null, and they default to null.

Note that it doesn’t matter here whether we define myProperty using property syntax or as a simple Boolean instance variable. The behavior is the same.

It’s the case in other contexts as well that referencing a null Boolean value produces a NullPointerException rather than evaluating to false. (This can be particularly confusing to debug, as we’re conditioned to look for property access when troubleshooting a NullPointerException). These conditionals, for example, produce NullPointerException:

Boolean b = null;

System.debug(b ? 'true' : 'false'); // NullPointerException
if (b) System.debug('b is true!'); // NullPointerException

At a certain level, this is fair enough. We have some weird behaviors to look out for, but there’s nothing all that wrong with tri-valued Booleans (although it’s arguable that the behavior of System.assert(null) in particular is poorly designed). We should never rely on uninitialized values in our classes or local variables anyway, but always explicitly initialize them.

What’s perhaps more confusing, though, is that there are other layers of the Salesforce platform where Booleans are not treated as tri-valued.

Booleans and SOQL

Consider SOQL. The SOQL and SOSL Reference has this to say on filtering on Booleans:

You can use the Boolean values TRUE and FALSE in SOQL queries. To filter on a Boolean field, use the following syntax: WHERE BooleanField = TRUE WHERE BooleanField = FALSE

But we learn elsewhere in the reference another facet:

In a WHERE clause that uses a Boolean field, the Boolean field never has a null value. Instead, null is treated as false. Boolean fields on outer-joined objects are treated as false when no records match the query.

So Booleans in the database and in Apex are tri-valued, but in SOQL are treated as binary-valued. A filter in SOQL against null is treated exactly like one written against false, so the following queries are treated as equivalent:

[SELECT Id FROM Boolean_Object__c WHERE Boolean__c = false]
[SELECT Id FROM Boolean_Object__c WHERE Boolean__c = null]

Given this sort of almost-mismatch between SOQL and Apex, one might suppose that one could see the following happen, if we really drive a wedge into the gap:

Boolean_Object__c b = new Boolean_Object__c();

System.assertEquals(null, b.Boolean__c); // Should pass
b.Boolean__c = null;
System.assertEquals(null, b.Boolean__c); // Should pass

insert b;

Boolean_Object__c c = [SELECT Boolean__c 
                       FROM Boolean_Object__c
                       WHERE Id = :b.Id];

System.assertEquals(b.Boolean__c, c.Boolean__c); // Should fail.

But in fact we don’t, because sObject instances don’t behave like other Apex classes. In fact, assertion 1 fails, because Booleans are initialized not to null but to false in sObject class instances. Further, we’ll get a DMLException at insert b, because null is not treated as a legal value for inserting Boolean (Checkbox) fields. We can’t actually create a situation where there’s a null Boolean in the database.

So Booleans are tri-valued in Apex, but are clamped to true/false around DML statements and SOQL queries, and there are some special-case behaviors to remain aware of. In particular, users of wrapper or shadow Apex classes from which sObject instances are ultimately generated should keep in mind that the Boolean initialization behaviors differ between the two class types.

Consequences: Hierarchy Custom Settings

One area where this curious Boolean behavior has practical consequences is the Hierarchy Custom Setting. Custom Settings, of course, are custom objects, and they can contain checkbox fields. But Hierarchy Custom Settings have a unique feature allowing them to cascade populated field values down the hierarchy (Organization to Profile to User) until overriden by a non-null value at a lower level.

Suppose we have a custom setting Instance_Settings__c, with a single Checkbox field Run__c and some arbitrary set of other fields - say Test__c, a text field.

If we populate these fields at the Organization level (SetupOwnerId = UserInfo.getOrganizationId()), we expect rightly that the values at the Organization level will cascade down to the User level, if they’re not overriden. And for Test__c, our text field, that’s true. If we set that field to "foo" at the Organization level, and null at the User level, sure enough our Instance_Settings__c.getInstance() value will inherit the Organization’s value "foo".

But Booleans work differently, because they’re treated as binary-valued here - they’re never null. So a true value for Run__c will never cascade down to the User or Profile level of our Custom Setting if an instance is populated at that level. The instances at those levels always have false set for that Checkbox field if we don’t explicitly populate true upon creation at that setup level.

Instance_Setting__c s = new Instance_Setting__c();
s.Test__c = 'So say some';
s.Run__c = true;
s.SetupOwnerId = UserInfo.getOrganizationId();
insert s;

Instance_Setting__c instance = Instance_Setting__c.getInstance();

System.assertEquals('So say some', instance.Test__c); // Passes
System.assert(instance.Run__c); // Passes

s = new Instance_Setting__c();

s.SetupOwnerId = UserInfo.getUserId();
s.Test__c = 'So say we all';
insert s;

instance = Instance_Setting__c.getInstance();

System.assertEquals('So say we all', instance.Test__c); // Passes
System.assert(instance.Run__c); // Fails

The Upshot is…

  • Booleans are more complicated that one might expect.
  • Never rely on behavior around uninitialized variables (in any language!)
  • NullPointerException can arise even without a dot-notation object dereference.
  • Booleans can’t be relied upon to cascade in Hierarchy Custom Settings.

In Response to 10,000 GDPR Emails

Over the last week or two, every website in the known universe has sent us all emails about the GDPR and the vast scope of what data they collect and what they do with it. As a tiny response to this trend, I’ve gone the other direction: I’ve removed all analytics from ktema.org.

While I cannot speak for my hosting and infrastructure services (GitHub and Cloudflare), this site collects no information at all, sets no cookies, maintains no logs of any kind, and knows nothing at all about you. Because really, why would it need to?

DML-ish Operations in the Visualforce Controller Constructor

DML is not allowed in a Visualforce page controller’s constructor. This isn’t news.

However, a colleague and I discovered that the behavior with various DML-equivalent (“DML-ish”) code is inconsistent and rather strange.

Here’s a minimal Visualforce page:

<apex:page controller="TestBatchFiringController">
</apex:page>

and its corresponding controller:

public class TestBatchFiringController {
    public TestBatchFiringController() {
        // These statements successfully completed.

        // Results in a valid AsyncApexJob id, but batch does not execute.
        // AsyncApexJob Id is not valid after method executes (but it is in test context!)
        System.debug('My job id is: ' + Database.executeBatch(new TestBatchFiringBatch(), 1));

        // Results in a valid CronTrigger id, but schedulable does not execute.
        // CronTrigger Id is not valid after method executes (but it is in test context!)
        System.debug('My schedulable job id is ' + System.schedule('Test Job', '20 30 8 10 2 ?', new TestSchedulableFiring()));

        // This would fail with a `LimitException` - DML is not allowed.
        // Account a = new Account(Name = 'Testy Test Test');
        // insert a;
        // System.debug('My Account\'s Id is ' + a.Id);

        // Setting a savepoint is DML-equivalent - this also throws a `LimitException`
        // System.Savepoint sp = Database.setSavepoint();
    }
}

Performing DML results in just the LimitException expected, as does setting a save point - a DML-equivalent operation. Neither are allowed in this context.

However, the behavior of invoking Schedulable or Batchable classes from a Visualforce controller is stranger. These are both DML-ish operations, involving serializing and persisting the job data. But while neither actually works, we also don’t get a LimitException.

Both Database.executeBatch() and System.schedule() execute successfully, and each returns an apparently valid Id value for an AsyncApexJob or CronTrigger respectively. These Ids can be output with System.debug() and are visible in the logs if you preview the Visualforce page. The corresponding records, however, don’t exist following the completion of the controller (not even in a failed state), and the asynchronous jobs do not execute at all. In the batch case, neither start() nor execute() is called. It’s as if the asynchronous job was never invoked, or if the entire constructor is wrapped in a savepoint/rollback structure.

This behavior is unique to the constructor being run as the controller of the Visualforce page. In a test context or Anonymous Apex, with the exact same controller being instantiated directly, both asynchronous jobs complete successfully, and the AsyncApexJob and CronTrigger records can be inspected afterwards. Both batchable and schedulable jobs can be successfully invoked from an action method, including when the action method is fired by <apex:page action="{! myAction }"> upon page load.

Now, performing these kinds of operations in a controller constructor is a bad idea (and a security risk) for a wide variety of reasons. It’s not a missing capability. Rather, it seems to just be an interesting edge case of undefined behavior on the Salesforce platform, and something to watch out for in Visualforce-land.