Wednesday, February 15, 2012

Forcing a WP7 App to update from the marketplace

The marketplace can take a few days to prompt that an update for an App is available. If you know you don't have the latest version you can follow these steps to get it:

  1. Go to the Marketplace on the phone.
  2. Search for the app that needs the update.
  3. Open up the apps marketplace listing to view the details. You should be able to see that the current version is greater than the version you have installed.
  4. Press the Back button on your phone once to take you back to the search results.
  5. Select the app icon again and you should see that the update is available and ready to install

It occurred to me that it might be useful to provide a direct link from an applications about page to the marketplace listing using MarketplaceDetailTask.

Friday, February 10, 2012

Salesforce LimitException: Apex heap size too large

Monitoring the Heap Size

From the log files you can monitor heap memory allocation:

09:43:02:056 HEAP_ALLOCATE [62]|Bytes:5

Or get the Maximum heap size from the LIMIT_USAGE_FOR_NS log entry.

LIMIT_USAGE_FOR_NS from a sandbox:

12:18:50.117|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 5 out of 100
  Number of query rows: 9 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 1 out of 150
  Number of DML rows: 1 out of 10000
  Number of script statements: 212 out of 200000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 3 out of 10
  Number of Email Invocations: 0 out of 10
  Number of fields describes: 0 out of 100
  Number of record type describes: 0 out of 100
  Number of child relationships describes: 0 out of 100
  Number of picklist describes: 0 out of 100
  Number of future calls: 0 out of 10

LIMIT_USAGE_FOR_NS from production:

11:36:46.701|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 22 out of 100
  Number of query rows: 68 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 0 out of 150
  Number of DML rows: 0 out of 10000
  Number of script statements: 543 out of 200000
  Maximum heap size: 5744310 out of 6000000 ******* CLOSE TO LIMIT
  Number of callouts: 3 out of 10
  Number of Email Invocations: 0 out of 10
  Number of fields describes: 0 out of 100
  Number of record type describes: 0 out of 100
  Number of child relationships describes: 0 out of 100
  Number of picklist describes: 0 out of 100
  Number of future calls: 0 out of 10

Note how the the sandbox doesn't seem to be tracking the heap size.

Use Limits to check the values in Apex:

System.debug(LoggingLevel.Debug, 'Heap Size: ' + Limits.getHeapSize() + '/' + Limits.getLimitHeapSize());
09:43:04:672 USER_DEBUG [155]|DEBUG| Heap Size: 8877586/6000000

Note how the actual Heap Size reported here is greater than the Heap Limit size! This seems to be an oddity of the sandbox environment as in production a LimitException would occur.

Use SOQL For Loops rather than standard SQOL queries

From the documentation on SOQL For Loops:

Developers should always use a SOQL for loop to process query results that return many records, to avoid the limit on heap size.

So rather than

List accs = [SELECT Id, Name FROM Account];  
for(Account a : accs){
   System.debug(a.Name);
}

You would have:

for(Account a : [SELECT Id, Name FROM Account]){
   System.debug(a.Name);
}

Use a Batch Apex or a future method

I recently had a task to upload the binary/Blob for every attachment on an opportunity to a web service as a base64 encoded string. Even after switching to a SOQL for loop it was possible to reach the standard heap size of 6000000 bytes. The attachments could be up to 5 MB in size, so just by loading the attachment Blob out of the database for one record there wasn't much space left over to base64 encode it using EncodingUtil.base64Encode.

Switching to Batch Apex increased the heap limit to 12000000 bytes. Also, by setting the scope to 1 only one attachment was processed at a time.

Tuesday, February 7, 2012

Nokia Online Marketing Tool for generating WP7 Banner Ads

Nokia have released a Online Marketing Tool (OMT) for generating banner ads directly from a WP7 Marketplace listing.
It will generate a range of dimensions and includes an option where they host the images for you.

Saturday, January 28, 2012

Converting UTC DateTime to local timezone in WP7

I'm currently using the following to convert a DataTime know to be in NZST to NZDT when applicable. I.e. The code will automatically determine if daylight savings should be applied.

    public static DateTime DateTimeFromNzst(DateTime nzstDateTime)
    {
        // Known offset from UTC
        TimeSpan nzstOffset = new TimeSpan(12, 0, 0);
        DateTimeOffset dateTimeOffset = new DateTimeOffset(nzstDateTime, nzstOffset);
        DateTimeOffset dateTimeOffsetConvertedToLocal = TimeZoneInfo.ConvertTime(dateTimeOffset, TimeZoneInfo.Local);
        return dateTimeOffsetConvertedToLocal.DateTime;
    }

It should also be possible to convert from UTC to the local timezone by adjusting the known UTC offset to 0.

    public static DateTime DateTimeFromUtc(DateTime utcDateTime)
    {
        // Known offset from UTC
        TimeSpan utcOffset = new TimeSpan();
        DateTimeOffset dateTimeOffset = new DateTimeOffset(utcDateTime, utcOffset);
        DateTimeOffset dateTimeOffsetConvertedToLocal = TimeZoneInfo.ConvertTime(dateTimeOffset, TimeZoneInfo.Local);
        return dateTimeOffsetConvertedToLocal.DateTime;
    }

Friday, January 13, 2012

Salesforce Inbound Change Sets Deploy Error - organization's administration setup lock

During the validation and deployment of an inbound change set into a Salesforce production organisation I often run into the following issue.

I'll start either the validation or deployment action, wait awhile for the spin wheel to complete and then get a failed result.

The failure reason will be given as:

API Name: Deploy Error
Problem: The changes you requested require salesforce.com to temporarily lock your organization's administration setup. However, the administration setup has already been locked by another change. Please wait for the previous action to finish, then try again later.

If I wait for a period of time without any further action another deployment history will appear with the actual results of the action I started. It's like the deployment action splits off at some point and shows as both a failure due to the org admin setup lock and then later a success when it actually completes.

Wednesday, December 14, 2011

Safe URLs for use with Salesforce Custom Buttons/Links and PageReferences

I encountered an issue today with an inherited Salesforce App and references to custom Visualforce pages. As they were, the references would work fine in the development sandbox or when deployed via an unmanaged package. However, when they were deployed via a managed package with the associated namespace the links from the custom buttons would break.

The custom button would have a link to the Visualforce page like:

/apex/TheVisualforcePage?param1={!Opportunity.Id}

The issue is the missing namespace prefix from the Visualforce page when used in a managed package.

To resolve this the Visualforce page reference should be done using URLFOR. Ideally with some form of explicit reference to the page like this:

{!URLFOR($Page.TheVisualforcePage,'',[param1 = opportunity.id])}

However, the $Page Global Variable isn't an available function and results in an error. To help resolve this please go to IdeaExchange: Custom Button: Expose $Page in the URL Content Type and promote the idea.

The same issue occurs within the Apex code with PageReference. There were a number of instances like:

Pagereference ref = new Pagereference('/apex/TheVisualforcePage?param1=' + opportunityId);

The preferred method was to use explicit references to the page.

PageReference p = Page.TheVisualforcePage;
p.getParameters().put('param1', opportunity.id);

See Also:

Friday, December 2, 2011

Salesforce INSUFFICIENT_ACCESS: Upsert requires view all data on a non-unique custom index

INSUFFICIENT_ACCESS: Upsert requires view all data on a non-unique custom index

I recently encountered this error when trying to do an upsert through the Partner API using a particular users session. For other users it works fine.

Here is the same message with the important part emphasised: "INSUFFICIENT_ACCESS: Upsert requires view all data on a non-unique custom index". This also ties back to the Partner API upsert documentation:

If you are upserting an object that has the External ID attribute selected but not the Unique attribute selected, (a non-unique index) your client application must have the permission "View All Data" to execute this call. Having this permission prevents the client application from using upsert to insert an accidental duplicate record because it couldn’t see that the record existed.

The "View All Data" permission is set under the Administrative Permissions section of a profile.

As per its section (Administrative Permissions) and typically only being assigned to the System Administrator profile it is a fairly powerful permission to have. From the help doc:

Permission Name Description Functions Controlled Profiles
View All Data View all organizational data, regardless of sharing settings. View all data owned by other users System Administrator