April 13, 2011

Visualizing Record Locking in SOQL i.e. for update !

Today, I came across this interesting tweet.

Luckily Smile I knew the answer, and found the same in apex docs too i.e.

“While an sObject record is locked, no other program or user is allowed to make updates.”

But a “t” germ inside me provoked to test this out and create a POC to see “FOR UPDATE” in action, I want to see what crash/error comes, when one attempts to update a locked record from other user !

So, I tried to create a multi-user scenario updating  same locked records in various ways like

  1. Visualforce page & Apex Controller
  2. Apex Test class to simulate two users

Failed approach - Visualforce page & Apex Controller

I created a visualforce page that does a lot of things after locking some Account records. These lot of things include:  call outs, iterating nearly 1,90,000 times to add extra delay and still stay out of governor attacks. But this can’t succeed as it was really next to impossible to time two users in two browsers hit the page at exact same interval. So dumped this silly experiment, without wasting any more time #@$#$ Winking smile

Apex test class to simulate two users – need your views here !

Create an Apex test class that tries to simulate the same using System.runAs().

My use case(scenario) was to create a test method in this class that :

  • Creates an account from the logged in user.
  • Query that same account back using “for update”.
  • Start new context using “System.runAs()” from some other user i.e. not the logged in user.
  • Try updating the same locked account, using that other simulation user.

Here is the apex test class, if you want to experiment.

public class TestRecordLocking {
  testmethod public static void testLocking() {
    // create a account from logged in user
    Account accoutU1 = new Account(Name = 'Abhinav Gupta');
    insert accoutU1;
    // lock the same account    
    accoutU1 = [Select Id, Name, Website from Account where Id =:accoutU1.id for update];
    
    //Start updating it
    accoutU1.website = 'www.salesforce.com';
    
    // Now lets say, some other user comes and tries to update the same record
    User u2 = [Select Id from user where id !=:UserInfo.getUserId() limit 1];
    System.runAs(u2) {
    // other user queries the same account 
      Account accountU2 = [Select Id, Name, Website from Account where Id =:accoutU1.id ];
    // other user tries to update it
      accountU2.WebSite = 'www.yahoo.com';
      update accountU2;      
    }
    
    // Now update the original account back.
    update accoutU1;    
  }  
}

Expected Result on Executing this Apex Test ?

It should fail, isn’t ? As I am trying to update record locked by some other user. Unfortunately it works like a charm. I believe, System.runAs() is not creating exact multi user context that might be created in real life ?

Please let me know your views and ideas on this ?

April 11, 2011

How to read cookies set by JavaScript in Apex/Visualforce controller !

This might be a requirement, if you are developing some complex navigation, for ex.

  • User lands on a visualforce page  “A”
  • from here user is redirected to some other page “B”
  • Now when user returns to page “A” you want to make sure, some operations are done on page “B”, of course one can create database records for the same. But that might be too much to maintain for some trivial tracking requirements. So Cookies seems to be a good fight in this scenario, if some desired operation is done on page “B” a cookie can be set, that in turn can be read by Apex Controller at page “A”.

Life is good, if you are setting and retrieving cookies both by Apex code. But but but, if you are using some other mechanism to create Cookie i.e.  via Javascript, then we need to do a little “HACK” to make those cookies available to apex.

WTH – What the “HACK” ?

Cookies created and read by Apex code are transparently prefixed by “apex__”, if you are creating a cookie called “ShoppingCart” in apex, it will  be actually a cookie with name “apex__ShoppingCart” created on client’s machine. So, if one is creating cookies from non-apex code, that are finally meant to be read back by apex code, then just prefix the cookie name by “apex__".

So if you want to create cookies by Javascript, then simply prefix them with “apex__”.For ex. I want a cookie named “external_cookie” available to Apex code, this cookie should be created via Javascript as shown below

<apex:page>
  <script>      
      function setCookie(c_name,value,exdays)
        {
        var exdate=new Date();
        exdate.setDate(exdate.getDate() + exdays);
        var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
        document.cookie=c_name + "=" + c_value;
        }
        setCookie('apex__external_cookie', 'some value set from external cookie', 3);
  </script>
</apex:page>  

Now in your apex code you can easily access this cookie by name “external_cookie” in your apex code, code snippet shown below :

System.Cookie cookie = ApexPages.currentPage().getCookies().get('external_cookie');

References & thoughts !

I would like to express thanks to Mr. Andred Mahood, this interesting knol was figured out during twitter discussions with him.

image

Looking forward for your thoughts and views on this !

Passing report filters as query params to Salesforce reports !

Today, I stumbled across an interesting problem, where one needs to

“Run a report by passing parameters through link or button click”

Their is no direct way visible in report configuration screen for the same. So I scanned salesforce developer boards for the same, and found this solution that worked perfectly for me, thanks “mikef” for sharing this solution(can’t find your twitter profile Sad smile). Using the same example in discussion board, we will see next how you create a sample Account report and pass its filters as query params. These steps mentioned by “mikef”  will be followed next in the post:

First step: Create your report. Make sure the report is saved somewhere other then your customer report folder.

Second step: Add all the filters you need, ie if you want to pass in the State value create a fileter ShippingState equals and leave the value box blank.

Third step: Create your button to the report. use the id of the report. ie. /00O800000044Dq6?pv0={!ShippingState}

Now you see the /ID and then after that is a '?' that starts your url query string. The 'pv0' is the first value box in the report filters, so if you want the second you say pv1 and so on.

 

First Step : Create an Account report !

I am assuming we all are too good in this, so skipping details of this Smile I have completed this step and I am on report configuration page for Account sobject.

Second Step : Add filters !

On the Account report configuration view, I added filters on two fields i.e. “Type” and “Industry”, each with value equals blank(“”). Same is shown below :

image

Hit “Save”, followed by “Run report”. Obtain the report ID shown next after you save or run report, easiest way to do that is copy it from your browser’s address bar. In my case its something like “https://ap1.salesforce.com/00O90000001HwVr”, so my report id turns to be “00O90000001HwVr”

Third Step : Create a link/button for executing this report.

Here is the sample visualforce page code that creates the hyperlink and passes two params, each for “Type=Customer-Direct” and “Industry=Energy”. 

<apex:page >
    <a href="/00O90000001HwVr?pv0=Customer%20-%20Direct&pv1=Energy" > Run Accounts Report </a>
</apex:page> 

One can also run this report by directly hitting following URL in browser, I used this practice to test several combinations as its much faster then creating pages and buttons.

https://ap1.salesforce.com/00O90000001HwVr?pv0=Customer%20-%20Direct&pv1=Energy

Either way, i.e. hyperlink or direct url. Results will be as follows

image

You might need to tweak and play with param “pv”, “pv0--------pvN” to get the correct index of filters in query params. One can easily do that by saving the report once and playing with params using the report id url shown above.

Needless to say, you can create hyperlink and buttons in many more smarter and dynamic ways, to pass user-provided inputs to run these reports in visualforce pages etc, (including that piece is out of scope for this post)

Important points to consider !

As said by mikef in the discussion board thread

Some negative issues are if a user changes your report the button might not work correctly, and there is no error checking for null values.

So if we are using this hack, one needs to be careful about changes/addition of new filters to the reports.

If you have other interesting ways to pass dynamic params to report, please share !

Support of CDATA section & Apex DOM Classes !

imageI saw this tweet today about apex-fast-xml-dom library. Unfortunately its true that FastXMLDom as of now can't support CDATA sections. Its because, FastXMLDom is wrapper on top of Spring'10 Apex DOM classes and DOM classes as of now doesn't support CDATA section.

In this post we will see

  • What is FastXMLDOM library for Apex ?
  • How to parse XML with CDATA sections in Apex ?
  • Promote an idea to have CDATA support in Apex DOM classes !

For those how don't know "What is FastXMLDom" ?

First, those who don't know what is FastXMLDom library, its basically wrapper on top of Spring'10 DOM classes. You must be thinking, what is the requirement of wrapping the API ?

I created this FastXMLDOM wrapper because

  • Apex DOM Classes are not following the W3C Dom model and require some learning curve to use. FastXmlDOM is based on W3C DOM API model, so no need to learn new API.
  • We all used awesome XMLDOM.cls library created by RonHess. This library did a great job of saving one from the hassle of using Apex XmlStream classes. But as its all written in Apex, it shares the "Script Statements" governor limits with your code. This doesn't hurts in general but for huge xml responses, it can be a trouble. (View benchmarks !)
  • FastXMLDom uses Spring'10 DOM classes internally, so all heavy duty operations of parsing the XML string and creating DOM out of it, is taken care of by native system library code. So this library tried to give what XmlDom.cls was giving, with lesser script statement consumption.

How can I parse CDATA in Apex ?

As Spring'10 DOM classes are not supporting CDATA element as of now, so both DOM classes  and FastXMLDom can't be used for that purpose.

Though, one can use XmlStream based classes i.e. XmlStreamReader to parse CDATA information from XML. Good news is that you can XMLDOM.cls library from Ron Hess, as this library uses XmlStreamReader internally so one can get CDATA information using it.  

Support Idea - We need CDATA section support Apex DOM Classes !

As XML is vital media of integrating external systems with SFDC, having support for CDATA section would be great. As of now one can't use DOM classes(+FastXMLDOM) to parse CDATA information, as mentioned about one can use XmlStreamReader or XmlDOM class for the same. But XmlStreamReader is hard to use and XmlDOM class can be heavy on script statements for huge XMLs.

So I was planning to add this as an idea on IdeaExchange, but luckily found one posted. I request you all to please vote up and support this idea.

Looking forward for your views and thoughts on this !