Pages

Friday, March 5, 2010

Three Reasons Why You Should Do Away From Force.com Sites!

Working on several large projects in the past to address community needs in the CRM arena, force.com Sites seems to be an attractive and suitable choice which directly integrates with your CRM application. You can open up and share your CRM tabs, objects and data to your target community on the web and it's all fully secure.

The basic customer portal theme seem to be a bit tasteless if you are to use the standard Visualforce components however utilizing them has two benefits, first and the most important is speed in delivery of the project, secondly if you are to add your ideas or solutions, as is, then all your tabs and pages would enjoy the same look and feel.

However, there are some major issues that Salesforce.com still needs to address before this platform is fully ready for large scale projects.

Here are some of those, I came across:

1) Stability 
Since the technology is fairly new and Salesforce.com is rapidly trying to introduce new ones and get ahead of the competition, it seems that they inevitably had to make some sacrifices. In many cases I encountered bugs that could have been easily caught and removed from the system.
One example of which is the sign up process in force.con customer portals, when a new user registers, two records will be created, a Contact record and Portal User record. The Contact record should be linked to an Account record as well.
The problem is that if you created an account at the sign-up time and then tried to link the new Contact to that Account, because the Guest user in Salesforce.com does not have a role and therefore can not be the owner of an account in Salesforce then the whole process would fail (so why allowing the guest user to create an Account in the security profile!). Furthermore, if the Account owner is a deactivated-user, the same error would be thrown!

In many other cases I encountered styling issues, where, in ajax calls the portal styles would go nuts or I heard some user complains from that the pages were not behaving/rendering the same way in different browsers.

Funny thing is that even if you turn off standard styling in your visualforce page, force.com still includes them in your pages, causeing conflicts and a lot of headache!


2) Diagnostics
There are no system diagnostics provided to you out-of-the-box. You are on your own to implement one to be able to find out what's going on with the internet users trying to sign up, login and use your customer portal!

The thing that makes the sites de-bug and diagnostic process very hard is that it runs your visualforce pages on a different security model (two different profiles compare to standard profiles in Salesforce) which you can not mimic in your own salesforce.com environment.So you are forced to create sample portal users and browse it like any other user and see/test it for yourself. The downside to this is that the errors are not really shown to you properly when you are logged in as a Guest user or Customer portal User.

In many cases, I saw as a result of an Apex exception the user sees the "under construction" page!!
Even though you have specifically configured your site to show your own custom error page! Also if the user browses a page URL that does not exists.... they do not necessarily always see your 404 error page but rather the one and only "under construction" page again!!

All for you to rely on is the famous Apex error notification email which potentially can indicate something went wrong for a user. The other problem is that only the last person who has deployed the code receives these emails. In larger projects were a group of developers are working on the tasks, this does not seem to be a practical solution to provide diagnostics.


3) Profile Limitations
Salesforce is imposing several security limitations on sites and customer portals that do not exist with API integrations. So folks who are using an external application as their community portal and integrate with Salesforce enjoy more freedom than others who want to utilize force.com sites!

One example of which is that the customer portal profile can not update Contact records. This is all nice if you want to protect your business data and separate them from user's profile data. However, in many business cases your requirement may be to add a few custom fields on the contact record and make them available to the portal (as a self-service data management type of business operations, where possible you'd rather have the customer manage their own data).

I have seen people adding that to the ideas site, but this is a practical need which is not available at the moment in force.com sites.

Monday, February 1, 2010

Utilizing the Power of Batch Apex and Async Operations

If you have tried before to remove custom object records in bulk from your org, you know the hassle you need to go through to export the records into an excel file and then have the DataLoader delete those records.

Not only that, what if you need to perform further data integrity tasks before removing the records?

Mass updating, inserting and other similar scenarios like this can be more conveniently handled with force.com's Batch Apex. With Batch Apex you can now build complex, long-running processes on the platform. This feature is very useful for time to time data cleansing, archiving or data quality improvement operations.

One thing that you need to consider is that you should trigger the batch job using Apex code only and force.com by default does not provide scheduling feature your batchable Apex classes. In order to do that you need to write Apex class that implements a "Schedulable" interface.

The following example shows how you can utilize the model to mass delete records of any object in force.com platform.

In order to develop your Batch Apex, you need to create a new Apex class which extends "Database.Batchable" interface.

This interface demands for three methods to be implemented:
  • start
  • execute
  • finish
"start" method is called at the beginning of a batch Apex job. Use this method to collect the records (of objects) to be passed to the "execute" method for processing. The Apex engine, automatically breaks the massive numbers of records you selected into smaller batches and repeatedly calls the "execute" method until all records are processed.

The "finish" method is called once all the batches are processed. You can use this method to carry out any post-processing operation such as sending out an email confirmation on the status of the batch operation.

Let's take a closer look at each of these methods:


global Database.QueryLocator start(Database.BatchableContext BC) {
//passing the query string to the Database object.
return Database.getQueryLocator(query);
}



Use the Database.getQueryLocator in the "start" method to dynamically load data into the Batch Apex class. Using a Querylocator object, the governor limit for the total number of records retrieved from the database is bypassed.

Alternatively you can use the iterable when you need to create a complex scope for your batch job.

Execute method:

global void execute(Database.BatchableContext BC, List<sObject> scope) {

// in this sample, we simply delete all the records in scope
delete scope;
}



This method provides two parameters Database.BatchableContext and a list of records (referred to as "scope"). BatchableContext is generally used for tracking the progress of the batch job by all Batchable interface methods. We will use this class more in our "finish" method.

Finish method:

global void finish(Database.BatchableContext BC){
// Get the ID of the AsyncApexJob representing this batch job
// from Database.BatchableContext.
// Query the AsyncApexJob object to retrieve the current job's information.

AsyncApexJob a = [Select Id, Status, NumberOfErrors, JobItemsProcessed,
TotalJobItems, CreatedBy.Email
from AsyncApexJob where Id =:BC.getJobId()];
// Send an email to the Apex job's submitter notifying of job completion.

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {a.CreatedBy.Email};
mail.setToAddresses(toAddresses);
mail.setSubject('Apex Sharing Recalculation ' + a.Status);
mail.setPlainTextBody
('The batch Apex job processed ' + a.TotalJobItems +
' batches with '+ a.NumberOfErrors + ' failures.');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}



By using the BatchableContext we are able to retrieve the jobId of our batch job. AsyncApexJob object in force.com allows you to gain access to the status of the async jobs as shown in the above example.

In this method I have utilized the Apex email library to send a notification to the owner of the batch job (whoever triggered the job in the first place).

Now, let's put it all together:


global class MassDeleteRecords implements  Database.Batchable<sObject> {

global final string query;

global MassDeleteRecords (String q)
{
query = q;
}

global Database.QueryLocator start(Database.BatchableContext BC){

return Database.getQueryLocator(query);
}

global void execute(Database.BatchableContext BC, List<sObject> scope){

delete scope;
}


global void finish(Database.BatchableContext BC){
// Get the ID of the AsyncApexJob representing this batch job
// from Database.BatchableContext.
// Query the AsyncApexJob object to retrieve the current job's information.

AsyncApexJob a = [Select Id, Status, NumberOfErrors, JobItemsProcessed,
TotalJobItems, CreatedBy.Email
from AsyncApexJob where Id =:BC.getJobId()];

// Send an email to the Apex job's submitter notifying of job completion.
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {a.CreatedBy.Email};
mail.setToAddresses(toAddresses);
mail.setSubject('Apex Sharing Recalculation ' + a.Status);
mail.setPlainTextBody('The batch Apex job processed ' + a.TotalJobItems +
' batches with '+ a.NumberOfErrors + ' failures.');

Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}

}



Ok, that's as far as we go for the Batchable Apex class in this article.

Now let's write the code in Apex to run the class and test it:

String query = 'SELECT id, name FROM Account WHERE OwnerId = \'00520000000h57J\'';
MassDeleteRecords batchApex = new MassDeleteRecords(query );
ID batchprocessid = Database.executeBatch(batchApex);



The above code removes all accounts that the user id= 00520000000h57J owns them.

As it is apparent, now you can run the batch job from within your Visualforce pages, triggers (with caution), etc.

For governing limit and best practices documentation you can refer to the Apex Developer Guide.

Thursday, January 14, 2010

Utilizing Apex Pattern and Matcher Classes

In many projects I am involved with I need to validate a string of data or transform the string into a new one with a specific format. Processing the text by the means of String primitive type methods or your custom handling could a big undertaking and time consuming task.

Sometimes, one needs to write hundreds of lines of code, to process a string and make sure it’s valid (formatted as expected) or transform it into proper format. Some examples of this are validating a string to see if it’s a correct email address, postal code, phone number or URL. Some other examples are grabbing html tags or striping down the XML or HTML tag to get a clear text, trimming the whitespaces, removing duplicate lines or items and many more.

Apex in Force.com platform has just the right set of classes to help you carry out such operations pretty much the same way Java does it.

“A regular expression is a string that is used to match another string, using a specific syntax. Apex supports the use of the regular expression through its Pattern and Matcher classes.” Quoted right from the holly guide. Any regular expression that is written for Java can be used with Apex as well.

In order to utilize these classes we first need to know what each of them does.

Pattern class is designed to contain the regular expression string and you compile the expression into an object of this class. You only need to use this class once. Using this class you will be able to create a Matcher object by passing your string (on which you want to carry out surgery or validation).


pattern myPattern = pattern.compile('(a(b)?)+');




Matcher in turn allows you to do further actions such as checking to see if the string matched the pattern or allows you to manipulate the original string in various ways and produce a new desired one.



matcher myMatcher = myPattern.matcher('aba');



Let’s explore some samples of using regular expressions in Apex and see how we can benefit from them:

My first example will be how to validate an email address. I personally had some struggles with this since the email addresses can get pretty ugly at times. Imagine this email address:

name.lastname_23@ca.gov.on.com



String InputString = 'email@email.com';
String emailRegex = '([a-zA-Z0-9_\\-\\.]+)@((\\[a-z]{1,3}\\.[a-z]{1,3}\\.[a-z]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})';
Pattern MyPattern = Pattern.compile(emailRegex);

// Then instantiate a new Matcher object "MyMatcher"
Matcher MyMatcher = MyPattern.matcher(InputString);

if (!MyMatcher.matches()) {
// invalid, do something
}



Some more examples on validations:



// to validate a password
String RegualrExpression_Password = '((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})';

//image file extention
String RegualrExpression_ImgFileExt = '([^\s]+(\.(?i)(jpg|png|gif|bmp))$)';

//to validate an IP Address
String RE_IP = '^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$';

//date format (dd/mm/yyyy)
String RE_date = '(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\\d\\d)';

//to match links tag "A" in HTML
String RE_ATags = '(?i)<a([^>]+)>(.+?)</a>';






Another way that you can benefit from the Matcher class to to reformat the string.

Below is an example that shows you, how you can strip the HTML tags from a string and extract the plain text. This is very useful when you want to record email contents into Salesforce or covert the HTML version of an email into it's plain text counterpart.



string html = 'your html code';
//first replace all <BR> tags with \n to support new lines

string result = html.replaceAll('<br/>', '\n');
result = result.replaceAll('<br />', '\n');

//regular expression to match all HTML/XML tags
string HTML_TAG_PATTERN = '<.*?>';

// compile the pattern
pattern myPattern = pattern.compile(HTML_TAG_PATTERN);

// get your matcher instance
matcher myMatcher = myPattern.matcher(result);

//remove the tags
result = myMatcher.replaceAll('');





For complete reference of Java regular expressions please refer to: here