Appirio's Tech Blog

Wednesday, January 28, 2009

Organization-Specific Variables


If you are a developer that has done any involved programming in Apex, you have probably thought at one point or another that it would be nice to have a spot to store organization-specific global variables.

This would be useful when your Apex code references:
  • A record id, object id, or field id that is different in sandbox and production
  • A URL that needs to be different in sandbox and production
  • A soql query that includes a bound variable that needs to be different in sandbox and production due to differences in the data

You soon realize that trying to keep this information as constants in an Apex class creates a problem since you can't make changes to most Apex code in production; so you'd be stuck with the sandbox org values in production.  Sure, you could store all this information as records in a custom object, but that requires one or more SOQL calls to retrieve the information, which you are desparately trying to avoid in order to stay on the Governor's good side.  In other programming languages like Java, you might store this type of information in environment variables and/or properties files.

One solution for this conundrum that I use regularly is to create a custom object called CONFIG__c that has one picklist called ORG__c and no records.  Add a single picklist value in ORG__c that represents the current org in which the object is deployed.  For example, in my sandbox, I name the one picklist value "sandbox" and when I deploy the object to production, I edit the single picklist value from "sandbox" to "production".

Next create an Apex class with a method that will inspect the metadata of CONFIG__c.ORG__c to determine what the current org is:

public class Constants {   public static final String ORG_SANDBOX = 'sandbox';   public static final String ORG_PRODUCTION = 'production';   public static final String CURRENT_ORG = getOrg();   public static String getOrg(){     Schema.DescribeFieldResult result = CONFIG__c.ORG__c.getDescribe();     List picklistValues = result.getPicklistValues();     if (picklistValues == null || picklistValues.size() == 0)       return '';     else       return picklistValues[0].getValue();   } }

The variable CURRENT_ORG can then be used to conditionally set all org-dependent constants in your Constants class in the following manner:

  public static final String BUYER_PROFILE_ID = getBuyerProfileId();   private static String getBuyerProfileId() {     if (CURRENT_ORG == ORG_SANDBOX)       return '00eR0000000IAYr';     else //if (CURRENT_ORG == ORG_PRODUCTION)       return '00e50000000ilvj';   }    public static final String BASE_SERVER_URL = getBaseServerURL();   private static String getBaseServerURL() {     if (CURRENT_ORG == ORG_SANDBOX)       return 'https://cs1.salesforce.com';     else //if (CURRENT_ORG == ORG_PRODUCTION)       return 'https://na3.salesforce.com';   } 

Note: This solution will use one of your field describe calls, but these are typically not in as high demand as SOQL calls.

Sunday, January 25, 2009

We've Updated to the New Blogger...

We've just moved this blog to the latest and greatest version of Blogger to take advantage of some of the additional capabilities that it offers. The look will remain largely the same, but over time we will be adding features/functionality that will enhance the experience for our readers. No action needed on your part - we've made sure that the RSS feeds and redirects are taken care of.

So please continue to leave comments, pass on stuff you find interesting, and be part of the dialogue.

Saturday, January 10, 2009

A Work-Around for the SFDC Auto Number Dilemma

Joe Krutulis

Some of the trickiest Force.com deployment problems we've faced at Appirio have involved custom fields of type Auto Number. Values in these fields cannot be updated without first temporarily changing their type to Text -- but if the field is referenced in any Apex code, then the type cannot be changed at all!

Thankfully, dynamic SOQL and the get() and put() methods have made this pain a thing of the past.

To illustrate the technique, create a custom object AutoNumberTest__c with which to experiment. The object should have a single custom field called "autonumber__c" of type Auto Number.

After creating the object, you can experiment to prove that the type of the Autonumber__c field can be freely changed from Auto Number to Text and back to Auto Number again, allowing for necessary updates as part of conversion or recoveries.

As a side note, this does not apply to the standard Name field set as auto number. Name fields are treated differently than custom fields, and the auto number attribute of the name field can be changed without running afoul of the Apex compiler's type checking.

Now let's write a trigger after insert or update of an AutoNumberTest__c object that references the autonumber__c column.

The following example code uses the field to create a Note object and attach it to the AutoNumberTest__c object.

 trigger AutoNumberTestMsg
on AutoNumberTest__c (after insert, after update) {

List<Note> newNotes = new List<Note>();

for (AutoNumberTest__c a : Trigger.new ) {

Note msg = new Note(
parentId = a.id,
title = '' + a.autonumber__c,
body = '' + a
);
newNotes.add(msg);
}
insert newNotes;
}

This code iterates through all the new values in the trigger batch, creates a new Note object containing a string representation of the AutoNumberTest__c object, and, after looping through all the objects, inserts the new notes in a single DML statement. (The expression '' + a.autonumber__c returns a string representaion of the value autonumber__c field.)

After creating this trigger, attempting to change the column type from Auto Number causes the following error:

Validation Errors While Saving Record(s)
There were custom validation error(s) encountered
while saving the affected record(s). The first validation
error encountered was "Cannot change field type of a
custom field referenced in Apex class or trigger:
AutoNumberTestMsg".

In this particular example, it would be quite easy to comment out the existing code, change the field type to perform maintenance on data values, and then uncomment the code. It's seldom easy and sometimes nearly impossible when promotion is involved or when the dependent code is more complicated.

The Winter '09 introduction of the Object.get() and Object.put() methods provides a new alternative. We can change the expression a.autonumber__c to g.get('autonumber__c') and escape the compiler!

The new trigger is as follows:

 trigger AutoNumberTestMsg
on AutoNumberTest__c (after insert, after update) {

List<Note> newNotes = new List<Note>();

for (AutoNumberTest__c a : Trigger.new ) {

Note msg = new Note(
parentId = a.id,
title = '' + a.get('autonumber__c'),
body = '' + a
);
newNotes.add(msg);
}
insert newNotes;
}

It is now possible to change the type of the autonumber__c column without generating an error.

Of course, this change exposes us to a new class of run-time errors that otherwise would have been prevented by the compiler. For example, if we now change the API name of the autonumber__c field without also changing the trigger, the code will cause a run-time exception.

In more realistic scenarios, the changes required are usually more complicated and involve more significant risks of run-time problems.

For example, if we use a SOQL expression such as:

List<AutoNumberTest__c> aList =
[select id, autonumber__c from AutoNumberTest__c];

We'll encounter the same compiler-enforced restriction on changing the type of the autonumber field.

We need to resort to dynamic SOQL and the Database.query() method to avoid the direct compiler reference to the field.

List<AutoNumberTest__c> aList =
Database.query('select id, autonumber__c from AutoNumberTest__c');

Switching to dynamic soql queries can be unwieldy and could also be more difficult to maintain. (With the Spring '09 release, we gain the ability to use ':' interpolation in dynamic SOQL, which will make things a little easier.) However, in cases involving the need to temporarily change an Auto Number field to allow for updates, switching to dynamic SOQL will be far, far easier than some of the horrendous contortions that have been required in the past!
 
2006-2010 Appirio Inc. All rights reserved.
Appirio.com | Support | Resource Center | Contact | Careers | Privacy Policy